Skip to content

Rolling Out a Custom WCF Software Updater for Win Forms / WPF

Alright, a big one today peoples.  I’ve recently had the opportunity of writing a commercial custom self-updating software package after more than merrily ditching ClickOnceAndHope to the kerb due to exhaustive problems and restrictions, and I would like to share with you today a version I have made for you guys componentised and ready to go :) Now for my gripes with ClickOnce, that story is for another day…

So if you would like some insight into how a custom program updater works, and by that we are talking about a feature within your program where you can click “Check for Updates” and it will download and install the latest update on the fly, then this is for you; or if you would just like to compare a custom solution to an OOTB solution check out the sample I have put together below and after all, what’s better than complete control.

Custom Program/Software Updater


STEP 1: CREATE SUPPORTING DATABASE TABLES

To get started we need to create our new database tables which will be used for our application updates, I use four tables for my custom updater, Application, ApplicationVersion, ApplicationUpdate, ApplicationUpdateFile you may wish to cut a layer or two off this, but I believe this is a nice balance of simplicity : power.

As you can see from the diagram above we have depicted four tables for our new database schema: -

  1. Application: Defines information for the application which will be using the update system.
  2. ApplicationVersion: Defines information about the version state and numbering applicable to each application using the update system.
  3. ApplicationUpdate: Defines a set of updates which have been added over time and relates to an application version.  Updates defined in this section is what governs the client via WCF on whether it should be downloaded.  We can denote whether this is a compulsory update and some simple information such as title and description.
  4. ApplicationUpdateFile: Defines the file which is to be downloaded and executed on the client (such as an installer package), the table is designed in a way it could be expanded on to support a multi-file download system.

You will note a few things on our schema, firstly, there are four foreign keys pointing off to a “Status” table from StatusID, this is not necessary for your own project but I like to keep a status row representing logical delete or active for projects I work on.

Secondly, you may or may not want to keep the “Application” table.  I am using this as I consider this custom application updater perhaps might manage multiple projects (applications) so I can define an updater with independent version numbering for App A, App B, App C.. etc.


So that’s the schema, now let’s look at the data we might store and definitions of the columns (only unique columns per table will be explained).

SELECT * FROM [Application]

ID StatusID Name Description UpdatedAt CreatedAt
1 1 Application Name A description for a great application, it’s great because it updates itself! NULL 2009-08-19 15:48:28.680

ID = Unique row identifier
StatusID = Logical status
Name = Name of your application or product. (usage of this is not demonstrated in the sample.)
Description = Description of your application or product. (usage of this is not demonstrated in the sample.)
UpdatedAt/CreatedAt = columns you may wish to up-keep if this data is editable from a front-end system.

SELECT * FROM [ApplicationVersion]

ID StatusID ApplicationID Version Obsolete UpdatedAt CreatedAt
1 1 1 1.0.0.0 True NULL 2009-08-19 15:48:43.867
2 1 1 1.0.0.1 False NULL 2009-08-26 11:07:39.310

ApplicationID = Parent application ID this version belongs to.
Version = A version this application released at some point in time.
Obsolete = Denotes whether this version is now obsolete. (will affect if a user can receive updates for this version)

SELECT * FROM [ApplicationUpdate]

ID StatusID ApplicationVersionID RollbackApplicationUpdateID Title Description Compulsory UpdatedAt CreatedAt
1 1 1 NULL Initial version First release with not much at all. True NULL 2009-08-19 15:49:52.307
1 1 2 NULL Introducing Updater New version of the application with a swanky new updater! True NULL 2009-08-19 15:49:52.307

ApplicationVersionID = Parent application version this update belongs to.
RollbackApplicationUpdateID = Points to a sibling application update ID and will be the update returned to the user if an attempt to download this version occurs. (Useful in cases where a version was released too early or a critical bug has been found since it’s release and a subsequent version update is simply not viable.  This will allow you to get out of trouble by pointing to an existing update effectively “rolling back” and redirecting any users attempting to download this version to an older version.)
Title = Title or name of the application update. (usage of this is not demonstrated in the sample.)
Description = Description or contents of the application update to outline to your users. (usage of this is not demonstrated in the sample.)
Compulsory = Denotes whether this update is a compulsory update, if so the user must update when prompted rather than be given a choice.

SELECT * FROM [ApplicationUpdateFile]

ID StatusID ApplicationUpdateID Filename Location FileSizeInBytes UpdatedAt CreatedAt
1 1 1 MyGreatAppSetup1.0.exe http://greatappsoftware.com/MyGreatAppSetup1.0.exe 14716928 NULL 2009-08-19 15:50:41.900

ApplicationUpdateID = Parent application update which denotes what update these file(s) belong to. (multiple file usage is not demonstrated in the sample.)
Filename = Filename of the update file, typically a setup application filename.
Location = Location of the update file, this must be a valid URL.


STEP 2: DOWNLOAD DB SCHEMA & SAMPLE DATA

With that explained, to make things easy I have wrapped up the schema as and some sample data in an SQL script which you can download below. You can at this point for testing install this to a new database, or directly into an existing database of yours if you are plugging in this updater directly into your app at this point.

(replace “YOUR_DB_NAME” with your database name obviously.)


STEP 3: DOWNLOAD SAMPLE PROJECT

I was going to outline all steps involved to create this custom software updater, but there are just too many intricacies to list.  So I have offered you the next best thing, a sample application which usually gives better insight into what’s happening in a project anyway.

So please feel free to download below and see the ‘running up’ and ‘extensibility tips’ section for further information.


STEP 4: WHAT’S WHAT

  1. CustomSoftwareUpdater: Sample program which can be updated via UI.
  2. CustomSoftwareUpdater.Bootstrapper: Program which launches downloaded installer files and waits for main application process exit. Explained below.
  3. Data: Data layer which contain domain model classes for use within the UI and WCF project.
  4. Service: WCF Web Service layer which returns application update information based on availability.  It accesses the database via LINQ to SQL.
  5. Business.Client.Services: This is used by the CustomerSoftwareUpdater UI to talk to the WCF services.


STEP 5: RUNNING UP

  1. I won’t go into novice detail here, so I will start from the point of the solution being open.
  2. Firstly, Right Click the “Data” project and click “Properties“. Edit the “Settings” tab connection string with a Username and Password which is valid on your SQL [Express] Server instance and the database you have installed the schema to.
  3. Next do the same thing for the “Web.config” file in the “Service” project. This will be required for your WCF Service to communicate via LINQ to SQL to your database.
  4. The WCF Service has been configured to run on http://localhost:60888/ ensure that your WCF is now functioning by Right Clicking on the ApplicationUpdateService.svc in the “Service” project and pressing “View in Browser“. If it has not loaded successfully, or the port is taken check the App.config and Web.config files of the UI and Service projects, or you may publish to IIS and update the config files accordingly if you so wish.
  5. All working? made it this far? good stuff. At this point you can go ahead and run the CustomSoftwareUpdater UI project.  You will be presented with the current and available application version and a “Check For Updates” button.
  6. View your database tables now, particularly the “ApplicationUpdateFile” table.  Out of the box, the updater should now work and you will download a copy of the Irfan View image viewer (one of the best image viewers around!), so go ahead and attempt the update and all should be good.  The main executing program will launch the “Bootstrapper” and the “Boostrapper” will wait for the main executing program to terminate and then launch the installer.  In a real-world scenario, the installer package would be your installer package and would re-run the main executing assembly afterwards to provide the user with a smooth upgrade process, but in our case, the Irfan View installer will be presented and that’s the end of the line.  If for some reason the update fails, check the exceptions or validate you can in fact access the URL and/or database in reference. If you are having trouble with the Bootstrapper, ensure it is outputting it’s executable into the main UI bin\debug\ folder and to debug it you must use “Attach to Process“.
  7. Once you have tried an update, you will notice when you rebuild the program you are still on version 1.0.0.0 and this as explained is due to a sample installer in use.  You may at this point label off the sample app as Version 1.0.0.1, throw it up on a web server and give it a shot with a real installer, and watch it re-launch with an upgraded version it’s now up to you! At this point would be a good time to play with the compulsory and obsolete flags to get familiar with the system.
  8. A bit more information on the Bootstrapper: Once the update has successfully finished downloading, you will note it will be executed via the Boostrapper.  The bootstrapper is simply a program which spawns your installer file, however with the key being it’s job is to do this is only after your main application executable has successfully terminated.  If however you were not to use the Boostrapper for instance, and you had your main program exe run the the installer directly after the update, you could run into trouble where your main application is potentially still running when your updater attempts to update it, this would result in a file lock and in turn, an unsuccessful update.


EXTENSIBILITY & GENERAL TIPS:

  1. This sample uses a LINQ to SQL data context directly from within the service itself for simplicity, I would not recommend leaving it like this for larger projects but rather choose a decent architectural pattern like the IRepository method I blogged about back in July.
  2. I have included a basic error handling strategy in the WCF request/response classes, in your real world application you would probably want to make this more robust and alter it to suit your needs.
  3. The “Check For Updates” procedure could be easily called at the start or end of the application lifecycle or placed into a Timer for periodic update checking, ensuring your users are always informed of new updates regardless if they are checking or not.
  4. You may or may not want to authenticate that a user has access to perform this update, this can be performed within the GetAvailableUpdate() service operation.
  5. You can use something like the IDbTable .TT template mentioned here tweaked to upkeep the WCF*.cs model objects.
  6. You can easily edit the WCF configuration by right clicking on the App.config file and clicking “Edit WCF Configuration…
  7. I’m using the TaskDialog in the win Forms version from the Microsoft Windows API Code Pack for added fun with my Win Vista+/WinXP fallback enhancement, you can get rid of it and display message dialogs however you please!
  8. For a smooth update system, you might want to ensure your application runs with administrative privileges, particularly in Windows Vista or higher, as to avoid unwanted permission dialogs. Particularly in silent updates.
  9. As noted above in ‘running up’, when building your installer package, ensure that it is a different installer to that of your normal program install, as the last operation that should occur in the installer is a re-running of the main executable.  This will provide a much more robust user experience, where your program will be updated and restarted without any user intervention.
  10. And finally, you will note not all properties are being used but provide placeholders for extensibility points, such as file size and file name.


FINAL NOTES:

Feedback is always welcome, I especially welcome any recommendations around the spam of (!appClosing) blocks I had to perform to avoid thread issues on form close mid way through updates. I also noticed, not a biggy, that the form is hanging temporarily on close if you shut down whilst the DownloadFileAsync() is occurring.  If anybody has any workarounds for that I would love to know.

On another note, I’m sorry I did lie, but I simply don’t have the time to put together the WPF sample at present, however following through the Win Forms version you will find the business logic would be very similar, I’ll happily accept community contributions on this to expand the sample.

All in all I hope I could provide you with a library that’s useful in your next project. Until next time.

You may use and customise this software freely, with credit where credits due :) It’s licensed under the Microsoft Public License (MS-PL).

Graham O’Neale.

VN:F [1.9.10_1130]
Rating: 5.0/5 (3 votes cast)
VN:F [1.9.10_1130]
Rating: 0 (from 0 votes)
Rolling Out a Custom WCF Software Updater for Win Forms / WPF, 5.0 out of 5 based on 3 ratings
Bookmark and Share
kick it on DotNetKicks.com
Shout it

NOW, FOR A WORD FROM OUR SPONSORS

7 Comments

  1. Serge

    Neat post. Thanks for sharing!

    VA:F [1.9.10_1130]
    Rating: 5.0/5 (1 vote cast)
    VA:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 22-Dec-09 at 3:37 pm | Permalink
  2. Graham O'Neale

    No problem!

    VN:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VN:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 19-Feb-10 at 3:00 pm | Permalink
  3. Hi, Graham
    Your CustomSoftwareUpdater is excellent and tells me I’ll never be a programmer…

    I’ve just finished upgrading it to VS 2010 and converting it to VB (I’m an old dog, C# wasn’t even dreamed of when I started programming…).

    Anyway, the CloneProperties doesn’t seem to translate to VB –
    For Each destinationProperty As var In destination.[GetType]().GetProperties()

    breaks on ‘var’. What type is is supposed to be? If I get this fixed, the project runs.

    Thanks,
    Steve J
    Portland, OR, US

    VA:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 03-Jul-11 at 12:25 pm | Permalink
  4. OK, got it working. I posted the code on the Cloning Object Properties post, but here it is again:

    Public Module ReflectionExtensions
    ”’
    ”’ Clone properties from an original object to a destination object.
    ”’ See for more info: http://goneale.com/2009/02/16/cloning-object-properties-via-reflection/
    ”’ NOTE: Does not currently handle list’s.
    ”’
    ”’
    ”’
    ”’
    ”’
    _
    Public Sub CloneProperties(Of T1, T2)(ByVal origin As T1, ByVal destination As T2)
    ‘ Instantiate if necessary
    If destination Is Nothing Then
    Throw New ArgumentNullException(“destination”, “Destination object must first be instantiated.”)
    End If
    ‘ Loop through each property in the destination
    For Each destinationProperty As Reflection.PropertyInfo In destination.[GetType]().GetProperties()
    ‘ find and set val if we can find a matching property name and matching type in the origin with the origin’s value
    If origin IsNot Nothing AndAlso destinationProperty.CanWrite Then
    For Each originProperty As Reflection.PropertyInfo In origin.[GetType]().GetProperties()
    If destinationProperty.CanWrite AndAlso originProperty.CanRead AndAlso (originProperty.Name = destinationProperty.Name AndAlso originProperty.PropertyType Is destinationProperty.PropertyType) Then
    destinationProperty.SetValue(destination, originProperty.GetValue(origin, Nothing), Nothing)
    End If
    Next
    End If
    Next

    End Sub
    End Module

    The C# to VB converters are pretty good but not perfect…

    VA:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 03-Jul-11 at 3:33 pm | Permalink
  5. :cry: OK, I have the updater working as a stand-alone project but if I try to incorporate it into an existing solution, I end up in namespace hell. I’ve tried everyting I can think of, to no avail. Do you have some notes on how to incorporate the updater into an existing application (ie, check the version of the application, not the updater)?
    Thanks,
    Steve

    VA:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 05-Jul-11 at 2:05 pm | Permalink
  6. Graham O'Neale

    Hi Steve,

    Sorry for the delays in posting back to you, if you haven’t figured it out yet, ‘var’ denotes the type will be inferred by Visual Studio and resolve based on whatever is assigned to it (implicitly typed, I think!) so to convert that piece of code you wanted you would most likely be looking at:

    For Each destinationProperty As PropertyInfo In destination.GetType().GetProperties()

    I think. You don’t need the brackets, and I’m not sure how you do var in VB.NET, so just cast it as PropertyInfo, what it should be (we know this as we look at the returning type of GetProperties())

    For a helpful guide on translation between c# and VB.NET see here http://www.harding.edu/fmccown/vbnet_csharp_comparison.html

    As for your updater not working, hmmm :( sad to hear you are having problems incorporating it! Are you receiving errors? What namespace problems are you having? Do you use ReSharper (http://www.jetbrains.com/resharper) that might give you more insight into problematic or mismatching namespaces.

    You should just be able to test the Version of the current running assembly. Sample updater app, or your own app. Good luck!

    Graham

    VN:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VN:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 06-Jul-11 at 9:14 am | Permalink
  7. Hi, Graham
    Thanks for getting back to me on this. What happens when I include the 5 projects into an existing solution is the My namespace is ambiguous. This particular project is an Outlook addin, also, so the versioning information has to come from the My namespace, otherwise it shows the version of Outlook.

    I’ve tried changing the root namespace of the updater projects but this does not resolve the problem.

    Also, changing the framework to 4.0 causes the service to fail – is this a schema issue?

    VA:F [1.9.10_1130]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.10_1130]
    Rating: 0 (from 0 votes)
    Posted on 06-Jul-11 at 9:25 am | Permalink

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*

My name is Graham O'Neale and I'm a software architect from Gold Coast, Australia. I am an overtime thinker, full time coder and awake part time in the real world. I have a keen interest in software development, particularly in the realm of programming (C#, ASP.NET, ASP.NET MVC, LINQ (2 SQL), Entity Framework, Silverlight, Blend, WCF, WPF) and a keen interest in the cutting edge and innovation. I have a new found love for design patterns, ALT.NET practices and well crafted software architecture. The purpose of this blog is to express any thoughts, findings, tips and gripes along my travels in the wonderful world of coding and technology...