我该如何实现自动更新程序?

81
许多程序都包括自动更新程序,该程序会定期在线查找更新,并下载和应用发现的任何更新。程序错误被修复,支持文件被修改,事情变得更好(通常如此)。
不幸的是,无论我如何搜索,我都找不到有关此过程的信息。似乎已经实施的自动更新程序要么是专有的,要么不被认为很重要。
看起来很容易实现在网络上寻找更新并在可用时下载它们的系统。这个自动更新程序的那部分将从实现到实现变化很大。问题是应用补丁的不同方法是什么。只是下载文件并用新文件替换旧文件、运行已下载的迁移脚本、猴子补丁系统的某些部分等等?概念优先,但Java、C、Python、Ruby、Lisp等语言的示例将不胜感激。

6
我一直在查看谷歌的omaha。如果你仍然感兴趣的话,这可能是值得关注的东西(4年后...)。 - funseiki
17个回答

58

我认为“语言无关”将成为一个限制因素。 应用程序有各种不同的形状和尺寸,因此没有一种通用的答案。 我已经在几种不同的编程语言中实现了几个自动更新器,但其中没有两个相似。

最常见的方法是应用程序通过某个主页位置(网络地址、网络查询、企业网络位置等)检查它的版本是否最新,如果需要更新,则每个情况下的更新过程都不同。

另一种流行的方法是邀请主页位置在应用程序启动时运行脚本。该脚本可以检查版本,如果需要则下载更新,并请求使用反馈。

如果您缩小参数范围,我们可能可以提供更好的帮助。

更新:修补程序的方法也取决于应用程序的性质,在这里有非常广泛的多样性。例如,如果您只有一个可执行文件,则替换可执行文件可能是最实际的方法。 如果您的应用程序有许多文件,则应寻找最小化替换文件数量的方法。 如果您的应用程序高度定制或参数化,则应尽力最小化重新定制的工作量。 如果您的应用程序采用解释性代码(例如Excel VBA应用程序或MS Access MDB应用程序),则可以替换代码的部分内容。 在Java应用程序中,您可能只需要替换一个JAR文件,甚至是JAR文件的子集。 您还需要一种识别当前客户端版本并适当更新的方法。 我可以继续说下去,但我希望您能理解我关于多样性的观点。 这是许多情况下最好的答案通常以“哦,这要看......!”开始的原因之一。 这就是为什么许多答案都包括“请缩小参数范围”的原因。


21

请确保考虑关于获取更新信息以及更新二进制文件的安全性问题。

您信任下载来源吗?您可能正在向一个服务器请求获取更新,但如果有“中间人”将您重定向到恶意服务器会怎样呢?使用 HTTPS 或类似的安全连接可以帮助防范此类攻击,但建议通过数字签名检查来双重确认最终下载的位是否安全。


8

首先,您需要在应用程序主页网站上拥有一个包含最新版本的文件。我认为最好的方式是创建一个专门的SQL表格来完成这个任务,并在发布新版本/夜间构建完成后自动填充它。 您的应用程序会创建一个新线程,请求内置的http链接以获取版本号并将其与当前版本进行比较。在.NET中,您可以使用以下代码:

Version GetLatestVersion() {
HttpWebRequestrequest = (HttpWebRequest)WebRequest.Create(new Uri(new Uri(http://example.net), "version.txt));
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
if (request.HaveResponse)
{
  StreamReader stream = new StreamReader(response.GetResponseStream(), Encoding.Default);
  return new Version(stream.ReadLine());
}
else
{
  return null;
}
}

Version latest = GetLatestVersion();
Version current = new Version(Application.ProductVersion);
if (current < latest)
{
  // you need an update
}
else
{
  // you are up-to-date
}

在这个例子中,version.php 只是一个类似于 1.0.1.0 的普通字符串。
我可以给出另一个提示 - 如何下载更新。 我非常喜欢下面的想法:在应用程序资源中有一串CLR代码,你可以使用CodeDom动态编译到临时文件夹中,主应用程序调用它并关闭。更新程序读取参数、设置或注册表,并下载新模块。然后调用主应用程序删除所有临时文件。完成!
(但这里的一切都与.NET有关)

6

许多程序都采用最简单的解决方案:运行旧版本的卸载程序,然后运行新版本的安装程序(可选择跳过用户已经回答过的问题,如最终用户许可协议)。唯一的问题是,新版本必须能够从旧版本中读取配置选项。

此外,在Windows操作系统上,您无法删除正在使用的可执行文件,因此您可能需要在Temp文件夹中放置一个小型可执行文件,该文件运行整个过程,然后从启动的新版本实例中删除它(或者仅注册以在下次重新启动时删除)。


16
虽然您无法删除正在使用的EXE文件,但可以将其重命名。因此,运行中的实例可以将自身重命名,插入新版本并在下次启动时检查已重命名的先前版本是否存在,并将其删除。 - Oliver Giesen
哇,太棒了!感谢你的提示! - Grey Panther

4

还有一件事情没有被提到,那就是你应该认真考虑运行程序的用户可能没有足够的权限来升级它。对于企业用户来说,这应该相当常见,对于家庭用户来说可能较少。

出于安全原因,我总是使用(自我限制的)受限帐户进行工作,而且最让我恼火的是,大多数自动更新程序只是假设我正在以管理员身份运行,然后在下载后失败,除了实际关闭程序并以管理员身份重新运行之外,没有其他执行更新的方式。大多数甚至不会缓存已下载的更新,必须重新开始全部操作。

如果自动更新程序需要时简单地提示管理员凭据并继续进行将会更好。


1
这正是我们的更新程序所做的。wyUpdate(请参见:http://wyday.com/wyupdate/)是用C#编写的开源更新程序。 - Wyatt O'Day

3
因为自动更新是一个常见的场景,大多数语言都至少有一个可用的包来支持这一点。(下面列出了一些可用的包)
其中一个非常好的想法是 .NET 的 ClickOnce 分发,它是一个安装程序,可以在用户上下文中沙箱化您的应用程序并进行安装,因此不需要管理员权限。您可以在发布时配置 ClickOnce 以在每次应用程序启动时检查更新。
Java 有 Java Web Start,为 Java applet 提供相同类型的功能。
Delphi 有很多关于自动更新的文章,Torry 列出了 WebUpdate 组件 的列表,例如 GoUpdater 似乎具有非常广泛的功能。
他们都使用网站/网络共享来检查新版本,然后获取补丁或完整安装文件并运行它。因此,您应该尝试找到一个好的软件包,以节省开发和维护自己的解决方案的麻烦。

2
这不是一个完整的答案,而是我最近实现的自动更新机制的一个例子。它与传统的像Firefox这样的用户应用程序有些不同,因为它是一个在工作中使用的内部工具。
基本上,它是一个管理Subversion分支队列的小脚本,用于构建和打包一个安装程序。它读取一个小文件,在其中写入分支的名称,获取第一个分支,将其重新写入文件末尾,并启动构建过程,其中涉及调用一堆脚本。每个要构建的分支的配置都写在一个.INI文件中,与工具本身一起存储在Subversion存储库中。
由于该工具在多台计算机上运行,我希望能够在对工具本身或配置脚本进行更改后,立即在所有计算机上自动更新它。
我实现的方式很简单:当我启动该工具时,它变成了一个“外壳”。这个外壳做了两件非常简单的事情:
- 对自身和配置文件执行svn update。 - 再次启动本身,这次作为“内壳”,实际上处理一个配置(然后再次退出)。
这个非常简单的循环更新自己的系统已经服务了我们几个月。它非常优雅,因为它是自包含的:自动更新器是程序本身。因为“外壳”(自动更新器部分)非常简单,所以它并不受益于像“内壳”(每次从更新的源文件中执行)那样的更新。

顺便提一下,Metasploit 在更新过程中也使用“svn update”。 - Grey Panther

2
最简单的方法是让你的程序向服务器(网站)查询是否有更新。如果有更新,你可以向用户显示一条消息,提示他们下载更新版本并提供链接。
另一种更复杂的解决方案是创建一个小型的Windows服务(或Unix守护进程),定期检查是否有更新,该服务可以下载更新并启动安装程序。
总体架构是你拥有一个中央服务器,该服务器知道最新版本和获取最新版本的位置。然后程序查询服务器。我不会包括示例代码,因为它高度依赖于服务器和你选择的格式。但这并不是非常困难。

我要问的问题(我已编辑问题以反映这一点)是如何应用补丁? - num1

1

我假设您是在使用Windows操作系统。

以下方法似乎很有效:

在安装程序中执行以下步骤:
1. 创建一个手动启动的服务,以LocalSystem身份运行,当启动时执行更新操作,然后停止。
2. 更改服务权限,使所有用户都可以启动该服务(如果所有用户都应该能够在没有管理员权限的情况下进行更新)。
3. 更改主程序,使用简单的机制在启动时检查更新。如果检测到更新,提示用户是否要应用它。
4. 如果用户接受更新,请启动服务。

如果架构允许,创建一种监视更新进程的方式。


1
手动启动服务以LocalSystem身份运行,所有用户都可以启动?这听起来像是一场等待发生的安全灾难。 - Milan Gardian
1
只有软件更新是安全灾难时才需要进行更新。开启服务的能力并不等同于攻击该服务的能力,除非他们可以对服务二进制文件进行写入操作。 - Joshua

0
如果你的软件是开源的,并且面向Linux或开发人员,那么将你的软件安装为git仓库并定期拉取稳定分支是非常有趣的。
当你的应用程序通过npm、sbt、mavan、stack、elm-package或类似工具进行管理时,这一过程尤其简单。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接