The philosophy behind NAppUpdate
About 2 years ago I was building a .NET desktop application, and needed an easy way to allow it to auto-update itself. I looked for libraries that do that, and all I could see was either complicated, commercial, or geared towards a very particular (usually common) use case. I didn't want anything of the sort, so this is how NAppUpdate was born. NAppUpdate is a very lightweight library, taking no dependencies and doesn't use anything fancy (it actually runs on .NET 2.0). It was designed to be able to perform any update process you can think of, and do all the heavy lifting for you. Using very few simple API calls you can get your application to self-update from the web, local network, BitTorrent or whatever, and along the way perform DB schema updates, registry changes, additional installations and what not. Some functionality is supported out-of-the-box, and whatever is not - can be very easily added. With very few lines of code you can make it behave any way you want. NAppUpdate is being used quite widely, but has nearly no documentation. I believe simple software doesn't really need docs, although some sort of explanation on how it works and what it is capable of doing is still important to have. This is what this post is about, and while at it I will discuss some of the key principle behind the design of the library. For future readers, please note all code samples work with version 0.2.Getting started
To get started, you will need to reference the NAppUpdate DLL (only one DLL) from your project. Grab the latest release binaries or compile from source. NAppUpdate is implemented as a singleton, and the public facing API is called UpdateManager. Once you got it referenced, all the operations will be available to you using UpdateManager.Instance. You don't need to initialize anything, it will just work. NAppUpdate only needs to know where to get the data from, but that's about the only thing you will need to do to get started. This is done by simply providing NAppUpdate with an IUpdateSource implementation. Bundled with NAppUpdate currently are basic implementations for getting data from the web using SimpleWebSource (HTTP, FTP, Proxies and all that stuff), from a UNC source, and an in-memory source. Common usage would look something like this - and you want to put it when your app starts: [code lang="csharp"] UpdateManager.Instance.UpdateSource = new NAppUpdate.Framework.Sources.SimpleWebSource("http://mydomain.com/feed.xml"); // provided is the URL for the updates feed UpdateManager.Instance.ReinstateIfRestarted(); // required to be able to restore state after app restart [/code]Checking for updates
It's as simple as it gets: [code lang="csharp"] if (UpdateManager.Instance.CheckForUpdates()) { DialogResult dr = MessageBox.Show( string.Format("Updates are available to your software ({0} total). Do you want to download and prepare them now? You can always do this at a later time.", UpdateManager.Instance.UpdatesAvailable), "Software updates available", MessageBoxButtons.YesNo); if (dr == DialogResult.Yes) { UpdateManager.Instance.PrepareUpdatesAsync(OnPrepareUpdatesCompleted); } } else { MessageBox.Show("Your software is up to date"); } [/code] You can do this in a blocking manner as shown above, or async. Since this will usually involve network traffic, it is recommended to have this running on a non-UI thread. If you don't have any spare thread handy, just call CheckForUpdatesAsync, it will do all the heavy lifting for you. The common practice is to call CheckForUpdateAsync with a callback. In the call back you can handle the news of new updates as you see fit. CheckForUpdates will retrieve the updates feed using the IUpdateSource implementation you provided originally (or you can pass it a new one), and will parse it to produce a list of update tasks. The feed is parsed using an IUpdateFeedReader implementation. You can roll your own, or use NauXml.NauXml: Tasks and Conditions
Internally, NAppUpdate executes update tasks (concrete classes implementing IUpdateTask), and allows you to define conditions on them. Task without any conditions, or with trivial ones, will always execute. Conditions are simply concrete classes implementing the interface IUpdateCondition. There is quite a handful of them built-in, like FileVersionCondition, FileChecksumCondition, OSCondition and many more. It is quite trivial to add any other condition as well. To reflect that structure in the best way possible, NAppUpdate defines an XML schema we call NauXml. It is quite trivial to understand what's going on, and to write one yourself: [code lang="xml"] <?xml version="1.0" encoding="utf-8"?> <Feed> <Tasks> <FileUpdateTask hotswap="true" updateTo="http://SomeSite.com/Files/NewVersion.dll" localPath="CurrentVersion.dll"> <Description>Fixes a bug where versions should be odd numbers.</Description> <Conditions> <FileChecksumCondition checksumType="sha256" checksum="6B00EF281C30E6F2004B9C062345DF9ADB3C513710515EDD96F15483CA33D2E0" /> <FileDateCondition type="or" what="is" timestamp="20091010T000000" /> </Conditions> </FileUpdateTask> </Tasks> </Feed> [/code] NAppUpdate has an appropriate feed reader for the NauXml format built-in, obviously, and it is the default FeedReader implementation used. You can use any other format by handing NAU another IUpdateFeedReader implementation.Preparing updates
So, you were notified of new updates, now what? You could either notify the user of them and start preparing them if the user wishes to proceed (like in the example shown above), or prepare them silently and only notify the user when everything is ready to roll. It's completely up to you, just like the way you would be notifying them about the updates. You can track the progress of the update preparation by subscribing to the UpdateManager.Instance.ReportProgress event. It will notify you of the general progress, and which task is currently preparing itself. There is a code sample showing exactly that in the github repository (available also within the download). The preparation process is defined by doing all the lengthy process required for an update, without changing anything in your system. As such, it is completely safe to abort it, and no rolling back is required.Applying updates
Once everything is prepared, all you have to do (probably after getting the user's consent) is call UpdateManager.Instance.ApplyUpdates(bool restartApplication). This will apply the updates. Some update tasks might require a cold-update, meaning they cannot complete while the application is running. This is either by request in the feed, or the task tried updating while the application is working, failed, and fell back to requesting a cold update. If no cold-updates are required, the update process will finish here. If there are any cold updates pending, the application will restart itself and apply them when it is off. You can ask NAppUpdate to bring the app back up after performing the update. You also defer the update process to be performed when the user exists the application. This will ensure the process doesn't get in his/her way, and again, it is a matter of simply calling ApplyUpdates(false) on close. Applying updates has to be called from the main UI thread if it may involve shutting down the application, this is to ensure right order, and that everything shuts down correctly.Rolling back on failure
While preparing updates, or just before applying them, a rollback plan is prepared. In FileUpdateTask for example, the original file if exists is being copied to a backup location. Should anything go wrong, NAppUpdate will call the IUpdateTask Rollback() method of the failed task, and that will restore everything to normal.Mind the language
Take note of the language I've been using while describing the update process. You are not necessarily "downloading" anything, nor "replacing files". You simply "Prepare" and "Apply", potentially in a cold manner.
This is a fundamental concept of NAppUpdate, and what makes it so strong. All common scenarios for performing application updates are already supported by the built-in funcionality; but it is all done using generic concepts, so any update process will fit.