目录
介绍
背景
使用代码
兴趣点
本文介绍如何在不使用ClickOnce的情况下为您的应用程序提供一键更新或自动更新功能。
- 下载演示项目-586.48 KB
- 下载源-1.13 MB
- 下载Readme.txt -413 B
- 下载版本更新文件-14.87 KB
如果要为用户提供一种更新应用程序的方式,则有几种不同的选择,其中一些是:
- 让用户手动下载和安装更新——通过电子邮件通知它们或依靠它们进行在线检查。
- 使用ClickOnce。
- 允许应用程序在线检查更新,并自动更新或向用户提供一键式更新功能——无需使用ClickOnce。
本文演示了以上列表中的最后一种方法——为您的应用程序提供一键更新或自动更新功能,而无需使用ClickOnce。
该更新是通过安装在线更新的类和应用程序完成的,并通过该UpdateMe应用程序进行了说明(包括在本文的下载中)。
没有太大的困难,您应该能够使用update通过UpdateMe 应用程序显示的类和应用程序将自动更新发布给软件用户。
我已使该UpdateMe应用程序尽可能简单,以便您可以使用代码,而不必花费太多时间来筛选与您自己的应用程序相关的内容。
要使用该UpdateMe应用程序,您将需要具有在线存储文件的能力(不过请阅读下一行)。注意:在本文首次发布时,我将相关文件(版本和更新)放置在公用的Dropbox文件夹中,该文件夹由UpdateMe文本框中的默认值指向——所有情况都是无常的,此文件夹可能不会长期可用,我建议您使用自己的网站空间。
提示:使用本文描述和包含的类、方法和应用程序的一个优点是,您可以在免费的文件共享站点上存储版本信息并下载文件,例如Dropbox,而不必花一分钱保存更新信息或自己下载。
请参阅下载中包含的readme.txt文档,以了解如何在您自己的网站空间中设置版本和下载文件。
背景该代码源自我编写的TeboCam网络摄像头安全应用程序,该应用程序很快将以开源形式发布。
该实现采用以下三个关键组件的形式:
- 联机驻留的版本文件——update类用于检查联机更新,并且还包含有关联机更新位置的信息。
- 一个类——包含查询在线版本文件的update方法。对于本文,该类包含在示例UpdateMe应用程序中。
- 应用程序——从联机文件夹下载更新的update,然后重新启动命名的应用程序(在这种情况下是UpdateMe)。
本文主要是关于update类及其在示例UpdateMe应用程序中的用法。
处理顺序如下:
- 主应用程序(在本例中为UpdateMe)下载版本文件并检查该文件以查看是否有可用的更新。
- 如果有可用的更新,则update会调用该应用程序,并向其传递更新文件的位置和名称以及可选的命令行和更新完成后要启动的应用程序名称。然后,主应用程序关闭。
- 该update应用程序确保主应用程序未运行,如果正在运行则将其终止,然后下载文件并启动使用可选命令行参数指定的应用程序。
真的就是这么简单(他说,花了整个周末整理代码后……)。
最初,当我编写此代码时,有人问“如何更新应用程序?”。——回到代码中去——解决方案非常简单:
与该update应用程序相关的所有文件均以前缀在线保存——update类包含一个方法,该方法仅重命名这些文件即可覆盖该update应用程序。
因此,只需一个按钮,就可以更新主应用程序以及update应用程序本身。
本文中有更多有关处理此问题的方法的信息。
有两种更新模式:
- 您可以立即下载文件——这是我们用来下载版本文件以检查是否有在线更新的方法。
- 您可以关闭程序以进行应用程序更新。
此应用程序利用Ionic zip开源库来解压缩文件。
我还利用了我在Google上找到的方法等——如果您发现任何我需要归功于他人的东西,请告诉我。
使用代码update类包含用于查询的版本文件的方法。
您需要在线放置的版本文件采用管道分隔文件的格式。
处理顺序为:
- 检查是否存在新的update应用程序文件,并在名称中定义了前缀,并重新命名这些文件(如果存在)——这使我们能够更新update应用程序。
- 从在线版本文件中获取更新信息。
然后,我们可以选择版本文件是否包含指向新文件的信息:
- 在不重新启动应用程序的情况下下载文件,或者
- 调用update应用程序以下载文件。
获取更新的可用性信息:该getUpdateInfo方法将管道分隔的文件(将列拆分)下载到一个List 对象中。
需要注意的一点是开始从中读取数据的行号——是从零开始的索引,这意味着第一行为0。
在下面的示例中,我们从第二行开始读取——我更喜欢在有标题行的情况下通过查看文件中哪些信息可以到达何处,我可以修改版本文件。
info = update.getUpdateInfo(downloadsurl.Text, versionfilename.Text,
Application.StartupPath + @"\", 1);
由于读取版本文件的结果是该getUpdateInfo方法返回的List对象,因此您可以将所需的任何内容放在此文件中以获取更新信息。
我建议首先保持版本简单,下载URL和下载文件名是更新所需的唯一重要信息。
app|version|release date|url|file
updateme|1.2|24/09/2011|http://changeThisUrl.com/downloadFiles/|updateme.zip
在此版本文件中保留在线信息时要注意的一件事是,它使您可以指导用户的应用程序从所需的任何位置提取新的应用程序。
提示——运行UpdateMe 应用程序时,请对“此版本号”文本框进行更改,以测试更新是否被获取。
关于安装更新,存在两种方法:
- installUpdateNow——这将安装下载的文件,而无需重新启动应用程序,并且
- installUpdateRestart——这将通过更新应用程序安装下载的文件,并导致应用程序重新启动。
该installUpdateRestart方法是UpdateMe应用程序的业务端——安装更新并通过update应用程序重新启动应用程序的UpdateMe新版本。
此方法将启动update应用程序——传递参数以指示文件下载完成后要下载的文件以及要启动的进程。
public static void installUpdates(string downloadsURL, string filename,
string destinationFolder, string processToEnd, string postProcess,
string startupCommand, string updater)
{
string cmdLn = "";
cmdLn += "|downloadFile|" + filename;
cmdLn += "|URL|" + downloadsURL;
cmdLn += "|destinationFolder|" + destinationFolder;
cmdLn += "|processToEnd|" + processToEnd;
cmdLn += "|postProcess|" + postProcess;
cmdLn += "|command|" + @" / " + startupCommand;
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = updater;
startInfo.Arguments = cmdLn;
Process.Start(startInfo);
}
该webdata类由UpdateMe应用程序和update应用程序双方共同使用。
此类包含用于从在线位置下载数据的方法。
我认为值得指出的一个方面是自定义bytesDownloaded事件的使用。
如果您对.NET不太熟悉,那么强烈建议您使用和理解自定义事件(即了解如何使用它们以及为什么它们如此方便)。
为了使update应用程序中的进度条能够正确显示数据下载的距离,我们需要能够指定下载的字节数以及下载的总字节数。
我们首先定义一个委托和一个保存自定义事件参数的类——该ByteArgs类继承自EventArgs类。
public delegate void bytesDownloaded(ByteArgs e);
public class ByteArgs : EventArgs
{
private int _downloaded;
private int _total;
public int downloaded
{
get
{
return _downloaded;
}
set
{
_downloaded = value;
}
}
public int total
{
get
{
return _total;
}
set
{
_total = value;
}
}
}
开始下载数据时,我们将创建ByteArgs类的新实例,并分配已下载的值和总值。
调用该事件时要注意的一点是null——我们测试它是否是第一个事件——如果我们不测试该事件是否是事件null并且该事件没有被使用,我们将得到一个NullReferenceException。
这很重要,因为update应用程序出于进度条的目的而使用bytesDownloaded事件,而UpdateMe应用程序却没有。
//let us declare our downloaded bytes event args
ByteArgs byteArgs = new ByteArgs();
byteArgs.downloaded = 0;
byteArgs.total = dataLength;
//we need to test for a null as if an event is not consumed, we will get an exception
if (bytesDownloaded != null) bytesDownloaded(byteArgs);
在本文的前面,我提到了一个问题—— “如何更新应用程序?” Update类中称为updateMe的方法可以解决此问题:
update.updateMe(updaterPrefix, Application.StartupPath + @"\");
我们只需将update应用程序文件的前缀及其位置传递给此方法——前缀使我们能够在update应用程序运行时下载这些文件,而不会遇到任何文件共享/锁定问题。启动时调用此UpdateMe方法使我们可以安装与该update应用程序相关的所有新文件。
提示:您会注意到,本文下载中包含的um.zip文件带M1234_前缀的文件——这些文件将删除其前缀,并且在运行UpdateMe时发布更新,它们将替换或补充应用程序文件夹中的任何文件。
兴趣点最初,我使用HTML网页来保存更新信息——代码将检查此页面上的更新。这有时会导致连接失败的问题——因此,我决定将更新信息放在可以下载的管道定界的在线文件中,由于某种原因,文件下载选项更可靠——下载103字节(版本文件应为大约这个大小)不会占用太多带宽。
本文未涉及的一个领域是从应用程序文件夹中清理旧文件的问题——当该updateMe方法在您的主应用程序中运行时,可以潜在地解决该问题(我的意思是旧文件是由您的应用程序的先前版本,将不再使用)。
或者,您可以将旧文件保留在用户的计算机上——我承认这并不理想,但是由于存在删除用户故意放置在应用程序文件夹中的文件的风险,允许保留这些旧文件可能会更好。
https://www.codeproject.com/Articles/265751/Application-Auto-update-via-Online-Files-in-Csharp