An AutoUpdate Trick

Introduction

The company I work for creates software that manages updates for Win32-based software, which is great for mid- to large-scale users who need such a solution, but is a bit of overkill for the small developer. Many small developers only require a simple update solution, and frequently, it is only required to manage the update of the main project executable. This article will describe an autoupdate class I created; with it, it is very simple to add to a project and run a little trick to update the main project executable.

Using the Code

A class called CAutoUpdater was designed, with the goals that it is easy to add to any application, fully manages the update process, and correctly handles any errors. To use this class, simply call the CheckForUpdate method passing in the URL of your update server.

void CMFCUpdaterDlg::OnUpdatecheck()
{
   CAutoUpdater updater;
   if (CAutoUpdater::Success ==
      updater.CheckForUpdate("http://AutoUpdatePlus.com/test/"))
   {
      // ... Notify user of success
   }
}

The update process consists of the following steps:

  1. Ensure that an Internet connection is available.
  2. Retrieve the config file from the update server.
  3. Check the version in the config file. Compare to the existing application version.
  4. If an update is required, download the new application from the update server.
  5. Switch in the latest application version with the existing version.

The first, and frequently overlooked step, is to ensure that an Internet connection is available. This is essential to do; otherwise, you may trigger an automatic dial-up to the Internet, which is likely to make most of your application users unhappy.

// Important step - ensure that we have an Internet connection.
// We don't want to force a dial-up.
DWORD dwType;
if (!InternetGetConnectedState(&dwType, 0))
{
   return false;
}

Retrieving files from an HTTP server is a simple task using InternetOpenUrl, but it can fail if the URL supplied is not canonicalized. This simply means that bad characters, such as spaces, must be converted into escape characters, which is done using InternetCanonicalizeUrl. The GetSession method manages this process, returning a handle to a file on the HTTP server for a given URL. To check for updates, you must define an update config file on the server that lets us know what application version to expect. In this case, the update config file is called update.txt and contains the version of your application update. After getting a session handle to the update config file, you can retrieve the data into a buffer using the DownloadConfig method. You now have the update version available for your use.

HINTERNET CAutoUpdater::GetSession(CString &URL)
{
   // Canonicalization of the URL converts
   // unsafe characters into escape character equivalents
   TCHAR canonicalURL[1024];
   DWORD nSize = 1024;
   InternetCanonicalizeUrl(URL, canonicalURL, &nSize, ICU_BROWSER_MODE);

   DWORD options = INTERNET_FLAG_NEED_FILE|INTERNET_FLAG_HYPERLINK|
                   INTERNET_FLAG_RESYNCHRONIZE|INTERNET_FLAG_RELOAD;
   HINTERNET hSession = InternetOpenUrl(hInternet,
                        canonicalURL, NULL, NULL, options, 0);
   URL = canonicalURL;

   return hSession;
}

bool CAutoUpdater::DownloadConfig(HINTERNET hSession,
                                  BYTE *pBuf, DWORD bufSize)
{
   DWORD    dwReadSizeOut;
   InternetReadFile(hSession, pBuf,
                    bufSize, &dwReadSizeOut);
   if (dwReadSizeOut <= 0)
   {
      return false;
   }

   return true;
}

At this point, yu have retrieved the update config file and now must figure out whether an update is required. The text in the update config file is a simple version tag; in this case, the update version 2.0.0.1. This must be compared against the version of the executable you are currently running. Surprisingly, getting the version number from the executable and running a version number comparison is not a trivial task and can get a bit messy. If it turns out that your existing application version is out-of-date, you should notify the user and then proceed to download the latest application executable from the update server. Note that your new executable will be downloaded to a temporary user directory given by GetTempPath.

Once the latest executable version is downloaded, it must somehow be switched in to replace the existing out-of-date application executable. However, a problem exists! The current executable is locked because it is in use, and hence cannot be copied over. Nevertheless, a simple Windows trick does exist. Files in use can still be renamed, leaving space open for the update executable to be switched in to the application directory. Note that this update trick may not always be available because some files can even get locked from renaming. In this case, the MoveFileEx with MOVE_DELAY_UNTIL_REBOOT option can be used, which delays renaming until a reboot occurs to free the file.

bool CAutoUpdater::Switch(CString executable,
                          CString update, bool WaitForReboot)
{
   int type = (WaitForReboot) ?
              MOVEFILE_DELAY_UNTIL_REBOOT : MOVEFILE_COPY_ALLOWED;

   const TCHAR *backup = _T("OldExecutable.bak");
   CString directory = executable.Left(executable.ReverseFind(_T('\\')));
   CString backupFile = directory + _T('\\') + CString(backup);

   DeleteFile(backupFile);
   if (!MoveFileEx(executable, backupFile, type))
   {
      return false;
   }

   BOOL bMoveOK = (MoveFileEx(update, executable, type) == TRUE);
   int i = GetLastError();

   return bMoveOK;
}

Points of Interest

The class provided manages updates for a simple application, but has a few drawbacks:

  • The update check is a synchronous task that does not provide any user feedback about its progress.
  • Having the update check within the application limits its capabilities. It is wiser to have the update check run from an external executable.
  • A program that appears trivial quickly becomes complex. The current check cannot manage complex situations, such as multiple file updates.

History

  • 10 Feb, 2005 - First release.


Downloads

Comments

  • Update management at http://www.AutoUpdatePlus.com

    Posted by SEllis on 03/16/2005 10:17pm

    Hey guys, If you are interested in checking more about the source-code or update management in general, then check our company web-site at:

    http://www.AutoUpdatePlus.com

    We have two products that may be of interest if you're looking for a pre-built solution. They are called AutoUpdate+ and EasyUpdate, and any feedback on them is always welcome.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Learn How A Global Entertainment Company Saw a 448% ROI Every business today uses software to manage systems, deliver products, and empower employees to do their jobs. But software inevitably breaks, and when it does, businesses lose money -- in the form of dissatisfied customers, missed SLAs or lost productivity. PagerDuty, an operations performance platform, solves this problem by helping operations engineers and developers more effectively manage and resolve incidents across a company's global operations. …

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

Most Popular Programming Stories

More for Developers

RSS Feeds