A .Net 自动更新项目模板






4.15/5 (14投票s)
2004年3月5日
10分钟阅读

62453

1638
一个基础模板,演示如何使用 .Net 框架创建自动更新的应用程序。
引言
本文假定您具备 .Net 编程知识。所有示例都将使用 C# 和 Visual Studio。还需要本地安装 Internet Information Services 以便进行测试。
在我看来,.Net 提供的一项最酷的技术是便捷的自动更新。然而,关于这方面的优秀入门教程并不多,唯一的例外几乎都是 Chris Sells 撰写的相关文章(您是我的偶像)。
我的文章将展示我如何设置自动更新应用程序的框架,读完后您应该会有一个很好的起点来开发您想要的东西。或者至少,您会更清楚一个简单的应用程序可以多么轻松地实现自动部署。
场景
自动更新应用程序有许多不同的场景。我将给出我对它们的看法和术语。
1. 一键式。这基本上是一个始终从 Web 链接运行的应用程序。用户单击链接,所需文件将在需要时被拉取到客户端的 GAC(全局程序集缓存)的临时下载缓存中。如果服务器文件不是最新的,就不会被拉取。
2. 完全连接的存根。在这种场景下,一个小型的存根可执行文件安装在客户端计算机上。该存根的作用只是调用位于网络另一台计算机上的程序集。自动更新仅通过更新网络上的程序集来实现。
3. 部分连接的存根。这需要实现一个拉取机制。完整的应用程序安装在客户端计算机上。存根可执行文件负责检查连接性,然后从其他地方拉取任何更新的程序集。
4. 完全断开连接的存根。好吧,我不得不告诉您,这里没有自动更新。您没有任何可以拉取更新的地方。不行,我帮不了您。抱歉。
这显然不是一个包罗万象的列表。肯定还有其他情况和处理方法。我只是列出我发现最常见的场景以及我如何处理它们。
开始
在本篇文章中,我将处理场景 2。如您所见,它的实现并非最容易,但仍然相当简单,并且能让您体验更高级的功能。那么,开始吧。
我们将首先在 Visual Studio 中创建一个新的“空白解决方案”。我们将此解决方案命名为 AutoUpdaterTemplate。接下来,我们将向此解决方案添加 2 个项目。第一个将是 Windows 应用程序项目,它将成为我们的存根可执行文件。第二个将是类库项目,它将封装我们的 UI。因此,我现在有一个解决方案 AutoUpdaterTemplate,以及两个空项目 AutoUpdaterTemplate.Deployment 和 AutoUpdaterTemplate.UI.Win。我的项目命名约定仅仅是为了帮助我理清思路。您可以随意使用任何您觉得舒服的名称。只是记住以后在遇到问题时,这些命名将有所帮助;)
我们要做的第一件事是创建一个非常基础的 UI。没有它,就没有东西可以部署。所以,我们删掉 UI 项目中的 Class1,替换为一个 WindowsForm。您可以随意在窗体中添加任何内容,但为了简单起见,我将只保留一个显示“Auto Updater Version 1.0”的标签。
目前这对我来说足够了。接下来是更有趣的部分。
自动部署 #1
为了实现基本的自动部署功能,我们首先需要删除部署项目中烦人的通用窗体,并用一个空类替换它。您以后可能会发现这个窗体很有用,但现在我们只关注基础。所以,我们现在有一个类 AutoUpdaterMain,可以开始工作了。
显然,我们需要一个标准的 `static void Main()` 方法,否则编译器会非常生气。所以,我们将移除类的构造函数(不需要),并用以下代码替换:
[STAThread] static void Main() { }
好了。我们现在有了一个什么都不做的应用程序。没什么意思。让我们让它显示我们的 UI。在部署项目中添加对 UI 项目的引用。打开主类,确保您有一个 `using System.Windows.Forms;` 引用,并将以下代码添加到您的 main 函数中(或基于您的项目和窗体名称的类似代码):
AutoUpdaterTemplate.UI.Win.MainForm form = new AutoUpdaterTemplate.UI.Win.MainForm();
Application.Run(form);
恭喜。您现在有了一个可运行的应用程序。试试看。不仅如此,您还编写了所有实现自动更新场景 1 所需的代码。不相信我?我来给您展示。但首先,一个小改动。打开位于 UI 项目中的 AssemblyInfo.cs 文件。更改此行:
[assembly: AssemblyVersion("1.0.*")]
改为这样。
[assembly: AssemblyVersion("1.0.0.0")]
您以后会感谢我的。
现在,在您的计算机上创建一个目录,并将部署可执行文件和 UI DLL 从它们各自的调试文件夹复制到其中。接下来,在 IIS 中创建一个名为 AutoUpdater 的虚拟目录,并将其指向这个新目录。最后,在您的 Web 浏览器中输入:
https:///AutoUpdater/AutoUpdaterTemplate.Deployment.exe
瞧,您的应用程序运行了。但是它是否自动更新?当然是。这是测试方法。打开您的 Visual Studio 命令提示符,然后输入 `gacutil /ldl`。这将列出您 GAC 临时下载缓存中的所有文件。看到您的 UI DLL 了吗?看到它的版本了吗?应该是 1.0.0.0。好的,现在更改一下您的 UI 窗体。我将我的标签设置为:
现在将 AssemblyVersion 更改为如下所示:
[assembly: AssemblyVersion("1.0.0.1")]
重新编译,将新的 DLL 复制到您的 IIS 目录中,然后在浏览器中再次运行它。您会看到您所做的更改已经显示出来。不仅如此,再次尝试 `gacutil /ldl` 命令。现在列出了 UI DLL 的两个版本。没错,CLR 确定您缓存中的版本已过时,并下载了新版本。真正的 .Net 自动部署。
自动部署 #2
到目前为止,我们必须要求用户始终通过链接访问我们的应用程序。虽然这对于许多应用程序来说已经足够,但我们希望使其像运行我们计算机上的任何其他应用程序一样简单。无需浏览器。所以我们将继续。
理想情况下,我们希望它尽可能轻量级,最好情况下只有一台机器上的存根可执行文件,以及可能相关的 `app.config` 文件。然而,这意味着 UI DLL 不会本地存储在用户计算机上。再见,引用。我们现在将其从部署项目中删除。但是,既然我们没有对 DLL 的引用,我们主方法中的代码就broken了。我们无法以同样的方式创建主 UI 窗体的实例。答案是什么?反射。我将向您展示。
本文无意教您反射。这是一个过于宽泛的话题,远远超出了我当前的范围。就当前问题而言,您只需要知道反射将允许我们从我们没有直接引用的程序集中创建对象。
我们需要解决的第一个问题是我们的可执行文件如何知道去哪里查找我们的 UI DLL。通常,CLR 会使用我们可执行文件的 `CodeBase` 属性。这是对可执行文件所在位置的引用。然而,在我们的情况下这是没有用的。我们需要一种方法来告诉 CLR 去哪里查找。或者,更好的是,我们将自己获取 DLL。这将涉及使用 `Assembly` 类,这将迫使我们在 `using` 语句列表中添加 `using System.Reflection;`。我们现在将开始以这种新方式重建我们的主方法。我们发现可以使用 `Assembly` 类的 `LoadFrom` 方法自行加载程序集。我们得到的是:
[STAThread] static void Main() { try { string codeBase = "https:///AutoUpdater/"; Assembly uiAssembly = Assembly.LoadFrom(codeBase + "AutoUpdaterTemplate.UI.Win.dll"); } catch(Exception err) { MessageBox.Show(err.Message); } }
出于您将在稍后看到的原因,我选择抽象化 `codeBase`。现在我们有了 UI DLL 的实例,我们将需要一点反射来提取我们的窗体。这行代码应该可以做到:
Type type = uiAssembly.GetType("AutoUpdaterTemplate.UI.Win.MainForm", true, false);
最后,我们将这个窗体通过这个方法重新插入到我们的应用程序中:
Application.Run((Form)Activator.CreateInstance(type));
就是这样。您现在拥有了一个功能齐全的存根可执行文件。问题是什么?还记得 `codeBase` 变量吗?是的,硬编码它是个糟糕的主意。我们将查找 `System.Configuration` 命名空间来解决这个问题。现在就添加该 `using` 引用。
使其灵活
`System.Configuration` 将允许我们从 `App.config` 文件中拉取动态设置。因此,我们需要一个这样的文件。让我们向部署项目添加一个。在我们新的 `App.config` 文件中,我们需要一个 `appSettings` 元素以及其中的一个 `codeBase` 元素。最终,我们的 `App.config` 文件应该如下所示:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key=”codeBase” value=”http:///AutoUpdater/” /> </appSettings> </configuration>
现在我们可以更改我们主方法中的那个变量初始化行,我们得到了这个:
[STAThread] static void Main() { try { string codeBase = ConfigurationSettings.AppSettings["codeBase"].ToString(); Assembly uiAssembly = Assembly.LoadFrom(codeBase + "AutoUpdaterTemplate.UI.Win.dll"); Type type = uiAssembly.GetType("AutoUpdaterTemplate.UI.Win.MainForm", true, false); Application.Run((Form)Activator.CreateInstance(type)); } catch(Exception err) { MessageBox.Show(err.Message); } }
这是一个好得多的设置,因为我们现在可以更改 UI DLL 的位置,而无需重新编译,只需更改配置文件即可。
现在您会很高兴地看到,如果您将部署可执行文件及其相关配置文件复制到您计算机上的任何位置,应用程序都会按预期运行。
但是……它会自动更新吗?嗯,自己做个测试。更改您的 UI。更新 `AssemblyVersion`。将其复制到您的 IIS 目录。现在从您在计算机上复制可执行文件的任何位置重新运行该应用程序。它工作了吗?当然。我会误导您吗?
好消息
FYI:`AssemblyVersion` 更新?是的,CLR 使用版本来确定是否拉取新程序集。您不必每次都更新版本,但除非您这样做,或者运行 `gacutil /cdl` 命令,否则新功能不会被下载。这将清除您 GAC 的临时下载缓存,迫使 CLR 下载可用的任何版本。
总结
就是这样。我们现在已经创建了一个自动更新的应用程序,并成功地将其部署到了两种不同的场景中。就是这么简单。但是真的这么简单吗?遗憾的是,并非如此。所有这些都已成功,并且对于非常简单的应用程序来说都能正常工作,但我们必须记住,前面情况下的自动部署应用程序是从互联网上拉取资源的,因此会受到 Microsoft 对 InternetZone 施加的严格限制。要实现更高级的功能,我们需要开始研究代码访问安全和强命名等主题。唉,那些是更高级别文章的主题。如果你们中有任何感兴趣的人,请告诉我,我很乐意提供。
祝你好运。
Travis Merkel
v-trmerk@microsoft.com