在编写我自己的自动更新程序时,是否有一个我应该遵循的通用框架?
不久前,我正在阅读如何创建一个“引导程序”,该程序将在主应用程序之前首先加载(因为由于文件锁定等原因无法更新正在运行的应用程序)
那么有什么提示/最佳实践吗?
在编写我自己的自动更新程序时,是否有一个我应该遵循的通用框架?
不久前,我正在阅读如何创建一个“引导程序”,该程序将在主应用程序之前首先加载(因为由于文件锁定等原因无法更新正在运行的应用程序)
那么有什么提示/最佳实践吗?
您可能必须自己编写。如前所述,基本思想是将您的程序的最新版本(我假设一个 EXE)放在服务器上,然后让您的应用程序在启动时与服务器检查,如果它是从服务器下载 EXE较新的版本。
我通常将其实现为应用程序在启动时调用的 Web 服务。关于这种方法的几个警告:
Web服务方法需要获取服务器上EXE的版本号,并与调用者的版本号进行比较。如果您使用 Assembly 类来读取服务器 EXE 的版本号,只要 Web 服务实例正在运行(至少 20 分钟),这将锁定文件。因此,您有时可能无法用较新版本替换服务器上的 EXE。请改用 AssemblyName 类 - 这允许您在不加载程序集(并锁定它)的情况下读取程序集信息。
调用者应用程序不能用新版本替换它自己的文件——你不能删除或更新正在运行的应用程序文件。然而,它可以做的是在运行时重命名自己的文件。所以自动更新的技巧是应用程序重命名自己(例如“MyApplication.exe”到“MyApplication_OLD.exe”),将新版本下载到应用程序文件夹(名为“MyApplication.exe”),通知用户发生需要重新启动应用程序的更新,然后结束。当用户重新启动应用程序时,将启动较新的版本 - 此版本会检查并删除旧版本。
在这样的更新之后自动重启应用程序的自动更新是非常棘手的(它涉及启动另一个进程,然后在自动重启进程启动之前结束它自己的进程)。我从来没有用户抱怨必须重新启动应用程序。
好的,首先,如果市场上的安装程序/更新程序产品满足您的需求,您可能应该使用这些产品。话虽如此,不久前我有幸自己构建了这样的系统。是的,我们的安装程序/更新程序在客户端包括两个部分,因此:
~ A 部分将连接到存储和发布最新版本的服务器;如果有更新版本的 B 部分可用,它将下载并启动它
~ B 部分将专注于安装/更新实际应用程序(并且可以下载和安装 A 部分的更新)。
除此之外,我建议始终在安装程序/更新程序中考虑以下 4 个操作:
安装和卸载
更新和回滚(即撤消上次更新)
当您的用户拥有一个可以在一夜之间自动更新的系统时,回滚是至关重要的。如果每次更新都搞砸了,他们可以在您解决问题时回滚并继续工作。
最后,安装程序/更新程序应尝试对其安装/更新的应用程序不可知论,以便当该应用程序更改时,安装程序/更新程序系统受到的影响尽可能小。
我为我使用 C++ 开发的应用程序编写了一个更新程序,但总体结构是相同的。
这对我们来说效果很好,因为它总是首先下载一个新的“更新程序”,所以我们可以处理一些可能无法正常工作的时髦新事物。
编写了我们自己的自动更新软件。所以这是我的建议......不要这样做!当然,这一切都取决于您的具体情况,但以下是我公司遇到的问题:
我们软件的问题在于,我们需要在所有 Windows 平台上提供很大的灵活性和支持。我们编写的解决方案运行良好,但是当 Windows 版本发生变化或与我们实验室中的版本不太一样时,它无法扩展并开始失败。我们最终购买了软件,我建议您也这样做。如果您有兴趣,我们购买了一个名为 AutoUpdate+ 的产品(链接文本)。
这是我为解决我们对 WinForms 和 WPF 应用程序的特定需求而编写的一个开源解决方案。总体思路是以尽可能低的开销获得最大的灵活性。
因此,集成非常简单,该库几乎可以为您完成所有工作,包括同步操作。它也非常灵活,可以让您确定要执行哪些任务以及在什么条件下执行 - 您制定规则(或使用一些已经存在的规则)。最后一点是对任何更新源(网络、BitTorrent 等)和任何提要格式的支持——任何未实现的内容您都可以自己编写。
还支持冷更新(需要重新启动应用程序),并且会自动完成,除非为任务指定“热交换”。
这归结为一个 DLL,大小不到 70kb。
更多详情请访问http://www.code972.com/blog/2010/08/nappupdate-application-auto-update-framework-for-dotnet/
代码位于http://github.com/synhershko/NAppUpdate(在 Apache 2.0 许可下获得许可)
当我有更多时间时,我计划进一步扩展它,但老实说,你应该能够自己快速增强它,以应对它目前不支持的任何东西。
ClickOnce 对我们来说效果不佳。我们将数据库安装在客户的服务器上。在我们进行更新之前,他们的数据库需要更新。我们的客户不止 2 或 3 个,因此为我们的应用程序执行 ClickOnce 并不是最好的主意,除非我遗漏了有关 ClickOnce 的一些重要内容。
我所做的是向数据库中添加一个字段以获取版本号。在我们的 ftp 站点上,我有一个版本文件夹,其中包含我们应用程序的每个版本号的文件夹。在该特定版本的文件夹中,我们放置了一个 zip 文件,其中包含 setup.exe 和 setup.exe 将启动的 msi 文件。所有的先决条件都是从供应商站点下载的,以确保我们的 FTP 站点不会受到大量下载的影响(当我们迁移到 .Net 3.5 时)。当我们的应用程序启动时,它会检查数据库中的版本号字段,如果它与当前版本的程序集版本不同,它将连接到 ftp 站点,从新版本的文件夹下载 zip 文件,解压缩,然后执行设置。这将安装较新版本的 .Net 或我们可能添加的任何其他要求,
如果您使用的是 .Net,为什么不直接使用 ClickOnce?它将完成您所说的所有开箱即用的操作,并且几乎需要零设置。
此 zip 包含单词搜索生成器工具的源代码,其中包括 AppUpdater 样板代码。
http://cid-842434ebe9688900.skydrive.live.com/self.aspx/Games/WordSearchGenerator-src-v1.3.zip
查找名称中包含“AppUpdater”的 3 个源模块。
它非常简单,仅适用于单组件应用程序。没有 MSI 文件。只是一个EXE。理念是更新检查会自动发生,但只有在用户确认后才会安装更新。
更新程序的工作方式:
它从包含“最新版本”信息的 URL 以及实际新版本所在的第二个 URL 加载 XML 文档。更新程序逻辑会验证 XML 文档上的签名,但您可能并不关心这一点。然后更新程序将当前版本与最新版本进行比较,并且可以告诉应用程序是否有可用更新。更新程序还处理就地替换问题。
在此模型中,应用程序在更新期间存在三个“生命周期阶段”。在正常过程中,应用程序会检查更新,然后正常运行。在某些时候,用户可能会确认他们想要安装可用的更新,并且更新程序将应用程序下载到一个临时位置,然后使用新下载的 exe 启动一个进程。然后更新程序逻辑退出第一个进程。第二个进程,根据第一个进程给它的命令行参数,意识到它是一个新下载的副本,需要复制自己。它将自己复制到原始位置(在命令行上指定),开始exe,然后退出。第三个进程正常启动,看到有更新,然后删除临时 exe 副本。然后它会正常运行,包括检查更新。它会发现没有更新,然后会正常运行。这涵盖了就地更新逻辑的操作。
这一切都由 Windows 窗体或 WPF 窗口的构造函数中的这些行处理:
_Updater = new AppUpdater.SimpleAppUpdater(_MyManifestUrl);
_Updater.Startup(App.CommandLineArgs);
检查更新问题也由构造函数中的几行代码处理,它们创建并运行后台工作线程:
_Worker = new System.ComponentModel.BackgroundWorker();
_Worker.DoWork += CheckLatest;
_Worker.RunWorkerCompleted += CheckCompleted;
_Worker.RunWorkerAsync();
CheckLatest 是这样的:
void CheckLatest(object sender, System.ComponentModel.DoWorkEventArgs e)
{
lock (_Updater)
{
if (_Updater.UpdateIsAvailable) // Checks for update
{
// can update a textbox (etc) here. Be careful of InvokeRequired.
}
}
}
完成的事件是这样的:
void CheckCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (!e.Cancelled && e.Error == null)
{
lock (_Updater)
{
// only display the about box if there is an update available.
if (_Updater.HaveCheckedForUpdate && _Updater.UpdateIsAvailable)
{
// do whatever you want here. Eg, pop a dialog saying
// "an update is available"
About about = new About();
about.Message= "An update is available";
about.ShowDialog();
}
}
}
}
它适用于 WinForms 或 WPF 应用程序。我想它也适用于控制台应用程序,但我从未尝试过。
创建(可能已签名的)清单文件是一项单独的任务,此处未介绍。
考虑一下,这可能更好地打包为基类 AutoUpdatingForm(用于 WinForms)或 AutoUpdatingWindow(用于 WPF)。但我从来没有迈出那一步。
十年前,我写了一个自动更新程序(不是在 .NET 中),但它的要点是应用程序有一个在启动时检查的版本 ID。在这种情况下,应用程序会查询具有当前版本的数据库表,如果运行版本低于当前版本,则会提示用户下载最新版本。通过检查 URL 可以完成同样的事情。
我尚未尝试的解决方案之一是拥有一个可以在静默模式下运行的安装程序。
应用程序调用服务器,如果需要更新,它会下载最后一个安装程序,然后使用静默或更新标志运行它。
它有很多好处: