Last week I was taking a look at Squirrel which states

Squirrel: It’s like ClickOnce but Works™

The goal of me looking at Squirrel was to find a decent replacement to ClickOnce, I read a tweet from @paulcbetts mentioning his work and how he had created Squirrel as a replacement to using ClickOnce.

Having spent some time implementing this and coming across one or two issues which I overcame, the good news is ClickOnce is hopefully no longer needed.

Squirrel works and does exactly what we require, our windows forms application’s can have a ‘check for updates’ option, which updates to the latest release, close the old application down after its updated, re-open from the original desktop or start menu shortcut and your done.

Steps to get Squirrel working

  1. Sourcecode is on GitHub.
  2. Create a simple Windows Forms Application
  3. Add the nuget package called squirrel.windows nugetinstalllike so:-
  4. Add the following to the program.cs like so (line 19 is the only one I added) :- [sourcecode language=”csharp”] using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Windows.Forms;

    using Squirrel;

    namespace WindowsFormsApplication1
    {
    static class Program
    {
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread] static void Main()
    {
    SquirrelAwareApp.HandleEvents(onAppUpdate: Form1.OnAppUpdate,
    onAppUninstall: Form1.OnAppUninstall,
    onInitialInstall: Form1.OnInitialInstall);

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
    }
    }
    }
    [/sourcecode]

  5. Add some content like a simple button or a menu with the choice to check for updates
  6. in the code behind we implement squirrel like so:- [sourcecode language=”csharp”] using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;

    using System.Configuration;
    using Squirrel;
    using System.Reflection;
    using System.IO;

    namespace WindowsFormsApplication1
    {
    public partial class Form1 : Form
    {
    private const ShortcutLocation DefaultLocations = ShortcutLocation.StartMenu
    | ShortcutLocation.Desktop;

    public Form1()
    {
    InitializeComponent();
    }

    private void checkForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
    {
    // Check for Squirrel Updates
    var t = UpdateApp();
    }

    public async Task UpdateApp()
    {
    var updatePath = ConfigurationManager.AppSettings["UpdatePathFolder"];
    var packageId = ConfigurationManager.AppSettings["PackageID"];

    using (var mgr = new UpdateManager(updatePath, packageId, FrameworkVersion.Net45))
    {
    var updates = await mgr.CheckForUpdate();
    if (updates.ReleasesToApply.Any())
    {
    var lastVersion = updates.ReleasesToApply.OrderBy(x => x.Version).Last();
    await mgr.DownloadReleases(new[] { lastVersion });
    await mgr.ApplyReleases(updates);
    await mgr.UpdateApp();

    MessageBox.Show("The application has been updated – please close and restart.");
    }
    else
    {
    MessageBox.Show("No Updates are available at this time.");
    }
    }
    }

    public static void OnAppUpdate(Version version)
    {
    // Could use this to do stuff here too.
    }

    public static void OnInitialInstall(Version version)
    {
    var exePath = Assembly.GetEntryAssembly().Location;
    string appName = Path.GetFileName(exePath);

    var updatePath = ConfigurationManager.AppSettings["UpdatePathFolder"];
    var packageId = ConfigurationManager.AppSettings["PackageID"];

    using (var mgr = new UpdateManager(updatePath, packageId, FrameworkVersion.Net45))
    {

    // Create Desktop and Start Menu shortcuts
    mgr.CreateShortcutsForExecutable(appName, DefaultLocations, false);
    }
    }

    public static void OnAppUninstall(Version version)
    {
    var exePath = Assembly.GetEntryAssembly().Location;
    string appName = Path.GetFileName(exePath);

    var updatePath = ConfigurationManager.AppSettings["UpdatePathFolder"];
    var packageId = ConfigurationManager.AppSettings["PackageID"];

    using (var mgr = new UpdateManager(updatePath, packageId, FrameworkVersion.Net45))
    {
    // Remove Desktop and Start Menu shortcuts
    mgr.RemoveShortcutsForExecutable(appName, DefaultLocations);
    }
    }
    }
    }
    [/sourcecode]

  7. We now need to create a .nuspec file which we will use to package up our application, Squirrel needs to have every file within the lib\net45 folder.

    To create a .nuspec file I simply copied nuget.exe into the solution and then went to a command prompt and typed:- nuget spec, this creates a .nuspec file which we can then hand edit inside Visual Studio and looks like this:-
    [sourcecode language=”csharp”] <?xml version="1.0"?>
    <package >
    <metadata>
    <id>WindowsFormsAppliation1</id>
    <version>1.0.0.0</version>
    <authors>Gregor Suttie</authors>
    <title>WindowsFormsAppliation1 Squirrel Tester</title>
    <description>Testing out Squirrel</description>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <releaseNotes>None</releaseNotes>
    <copyright>Copyright 2015</copyright>
    </metadata>
    <files>
    <file src="bin\Debug\*.*" target="lib\net45\"></file>
    </files>
    </package>
    [/sourcecode]

  8. Next we need to pack the .nuspec file to generate the nuget package, so run the following:-
    Nuget pack WindowsFormsApplication1.nuspec
  9. From the package manager console inside Visual Studio we now need to run the following command:-

    squirrel –releasify C:\Squirrel\WindowsFormsApplication1\WindowsFormsApplication1\WindowsFormsApplication1.1.0.0.0.nupkg

  10. In the folder above we should now have a folder called Releases, containing a setup.exe which we use to install the application.
  11. Run setup.exe and your app will install with shortcuts on the desktop and the start menu if you use the code above which has defined these as shortcut options.
  12. If you then run check for updates, the code will go off and check the folder you specify in the app.config like so:- [sourcecode language=”csharp”] <add key="UpdatePathFolder" value="c:\updatesForMyApp\"/>
    [/sourcecode]
  13. In order to update our app we also need the following appsetting which is the id of the Nuget package we created earlier, be careful not to have the id with a space or a dot (.) within the Nuget package Id.:-
    [sourcecode language=”csharp”] <add key="PackageID" value="WindowsFormsApplication1"/>
    [/sourcecode]
  14. Also add this one line to the assmblyinfo.cs:-
    [sourcecode language=”csharp”] [assembly: AssemblyMetadata("SquirrelAwareVersion", "1")] [/sourcecode]
  15. Once you install the application, go back and make a change to the code, save it, update the .nuspec file version number from 1.0.0 to 1.0.1 and then redo steps 7 and 8 (with the path to the new WindowsFormsApplication1.1.0.0.1.nupkg file)
  16. Almost done, copy the content of the releases folder to our location where we store the updates (our app.config setting for UpdatePathFolder which was set to c:\updatesForMyApp\)
  17. Run the app and choose the menu option ‘Check for Updates’ and you’ll get the latest version.
  18. Also note of you add a Debugger.Launch() statement in the code you can debug the code and step through line by line.

To find out more about further options available and much more about Squirrel click here.