Code972 Coding from the back of a camel

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:

  1. Seamless integration - one DLL to reference, a few lines of code in the right places and you are all set.
  2. Requires .NET 2.0 and up. No other silly requirements.
  3. Cold updates supported. The updater executable used for replacing files that cannot be hot-swapped is packaged within the library's DLL, and is self-deleting, once done updating.
  4. Shoot and forget, just tell NAppUpdate to prepare and apply your updates once you have notified of their existence, and it will do everything for you, including restarting your application after a cold update.
  5. State-based update process (Did / didn't check for updates; prepared updates; applied updates successfully; rollback required). You can quit any time you wish.
  6. Updates are tasks, so practically every type of update is supported. Right now the only implemented task is FileUpdateTask, used for simple file download and replacement. Implementing more tasks is very easy, and can be done right from your application simply by implementing an interface, without any extra work.
  7. Potentially supported tasks include registry updates, database updates (schema and/or data), Lucene index changes, diff patches, and virtually anything you can imagine.
  8. Backup and rollback are supported in the task level, giving you full protection against failures.
  9. Conditional updates - every task can have unlimited number of conditions attached to it. Conditions are evaluated in a boolean manner, and can be grouped together with AND, OR'd, or being defined as NOT. If the conditions aren't met, the task is being ignored.
  10. Just like with tasks, custom conditions can be created very easily by implementing an interface with your own logic, within your very own application.
  11. Condition types supported at the time of this writing include file properties checks (exists, assembly version, size, date, SHA256 checksum) and OS bitness condition (32/64 bit). More planned conditions include registry checks, more OS checks, prerequisites checks (.NET version and other common applications), etc.
  12. Multiple update feed formats, including Adobe Appcast and our very own NAppUpdate XML (NauXml). You can create your own by simply implementing IUpdateFeedReader.
  13. Abstract download sources allow you to create your own implementation for downloading feeds and update files / packages. Currently we have SimpleWebSource and MemorySource implemented. Your own implementations can include whatever other source you want, for example transfer files over BitTorrent or other P2P protocol.

Here are some code snippets, taken from the WinForms sample application. First is the method checking for updates (UpdateManager.Instance was loaded with some configurations in the Form constructor):

// Get a local pointer to the UpdateManager instance
UpdateManager updManager = UpdateManager.Instance;

// Only check for updates if we haven't done so already
if (updManager.State != UpdateManager.UpdateProcessState.NotChecked)
{
    MessageBox.Show("Update process has already initialized; current state: " + updManager.State.ToString());
    return;
}

// Check for updates - returns true if relevant updates are found (after processing all the tasks and
// conditions)
if (updManager.CheckForUpdates(source))
{
    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.",
        updManager.UpdatesAvailable),
        "Software updates available",
         MessageBoxButtons.YesNo);

    if (dr == DialogResult.Yes)
    {
        updManager.PrepareUpdatesAsync(OnPrepareUpdatesCompleted);
    }
}
else
{
    MessageBox.Show("Your software is up to date");
}

... and after a notification on the updates has been sent, and the updates were prepared for installation, here is how this is done:

UpdateManager updManager = UpdateManager.Instance;

DialogResult dr = MessageBox.Show(
        "Updates are ready to install. Do you wish to install them now?",
        "Software updates ready",
         MessageBoxButtons.YesNo);

if (dr == DialogResult.Yes)
{
    // This is a synchronous method by design, make sure to save all user work before calling
    // it as it might restart your application
    updManager.ApplyUpdates();
}

More to come soon...

Comments (26) Trackbacks (1)
  1. Good stuff. I like this a lot.

    One question I had on the FileVersionCondition – if I wanted to apply an update if a local file version was below the currently available, is the only way of doing that to have an ‘above’ and ‘is’ wrapped in a ‘NOT’ boolean? Would it not be more sensible to implement a ‘below’ condition, to allow out-of-date files to be updated?

    • Sure there is a ‘below’ condition – FileVersionCondition defaults to that behavior since its the one which is mostly used. So, unless you specify ‘is’ or ‘above’ that condition will return true if the local file version is below what you specified in the feed.

      Here’s how I use this in an application of mine:

          <FileUpdateTask localPath="htmlayout.dll">
            <Conditions>
              <OSCondition bit="32" />
              <FileVersionCondition what="below" version="3.3.2.11" />
            </Conditions>
          </FileUpdateTask>
      

      HTH

      • Aha! I’d completely missed that. Thanks :)

        On a separate, unrelated issue, I’ve run into an issue running on Windows 7 with an application that has been installed into c:\program files\. Because the application itself doesn’t have permission to write to its install directory, the updater code cannot create the ‘Backups’ directory, and so it seems to fail silently.

        I guess it needs to know to elevate its permission to create and write to the Backups folder, or use a temporary location for the backup folder.

        • If so, the application will not be able to update itself at all, not just fail to create a backup folder?

          I knew permission issues will rise at some point. I won’t be able to work on resolving these in the near future, mainly because I don’t have W7 handy. It’d be great if you could look into this.

          • I just did some research, and I believe that it’s not possible for an application to elevate its own permissions with a UAC prompt. It can start another process as elevated, but can’t change it’s own elevation.

            A better workaround would be to expose the BackupFolder variable of the UpdateManager as a property to allow people to specify their own BackupFolder. Then, the onus is on the implementor to ensure that the BackupFolder is a location the application can write to. Unless there’s any reason that the Backup folder needs to be in the same directory as the application, this should work.

            If you like, I can do some testing and commit some changes in git and push them back to you?

          • Sure I’d like that; thats the beauty of open-source :)

            The main feature of NAppUpdate is that all the heavy-lifting internally. Whatever is possible for the library to do on its own, should not be left for the user to do. This design decision leads to a clean API and very easy-to-understand states.

            Exposing the backup folder path, and requiring the user to make sure the folder provided is writable by the application, is not something I’d want to have as a requirement, just a possible extension.

            The backup folder is in the same directory as the application itself to allow for easy manual recovery in a case of a serious failure, so if such occurs the user doesn’t have to look for it for too long.

            So here are two possible solutions from the top of my head:

            1. Keep things the way they are, but make the process smarter so it defaults to ($AppFolder)\Backup but knows to find an alternative if creating that folder (or writing into it) fails

            2. Initialize the updater.exe as a process with elevated rights, and make it do the backup when it loads. After creating the backup it will wait for further instructions from the application (install the updates or rollback). Quite a dirty approach IMO.

            Lets continue this discussion via e-mail, its going to be easier that way…

          • Hey,

            I’ve just started playing with the library as well, and so far loving what i see. My only question so far is why make this a singleton? I cannot really see a time when you would need multiple instances and it gives you a global access point to the class, but it will prevent any design time extensions being put in and i just don’t see it as necessary.

            Another issue i’ve had is the code:

            if (data.StartsWith(_byteOrderMarkUtf8))
                data = data.Remove(0, _byteOrderMarkUtf8.Length);
            

            Yes, the data from the website is marked Utf8, but the computer realises this, ignores it, and then removes 1 character from my xml string making it invalid. I haven’t been doing windows programming for long but is this necessary?

            Lastly, if you do continue the conversation can you mailing list it or something, this is going to affect me to and i’d love to see how it progresses.

            Thanks and congratulations, this is a beautiful little library that fills a very important gap in what is currently available.

          • Thanks for all the compliments!

            To answer your questions:

            1. The static/singleton architecture was inherited from the original code I refactored. I actually think it makes a lot of sense the way it is implemented – as you said yourself there is no reason to have multiple instances of the class. This API wraps all that nicely and prevents misuses, so I’d argue it is necessary. What design time extensions were you thinking on?

            2. This piece of code is a late addition, I think you can see that on github too. I added that because the UTF8 BOM wasn’t removed, and XmlDocument.LoadXml was failing with an “incorrect character” error. From your comment I gather you are using Mono, perhaps this is a place where they behave differently? Or you didn’t save your feed with a BOM?

            Also, those lines actually prevent the library from reading certain feeds correctly (ANSI, for example) when using the default Source object. I’d certainly vote for some research there.

            3. We did continue the conversation, and we are looking into a possible way of resolving that nicely. If you want you can drop me an email and I’ll CC you too – check the About page. A mailing list is a bit of an overkill, but if there will be enough interest I’d have to come up with something obviously.

  2. Itamar,

    I’m about to send through an email for the conversation as i’m about to have to do the changes on my own anyway. I know a list is overkill :) , just thought it was a way to make it public if you had a setup already.

    Re:
    1. I was more thinking of allowing design time configuration in VS. Inheriting component etc to add it to a form, adding some events (not a design time req, i know).
    I was just considering how i would go about enhancing the downloader, particularly to do things like fire an event after each file so that i could show a progress bar for downloading. I thought that it would be nice to drag & drop an UploadManager, a downloader etc onto the form and then assign them. I was looking back through the code though and realize there are a number of place where you reference the Instance from the plugins and don’t think the refactoring is worth the gain at the moment.

    2. I’m not using mono but my server is a linux server and i just hadn’t thought about adding the BOM which is not in place by default. It would be interesting to see how this behaved under mono, but not today – i’ll let you know if i get there.

    • #1 is definitely in my TODO list, I just need to find time to do some planning as of to what events to add and how they should interact with the misc plugins. It will probably include OnUpdateProcessStateChanged and OnUpdateProgress. All the mid-cases are the ones I’ll need to think about.

      Drag-n-drop for the UpdateManager isn’t something I’m planning on spending time on. It is already too easy to integrate it with existing code even without that.

      Whats weird about #2 is it shouldn’t omit the first bytes if they aren’t the BOM, hence the check if (data.StartsWith(_byteOrderMarkUtf8)). For some odd reason it doesn’t work right for you, and I wonder why. Anyway, SimpleWebSource needs to support all types of text files in GetUpdatesFeed, I’ll try to look into how to resolve this.

  3. Are there a download of the compiled version? I have see only the sources in the git repository.

    Are there a documentation?

    Must the downloads be signed? Or do you download and install all?

    • Are there a download of the compiled version? Are there a documentation?

      Not yet. First I want to tag a thoroughly tested version as 0.1, and then I will publish binaries. There is an ongoing work on making the library work better on Win7 and Vista.

      Must the downloads be signed? Or do you download and install all?

      I’m assuming you’re referring to the application updates downloaded by an NAppUpdate instance. The default implementation (FileUpdateTask) just downloads the file and replaces the local one with it, but it can verify the download using a SHA1 checksum (using the sha1-checksum attribute in your NauXml feed).

      If you need a different implementation – for example verifying digitally signed files, different checksum algorithms or zip file extraction – it is very easy to do by creating your own task. Drop me a mail if you need assistance.

  4. I try download source in github.com.

    but I get error when try download http://nodeload.github.com/synhershko/NAppUpdate/zipball/master

    any suggestions, thanks in advanced, greet tool

  5. What do you think about using this to update a VB6 application?

  6. The example uses an XML file locally, I need to host it on a server. What is that SimpleWebSource you refer to, but I cannot find any instance of??

    • It is being used also in the example, see WinFormsSampleApp/Form1.cs line 44. SimpleWebSource uses WebClient internally to make the actual download. Meaning, the path to the local file can be replaced with a standard web path (http/ftp etc) and it will work just the same.

  7. i had been wait for whole day. but finally moderator deleted my post…
    sir,i have setup IIS server then put the .xml file into server with the link
    http://eramesra.no-ip.org/SampleUpdateFeed.xml
    then i open the winformssampleApp press the custom feed button and insert the link.but why occur the error with http://imageshack.us/photo/my-images/21/errorqu.jpg/?
    any support document for tutorial how to setup anything or not?

  8. Hi Sr, is a pleasure to greet…i´m starting to use your library….so i’ve many questions:
    1. It’s work for Windows CE for compact framework applications?
    2. in above case, what´s the path in the naxml file?

  9. Hi Mr. Syn-Hershko, i downloaded the library, samples and the others stuff, so i get an error when i tested the sample WinFormsSampleApp. The exception was about a ‘object reference not set to an instance of an object’ this, cause the variable ‘baseUrl’ is null in SimpleWebSource class…i add a condition (if (!string.IsNullOrEmpty(baseUrl)) { if (!baseUrl.EndsWith(“/”)) baseUrl += “/”; }) so, it worked…is this a bug?

    Another concern is … where I can parameterize the path where you are downloading the file? …i mean, the update always is done en the appupdater path…

    thanks a lot…

  10. Hi Sr, thanks for the quick reply …

    My intention is to use your library for desktop applications and mobile application … I guess if you set the platform this is possible … so, what is the mailing list? I would like to discuss the issue …

    regards …


Leave a comment

(required)