自动化应用程序更新机制的实现

在本地网络环境中,应用程序的更新常常是一项耗时的任务,尤其是当应用程序分布在多个客户端上,并且更新频繁时。为了解决这个问题,编写了一个简单的函数,它能够在不需要用户干预的情况下自动检测应用程序的新版本并进行安装。

实现这一功能的前提是应用程序的安装文件(.msi)需要放置在一个预设的网络文件夹中,以便每个客户端都能够访问并检查它。

工作原理

应用程序的主函数需要通过包含下面的代码进行修改。现在,当客户端启动应用程序时,代码会检查.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()的函数中完成,需要将这个函数复制并粘贴到应用程序的主程序中。这个函数需要在Application.Run()之前调用,例如:

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

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

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 是更新安装包放置的位置;通常,它是一个网络文件夹,以便所有客户端都可以访问。 BatName 是由程序生成的临时批处理文件,用于执行所有的安装/卸载工作。 ProductCode 是应用程序的产品代码,可以直接从应用程序的设置属性页获得。 整个函数: 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 ; }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485