Tuesday, June 3, 2014

ClickOnce inside an MSI

So Recently I needed to create clickonce application inside an MSI and after many attempts, here is the resulting custom action which will publish the application to the location set in the properties.  To use the code you need the following inside your msi..




inside your wxs file you'll also need to create the asset folder which will be removed during install




drop your files into the ClickOnceDeploy Folder above wihtout the .deploy extension



finally you'll need to schedule your custom action...



Some example values for the properties...

FilePath = [WebApiFolder]ClickOnceDeploy
ManifestPath=[WebApiFolder]ClickOnceDeploy\
AppPath=[WebApiFolder]my.Calculator.application
ClickOnceAssetFolder=[ClickOnceAssetFolder]
IconFilePath=calculator.ico
VersionNum=1.2.3
AppDisplayName=My Calculator



  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
using System;
using System.Diagnostics;
using System.IO;
using System.Windows;
using Microsoft.Deployment.WindowsInstaller;

namespace Gazprom.MT.Ops.Deployment
{
    public class ClickOnceActions
    {
        private const string DeployExt = ".deploy";
        private const string ManifestExt = ".manifest";

        [CustomAction]
        public static ActionResult UpdateResignDeploy(Session session)
        {
            session.Log("Begin UpdateRe-SignDeploy");

            var appPath = new FileInfo(session.CustomActionData["AppPath"]);
            var manifestPath = new FileInfo(session.CustomActionData["ManifestPath"]);

            try
            {
                foreach (var data in session.CustomActionData)
                {
                    session.Log("Key: " + data.Key + " Value: " + data.Value);
                }

                session.CustomActionData["AppFileName"] = appPath.Name;

                session.CustomActionData["InstallationUrl"] = session.CustomActionData["Web_Api_Uri"] + session.CustomActionData["AppFileName"];
                session.CustomActionData["ManifestDirectory"] = manifestPath.DirectoryName;
                
                LogPropertyValue(session, "FilePath");
                LogPropertyValue(session, "ManifestPath");
                LogPropertyValue(session, "AppPath");
                LogPropertyValue(session, "AppDisplayName");
                LogPropertyValue(session, "VersionNum");
                LogPropertyValue(session, "AppFileName");
                LogPropertyValue(session, "ManifestDirectory");
                LogPropertyValue(session, "InstallationUrl");
                LogPropertyValue(session, "IconFilePath");
                
                // Manifest
                CreateAppManifest(session);

                AddIconFileToManifestFile(session);

                ApplyDeployExt(session);

                SignAppManifest(session);

                //Application
                CreateApplicationFile(session);

                AddMapFileExtToApplicationFile(session);
               
                SignApplicationFile(session);

                session.Log("New version:  " + session.CustomActionData["VersionNum"]);
                session.Log(".manifest file is available at: " + session.CustomActionData["ManifestPath"]);
                session.Log(".application file is available at:  " + session.CustomActionData["AppPath"]);
            }
            catch (Exception ex)
            {
                const string failedToPublishClickonceApplication = "Failed to Publish ClickOnce Application: ";

                session.Log(failedToPublishClickonceApplication + ex.Message + ex.StackTrace);
                if (appPath.Exists)
                    File.Delete(appPath.FullName);

                if (manifestPath.DirectoryName != null && manifestPath.Exists) 
                    Directory.Delete(manifestPath.DirectoryName, true);

                MessageBox.Show(failedToPublishClickonceApplication + ex.Message, "Publish Application",
                    MessageBoxButton.OK, MessageBoxImage.Error);

                return ActionResult.Failure;
            }

            return ActionResult.Success;
        }

        private static void LogPropertyValue(Session session, string property)
        {
            var param = session.CustomActionData[property];
            session.Log(string.Format("located variable {0} with value {1}", property, param));
        }

        private static void CreateAppManifest(Session session)
        {
            session.Log("Creating Application Manifest with AppName and adding any other files that aren't part of the publish process");

            string args = "-New Application -ToFile \"" + session.CustomActionData["ManifestPath"]
                          + "\" -FromDirectory \"" + session.CustomActionData["FilePath"]
                          + "\" -Name \"" + session.CustomActionData["AppDisplayName"]
                          + "\" -Version " + session.CustomActionData["VersionNum"]
                          + " -Processor x86";

            var output = CallMage(session, args);

            session.Log("Completed Creating Manifest " + output);
        }

        private static void SignAppManifest(Session session)
        {
            session.Log("Signing Manifest");

            string assetFolder = session.CustomActionData["ClickOnceAssetFolder"];

            string args = "-Sign \"" + session.CustomActionData["ManifestPath"] +
                          "\" -CertFile \"" + assetFolder + "CodeCert.pfx\" -Password 1234567890";

            var output = CallMage(session, args);

            session.Log("Completed Signing Manifest " + output);
        }

        private static void SignApplicationFile(Session session)
        {
            session.Log("Signing Application File");

            string assetFolder = session.CustomActionData["ClickOnceAssetFolder"];
            
            string args = "-Sign \"" + session.CustomActionData["AppPath"] +
                          "\" -CertFile \"" + assetFolder + "CodeCert.pfx\" -Password 1234567890";

            var output = CallMage(session, args);

            session.Log("Completed Signing Application File " + output);
        }

        private static string CallMage(Session session, string args)
        {
            string magePath = session.CustomActionData["ClickOnceAssetFolder"];

            session.Log("args:" + args);
            var p = new Process
            {
                StartInfo =
                {
                    UseShellExecute = false,
                    FileName = magePath + "mage.exe",
                    CreateNoWindow = true,
                    Arguments = args,
                    RedirectStandardOutput = true
                }
            };

            p.Start();

            string output = p.StandardOutput.ReadToEnd();
            p.WaitForExit();

            if (p.ExitCode != 0)
            {
                string error = "Failed to call mage with Args :" + args + " Exit Code: " + p.ExitCode + " Message: " + output;
                
                throw new ApplicationException(error);
            }

            return output;
        }


        private static void CreateApplicationFile(Session session)
        {
            session.Log("Creating Application File (Deployment Manifest)");

            string args = "-New Deployment -ToFile \"" + session.CustomActionData["AppPath"]
                          + "\" -Name \"" + session.CustomActionData["AppDisplayName"]
                          + "\" -Version " + session.CustomActionData["VersionNum"]
                          + " -AppManifest \"" + session.CustomActionData["ManifestPath"]
                          + "\" -Install true -ProviderURL " + session.CustomActionData["InstallationUrl"] +
                          " -Processor x86";

            var output = CallMage(session, args);

            session.Log("Completed Creating Application File " + output);
        }

        private static void AddIconFileToManifestFile(Session session)
        {
            session.Log("Adding 'asmv2:iconFile' attribute to .manifest file, so the application has the correct icon");

            var fileContents = File.ReadAllText(session.CustomActionData["ManifestPath"]);

            fileContents = fileContents.Replace("<application />", 
                string.Format("<description asmv2:iconFile=\"{0}\" xmlns=\"urn:schemas-microsoft-com:asm.v1\" /> \r\n <application />", session.CustomActionData["IconFilePath"]));

            File.WriteAllText(session.CustomActionData["ManifestPath"], fileContents);
        }

        private static void AddMapFileExtToApplicationFile(Session session)
        {
            session.Log("Adding 'mapFileExtensions' attribute to .application file, so install looks for .deploy extension");

            var fileContents = File.ReadAllText(session.CustomActionData["AppPath"]);

            fileContents = fileContents.Replace("<deployment install=\"true\">",
                "<deployment install=\"true\" mapFileExtensions=\"true\">");

            File.WriteAllText(session.CustomActionData["AppPath"], fileContents);
        }

        private static void ApplyDeployExt(Session session)
        {
            session.Log("Applying .deploy file extension");

            var d = new DirectoryInfo(session.CustomActionData["FilePath"]);
            var infos = d.GetFiles();

            foreach (FileInfo f in infos)
            {
                if (!f.FullName.Contains(ManifestExt))
                    File.Move(f.FullName, f.FullName + DeployExt);
            }

            session.Log("Completed applying .deploy file extension");
        }


    }
}

Friday, December 13, 2013

I Have a disorder

They say the first step to finding a solution to a problem is to admit you have a problem, I have a problem that I can't solve easily and must commit to pay servitude to everyday, its called Trichotillomania and it means I have an addiction to pulling out my own hair all the time.  It can be any hair and it can be from anywhere on my body, the past two days (I know that doesn't seem like much but in my mind its a breakthrough) have been the longest time in many years that I have been able to be free of this affliction.  Today is a good day, tomorrow could be awful but I try to live every day as it comes and hope that I have the power to control my will.  For some it may be drugs, for others alcohol but for me this is my poison, I'm not proud of it but I need to admit to the world the state of my mind, and hope the world accepts it.  

This time of year is the worst, Christmas is when I like to be with family and I am happily engaged to the man of life, but at the same time I am half a world away from the family that define who I am as a person.  I love each and everyone of them and being so far away and so rarely seeing them all breaks my heart many times within a year, it makes me sad to think that I missed the last days of my sisters life on earth because I live so far away.   I desperately try not to think about it but this fact will always be my pain that I cannot show or share because I have no-one to share it with, my partner never knew her and no-body here half a world away can appreciate the connection I shared with her.  I love my new home and I love my new family to be, but his alone cannot replace the family I already have, and I would love for this to be one and the same, but Australia and the United Kingdom are many hours away from popping over.  

After the death of sister I constantly worry about the phone call that will inform me of the next passing of time, who will it be? When will it be? Is this the call I shouldn't answer? I hope that as time passes I will have the opportunity to spend more time with those that I call home.  I used to think that a place defined your home but as I move forward in life I realize that its not a location that defines your home but the people, and that is the reason why my heart is broken, it is split between my wonderful partner and this open, loving country and my original family back home in Australia.  I love you back there and hope I can spend a lot more time with you, but life has a tendency to march ever forward without my input, the drums of time continue ever onward. 

I have disorder and broken heart but I am the luckiest man on earth, two families and single heart, a will that is forced and a forceful will. I surrender to it...

Saturday, September 3, 2011

Anthea’s Eulogy

Words simply cannot describe the warmth and care that Anthea showed the people in her world.  To some she was the caring nurse who sought to make their suffering less.  To others, the wild child who danced the night away, such was her energy.   But for all she wove a golden ribbon of happiness and joy into the moments she shared in our lives.  To me, she was my confidant.  The many discussions we had into the wee hours of the night, the gossip shared but never spoken, the hard decisions over boyfriends that couldn’t be condensed into under ten pages of email... my little bubs, I always thought that I was helping you, it seems in the end you were my rock, my lighthouse, my comfort from the storm.  

I so looked forward to showing you my world dear bubs, we’d grown apart these years gone past but it was to time to reconnect, you and I. First London town, new friends, new times and then on to the festival in Munich city to share steins of beer and laugh aloud like times gone past.  Twas not to be this new chapter in our history, for you departed early, with a gracious bow, but without goodbye. So for all the places you left unseen, and the sights and people in-between, Sayonara, Farewell, Shalom, Slan, Kwaheri, teanastellen, elalleqa, Auf Wiedersehen, Ciao, Au Revoir & Adios.  So my dear, for the people that you will never know, the moments you will never enjoy, the laughter we’ll never share, I will carry you with me forever.... and ever.


Friday, September 2, 2011

2011 : A year to forget or celebrate

  Wow what a year this has been so far, to start at the beginning I would need to even remember what I wanted to achieve with this year and I struggle to do that as so much has happened, originally I thought that 2010 had been a particularly hard year in terms of upheavals and changes in my life but 2011 makes it look tame in retrospect.

  I started out planning on making 2011 a simple year (maybe that's my problem there) with simple goals to get fit and save some cash to buy a house in a few years time. Myself and a couple of mates planned on having a little road trip to wales in the hope of creating a bit of a tradition having been to Scotland in 2010 and repeating said adventures. Everything seemed to be going swimmingly in the first ten weeks of the year I'd stuck to my exercise and diet routine consistently for the entire time, and the road trip had been booked along with another holiday to see a friend of mine in Japan whom I hadn't seen for quite a while.

Its here in mid March where things start to go awry, I may have to take a step back here and give people some back story on the weekend in question.  If you've been following my blog for a reasonable amount of time then you would know that several years ago I packed my life up and spent six months traveling through eastern Africa having a ball of a time drinking and partying with some of the coolest people I know, on the aforementioned weekend in March some of the guys from this trip were meeting up at my flat in central London for a reunion of sorts to catch up with two of the guys we hadn't seen in a few years, Colin and Sam.  It was on this bright and sunny day in mid March that I got a phone call from my father to inform me in what has to be the briefest conversation I've ever had with him.
Dad: 'Ben I need to tell you something'
Me: 'Yes'
Dad: 'Anthea's dead!'
After much sobbing and crying into the telephone I asked one final question
Me: 'Was it the (motor)bike?'
Dad: 'Yep, Ben I've got to go, I'll call you later'
while I sat crying sitting on the edge of my sofa, two of the greatest of people comforted me and I'm not sure they realize how much I appreciated them being there at the time, anyone whose ever lost someone close will tell you that being alone when finding out this information would have been an even greater tragedy.  So thank-you to Naomi and Geppetto, in the chaos that followed I'm not sure I ever really thanked you.  The next three weeks consisted of much drinking and reminiscing which I won't go into detail here because I'm starting to tear up again but suffice it to say we made Anthea proud.

Should've posted this in April...

The people we casually say goodbye to everyday, the small moments of reminiscing that normally just make us nostalgic for times gone past. Today I had one of these moments with an old friend of mine, it was in regard to a trip to Scotland we had last year, and how a spontaneous decision at the last minute ended up putting us in front of the train bridge that is used in harry potter. It reminded me of how Anthea had been the one to make me start reading the harry potter series and how little that moment in Scotland would have meant without her. Its funny the connections to people we have and never shed light on until they are gone.

The next thing that's happened this year is that I've met a guy who makes me happier than anyone I've ever met, but to be honest we actually met in 2010 on a snowy night in London when we were both very sozzled, and the only reason I remember is because I commented on the fact he was wearing wellies, but before I could get his details I was whisked away by a friend of mine who wanted to leave.  It was by shear luck that we met up again a few weeks later in our local pub on new years eve, the '2 Brewers' that we were able to get each others details (or the fact that we are both regulars there).  This time we lost each other early in the night and didn't see each other again until after I got back from all my reminiscing in early March that we finally decided to meet up as friends because at the time he was seeing someone.  If I was to say one thing about this year that has been the absolute highlight, he is it, a friendly, caring, sensitive guy that knows when to mince it up and more importantly when to tone it down as well, oh and did I mention he's also drop dead gorgeous ;).

In early July I found out that my only link to family over here in England is going to be heading back home to Australia in September, its been a grand five years so far living in England and having James and Tania in Brighton for the occasional weekend away, the time to share in the growth of their new family has been a rock for me.  To admit to myself that its going to be hard to lose this connection to family has been the one things I've been denying myself, because the thought of ever going back to Perth and spending the rest of my life there fills me with dread.  Being a gay man in London is fine but being a gay man in city of chav's (Perth, Australia) is a very different beast and one I don't particularly relish.  The one thing I have gotten out of all this so far is the knowledge that I one day want to have my own family, I see only two choices in life, to look into the future and create something grand to leave for those who come behind us, or live in the past reminiscing over things that will never happen again. 

It is with this in mind that Devin and I have made the decision to combine our lives together and move into our own flat, oh and did I mention that I recently resigned from my job and took a position working for BP in London.  I know that 2011 still has four months to go but I've been trying to reconcile the past six months for some time and with the pressure I keep getting from family in Australia to move back I suppose I'm just trying to clear some of the air around what has been a fairly roller coaster year.  The one thing I continue to realize is that life isn't ever clear cut, an answer today my be a problem tomorrow, for the people who continue to ask, I only have answers for the short term and they're all here.. Bx



 

Thursday, July 15, 2010

So what's next

Recently a friend and I have been sketching out the concept of building a dating website unlike anything you've ever seen. Bridging two very different ideas into a single entity, a prototype communication model currently under development over at google and the GPL model of freedom. Yes websites need money to stay afloat but why is it that some of the worst websites charge some of the most horrendous prices? Dating is about selling services, it shouldn't cost any money just to walk into a pub and start talking to someone, how exactly is this dating? What if we were to build a dating website which was free to walk into and didn't have all the restrictions of the existing models? What if instant messeger, email, games, fun and entertainment were all built directly into the fabric of the website itself? Communication is no longer a traffic light system, its the enabler of the perfect pick up line, how bout a game of something right inside the... lets call it a wave :) These waves will be central to the entire website people will be able to communicate in real time, schedule events and actually make mistakes like you would as if the person was right in front of you, no more construction of false selfs in static emails. Just you, me, us all as we should be... And fundamentally it will be free to use this communications system, no charge to the users and the only costs will be the addon services like the dating guru's automatic date construction or the automatic matching service. The are other ideas as well, to pull parts of some really awesome dating sites into this communication system, HTML5 has some location based API's that we'd love to use to help people connect based on where they are. Imagine being able to meet someone down the road, play a game of pacman or something random with them and ask down to your local pub just because you opened the iPhone app and used the fabric of the dating website to help you connect with your someone local? I'm excited, are you?...

Wednesday, May 12, 2010

Fix for RapidSVN Merge and Diff

For anyone out there that is looking for how to make my favorite comparison tool - Beyond Compare 3.0 work with RapidSVN (0.9.8) try...

Diff
Program: C:\Program Files\Beyond Compare 3\BComp.exe
Args: %1 %2 /title1=%1 /title2=%2

Merge (for doing three way merges)
Program: C:\Program Files\Beyond Compare 3\BComp.exe
Args: %1 %3 %2 %4 /title1=%1 /title2=%3 /title3=%2 /title4=%4

I know the args list looks a little wrong but this version will prioritise your local copy of the file over the head of the branch (cause you don't really want to loose your changes)

Hope this helps...

Friday, April 9, 2010

One week and counting

Well I've got a week to go before it's all over here at Dealogic, then I start a new job hopefully with a bit more excitement and lots more to do. So I need to figure out how to kill an afternoon doing nothing but web browsing, maybe a little bit of work on my game.. Project Dax. Then I can hand it over and get the developers to really start cranking the work out. Really looking forward to this weekend, lazy drinks in regents park on sat, and setting up my new speaker system on Sunday. :-) can't wait. Signing off. B


-- Post From My iPhone

Location:A4,London,United Kingdom