Site Map Contact Us Home
E-mail Newsletter
Subscribe to get informed about
Clever Components news.

Your Name:
Your Email:
 
SUBSCRIBE
 
Previous Newsletters
 




Products Articles Downloads Order Support
Customer Portal      

Adding automatic update support to your application

Submitted on March 25, 2003

Don't miss the Updating your application via the Internet article.

Currently the newer version of the Clever Internet Suite 6.2 is available. This version includes the Web Update component. Also please check the How to migrate to the Clever Internet Suite Version 5.0 article.

We would like to introduce a simple component which retrieves version information from the website and if there is a newer version available it will download update files via HTTP/HTTPS/FTP and run the update.

For your convenience we provide following downloads:

  • component.zip - an archive with WebUpdate component. Delphi 5, 6, 7 packages are available at this moment. 
  • demo1src.zip - an archive with source code for the first sample. 
  • demo2src.zip - an archive with source code for the second sample. 
  • demo1exe.zip - an archive with compiled executable file for the first sample. 
  • demo2exe.zip - archive with compiled executable file for the second sample. 
  • webupdate.xml - web resource which provides the update information. 
  • Clever Internet Suite downloads - demo version of Clever Internet Suite. 

Abstract

There are many algorithms of automating the process of receiving web updates.
But most of them have the same simple steps:

  • Connect to the web server and retrieve information about updates available for downloading.
  • Check whether the version available on the web is up-to-date comparing to the version of your program.
  • If the web version is up-to-date then download update file(s).
  • Process downloaded update file(s) with appropriate method: unpacking, copying, executing etc.

Also there are some additional requirements for web update process:

  • Updated application should have possibility for updating own exe file which it has been run from.
  • Downloading process should not interfere with main application process and should allow a user to continue his usual work.
  • Downloading process can be interrupted by a user at any moment and later continued from the last stop place.

Implementation

To implement the first step of web update we need to place some resource (file for example), which contains information about last update, on the website. In our case we use XML-based resource file webupdate.xml. If you like you can use old fashioned TXT file instead of fancy XML format - this is just a matter of your personal preferences.

The main reason why we use XML format is simplicity of parsing information. Since we need to check for matching between your actual program version and version of update available on the web (step 2) we have to save and store information about last update to the local disk.

There are two methods for loading and storing of update information by using the Microsoft Document Object Model (DOM) library:

procedure TclWebUpdate.GetUpdateInfo();
var
   Dom: IXMLDomDocument;
begin
   Dom := CoDOMDocument.Create();
   Dom.load(FDownloader.LocalFile);
   UpdateInfo.FURL := Dom.selectSingleNode('updateinfo/update/@url').text;
   UpdateInfo.FRunParameters := Dom.selectSingleNode('updateinfo/update/@runparameters').text;
   UpdateInfo.FVersion := Dom.selectSingleNode('updateinfo/update/@version').text;
   UpdateInfo.FSize := Dom.selectSingleNode('updateinfo/update/@size').text;
   UpdateInfo.FProductURL := Dom.selectSingleNode('updateinfo/product/@url').text;
   UpdateInfo.FProductName := Dom.selectSingleNode('updateinfo/product/@name').text;
   UpdateInfo.FAuthor := Dom.selectSingleNode('updateinfo/product/@author').text;
   UpdateInfo.FEmail := Dom.selectSingleNode('updateinfo/product/@email').text;

   Dom.load(LastUpdateInfoFile);
   ActualInfo.FSuccess := Dom.selectSingleNode('update/@success').text;
   ActualInfo.FUpdateDate := Dom.selectSingleNode('update/@updatedate').text;
   ActualInfo.FVersion := Dom.selectSingleNode('update/@version').text;
   ActualInfo.FFileName := Dom.selectSingleNode('update/@file').text;
end;

procedure TclWebUpdate.StoreLastUpdateInfo(ASuccess: Boolean);
var
   Dom: IXMLDomDocument;
   Node: IXMLDomElement;
begin
   Dom := CoDOMDocument.Create();
   Node := Dom.createElement('update');
   Dom.appendChild(Node);
   Node.setAttribute('file', ActualInfo.FileName);
   Node.setAttribute('version', ActualInfo.Version);
   Node.setAttribute('success', ActualInfo.Success);
   Node.setAttribute('updatedate', ActualInfo.UpdateDate);
   Dom.save(LastUpdateInfoFile);
end;

Here are the ActualInfo and the UpdateInfo properties which represent structures corresponding to current application version and version of update available on the web. If the downloading process has been completed successfully the update information has been saved to the local file (lastupdate.xml by default). So if you want to run web update one more time using our demo programs (demo1src.zipdemo2src.zip) you may delete this file or modify the version parameter to the previous value (in our case just replace the version attribute with the "1.0" value).

After the whole update info is received we need to compare versions and determine whether should we download and finally apply the update available on the web. If you are using simple enumeration for your updates (like 1, 2, 3 .... ) then the comparing method looks very simple:

function TclWebUpdate.CheckUpdateVersion(const ANewVersion, ActualVersion: string): Boolean;
begin
   Result := StrToIntDef(ANewVersion, 0) > StrToIntDef(ActualVersion, 0);
end;

This function returns True if the web version is up-to-date and needed to be downloaded.
In case of using the standard version format which consists of the following parts: major version.minor version.release.build (2.5.0.33 e.g.) then you need to perform some additional calculations.

This function converts each component version to the corresponding tens place:

function MakeCompleteNumber(S: string): Integer;
var
   i, Cnt, Len, OldPos: Integer;
begin
   S := Trim(S);
   Result := 0;
   Len := Length(S);
   Cnt := 0;
   OldPos := Len + 1;
   for i := Len downto 1 do
   begin
      if (S[i] = '.') then
      begin
         Result := Result + StrToIntDef('0' + system.Copy(S, i + 1, OldPos - i - 1), 0) * Round(Power(1000, Cnt));
         Inc(Cnt);
         OldPos := i;
      end else
      if (i = 1) then
      begin
         Result := Result + StrToIntDef('0' + system.Copy(S, i, OldPos - i), 0) * Round(Power(1000, Cnt));
      end;
   end;
end;

After all checks completed and web update is up-to-date then the update file downloading process begins. In order to perform the downloading in the background mode without interfering with main application process we use the Clever Downloader component from the Clever Internet Suite library. Following code sample shows how easy downloading process can be implemented:

...
FDownloader: TclDownloader;
...
FDownloader := TclDownloader.Create(nil);
FDownloader.OnProcessCompleted := DoOnProcessCompleted;
...
procedure TclWebUpdate.StartDownloading();
begin
   FDownloader.URL := UpdateInfo.URL;
   FDownloader.UserName := UserName;
   FDownloader.Password := Password;
   FDownloader.Start(True);
end;

Since the Start downloader method is called with Async parameter which equal to True the component returns control immediately after calling of this method. So to find out the time when the downloading process is completed we use the OnProcessCompleted downloader event.

To process downloaded update file(s) we implement simple algorithm with using of ShellExecute WinApi function. So if you are about to extract archived update files you can simple call this function as follows:

ShellExecute(Application.Handle, 'open', 'webupdate.zip', nil, nil, SW_SHOWNORMAL);

This function opens your archive with application which assigned as default one for such a file type. If you have WinZip installed and it is assigned as default application for the '*.zip' files then the webupdate.zip archive will be opened and unpacked. You can specify your own method of handling and applying updates to suit your own needs.

Full component source code is available at component.zip and allows you to customize the executing process with specifying the run application and its run parameters. This can be useful if you want to run a custom application with its specific command line parameters.

It's well known that to replace running application you have to terminate it before. You can make the update process more comfortable and include the code which closes the application just after ShellExecute call:

...
var
CanTerminate: Boolean;
...
if NeedTerminate then
begin
   CanTerminate := True;
   DoTerminating(CanTerminate); //raises an event which user can overwrite in order to perform some custom actions on application terminating request or cancel closing
   if CanTerminate then
   begin
      Application.Terminate();
   end;
end;

Requirements

You must have installed the Internet Explorer version 3.0 or higher on the client computer.

This code is constantly improved and your comments and suggestions are always welcome.

 

With best regards,
Sergey Shirokov
Clever Components team.
Please feel free to Contact Us

    Copyright © 2000-2024