Adding “Check for update” feature to your C# application
Posted on May 29th, 2008 in c#, programming |
Today I decided I would add a ‘check for update’ option to my Eyes Relax application. This is quite useful feature, especially when you host your application on many hosting servers (like download.com and others). In this case it can be difficult for the user to check if there is a newer version of your software available, because:
- the user does not remember where he downloaded the application from
- there is an older version on the hosting server, so the user is not aware that there is a newer version of your app available
- simple, but very common reason: the user is to lazy to look for the new version :P; because it’s easier to simply select the ‘update’ option in your application, it may work for lazy users
Beside those reasons this is a nice, small feature we can practice our c# programming skills on :). At least for me, because I’ve spent last two years mainly developing server modules in Python. Two major subjects are:
- HTTP file downloading
- simple XML parsing (XmlTextReader)
Let’s start.
General idea
First we need a way to provide our application with the information about the newest version. So what we do is put a little xml file anywhere on Internet (well, preferably on your homepage). In this xml file we store the number of the newest version of our app. While checking for update we download this xml, parse it and compare the version of running application with the version from the xml file.
Downloading the xml file
This is very easy - we just provide the XmlTextReader with the URL of our XML file. The XmlTextReader will download the file for us. What’s even better, it uses the .NET 2.0 internal solutions that detect the IE proxy settings (I’ve double-checked this and it really works :)), so this will work even when the user connects through a proxy.
Parsing the xml
As mentioned above, we will use XmlTextReader (just remember to use System.Xml). First take a look at our litte xml contaning the information about the newest version:
< ?xml version="1.0" encoding="utf-8"?> <ourfancyapp> <version>1.2.3.4444</version> <url>http://some.domain/our_app_homepage/</url> </ourfancyapp>
As you probably noticed we store the version information in the same string format as c# Application.ProductVersion. I’ve chosen this format because it will be easier to parse and compare the version numbers, as you will see later.
Ok, let use XmlTextReader to parse the xml:
// in newVersion variable we will store the
// version info from xml file
Version newVersion = null;
// and in this variable we will put the url we
// would like to open so that the user can
// download the new version
// it can be a homepage or a direct
// link to zip/exe file
string url = "";
try
{
// provide the XmlTextReader with the URL of
// our xml document
string xmlURL = "http://domain/app_version.xml";
XmlTextReader reader =
new XmlTextReader(xmlURL);
// simply (and easily) skip the junk at the beginning
reader.MoveToContent();
// internal - as the XmlTextReader moves only
// forward, we save current xml element name
// in elementName variable. When we parse a
// text node, we refer to elementName to check
// what was the node name
string elementName = "";
// we check if the xml starts with a proper
// "ourfancyapp" element node
if ((reader.NodeType == XmlNodeType.Element) &&
(reader.Name == "ourfancyapp"))
{
while (reader.Read())
{
// when we find an element node,
// we remember its name
if (reader.NodeType == XmlNodeType.Element)
elementName = reader.Name;
else
{
// for text nodes...
if ((reader.NodeType == XmlNodeType.Text) &&
(reader.HasValue))
{
// we check what the name of the node was
switch (elementName)
{
case "version":
// thats why we keep the version info
// in xxx.xxx.xxx.xxx format
// the Version class does the
// parsing for us
newVersion = new Version(reader.Value);
break;
case "url":
url = reader.Value;
break;
}
}
}
}
}
reader.Close();
}
catch (Exception)
{
}
That’s how we parse our XML. Now we check if the newVersion and url are not empty. If they’re not, it means we successfuly downloaded and parsed the xml file and now we can…
…compare the versions
The Application.ProductVersion is just a string, and it’s usually not sensible to compare strings containig numbers. Luckily in the .NET executing assembly there is a variable, which happens to be the same type as our newVersion. It makes the comparing a lot easier :) :
// get the running version
Version curVersion =
System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
// compare the versions
if (curVersion.CompareTo(newVersion) < 0)
{
// ask the user if he would like
// to download the new version
string title = "New version detected.";
string question = "Download the new version?";
if (DialogResult.Yes ==
MessageBox.Show(this, question, title,
MessageBoxButtons.YesNo,
MessageBoxIcon.Question))
{
// navigate the default web
// browser to our app
// homepage (the url
// comes from the xml content)
System.Diagnostics.Process.Start(url);
}
}
If the user answers ‘Yes’, we navigate the default browser to the URL we have from the downloaded xml file. I didn’t hardcode this URL in the code, because when we for example move our homepage, we would be in trouble. And in our solution we can simply change the url in the xml file and the users would be automatically directed to the new homepage.
Well, that’s all. As you see this was a pretty simple task. Hope you found it useful.
Enjoy,
mech
P.S. I wrote two more tutorials regarding this topic:
- How to download and install a new version of your C# application
- How to create a simple MSI installer using WiX
The both show how to implement ‘check for updates’ in a more mature way.
14 Responses
Excellent example! Very clean and easy to understand. Thanks!
Thanks for the sample. Easy to follow. i have one question: Say i want to update the version number in that XML file automatically when i make an update. How can i have the project plug the latest version into the file for me? Is it possible?
Hi Joe,
That’s an interesting topic you mentioned. To make it short: you can write you own (very simple) tool and attach it to the post-build event.
In Visual Studio you can use project build events to run your own console tool (f.e. publish.exe “$(TargetPath)”). This tool should open the compiled executable (the path is passed as a command line argument) and determine its version (you can use http://www.codeproject.com/KB/files/VersionApp.aspx as a starting point). At the end the tool should create a new xml containing the current version number (and optionally upload both the executable and the xml to your web server). Hope this helps.
And one more thing: I don’t that every version of the application that compiles without errors should be published, it should be tested first :) So it’s a good idea to create a new project configuration (called Publish and based on the Release configuration) and attach the tool I mentioned only in this config build events. In every day compiling you should use Debug/Release configurations and when you want to release the new version you should use Publish.
Thanks for the response mech. i haven’t quite got the hang of Publishing yet, as the application is small and in-house only. It would only be published to a network share. But you’re absolutely right, compiles != works. Since it’s still a small enough app, i do the testing myself in the VS environment. i suppose as it grows i’ll need to take your advice on a second build config. i’ll check into that link and see what i come up with.
Thanks again!
Question - how to do the actual update? If your patcher is launched from within the main app, how do you overwrite the files during run time? Also, for projects with multiple files (an exe, .config, and dlls) would you have a version.xml for all of them, or would you keep them all in one file?
Hi
I’ve assumed the simplest scenario here - our program just checks for the new version and allows user to download the installer for it. This solves the problem of multiple files.
Of course it would be easier if the application updated itself automatically. If you would like to do this I see two solutions:
1) Download the installer to the temporary directory. Execute the installer using Process.Start and immediately call Application.Current.Shutdown to end the running program. The installer should install the new version and re-launch our application. I wrote a simple tutorial about creating MSI installers.
2) If you don’t want to use the installer you can program the process yourself:
- check if the new version is available
- copy the current executable to a temporary location, execute it (Process.Start) and terminate the current process. While executing the app from the temporary location you should pass some parameters indicating it should perform an auto update.
- the new process downloads the new version, replaces the application files (while are not locked now), executes the new version using Process.Start and terminates
- the new version should perform a cleanup removing the temporary executable created at the beginning.
I found your site on technorati and read a few of your other posts. Keep up the good work. I just added your RSS feed to my Google News Reader. Looking forward to reading more from you down the road!
Hi guys,
I’d like to thank you all (Erwin, Joe, Carson and Alex) for your feedback. In fact for the last 2 moths I have a real mess at work with a lot of over hours and because of it I have both - a lot of ideas what I’d like to write about and almost no time to actually write it. All your feedback (being honest I wasn’t expecting any for the first few months) gives me the feeling that writing these articles actually makes sense, cos there are people that find it useful. And your feedback of course gives the motivation to continue the work. So again, thanks to all of you.
I hope I’ll write another article in till the end of August. Some of the comments (Carson’s in fact) shows that the solution I presented is a very basic one and I fully agree with him. I’d like to explain how to implement ‘Check for update’ in a more advanced way and how to create MSI installers (that we’ll need) using WiX.
Regards,
mech
Your blog is interesting!
Keep up the good work!
Could you fix one thing? In the begining the variable name is newVersion and later is newVer.
Thank you Kerie, somehow I overlooked this bug. It’s fixed now.
Great blog post! This really helped me out, thanks!
mech this was very helpful. Thanks.
Thanks so much! Your thread was very helpful.