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.
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: -
- Application: Defines information for the application which will be using the update system.
- ApplicationVersion: Defines information about the version state and numbering applicable to each application using the update system.
- 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.
- 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]
|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]
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]
|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]
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
- CustomSoftwareUpdater: Sample program which can be updated via UI.
- CustomSoftwareUpdater.Bootstrapper: Program which launches downloaded installer files and waits for main application process exit. Explained below.
- Data: Data layer which contain domain model classes for use within the UI and WCF project.
- Service: WCF Web Service layer which returns application update information based on availability. It accesses the database via LINQ to SQL.
- Business.Client.Services: This is used by the CustomerSoftwareUpdater UI to talk to the WCF services.
STEP 5: RUNNING UP
- I won’t go into novice detail here, so I will start from the point of the solution being open.
- 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.
- 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.
- 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.
- 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.
- 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“.
- Once you have tried an update, you will notice when you rebuild the program you are still on version 220.127.116.11 and this as explained is due to a sample installer in use. You may at this point label off the sample app as Version 18.104.22.168, 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.
- 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:
- 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.
- 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.
- 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.
- 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.
- You can use something like the IDbTable .TT template mentioned here tweaked to upkeep the WCF*.cs model objects.
- You can easily edit the WCF configuration by right clicking on the App.config file and clicking “Edit WCF Configuration…“
- 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!
- 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.
- 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.
- And finally, you will note not all properties are being used but provide placeholders for extensibility points, such as file size and file name.
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).