Click here to Skip to main content
15,881,709 members
Articles / Desktop Programming / Win32
Article

Updating application clients with a Windows Installer: a simple workaround

Rate me:
Please Sign up or sign in to vote.
4.37/5 (10 votes)
30 Sep 2008Public Domain3 min read 48K   454   48   12
How to update all the clients of your locally distribuited application using a Windows Installer and batch files.

Introduction

Updating an application deployed with Windows Installer can be a time-consuming task if the application itself is distributed over many clients in a local network and if updates are frequent.

I've written this simple function that automatically detects a new version of the application and installs it transparently, without user intervention.

The only requirement is that the installation file (.msi) of the application has to be placed in a predetermined network folder where every client can access and check it.

How it works

The application's Main function has to be modified by including the small code presented below. Now, when the application is launched on the client, the code checks if the .msi file features a new version, and in that case:

  • removes the current version of the application
  • installs the new one from the .msi file
  • launches the application again

The installation file is considered newer if its file date is at least two minutes greater than the executable's (.exe) file date (crude method, but effective).

Because the application can't uninstall itself while it's running, it quits itself and calls a batch file to do the uninstall/install work. This batch file is generated directly by the program and put in a temporary folder. It looks like this:

@echo off
ping localhost -n 2
C:\WINDOWS\system32\msiexec.exe /x {67BC5B51-1534-4C68-86C4-C74F4469C2BA} /qr 
C:\WINDOWS\system32\msiexec.exe /i "Z:\setup\MyAppSetup.msi" /qr
cd "C:\Program Files\Myapp"
start "" "C:\Program Files\Myapp\MyApp.exe"

The ping command at line 2 is used to add a small delay before the uninstall, just to make sure that the calling application has terminated. ping is used because there is no standard Windows command for introducing a delay (e.g., a sleep command).

At line 3, the old application in uninstalled using Windows Installer (/x switch). The /qr switch minimizes the user interface.

At line 4, the new version of the application is installed.

In lines 5 and 6, the application is launched again, providing the same working folder and the same command line parameters (if any). The start command is used to make the batch file continue its execution without waiting for the application to complete.

Using the code

All the work is done in a single function called CheckApplicationUpdate() that you have to copy and paste in the Main program of the application. This function has to be called just before Application.Run(), e.g.:

C#
static void Main()
{
   Application.EnableVisualStyles();
   Application.SetCompatibleTextRenderingDefault(false);   

   // add the following line to your main       
   if(CheckApplicationUpdate()) return;    // update is needed, quit               
         
   Application.Run(new Form1());
}

The CheckApplicationUpdate()<code> needs to be customized by modifying the three strings:

C#
static bool CheckApplicationUpdate()
{
   string InstallFile = "Z:\\setup_folder\\MyApp.msi";    
   string BatName = "c:\\windows\\temp\\update.bat";              
   string ProductCode = "{67BC5B51-1534-4C68-86C4-C74F4469C2BA}";
  • InstallFile is where you place the update installation package; usually, it is a network folder so that all clients may access it.
  • BatName is the temporary batch file generated by the program used to do all the install/uninstall work.
  • ProductCode is the Product Code of your application which can be obtained directly from the Setup property page of the application.

The whole function:

C#
static bool CheckApplicationUpdate()
{
    // location of setup file
    string InstallFile = "Z:\\setup_folder\\MyApplication.msi";
    // name of the .bat file that does the uninstall/install
    string BatName = "c:\\windows\\temp\\update.bat";
    // product code of the application
    string ProductCode = "{67BC5B51-1534-4C68-86C4-C74F4469C2BA}";
    
    // if install file is not available skip the whole process
    if(!File.Exists(InstallFile))
        return false;
    
    // calculates the time difference betwenn install package and executable
    DateTime EXE_Stamp = Directory.GetLastWriteTime(Application.ExecutablePath);
    DateTime MSI_Stamp = Directory.GetLastWriteTime(InstallFile);
    TimeSpan diff = MSI_Stamp - EXE_Stamp;
    
    // if installable is newer than 2 minutes, does the new install
    if(diff.Minutes > 2)
    {
       string msg = "A new version of "+Application.ProductName+ 
                    " is available.\r\n\r\nClick OK to install it.";
       MessageBox.Show(msg,"Updates available", 
                       MessageBoxButtons.OK, 
                       MessageBoxIcon.Information);

       // prepares the batch file
       string BatFile = "";
       string old_dir = Directory.GetCurrentDirectory();
       BatFile += "@echo off\r\n";
       BatFile += "ping localhost -n 2\r\n";
       BatFile += "C:\\WINDOWS\\system32\\msiexec.exe /x "+ 
                  ProductCode+" /qr \r\n";
       BatFile += "C:\\WINDOWS\\system32\\msiexec.exe /i \""+ 
                  InstallFile+"\" /qr\r\n";
       BatFile += "cd \""+old_dir+"\"\r\n";
       BatFile += "start \"\" "+ 
                  Environment.CommandLine+"\r\n";
       StreamWriter sw = new StreamWriter(BatName);
       sw.Write(BatFile);
       sw.Close();                            

       // executes the batch file
       System.Diagnostics.ProcessStartInfo psi = 
         new System.Diagnostics.ProcessStartInfo();
       psi.FileName = BatName;
       psi.Arguments = "";
       psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
       System.Diagnostics.Process p = new System.Diagnostics.Process();
       p.StartInfo = psi;
       p.Start();

       return true;
    }
    return false;           
}

Points of interest

This workaround is far from being elegant, and it is rather raw. But, it proved to be effective because it saved me lot of time when updating client computers, because I do frequent updates. Now, all I do is put the install file in the folder and forget about updating.

History

  • 30-Sep-2008 - First (and possibly only one) version.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Software Developer
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionPlace of adding this code. Pin
Member 1132406121-Dec-14 4:22
Member 1132406121-Dec-14 4:22 
AnswerRe: Place of adding this code. Pin
Antonino Porcino21-Dec-14 21:47
Antonino Porcino21-Dec-14 21:47 
GeneralRe: Place of adding this code. Pin
Member 1132406124-Dec-14 5:28
Member 1132406124-Dec-14 5:28 
GeneralRe: Place of adding this code. Pin
Antonino Porcino24-Dec-14 6:50
Antonino Porcino24-Dec-14 6:50 
Questionbatch command to install MSI in Windows 7 when logged in user doesn't not have admin rights Pin
udaygreddy20-Nov-13 7:36
udaygreddy20-Nov-13 7:36 
AnswerRe: batch command to install MSI in Windows 7 when logged in user doesn't not have admin rights Pin
Antonino Porcino21-Nov-13 1:50
Antonino Porcino21-Nov-13 1:50 
Generalgood example Pin
Donsw23-Jan-09 6:42
Donsw23-Jan-09 6:42 
GeneralMinor improvments Pin
TobiasP11-Oct-08 0:18
TobiasP11-Oct-08 0:18 
GeneralA thought Pin
Ilíon1-Oct-08 4:33
Ilíon1-Oct-08 4:33 
GeneralRe: A thought Pin
Antonino Porcino1-Oct-08 5:48
Antonino Porcino1-Oct-08 5:48 
GeneralRe: A thought [modified] Pin
Ilíon1-Oct-08 6:37
Ilíon1-Oct-08 6:37 
GeneralRe: A thought Pin
Antonino Porcino1-Oct-08 20:36
Antonino Porcino1-Oct-08 20:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.