Code972 Coding from the back of a camel

29Jun/122

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:

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

Checking for updates

It's as simple as it gets:

                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");
                }

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:

<?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>

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.

Error handling

NAppUpdate tries hard to stick to the KISS principle, but being able to provide important indications on what's going on is very important. So when designing the API we went with a very simplistic approach, that allows for easy operation and well-defined behavior, while not compromising the ability to understand what's going on.

Each method you call will return true if successful, and false if anything has gone wrong. If you were using the Async version of a method, the callback function will be handed that boolean value. When a NAU method returns false, you should check UpdateManager.Instance.LatestError to see what went wrong. For your convenience, common errors are available in NAppUpdate.Framework.Common.Errors as consts.

NAppUpdate has a well-defined state at any given point, and it is available via UpdateManager.State. Since it doesn't make sense to CheckForUpdates() if you already have pending update tasks, NAU will disallow that. You should check the current state yourself before calling any NAU method if the update process is not managed automatically.

Feed generator

A long requested feature is an automatic feed generator, and there have been a few efforts to come up with a working one. Recently NAppUpdate got a contribution of a very nice feed generator, screenshot of it is shown below. It is a very useful too, but doesn't really scratch the surface of what such a generator can look like, thanks to the flexible structure of NAppUpdate.

In a future post I'll follow up on this and discuss how I envisioned such a generator tool. Since I hate doing UI, I will leave it as a challenge for whoever is interested...

Links

NAppUpdate on github - https://github.com/synhershko/NAppUpdate

Official mailing list - https://groups.google.com/group/nappupdate

More NAppUpdate content - http://www.code972.com/blog/tag/nappupdate/

12Jun/116

Some updates on NAppUpdate

After having several issues with their auto-update mechanism, 2 weeks ago the Hibernating Rhinos profilers were updated to use NAppUpdate. Once again it was proven to be a very flexible and robust library, and several updates were already pushed to hundreds (thousands?) of users without any problem.

Before the profilers could start using NAppUpdate I had to make some updates to the library, namely: catch and expose the last error thrown (if any); fix an issue with UAC popping for updates on Windows 7 and Vista; better support for promptly cancelling a download mid-way; and a few other fixes and updates. These fixes are already available on github, and probably invalidate the 0.1 release...

Implementing NAppUpdate required custom implementation of a FeedReader and a Task, and the whole process didn't take more than one hour to code (testing is another story...). The profiler's AutoUpdateFeedReader makes a simple check against a very simple one-liner feed with the profiler's current version, and the it's AutoUpdateTask downloads the latest build as a zip file from the server, extracts it to a temporary folder and when told to overwrites the old files with the new ones in a bulk.

The actual task looks something like this - note the logical separation into steps, which are executed sequentially:

public bool Prepare(IUpdateSource source)
{
	// Clear temp folder
	if (Directory.Exists(updateDirectory))
	{
		try
		{
			Directory.Delete(updateDirectory, true);
		}
		catch {}
	}

	Directory.CreateDirectory(updateDirectory);

	// Download the zip to a temp file that is deleted automatically when the app exits
	string zipLocation = null;
	try
	{
		if (!source.GetData(LatestVersionDownloadUrl, string.Empty, ref zipLocation))
			return false;
	}
	catch (Exception ex)
	{
		Log.Error("Cannot get update package from source", ex);
		throw new UpdateProcessFailedException("Couldn't get Data from source", ex);
	}

	if (string.IsNullOrEmpty(zipLocation))
		return false;

	// Unzip to temp folder; no need to delete the zip file as this will be done by the OS
	return Extract(zipLocation);
}

public bool Execute()
{
	// since all we do is a cold update, nothing other than backup needs to happen here

	return true;
}

public IEnumerator&lt;KeyValuePair&lt;string, object&gt;&gt; GetColdUpdates()
{
	if (filesList == null)
		yield break;

	foreach (var file in filesList)
	{
		yield return new KeyValuePair&lt;string, object&gt;(file, Path.Combine(updateDirectory, file));
		Log.DebugFormat("Registering file {0} to be updated with {1}", file, Path.Combine(updateDirectory, file));
	}
}

Triggering the actual check for updates is a one-liner (after configuring the UpdateManager instance with a feed URL, a FeedReader and all that; the task is created and returned by the custom FeedReader):

UpdateManager.Instance.updateManager.CheckForUpdateAsync(StartDownloadingUpdate);

// ...

private void StartDownloadingUpdate(int updates)
{
	if (updates == 0) // no updates are available
		return;

	if (updates &lt; 0) // an error has occurred
	{
		Log.ErrorFormat("Error while checking for updates: {0}", UpdateManager.Instance.LatestError);
		return;
	}

	// If updates are found, start downloading them async
	UpdateManager.Instance.PrepareUpdatesAsync(success =&gt;
	{
		if (!success)
		{
			if (UpdateManager.Instance.LatestError != null)
			{
				Log.ErrorFormat("Error downloading updates: {0}", UpdateManager.Instance.LatestError);
			}
			return;
		}

		// Notify the user of the update, and call UpdateManager.Instance.ApplyUpdates() when ready
	});
}

It couldn't be simpler than that, and it just works...

This has triggered some interest in the project, and wheels are now in motion again and hopefully new features will be introduced soon, followed by a 0.2 release.

As always, you can grab the sources and file bugs here. Bugs and feature-requests can also be submitted to the mailing list.

30Sep/108

NAppUpdate first release: 0.1

NAppUpdate (pronounced: nap-date),  a small and flexible open-source library providing consumer applications with auto-update functionality, is now being officially released, and is ready for production.

About a month ago I released a first version of my local copy of the library, and posted this first post with some info and code samples showing how to use it from any .NET application. The response I received was tremendous, compared to the short time frame. I wish there was a way to know how wide its usage is now!

I would like to thank Andrew Rowson and Jamie Lennox for jumping in to make several code tweaks based on their experience with the library - mainly to resolve issues with non-privileged users. NAppUpdate is now confirmed to work very well on Windows 7, Vista and XP. Thanks guys!

The post mentioned above is a bit outdated now. Also, there are quite a few features I'd like to see added to the library, such as status reporting and a more flexible handling of tasks and rollbacks. Andrew, Jamie and I have already discussed several ways of tackling those features, hopefully you'll see them in soon. Right after the API stabilizes, I will work on creating some documentation in the library's git-wiki space on github, so anyone can jump-start easily with using it.

So far it has been a great experience. Hopefully the marking of version 0.1 will encourage more users and collaboration. See you again soon with 0.2!

NAppUpdate, available freely from: http://github.com/synhershko/NAppUpdate (ASL 2.0)

Binaries and a sample app with source: http://github.com/synhershko/NAppUpdate/downloads

(Edit) A user/developer discussion group is now available at: http://groups.google.com/group/nappupdate

16Aug/1026

NAppUpdate – Application auto-update framework for .NET

Any desktop applications programmer finds himself every now and then looking for a library providing an application auto-update functionality, that is flexible and easy-to-integrate. I had the intention of working on such a library for some time now, so it will do things the way I want it to. Last week I got to actually doing it, and I'm quite satisfied with the results.

NAppUpdate can be integrated easily with any .NET application - WinForms or WPF, and it offers great flexibility through interfaces, for feed readers, file sources, and update tasks. It also supports conditional updates, the implementation of which offers unlimited extendibility.

I based my work on Lee Treveil's .NET-Auto-Update, and although I practically rewrote almost everything, he certainly deserves this credit.

The library, released under the Apache 2.0 software license, can be downloaded / cloned from http://github.com/synhershko/NAppUpdate. Please share your thoughts, or any extension you make to it.

Update 30/03/2011: A user/developer discussion group is now available at: http://groups.google.com/group/nappupdate

Update 30/09/2010: although most of the post below is still true, it is already (!) a bit outdated. Please check the NAppUpdate tag in this blog for the latest information on the library.

At the time of this writing, here is what NAppUpdate offers:

16May/100

BusyObject: Easily get .NET WinForms apps look busy

Imagine you are working on a .NET WinForms application, and it has many small user tasks which require you to disable all input controls in your form. What happens if each of those also has quite a lot of possible exit points? returning and re-enabling the GUI in all of them is quite a pain.

The following code allows you to do this all in just 2 extra lines of code: