65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Windows Installer 更新应用程序客户端:一个简单的解决方法

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.37/5 (9投票s)

2008年9月30日

公共领域

3分钟阅读

viewsIcon

49729

downloadIcon

478

如何使用 Windows Installer 和批处理文件更新本地分发应用程序的所有客户端。

引言

如果使用 Windows Installer 部署的应用程序分布在本地网络的许多客户端上,并且更新频繁,那么更新应用程序可能是一项耗时的任务。

我编写了这个简单的函数,它可以自动检测应用程序的新版本,并以透明的方式安装它,无需用户干预。

唯一的要求是将应用程序的安装文件 (.msi) 放置在一个预先确定的网络文件夹中,每个客户端都可以访问和检查它。

工作原理

必须修改应用程序的 Main 函数,包括下面提供的小段代码。 现在,当应用程序在客户端上启动时,代码会检查 .msi 文件是否具有新版本,如果是,则

  • 删除当前版本的应用程序
  • .msi 文件安装新版本
  • 再次启动应用程序

如果安装文件的文件日期至少比可执行文件 (.exe) 的文件日期晚两分钟,则认为安装文件是较新的(粗略的方法,但有效)。

由于应用程序无法在运行时卸载自身,因此它会自行退出并调用批处理文件来完成卸载/安装工作。 这个批处理文件由程序直接生成并放入临时文件夹中。 它看起来像这样

@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"

第 2 行的 ping 命令用于在卸载之前添加一个小的延迟,以确保调用应用程序已终止。 使用 ping 是因为没有标准的 Windows 命令来引入延迟(例如,sleep 命令)。

在第 3 行,使用 Windows Installer (/x 开关) 卸载旧应用程序。 /qr 开关最小化用户界面。

在第 4 行,安装新版本的应用程序。

在第 5 行和第 6 行,再次启动应用程序,提供相同的工作文件夹和相同的命令行参数(如果有)。 使用 start 命令使批处理文件继续执行,而无需等待应用程序完成。

使用代码

所有工作都在一个名为 CheckApplicationUpdate() 的函数中完成,您必须将其复制并粘贴到应用程序的 Main 程序中。 必须在 Application.Run() 之前调用此函数,例如

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());
}

需要通过修改三个字符串来定制 CheckApplicationUpdate()

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 是您放置更新安装包的位置;通常,它是一个网络文件夹,以便所有客户端都可以访问它。
  • BatName 是程序生成的临时批处理文件,用于完成所有安装/卸载工作。
  • ProductCode 是应用程序的产品代码,可以直接从应用程序的“设置”属性页获取。

整个函数

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;           
}

关注点

这种解决方法远非优雅,而且相当粗糙。 但是,它被证明是有效的,因为它在更新客户端计算机时为我节省了很多时间,因为我经常进行更新。 现在,我所做的就是将安装文件放入文件夹中,然后忘记更新。

历史

  • 2008 年 9 月 30 日 - 第一个(可能也是唯一一个)版本。
© . All rights reserved.