Manual check for updates with ClickOnce

I really love [wikipedia:ClickOnce], because it removes the necessity to build a web application. In my years as a developer, I can’t count the projects that were web based, simply because of the deployment model. And then ClickOnce came around. Now I can’t count the times that I’ve explained during a training what ClickOnce is and why everyone should love it!

clickonce_autoupdate

Most people use the default behavior of ClickOnce, which presents a dialog window, checking for updates, as shown in the dialog above. I discourage people to use this default behavior because of two reasons. The first reason is that it’s plain ugly and annoying. The second reason is that when an update is available but people skip the update process, they’re not asked again for installation of the update, until a new version is deployed. We can solve this by manually checking for updates and this is even more interesting when doing it asynchronously.

Here’s the simplest code for checking for an update. Be sure to include a reference to System.Deployment and a using to System.Deployment.Application.

ApplicationDeployment updateCheck = ApplicationDeployment.CurrentDeployment;
UpdateCheckInfo info = updateCheck.CheckForDetailedUpdate();
//
if (info.UpdateAvailable)
{
  updateCheck.Update();
  MessageBox.Show("The application has been upgraded, and will now restart.");
  Application.Restart();
}
Here you can see we gather information. There’s also a property IsUpdateRequired which you can use to force users to upgrade (which is happening in this example, normally you’d give users a choice to update). In the default ClickOnce behavior, this results in automatic update, instead of being asked to update the application.

Of course we can add error checking and a BackgroundWorker to make things a little more pretty. We’ll start out with the BackgroundWorker, bind the events for when it should perform work and for when it’s completed and then start it up.
Note : In this example, we’ll be adding everything in the main form, but it’s better to apply the [wikipedia:Single_responsibility_principle] and delegate this to some helper classes.

BackgroundWorker bgWorker = new BackgroundWorker();
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorder_RunWorkerCompleted);
bgWorker.RunWorkerAsync();
Now we’ll have to add the code to peform the check. It’s partially the code from code-sample #1, without the updating part.
private enum UpdateStatuses
{
  NoUpdateAvailable,
  UpdateAvailable,
  UpdateRequired,
  NotDeployedViaClickOnce,
  DeploymentDownloadException,
  InvalidDeploymentException,
  InvalidOperationException
}
 
/// <summary>
/// Will be executed when works needs to be done
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
  UpdateCheckInfo info = null;
 
  // Check if the application was deployed via ClickOnce.
  if (!ApplicationDeployment.IsNetworkDeployed)
  {
    e.Result = UpdateStatuses.NotDeployedViaClickOnce;
    return;
  }
 
  ApplicationDeployment updateCheck = ApplicationDeployment.CurrentDeployment;
 
  try
  {
    info = updateCheck.CheckForDetailedUpdate();
  }
  catch (DeploymentDownloadException dde)
  {
    e.Result = UpdateStatuses.DeploymentDownloadException;
    return;
  }
  catch (InvalidDeploymentException ide)
  {
    e.Result = UpdateStatuses.InvalidDeploymentException;
    return;
  }
  catch (InvalidOperationException ioe)
  {
    e.Result = UpdateStatuses.InvalidOperationException;
    return;
  }
 
  if (info.UpdateAvailable)
    if (info.IsUpdateRequired)
      e.Result = UpdateStatuses.UpdateRequired;
    else
      e.Result = UpdateStatuses.UpdateAvailable;
  else
    e.Result = UpdateStatuses.NoUpdateAvailable;
}

The first thing you’ll notice is that I’ve added an enumeration. This is for passing a result back to the method that’s called when we’re done.
Note : This can be done more gracefully, but it’ll do for now without a lot of extra code. And it’s not even real ugly! ๐Ÿ˜‰

In the bgWorker_DoWork method we first check if the application is deployed via ClickOnce. Then we gather the detailed information about a possible update and perform some error handling if things don’t go the way we expect. Finally we check if an update is available and wether or not it’s a required update. We pass this information into the result and end this method.

Now we’re in need of the method when the work is done…

/// <summary>
/// Will be executed once it's complete...
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void bgWorder_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
  switch ((UpdateStatuses)e.Result)
  {
    case UpdateStatuses.NoUpdateAvailable:
      // No update available, do nothing
      MessageBox.Show("There's no update, thanks...");
      break;
    case UpdateStatuses.UpdateAvailable:
      DialogResult dialogResult = MessageBox.Show("An update is available. Would you like to update the application now?", "Update available", MessageBoxButtons.OKCancel);
      if (dialogResult == DialogResult.OK)
        UpdateApplication();
      break;
    case UpdateStatuses.UpdateRequired:
      MessageBox.Show("A required update is available, which will be installed now", "Update available", MessageBoxButtons.OK);
      UpdateApplication();
      break;
    case UpdateStatuses.NotDeployedViaClickOnce:
      MessageBox.Show("Is this deployed via ClickOnce?");
      break;
    case UpdateStatuses.DeploymentDownloadException:
      MessageBox.Show("Whoops, couldn't retrieve info on this app...");
      break;
    case UpdateStatuses.InvalidDeploymentException:
      MessageBox.Show("Cannot check for a new version. ClickOnce deployment is corrupt!");
      break;
    case UpdateStatuses.InvalidOperationException:
      MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application.");
      break;
    default:
      MessageBox.Show("Huh?");
      break;
  }
}

Here you’ll see responses to most error messages and them just showing up in a message box. Interesting is the NotDeployedViaClickOnce status, as this pops up every time you’re debugging your application from Visual Studio. You might want to remove that message box. You also might want to remove the messagebox for when no update is available, as users aren’t really interested in that. The final default switch should never occur.

Interesting are the switches for when there is an update, and when it’s required. In the first case the user can ignore the update which he can’t in the case it’s required. You might think of something so the user won’t be asked dozens of times every time the application is started.

Now we only need the UpdateApplication method.

private void UpdateApplication()
{
	try
	{
		ApplicationDeployment updateCheck = ApplicationDeployment.CurrentDeployment;
		updateCheck.Update();
		MessageBox.Show("The application has been upgraded, and will now restart.");
		Application.Restart();
	}
	catch (DeploymentDownloadException dde)
	{
		MessageBox.Show("Cannot install the latest version of the application. nnPlease check your network connection, or try again later. Error: " + dde);
		return;
	}
}

Here we again do some error checking for when things go wrong. But all we’re doing here is update the application and restart it.

This is now all happening in the background without the users being notified when there’s no update and without the annoying popup dialog. It’s also really easy to create your own “Check for updates” button or menu option now.

Have fun with ClickOnce and forget about those awful web based applications! ๐Ÿ˜‰

UPDATE : Here’s how you can turn off automatic updates with ClickOnce.

UPDATE 2 : Here’s an example solution, without the BackgroundWorker.

Technorati Tags:

You may also like...

52 Responses

  1. It’s amazing to see how many people can find this blog entry via Google but don’t provide any comments or anything! ๐Ÿ™‚

  2. Antonio says:

    Hi,
    This post is really interesting. I’m researching on clickOnce to see if we can using in my job. Everything looks great, but I have a problem. For some policies of the company, I need to change the installation path. I can’t have clickOnce to install my apps per user in the cache folder. In my research I found that this is not possible. So I wonder if I can make a classic msi installer to setup the app for the first time (and have it installed where I want), but still using clickOnce for checking updates using your code example.
    How can I make the app to ‘think’ that has been deploy using ClickOnce, so the NotDeployedViaClickOnce status says so?
    Is this possible?

  3. Douglas Ross says:

    Hi,

    Thanks for the article, it is exactly what I am looking for….. Unfortunately, I am not that good a coder, and do not understand exactly where to make these changes.

    I also cannot find where MS Studio makes the wizard changes (other than in properties).

    I thought that if I found the default stuff set up by the wizard then I should be able to overwrite it with the delegations you have written above.

    Would you be able to post an example forms C# project, that shows exactly where you place this code.

    Apologies in advance, if I am being terribly simple and the answer is obvious.

    Douglas

  4. Hi Douglas,

    I’m giving a training today. If I find the time between practices or so, I’ll create and upload a solution.

  5. Seems like I did it faster than I expected. I added the example for VS2005 without the BackgroundWorker.

    The link is just below the article, as UPDATE 2.

    If you want one with the BackgroundWorker included, or anything else, let me know.

  6. nicolas says:

    Hi,

    How can I manually specify the update location.
    The application will be installed in multiple clients where internet is not available. So I need the administrator of each of our clients to manually copy the files to a specific location so that all clients can be updated.

  7. Rob Kent says:

    The main issues I have with ClickOnce are the install directory and config overwrites. Let’s assume I want my users to be able to manually update the app.config – firstly, how do they know where it is? Secondly, what happens on next update and I have made changes to the central copy – this would overwrite the customer’s changes.

    I’ve looked at lots of ClickOnce articles and have still not seen a satisfactory solution to this problem.

  8. Constandinos Iezekiel says:

    Very helpful article Dennis. It helped a lot ๐Ÿ™‚

  9. Muttly says:

    Hi,

    It’s an interesting article Dennis.

    Without sounding like a complete retard (I have my moments!), what files do we need to put where to set up updates?

    Thanks in advance for your help!

    Muttly

  10. Dennis van der Stelt says:

    I am preparing a post about doing builds with FinalBuilder, explaining also the entire process that needs to be done if you’d do this by hand.

    Hold on for it, I’ll finish it any time soon.

  11. Geir Brandt says:

    Hi,

    thanks for a thorough and excellent explanation. It was exactly what I hoped Google would help me with ๐Ÿ™‚

  12. Edgar Rodriguez says:

    This is a pretty interesting blog post. I just have one quick question, how do I specify in the publish stage in Visual Studio if a update is required?, I mean, where does the info.IsUpdateRequired variable is set? Do I have to modify the deployment manifest manually?

    Thanks,
    Greetings.

  13. Dennis van der Stelt says:

    “Publish” tab in project’s properties window

  14. Rich says:

    A few questions.

    1. Does the background worker check for updates with out a user physically pressing a button?

    2. With this code:

    ApplicationDeployment updateCheck = ApplicationDeployment.CurrentDeployment;

    UpdateCheckInfo info = updateCheck.CheckForDetailedUpdate();

    //

    if (info.UpdateAvailable)

    {

    updateCheck.Update();

    MessageBox.Show(“The application has been upgraded, and will now restart.”);

    Application.Restart();

    }

    Can I place this on form Load?

  15. Dennis van der Stelt says:

    @Rich : 1. Yes, it can 2. Yes, you can.

    It’s probably wiser though to put the checking in parallel process with a BackgroundWorker for example.

    I did bump into one single Windows Forms application where this wasn’t wise, as it took 1 minute to load a LOT of data. After the 1 minute loading, it said “Hey, update, wanna install and restart the app?” and users of the app wouldn’t update because they had to wait another full minute. So checking for updates before the actual loading of the data took place was smarter! ๐Ÿ™‚

    Of course a complete redesign of the app was even more smarter, but there’s not always time for that.

  16. Rich says:

    Just wanted to reply, I did some other stuff, but by following your procedure I was able to help myself implement an update checker.

    I will try to figure out how to use the background checker in a future update ;).

    Thank you for posting this it has helped me.

  17. Dennis van der Stelt says:

    @Rich : No problem, good luck!

  18. Felipe de Jesús Meléndez Valencia says:

    How can i implement this solution in a class????

  19. Dennis van der Stelt says:

    @Felipe : What do you mean by that?

  20. BP says:

    Hi Dennis,

    This is really great stuff. Thanks for writing it up.

    I’m using Visual Studio 2010, could you tell me where to inject the deployment code? I have a WPF project and would like to turn off the automatic check for updates too, or at least control its behavior more.

  21. BP says:

    Nevermind, I ignore my last questions… I figured it out. Thank you for the article!

  22. Felipe de Jesús Meléndez Valencia says:

    It is possible, for example, if I have a BackgroundWorker in different forms in my application, that change in the role of main window. My question is: Can i put the code in the DoWork and RunWorkerCompleted events in a class and just call in the events, and how i can do it?. Thanks for all.

  23. Dennis van der Stelt says:

    @Felipe : Those questions aren’t ClickOnce related at all. You can put anything inside classes you want and have background workers there, or have backgroundworkers in your form execute the methods in your other class. There are a million possibilities there.

  24. Domnica says:

    Hi,

    I have some files needed for the application to run (dll-s that I cannot add as references because they are not .NET-style, other config files, etc, so they are not deployed by ClickOnce technology), so after publishing the app, I will copy by hand these files to the server, and when app starts I want to copy them dinamically from server to client local disk. (if this is the very first time the app is running on that machine and these files donโ€™t already exist on local disk). They will not modify from one version to another, so I wonโ€™t copy them to server every time.

    To copy these files from server to client, of course I have to know server (deployment) location and client location (directory where the app starts on local disk).
    For server location I tried to use ApplicationDeployment.CurrentDeployment.UpdateLocation, but it gave me http://193.33.94.254/MyApp/ MyApp.application (the content of deploymentProvider tag), so not the place where I can find
    For client location I tried to use ApplicationDeployment.CurrentDeployment.DataDirectory, but again this is not the directory from the local disk where the .exe locates.
    So I guess for client side I could use the old System.Windows.Forms.Application.StartupPath, right?

    But what can I do about server location? How to obtain http://193.33.94.254/MyApp/ Application%20Files/ MyApp_1_0_0_7/ letโ€™s say?

    Thanks,
    Domnica

  25. Patrick McGowan says:

    Hello Denis,

    Did you answer Antonio’s comment as I have the same issue and am unable to solve this. Can you help:-

    **********************
    Antonio said:
    Hi,

    This post is really interesting. I’m researching on clickOnce to see if we can using in my job. Everything looks great, but I have a problem. For some policies of the company, I need to change the installation path. I can’t have clickOnce to install my apps per user in the cache folder. In my research I found that this is not possible. So I wonder if I can make a classic msi installer to setup the app for the first time (and have it installed where I want), but still using clickOnce for checking updates using your code example.

    How can I make the app to ‘think’ that has been deploy using ClickOnce, so the NotDeployedViaClickOnce status says so?

    Is this possible?
    *****************

    Regards: Patrick

  26. RudolfGutlich says:

    Hi, Iยดve stopped by your site and I am trying to use your tips. I am now stuck with two problems:

    1 – Iยดve disabled the autoupdate and did the check programatically.
    When I install the application and there is no new version on the site, there is no warning (ok).
    If there is a new one, then a dialog is shown and if if I select OK, the app updates itself.
    But, if I bypass the update option and quit the app, next time I run it then the “AutoUpdate” window of ClickOnce appears, showing the “OK” and “Ignore” buttons. Why is this happening? I double checked the Publish settings… ๐Ÿ™

    2 – I have a version 1.0.0.96 in my web site, and I generate the .97 but I didnยดt published it to web, only installed directly.
    So, I have .97 installed in my computer and .96 in the site. But, when I start the app, it shows me that a newer version is on the site and, if I click OK to upgrade, then the .96 version replaces the .97 in my computer.
    How can I stop this bizarre behavior? It is a bug? It is also happening in other application that uses the AutoUpdate from ClickOnce.

    Well, besides that, very Thank You for your great site!

    Best Regards,

    Rudolf Gutlich.

  27. Carel says:

    Excellent! This solved a caching issue too for me!

  28. Shah says:

    Hi,

    I was wondering how ClickOnce detects that an application update is available. Does it look only at the application’s version number ? Lately, I changed my application clickonce deployment so that it is deployed in x86 mode instead of the MSIL mode. After doing this, clickonce was NOT updating my previously installed but it was installing a new copy of my application as if my application was being newly installed.

    Could anyone shed some light on how and when ClickOnce determines that it should update an application ? Whether changing parameters such as the application installation URL, Processor architecture and so on, make clickonce believe troubles Clickonce and make it consider the application being deployed as a brand new application ?

    Regards

  29. Shah says:

    I found the answer to my question here:

    http://blogs.msdn.com/b/winformsue/archive/2006/04/20/580223.aspx

    Cheers
    Shah MOHAMOD

  30. Dennis van der Stelt says:

    @Shah : Thanks a lot for replying with the link! Probably helps others as well!

  31. Nice. I was looking for a simple, manual way to check for updates, and lo and behold, I found a fantastic family of routines to do it and do it well.

    Thanks for sharing. ๐Ÿ™‚

  32. StevieC says:

    Just deployed my first two ClickOnce apps, before i read this post!! Now need to change them and redeploy them!! Wish i’d found this post first!! Great Post and expertly explained!!

    Thanks

  33. Art Colman says:

    Dennis,

    I’m wondering if you have any comments, suggestions, or opinions about how to use ClickOnce in an IE9 environment? I found the SmartFilter indication that a clickonce setup.exe is not typically run as very strong and would probably motivate a user not to try the program.

    Cheers,

    Art
    http://www.drybridge.com

  34. Dennis van der Stelt says:

    @Art : The .application file/link is the one you want. It should run with no problems from any IE version. Firefox needs a small update.

  35. Machado says:

    Good job and good article

  36. Peter Durst says:

    Very Nice Tutorial.

    Thank you for sharing!
    Greetz Peter

  37. Remember to user checkForDetailedUpdate(false), or the nasty Win3.1 style dialog box will keep on showing…

  38. Rajesh says:

    Please provide a solution :
    I want to delete existing SQLCE version at client PC during Clickone installation programatically

  39. Smilwe says:

    Is it possible to programatically set update source location?

  40. Smiler says:

    Is it possible to programatically set update source location?

  41. Dennis van der Stelt says:

    @Smiler: Nope

  42. juan herrera says:

    I have a problem with the manually update, when I unchecked the automatic checking for updates. In the next update my antivirus kaspersky Internet security 2012 detected like a virus. I don’t why does it happen?? do you have any ideas?

    The antivirus message:
    Detected: PDM.Worm.P2P.generic 01/10/2012 04:22:11 p.m.

    PD.
    I signined the clickonce manifests.
    If I check the automatic checking for updates. the antivirus doesn’t blocked my application.

  43. anand says:

    Hi.. I am also using Clickonce at work. I need to do manual checking for updates,and if the update is available need to update the application also. But I have a big problem,everything seems to be working fine. But the application never gets updated.Looks like Update() Method is not working.What could be causing so??..Any help would be appreciated..

  44. andy says:

    Hi Dennis,
    thanks for this stuff – i would like to implement this in my projects – was looking exactly for this kind of solution!! Two questions: the link in ‘Upate 2’ seems to not work anymore… do you still have this example code? And: is this explanation also available for VB.Net?
    Thanks, Andy

  45. Anand, sorry for the broken link. I’ve updated the article to include proper syntax highlighting and uploaded the solution again.

  46. Bharath says:

    Hi, Thanks for this very useful article on updating prgmatically with ClickOnce.
    I have a Winform application which has to be deployed using clickonce installer but the requirement is force updating when the application runs(like Chrome). I am not a very good programmer and would like to know your BackgroundWorker code example works for the Winform application too where the application should automatically checks for the updates without the intervention of the user and force update when needed or else inform the user about the update is available? Please do reply sir.

  47. saravan says:

    Hello Dennis,
    This is what exactly i need for my application. I have few questions.
    1.where do i need to deploy the above code.
    2.Need some reference on how to create ClickOnce application
    3.Will this automatically update the application or any manual process need to be done

  48. Hi Saravan,

    It’s been ages since I’ve used this. But code simply goes into your own application, just anywhere. And call it from where the application starts. You then have the option to force users to upgrade, or else quit the application. Or let them go through with older versions. Or version N-1, where only one version lower of the current one is used.

    There’s a ton of material online. Just create one and use the code from this article to update it manually.
    https://www.google.nl/webhp?q=how%20to%20clickonce

    ClickOnce can already be enabled to always automatically update. This code is there to give you the option. I always like being in control, which is why I created this article.

  49. Milind says:

    Hi Dennis,

    This post is really helpful and interesting, but I am facing some issues in clickonce like if WPF application contains dynamically loading modules then it does not deploy the dll of this modules and external config file.

  50. Hi Milind,

    It’s been so long since I’ve done anything with ClickOnce, that I can’t recall how to do this. Isn’t there somewhere where you can specify which assemblies (.dll) should be included in the deployment? And if it isn’t and there’s no reference whatsoever, the final solution would be the ability to download the assemblies/modules. But that’s really hard, working with versions/updates and all.

    I’d definitely find a way to include additional files when creating the deployment package.

    Or am I completely missing the real issue here? ๐Ÿ™‚

    • Milind says:

      Hi Dennis,

      Yes, the issue is how to include additional files when creating the deployment package, I have tried to find a way where we can keep required assemblies of dynamic modules but no luck.
      Thank you for replying. Please share if you find any solution on this. ๐Ÿ™‚

  51. Erc Backman says:

    Great concise and quick help, thanks so much!

Click on a tab to select how you'd like to leave your comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.