Monday, May 9, 2011

Gotcha - XBAP deployment updates for the client

We had another "gotcha" scenario happen recently that was hard to troubleshoot via the blogosphere so that is always my cue to add a post-mortem here.

Background - we have a Silverlight 4 application hosted in ASP.NET. We recently added functionality that required a bit more than Silverlight could give us so we created a WPF XBAP application that would be called by an ASP.NET page in the application in response to a Silverlight command to open that ASP.NET page.

The issue we were having was regarding keeping the XBAP in sync with the latest version. We were building and deploying new versions of the Silverlight, ASP.NET and XBAP but the XBAP was not being updated on the client machine - the first version installed would stay there despite new deployments to the server.

For most of our application, we are using auto-incrementing version #s as set in the AssemblyInfo.cs file in the project subdirectory. Doing this for the XBAP project results in having an autoincrementing version # when you right click on the XBAP's exe installer file and show "properties" - but this is not the version # that is used to determine whether your xbap is out of date.

We had the XBAP project in the same solution as the web + Silverlight projects, that in itself isn't a problem. However our normal process of "rebuild all, publish website" was insufficient to handle XBAP versioning. The following steps had to be taken to ensure a proper update.

1) In the XBAP project properties, check the "autoincrement version" option (and optionally set it to a base #, ex. 4.3.0.0, to start from).

2) Actively PUBLISH the XBAP, not just build it. Note that the publish wizard will want to know where the user is going to be getting the file from (i.e. a URL). I am honestly not sure why it is asking for this, because it doesn't seem to matter what you put in there. I put in a nonsense URL, since we were going to be deploying this to first a QA server, then a staging server, and then eventually production -- and I sure as heck didn't want to have to republish each time! Luckily, the only consequences I could see to putting nonsense in this field are a warning message during compilation -- it did not affect the actual build or publication adversely.

Note - Steps 1 & 2 will result in a new subdirectory for the explicit version # being created that hosts the installer files for the xbap. For example, if you specified to publish to c:\\someproject, it would put the XBAP in c:\\someproject, and then create the following folder:
c:\\someproject\\application files\\MyProjectName_MyVersionNumber
And put the installer exe, manifest etc. here.

As a result, unless you do something drastic, you're going to get a new folder every time you publish. Lucky you!

3) Publish the website

If you follow these steps you will notice that publishing the website copies ALL contents under c:\\someproject.

So, if you don't do some cleanup, you will have more and more folders being copied during each website publish. That's annoying and wasteful. So, I wanted an automatic way to remove these folders when I don't need them anymore.

I did a nice google search for "windows script delete folders" and found a nice little vbscript that removes all the folders in a subdirectory. I then added a pre-compile event* to remove all the folders in the c:\\someproject\\application files directory.

Interestingly enough this worked great in XP but when I deployed this to our Windows 2003 server, it started erroring out during the publish of the XBAP. I noticed that the script was removing the Application Files subfolder itself. Since technically the script was running twice - once during the 'rebuild all' and once during the 'publish xbap' step -- it was choking trying to delete a non-existant folder the second time. Again, only on the server - I didn't get any errors on my dev machine for non-existant folders.

So, I added another pre-compile event to re-add the Application Files subdirectory after deleting the folders inside it. It would have been more elegant to modify the VBScript, but that would have taken more time that just wasn't necessary.

Now, when I publish, only the latest xbap directory is there so only this one gets copied.

4) One more cleanup task needed (this technically goes earlier in the list, but I want to keep all my thoughts coherent). When you install an xbap, it puts it in your Application Cache. When you install a new version of an xbap, it makes a new folder for that new version but does not clean up the old versions. BAD XBAP! On your development machine is where this is going to be the biggest issue, because you're having to publish it constantly when troubleshooting, although it is also an issue on your end user's machines (though hopefully they only will get a handful of versions instead of hundreds).

On your machine, it is simple to clean these out again - again, with a pre-compile event. You need to use the mage command to do this, which is part of the .NET framework. I wish I knew of a way of running this command on an end user's machine without them having to do it themselves, so I could clean up old xbap versions from their cache. If anyone has a way of doing this, I would love to hear it because I'm not fond of leaving detritus on my users' machines when I can avoid it.

The precompile command to use to remove xbaps from your application cache is:
"C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\Mage.exe" -cc

*And for newer programmers who don't know what I'm talking about when I talk about precompile events, in VS 2010 you can go to your project file in the Solution Explorer, right click on the name and select "Properties", go to the Build Events tab, and add command line text to the Pre-build command event line box.

Hope this saves someone some time down the road! Please let me know if I do, it gives me the warm fuzzies!

2 comments:

Chad L. said...

So happy to see you blogging again Anye.

Chad

Anye said...

Thanks! I wasn't an active decision NOT to blog, just more a matter of not thinking about it and not having situations jump out at me that said "blog me!"

I hope all is well with you - shoot me an email and we can catch up :)