|
|
Posted on 2007-06-14 11:48 伊飏 阅读(16988) 评论(78) 编辑 收藏
这是本人第一次写比较复杂的文章,表达不清之处,请各位见谅。好,闲话少说,入正题。
最近单位开发一个项目,其中需要用到自动升级功能。因为自动升级是一个比较常用的功能,可能会在很多程序中用到,于是,我就想写一个自动升级的组件,在应用程序中,只需要引用这个自动升级组件,并添加少量代码,即可实现自动升级功能。因为我们的程序中可能包含多个exe或者dll文件,所以要支持多文件的更新。
首先,要确定程序应该去哪里下载需要升级的文件。我选择了到指定的网站上去下载,这样比较简单,也通用一些。在这个网站上,需要放置一个当前描述最新文件列表的文件,我们估且叫它服务器配置文件。这个文件保存了当前最新文件的版本号(lastver),大小(size),下载地址(url),本地文件的保存路径(path),还有当更新了这个文件后,程序是否需要重新启动(needRestart)。这个文件大致如下: updateservice.xml
<?xml version="1.0" encoding="utf-8"?>
<updateFiles>
<file path="AutoUpdater.dll" url="http://update.iyond.com/CompanyClientApplication/AutoUpdater.zip" lastver="1.0.0.0" size="28672" needRestart="true" />
<file path="CompanyClient.exe" url="http://update.iyond.com/CompanyClientApplication/CompanyClient.zip" lastver="1.1.0.0" size="888832 " needRestart="true" />
<file path="HappyFenClient.dll" url="http://update.iyond.com/CompanyClientApplication/HappyFenClient.zip" lastver="1.0.0.0" size="24576" needRestart="true" />
<file path="NetworkProvider.dll" url="http://update.iyond.com/CompanyClientApplication/NetworkProvider.zip" lastver="1.0.0.0" size="32768" needRestart="true" />
<file path="Utility.dll" url="http://update.iyond.com/CompanyClientApplication/Utility.zip" lastver="1.0.0.0" size="20480" needRestart="true" />
<file path="Wizard.dll" url="http://update.iyond.com/CompanyClientApplication/Wizard.zip" lastver="1.0.0.0" size="24576" needRestart="true" />
</updateFiles>

同时,客户端也保存了一个需要升级的本地文件的列表,形式和服务器配置文件差不多,我们叫它本地配置文件。其中,<Enable>节点表示是否启用自动升级功能,<ServerUrl>表示服务器配置文件的地址。 update.config
<?xml version="1.0" encoding="utf-8"?>
<Config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Enabled>true</Enabled>
<ServerUrl>http://update.iyond.com/updateservice.xml</ServerUrl>
<UpdateFileList>
<LocalFile path="AutoUpdater.dll" lastver="1.0.0.0" size="28672" />
<LocalFile path="CompanyClient.exe" lastver="1.1.0.0" size="888832 " />
<LocalFile path="HappyFenClient.dll" lastver="1.0.0.0" size="24576" />
<LocalFile path="NetworkProvider.dll" lastver="1.0.0.0" size="32768" />
<LocalFile path="Utility.dll" lastver="1.0.0.0" size="20480" />
<LocalFile path="Wizard.dll" lastver="1.0.0.0" size="24576" />
</UpdateFileList>
</Config>
使用自动各级组件的程序在启动时,会去检查这个配置文件。如果发现有配置文件中的文件版本和本地配置文件中描述的文件版本不一致,则提示用户下载。同时,如果本地配置文件中某些文件在服务器配置文件的文件列表中不存在,则说明这个文件已经不需要了,需要删除。最后,当升级完成后,会更新本地配置文件。
我们先来看一下如何使用这个组件。 在程序的Program.cs的Main函数中:
[STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false);
AutoUpdater au = new AutoUpdater(); try { au.Update(); } catch (WebException exp) { MessageBox.Show(String.Format("无法找到指定资源\n\n{0}", exp.Message), "自动升级", MessageBoxButtons.OK, MessageBoxIcon.Error); } catch (XmlException exp) { MessageBox.Show(String.Format("下载的升级文件有错误\n\n{0}", exp.Message), "自动升级", MessageBoxButtons.OK, MessageBoxIcon.Error); } catch (NotSupportedException exp) { MessageBox.Show(String.Format("升级地址配置错误\n\n{0}", exp.Message), "自动升级", MessageBoxButtons.OK, MessageBoxIcon.Error); } catch (ArgumentException exp) { MessageBox.Show(String.Format("下载的升级文件有错误\n\n{0}", exp.Message), "自动升级", MessageBoxButtons.OK, MessageBoxIcon.Error); } catch (Exception exp) { MessageBox.Show(String.Format("升级过程中发生错误\n\n{0}", exp.Message), "自动升级", MessageBoxButtons.OK, MessageBoxIcon.Error); }
Application.Run(new MainUI()); }
如上所示,只需要简单的几行代码,就可以实现自动升级功能了。 软件运行截图:    下面,我们来详细说一下这个自动升级组件的实现。 先看一下类图:  AutoUpdater:自动升级的管理类,负责整体的自动升级功能的实现。 Config:配置类,负责管理本地配置文件。 DownloadConfirm:一个对话框,向用户显示需要升级的文件的列表,并允许用户选择是否马上升级。 DownloadFileInfo:要下载的文件的信息 DownloadProgress:一个对话框,显示下载进度。 DownloadProgress.ExitCallBack, DownloadProgress.SetProcessBarCallBack, DownloadProgress.ShowCurrentDownloadFileNameCallBack:由于.NET2.0不允许在一个线程中访问另一个线程的对象,所以需要通过委托来实现。 LocalFile:表示本地配置文件中的一个文件 RemoteFile:表示服务器配置文件中的一个文件。 UpdateFileList:一个集合,从List<LocalFile>继承 我们先整体看一下AutoUpdater.cs:
 AutoUpdater.cs
public class AutoUpdater
  {
const string FILENAME = "update.config";
private Config config = null;
private bool bNeedRestart = false;

public AutoUpdater()
 {
config = Config.LoadConfig(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, FILENAME));
}
 /**//// <summary>
/// 检查新版本
/// </summary>
/// <exception cref="System.Net.WebException">无法找到指定资源</exception>
/// <exception cref="System.NotSupportException">升级地址配置错误</exception>
/// <exception cref="System.Xml.XmlException">下载的升级文件有错误</exception>
/// <exception cref="System.ArgumentException">下载的升级文件有错误</exception>
/// <exception cref="System.Excpetion">未知错误</exception>
/// <returns></returns>
public void Update()
 {
if (!config.Enabled)
return;
 /**//*
* 请求Web服务器,得到当前最新版本的文件列表,格式同本地的FileList.xml。
* 与本地的FileList.xml比较,找到不同版本的文件
* 生成一个更新文件列表,开始DownloadProgress
* <UpdateFile>
* <File path="" url="" lastver="" size=""></File>
* </UpdateFile>
* path为相对于应用程序根目录的相对目录位置,包括文件名
*/
WebClient client = new WebClient();
string strXml = client.DownloadString(config.ServerUrl);

Dictionary<string, RemoteFile> listRemotFile = ParseRemoteXml(strXml);

List<DownloadFileInfo> downloadList = new List<DownloadFileInfo>();

//某些文件不再需要了,删除
List<LocalFile> preDeleteFile = new List<LocalFile>();

foreach (LocalFile file in config.UpdateFileList)
 {
if (listRemotFile.ContainsKey(file.Path))
 {
RemoteFile rf = listRemotFile[file.Path];
if (rf.LastVer != file.LastVer)
 {
downloadList.Add(new DownloadFileInfo(rf.Url, file.Path, rf.LastVer, rf.Size));
file.LastVer = rf.LastVer;
file.Size = rf.Size;

if (rf.NeedRestart)
bNeedRestart = true;
}

listRemotFile.Remove(file.Path);
}
else
 {
preDeleteFile.Add(file);
}
}

foreach (RemoteFile file in listRemotFile.Values)
 {
downloadList.Add(new DownloadFileInfo(file.Url, file.Path, file.LastVer, file.Size));
config.UpdateFileList.Add(new LocalFile(file.Path, file.LastVer, file.Size));

if (file.NeedRestart)
bNeedRestart = true;
}

if (downloadList.Count > 0)
 {
DownloadConfirm dc = new DownloadConfirm(downloadList);

if (this.OnShow != null)
this.OnShow();

if (DialogResult.OK == dc.ShowDialog())
 {
foreach (LocalFile file in preDeleteFile)
 {
string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.Path);
if (File.Exists(filePath))
File.Delete(filePath);

config.UpdateFileList.Remove(file);
}

StartDownload(downloadList);
}
}
}

private void StartDownload(List<DownloadFileInfo> downloadList)
 {
DownloadProgress dp = new DownloadProgress(downloadList);
if (dp.ShowDialog() == DialogResult.OK)
 {
//更新成功
config.SaveConfig(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, FILENAME));

if (bNeedRestart)
 {
MessageBox.Show("程序需要重新启动才能应用更新,请点击确定重新启动程序。", "自动更新", MessageBoxButtons.OK, MessageBoxIcon.Information);
Process.Start(Application.ExecutablePath);
Environment.Exit(0);
}
}
}

private Dictionary<string, RemoteFile> ParseRemoteXml(string xml)
 {
XmlDocument document = new XmlDocument();
document.LoadXml(xml);

Dictionary<string, RemoteFile> list = new Dictionary<string, RemoteFile>();
foreach (XmlNode node in document.DocumentElement.ChildNodes)
 {
list.Add(node.Attributes["path"].Value, new RemoteFile(node));
}

return list;
}
public event ShowHandler OnShow;
}

在构造函数中,我们先要加载配置文件:
public AutoUpdater() { config = Config.LoadConfig(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, FILENAME)); }
最主要的就是Update()这个函数了。当程序调用au.Update时,首先检查当前是否开户了自动更新:
if (!config.Enabled)
return;

如果启用了自动更新,就需要去下载服务器配置文件了:
WebClient client = new WebClient();
string strXml = client.DownloadString(config.ServerUrl);

然后,解析服务器配置文件到一个Dictionary中:
Dictionary<string, RemoteFile> listRemotFile = ParseRemoteXml(strXml);

接下来比较服务器配置文件和本地配置文件,找出需要下载的文件和本地需要删除的文件:
List<DownloadFileInfo> downloadList = new List<DownloadFileInfo>();
//某些文件不再需要了,删除
List<LocalFile> preDeleteFile = new List<LocalFile>();

foreach (LocalFile file in config.UpdateFileList)
 {
if (listRemotFile.ContainsKey(file.Path))
 {
RemoteFile rf = listRemotFile[file.Path];
if (rf.LastVer != file.LastVer)
 {
downloadList.Add(new DownloadFileInfo(rf.Url, file.Path, rf.LastVer, rf.Size));
file.LastVer = rf.LastVer;
file.Size = rf.Size;

if (rf.NeedRestart)
bNeedRestart = true;
}

listRemotFile.Remove(file.Path);
}
else
 {
preDeleteFile.Add(file);
}
}

foreach (RemoteFile file in listRemotFile.Values)
 {
downloadList.Add(new DownloadFileInfo(file.Url, file.Path, file.LastVer, file.Size));
config.UpdateFileList.Add(new LocalFile(file.Path, file.LastVer, file.Size));

if (file.NeedRestart)
bNeedRestart = true;
}

如果发现有需要下载的文件,则向用户显示这些文件,并提示其是否马上更新。如果用户选择了马上更新,则先删除本地不再需要的文件,然后开始下载更新文件。
if (downloadList.Count > 0)
 {
DownloadConfirm dc = new DownloadConfirm(downloadList);

if (this.OnShow != null)
this.OnShow();

if (DialogResult.OK == dc.ShowDialog())
 {
foreach (LocalFile file in preDeleteFile)
 {
string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.Path);
if (File.Exists(filePath))
File.Delete(filePath);

config.UpdateFileList.Remove(file);
}

StartDownload(downloadList);
}
}

我们再来看一下StartDownload函数
private void StartDownload(List<DownloadFileInfo> downloadList)
 {
DownloadProgress dp = new DownloadProgress(downloadList);
if (dp.ShowDialog() == DialogResult.OK)
 {
//更新成功
config.SaveConfig(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, FILENAME));

if (bNeedRestart)
 {
MessageBox.Show("程序需要重新启动才能应用更新,请点击确定重新启动程序。", "自动更新", MessageBoxButtons.OK, MessageBoxIcon.Information);
Process.Start(Application.ExecutablePath);
Environment.Exit(0);
}
}
}

在这个函数中,先调用DownloadProgress下载所有需要下载的文件,然后更新本地配置文件,最后,如果发现某些更新文件需要重新启动应用程序的话,会提示用户重新启动程序。 至此,AutoUpdater这个类的使命就完成了,其实,整个的升级过程也就完成了。(废话)。 最后,我们来看一下这个组件是如何下载更新文件的
 DownloadProgress.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.IO;
using System.Diagnostics;

namespace Iyond.Utility
  {
public partial class DownloadProgress : Form
 {
private bool isFinished = false;
private List<DownloadFileInfo> downloadFileList = null;
private ManualResetEvent evtDownload = null;
private ManualResetEvent evtPerDonwload = null;
private WebClient clientDownload = null;

public DownloadProgress(List<DownloadFileInfo> downloadFileList)
 {
InitializeComponent();

this.downloadFileList = downloadFileList;
}

private void OnFormClosing(object sender, FormClosingEventArgs e)
 {
if (!isFinished && DialogResult.No == MessageBox.Show("当前正在更新,是否取消?", "自动升级", MessageBoxButtons.YesNo, MessageBoxIcon.Question))
 {
e.Cancel = true;
return;
}
else
 {
if (clientDownload != null)
clientDownload.CancelAsync();

evtDownload.Set();
evtPerDonwload.Set();
}
}

private void OnFormLoad(object sender, EventArgs e)
 {
evtDownload = new ManualResetEvent(true);
evtDownload.Reset();
Thread t = new Thread(new ThreadStart(ProcDownload));
t.Name = "download";
t.Start();
}

long total = 0;
long nDownloadedTotal = 0;

private void ProcDownload()
 {
evtPerDonwload = new ManualResetEvent(false);

foreach (DownloadFileInfo file in this.downloadFileList)
 {
total += file.Size;
}

while (!evtDownload.WaitOne(0, false))
 {
if (this.downloadFileList.Count == 0)
break;

DownloadFileInfo file = this.downloadFileList[0];


//Debug.WriteLine(String.Format("Start Download:{0}", file.FileName));

this.ShowCurrentDownloadFileName(file.FileName);

//下载
clientDownload = new WebClient();

clientDownload.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnDownloadProgressChanged);
clientDownload.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDownloadFileCompleted);

evtPerDonwload.Reset();

clientDownload.DownloadFileAsync(new Uri(file.DownloadUrl), Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.FileFullName + ".tmp"), file);
//等待下载完成
evtPerDonwload.WaitOne();

clientDownload.Dispose();
clientDownload = null;

//移除已下载的文件
this.downloadFileList.Remove(file);
}

//Debug.WriteLine("All Downloaded");

if (this.downloadFileList.Count == 0)
Exit(true);
else
Exit(false);

evtDownload.Set();
}

void OnDownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
 {
DownloadFileInfo file = e.UserState as DownloadFileInfo;
nDownloadedTotal += file.Size;
this.SetProcessBar(0, (int)(nDownloadedTotal * 100 / total));
//Debug.WriteLine(String.Format("Finish Download:{0}", file.FileName));
//替换现有文件
string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.FileFullName);
if (File.Exists(filePath))
 {
if (File.Exists(filePath + ".old"))
File.Delete(filePath + ".old");

File.Move(filePath, filePath + ".old");
}

File.Move(filePath + ".tmp", filePath);
//继续下载其它文件
evtPerDonwload.Set();
}

void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
 {
this.SetProcessBar(e.ProgressPercentage, (int)((nDownloadedTotal + e.BytesReceived) * 100 / total));
}

delegate void ShowCurrentDownloadFileNameCallBack(string name);
private void ShowCurrentDownloadFileName(string name)
 {
if (this.labelCurrentItem.InvokeRequired)
 {
ShowCurrentDownloadFileNameCallBack cb = new ShowCurrentDownloadFileNameCallBack(ShowCurrentDownloadFileName);
 this.Invoke(cb, new object[] { name });
}
else
 {
this.labelCurrentItem.Text = name;
}
}

delegate void SetProcessBarCallBack(int current, int total);
private void SetProcessBar(int current, int total)
 {
if (this.progressBarCurrent.InvokeRequired)
 {
SetProcessBarCallBack cb = new SetProcessBarCallBack(SetProcessBar);
 this.Invoke(cb, new object[] { current, total });
}
else
 {
this.progressBarCurrent.Value = current;
this.progressBarTotal.Value = total;
}
}

delegate void ExitCallBack(bool success);
private void Exit(bool success)
 {
if (this.InvokeRequired)
 {
ExitCallBack cb = new ExitCallBack(Exit);
 this.Invoke(cb, new object[] { success });
}
else
 {
this.isFinished = success;
this.DialogResult = success ? DialogResult.OK : DialogResult.Cancel;
this.Close();
}
}

private void OnCancel(object sender, EventArgs e)
 {
evtDownload.Set();
evtPerDonwload.Set();
}
}
}
在构造函数中,将要下载的文件列表传进来
public DownloadProgress(List<DownloadFileInfo> downloadFileList)
 {
InitializeComponent();

this.downloadFileList = downloadFileList;
}

在Form的Load事件中,启动下载线程,开始下载。
private void OnFormLoad(object sender, EventArgs e)
 {
evtDownload = new ManualResetEvent(true);
evtDownload.Reset();
Thread t = new Thread(new ThreadStart(ProcDownload));
t.Name = "download";
t.Start();
}

下载线程没什么特殊的,使用了WebClient的异步下载文件函数DownloadFileAsync,并且注册了两个事件,分别负责下载进度显示和下载完成后的处理:
clientDownload.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnDownloadProgressChanged);
clientDownload.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDownloadFileCompleted);

大家看一下就明白了。
private void ProcDownload()
 {
evtPerDonwload = new ManualResetEvent(false);

foreach (DownloadFileInfo file in this.downloadFileList)
 {
total += file.Size;
}

while (!evtDownload.WaitOne(0, false))
 {
if (this.downloadFileList.Count == 0)
break;

DownloadFileInfo file = this.downloadFileList[0];


//Debug.WriteLine(String.Format("Start Download:{0}", file.FileName));

this.ShowCurrentDownloadFileName(file.FileName);

//下载
clientDownload = new WebClient();

clientDownload.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnDownloadProgressChanged);
clientDownload.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDownloadFileCompleted);

evtPerDonwload.Reset();

clientDownload.DownloadFileAsync(new Uri(file.DownloadUrl), Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.FileFullName + ".tmp"), file);
//等待下载完成
evtPerDonwload.WaitOne();

clientDownload.Dispose();
clientDownload = null;

//移除已下载的文件
this.downloadFileList.Remove(file);
}

//Debug.WriteLine("All Downloaded");

if (this.downloadFileList.Count == 0)
Exit(true);
else
Exit(false);

evtDownload.Set();
}

最后,在OnDownloadFileCompleted函数中进行最后的处理。包括备份原文件,替换现有文件等。
void OnDownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
 {
DownloadFileInfo file = e.UserState as DownloadFileInfo;
nDownloadedTotal += file.Size;
this.SetProcessBar(0, (int)(nDownloadedTotal * 100 / total));
//Debug.WriteLine(String.Format("Finish Download:{0}", file.FileName));
//替换现有文件
string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, file.FileFullName);
if (File.Exists(filePath))
 {
if (File.Exists(filePath + ".old"))
File.Delete(filePath + ".old");

File.Move(filePath, filePath + ".old");
}

File.Move(filePath + ".tmp", filePath);
//继续下载其它文件
evtPerDonwload.Set();
}

其它的函数只是一些显示进度条和下载信息的,这里就不再详细介绍了。大家可以下载源码看一下。 最后说一下,由于个人能力和时间的问题,这个组件还有一些不完善的地方,比如配置文件比较复杂等,如果您对这个组件有什么改进的话,请务必发给我一份,我的邮件是: iyondkoo@gmail.com.谢谢。 再做个广告: VS2005专业教程站是小弟最近做的一个网站,主要是提供VS2005、ASP.NET相关教程,希望大家有什么问题可以去看一下,同时给我提些建议。谢谢大家啦。 源码下载
Feedback
不错,值得好好研究一下,这种直接升级的套件,我在Delphi下用过;这样比clickone的好处就是不需要安装,而且可以做到部分升级
看了下,写的不错,学习了下。但和一个很专一的自动更新组件差距还很大。。例如支持更新文件CRC校验,防止恶意文件。还有就是用更新组件可以更新自己(更新组件)等。。。
有个问题,你比较了版本号,并没有比较谁新谁旧,只要不同就下载。那还不如比较文件二进制,这个连版本号都不用设置,呵呵
嗯,我上面也说了,由于个人能力和时间的问题,这个组件还不是很完善,楼上说的支持更新文件CRC校验,防止恶意文件确实是不错的功能。以后有时间的话,一定会加上的。
版本号的问题我是这样考虑的:自动升级的前提是-服务器配置文件上的文件是最新的,客户端不可能有比服务器上还要新的程序。所以,只要本地配置文件的版本号和服务器配置文件的版本号不一样,肯定是服务器上的是正确的。
不比较新旧的另一个好处是:如果某一次发布的更新中有比较大的BUG的话,可以使用上一个版本替换新的版本,如果客户端已经下载了含有BUG的新版本,软件再次启动时,会自动下载上一个版本,而不会因为它的时间比较旧而不下载。
另外说明一下,目前这个组件可以更新自己:)
不知道可不可以压缩下载,如果更新的文件比较大的话,下载会很慢。
呵呵,你的解释是合理的。不检测是合理的,当然如果完整的产品或许可以给他很多选项,比如比较日期,binary,versioNO等。。
刚看了下你的代码,你的确可以实现更新自己,可能要改下代码。。你是先copy到temp下然后再move过去。不过好像需要设置needrestart =true..需要退出更新程序然后启动后在move过去
还有 DownloadFileInfo file = this.downloadFileList[0];
这个是为了防止别人盗版你的代码还是示范一下还是我没看懂,那里是不是要放个循环download的或者类似dowload Thread pool的? :)
加大码 :
他的文件已经zip了,dll,exe可以压缩的,不过有些文件好像压缩了也没变小多少。。
伊飏 :
你可以写个工具,选中多个要更新的文件,批量压缩批量上传(ftp) 到server上,包括server 配置的xml
这个组件并没有实现压缩传输。服务器配置文件里的文件扩展名是zip,只是简单的将扩展名从exe改成了zip,完全是为了下载方便,并不是真正的压缩。
DownloadFileInfo file = this.downloadFileList[0];
这一句是从当前要下载的队列中得到第一个文件,没有其它的用处。
看你的 vs2005.com 怎么和51aspx.com相似的呢? 先收藏
不过我不知道
updateservice.xml
这个文件是从哪里来的?
手工自己写嘛?那就我狂晕...
最好每个组件或总体加上升级引起的变更内容的说明提示给客户端,让用户知道升级了哪些功能,修正了哪些bug。
finesite:和51aspx的站长比较熟,直接用了他的界面,呵呵。
小隐任行:目前是直接写的。如果文件不多的话,还是可以忍受的吧。如果文件太多的话,可以写一个自动发布工具,包括FTP上传并且更新服务器配置文件。
加上文件的hash码的效验会不会好点
毕竟远程传输有时后会出毛病 :)
用2005实现了一下,可以很顺利的运行,可是在2003下不能用同样的方法来实现,因为1.0的类库中,WebClient 没有这两个事件:
clientDownload.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnDownloadProgressChanged);
clientDownload.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDownloadFileCompleted);
没有这两个相应的事件,我在2003下很难按你的思路来实现升级,
楼主,如果用VS 2003来实现的话,可以吗,如果行的话,能不能发一个2003版本的自动升级的这样的组件给我一下,不胜感激,我的邮箱:stl_1983@163.com
谢谢!
請問運行時提示Data at the root levelis invalid.Line1,position 1.
是什么原因?
楼上的问题我也遇见了,这个是在服务器端读取updateservice.xml时出现的问题,调试时document.LoadXml(xml)里的xml值有问题,如果你选择用文本可视化工具查看的时候会发现第一行的开头会莫名其妙多出来一个空格,还无法去掉。如果在本地读取这个xml文件就不会有这个问题。我也不知道该如何解决。
顺便再问一个问题,如果我想实现能下载文件夹,如何实现?
qj:你这个是XML文件编码的问题,写XML文件时,使用Utf-8,并且不添加UTF8标记。
你这个自动升级不错,下载后编译是dll文件.要在主程序里面调用这个dll文件吗?
不明白,怎么实现自己更新,就是说应用程序正在运行,怎么能把自己删除呢,谢谢
太好了,正想写一个,直接或者再加点就可以用了!谢谢!
最近有个项目,客户群体不定,所以要自动升级,这几天有可能开始考虑添加自动升级功能,看了文章之后,感觉很不错,没有细看,但是让我心里踏实很多。做的时候可以借鉴,谢谢作者。
能实现 AutoUpdater 的自我更新么?????
调用AutoUpdater的主程序,更新后直接出错。
是不是主程序不可以更新?
在.net2005中用您的 例子 下载下来的文件什么大小 变成0字节了?
我用了你的控件,"取消"操作不行,正在更新时,点"取消"按钮,弹出窗口后,点"否"后,再点"取消"按钮就没有反应了.问一下博主,这个问题怎么解决呢?谢谢.
我的怎么老师报这个错啊 ? 《“45197”的值对于“Value”无效。“Value”应介于 'minimum' 和 'maximum' 之间。 参数名: Value》
请问博主。为什么这个自动更新在vs2005里面。运行没有问题。但是更新完了以后。下载下来的文件就变成了0kb了。非常急。请博主赐教
因为更新完了以后客户端的EXE文件变成了0字节。所以也就没有办法再运行客户端程序了。也就是更新完了。客户端的程序也就完了。请问有遇到这个问题的吗。请赐教。
请问我把你源代码下下来之后
然后放入vs2005里为什么跑不出来报错
说启动项那个是“0”个加载
谢谢楼主了
急~!!
我邮箱wufan_14245670@163.com
把你完整得源代码
发过来谢谢了啊
运行到Download的this.objWebResponse = (HttpWebResponse)this.objWebRequest.GetResponse();
时报错:
远程服务器返回错误: (502) 错误的网关。
请问这是什么问题呀?
@martin214 更新自己(autoupdate.dll)如何操作?
先赞一个,下来再测试!希望能在我的博客发表一些相关的测试结果~呵呵
我的更新后也是变成了0KB,但更新的.rar文件就不会,请问楼主是不是因为Web服务器的安全设置问题?还是怎么样的呢?急!
原来真的是因为Web服务器的安全问题,IIS升级后,用http下载不了exe和dll这些文件,现在我改成了ftp服务就可以下载了!
我试用了一下楼主开发的这个控件,在开始下载后,总是有这个问题: “Value”应介于 'minimum' 和 'maximum' 之间
private void SetProcessBar(int current, int total) { if (this.progressBarCurrent.InvokeRequired) { SetProcessBarCallBack cb = new SetProcessBarCallBack(SetProcessBar); this.Invoke(cb, new object[] { current, total }); } else { this.progressBarCurrent.Value = current; this.progressBarTotal.Value = total; } } 好像是total这个变量太大了。想请教一下。
楼主,你的网站上不了啊,另外非常感谢你的代码,感觉非常有学习价值,而且你写这么详细真是不容易,太感谢了,学到了很多.....楼主是我的偶像
你好,看了你的写的组件,写的非常好,使我受益匪浅,在我使用的过程中,有一些问题,在运行程序过程中,会出现"无法找到指定资源,基础连接已经关闭:连接被意外终止"的错误,我的配置如下:web网站:http://192.168.101.12/ 可以正常访问,并将updateservice.xml文件设为此网站的默认页,内容为: <?xml version="1.0" encoding="utf-8" ?> - <updateFiles> <file path="AutoUpdater.dll" url="http://192.168.101.12/AutoUpdater.dll" lastver="1.2.0.0" size="32768" needRestart="true" /> <file path="storecontrolsystem.exe" url="http://192.168.101.12/storecontrolsystem.exe" lastver="1.0.0.0" size="118784" needRestart="true" /> </updateFiles>
update.config内容如下: <?xml version="1.0" encoding="utf-8" ?> <Config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Enabled>true</Enabled> <ServerUrl>http://192.168.101.12</ServerUrl> <UpdateFileList> <file path="AutoUpdater.dll" lastver="1.2.0.0" size="32768" /> <LocalFile path="storecontrolsystem.exe" lastver="1.0.0.0" size="118,784" /> </UpdateFileList> </Config> 请教是什么地方的问题,非常感谢!另外,update.config文件是放在哪里?我现在是放到组件里面的.
非常感谢 O(∩_∩)O~ 这么好的文章 竟然没什么人点推荐 太对不住博主了
嗯,学习中。有点感觉晕。如果有源代码请发给我测试下。学习。461169570@qq.com
楼主的程序确实有点小问题,不过稍微修改就可以 首先服务器端的那个xml的问题,总是提示 “根级别数据无效” 遇到这个问题的朋友可以这么解决: 将AutoUpdater类中的 string strXml = client.DownloadString(config.ServerUrl); 将以上代码注释,改为如下方式读取 StreamReader sr = new StreamReader(client.OpenRead(config.ServerUrl)); string strXml = sr.ReadToEnd(); sr.Close();
第二个问题。“Value”应介于 'minimum' 和 'maximum' 之间。“ 这个错误是因为百分比计算的问题 将DownloadProgress窗体中OnDownloadProgressChanged事件中的代码修改为: this.SetProcessBar(e.ProgressPercentage, (int)((nDownloadedTotal + e.BytesReceived) / total * 100)); 就可以搞定
楼主是先乘以100然后再除的总数。
第三个问题,有人说下载后文件变为了0k,这点需要注意,请你将要更新的文件的后缀都改为rar,压缩包形式的就可以了
“11000”的值对于“Value”无效。“Value”应介于 'minimum' 和 'maximum' 之间。 参数名: Value private void SetProcessBar(int current, int total) { if (this.progressBarCurrent.InvokeRequired) { SetProcessBarCallBack cb = new SetProcessBarCallBack(SetProcessBar); this.Invoke(cb, new object[] { current, total }); } else { this.progressBarCurrent.Value = current; this.progressBarTotal.Value = total; } }
其中到这一句报错: this.Invoke(cb, new object[] { current, total });
楼主,我用的时候在 Process.Start(Application.ExecutablePath); 这个地方出错了,应该是当前程序没关又打开才会报错的,请大侠们帮帮忙怎么解决。是不是我的用法不对,我是新建一个类库,然后把楼主的源码加上去的,为什么这样会有问题呢?
博主,能否传一demo呢,这样代码用了还是有很多问题啊,下载后文件是0字节,后缀名改成rar没用啊,大侠们能否帮帮啊?感谢了!!
我的问题解决了。 “Value”应介于 'minimum' 和 'maximum' 之间 这个问题是this.progressBarTotal.Value 太小,我们把值改大一些就可以了。就给后面的朋友一个例子。
兄弟们说的问题我都遇到了。根据你们说的修改也不成啊!谁有改好的源码啊能否发一套给我。谢谢!1360287576@qq.com
新版本多出了几个dll,先升级config文件,再升级? 站长工具
楼主的程序写的很好,针对以上提出的问题主要是max和min 的value值的变化,不再范围内,之前我也是困扰在这里,因为VS 没有调用OnDownloadProgressChanged和ShowCurrentDownloadFileName这2 个方法,没有达到值的转化,所以会溢出所在的范围,因为楼主的代码中,是进程,而多线程主要是通过委托来实现的。这样以来楼主的prosse中进度条的最大值是100就不能容纳了。 解决方法,1调试程序中的进度条的属性maxsize设置的尽可能大。 2调试程序使其可以调用以上的2个方法
|