引言
2015年第一篇,Winform在線更新,算是重操舊業吧,09年剛到北京時一直做硬件聯動編程,所以大多數時間都在搞Winform, 但是從來沒做過在線更新這個功能,前幾天參與部門另一個項目,上來就需要加一個在線更新。 該自動更新組件核心是聖殿騎士開發的,另外參考逆水寒龍使用情況,當我拿到這個組件源代碼時並不知道如何去用,花了大半天時間也沒調試通過,所以把我遇到的問題和解決方法記錄下來,方便后續其他Coder使用時查看使用方法。
在線更新思路
1、請求遠程站點Version.xml 或者請求Web服務器都可以,最主要是對比服務器端配置文件版本號和本地配置文件版本號。
2、從遠程站點下載文件。
3、覆蓋本地可執行文件及用到的Dll,當然覆蓋之前需要關掉本地exe進程或者windows服務進程。
4、文件覆蓋后修改本地配置文件AutoUpdater.config.
5、覆蓋后重新啟動可執行程序或者windows服務。
該AutoUpdater缺點(使用過程中已完善)
1、沒看到聖殿騎士如何使用該組件,參考逆水寒龍Demo時,找到了主程序關閉的地方。
2、在下載文件時沒有考慮到遠程服務器新增文件時處理本地AutoUpdater.config,也就是說更新程序處理時是基於本地配置文件config.UpdateFileList。
3、沒有針對windows服務處理進程(已完善windows服務處理類)。
使用時遇到的問題
1、不知道如何使用該組件?
使用該組件時項目應該具備4部分:Web站點用於接受請求及下載需更新文件、AutoUpdater更新組件、MainApplication(也就是你的主程序)、UpdateApplication(更新程序入口,通過主程序啟動,在這個程序內部調用AutoUpdater組件)。

2、 不知道合適去關閉主程序和windows服務
后在看逆水寒龍的Demo中看到他是在文件下載前,當你點擊確認下載時關閉進程。
if (bDownload)
{
//關閉windows service
ServiceProcess serviceProcess = new ServiceProcess();
serviceProcess.StopService();
//關掉主程序
OperProcess oper=new OperProcess();
oper.InitUpdateEnvironment();
DownloadConfirm dc = new DownloadConfirm(downloadList);
if (this.OnShow != null)
this.OnShow();
if (DialogResult.OK == dc.ShowDialog())
{
StartDownload(downloadList);
}
}
3、文件下載后臨時目錄和源程序主目錄口徑不一致
這個根據自己需要調整文件Copy、Move代碼。我們的程序可執行文件和主要Dll都是放在一個單獨的文件里而不是默認的Bin\Debug下。所以我的做法就是把下載文件從TempFolder移動到可執行源文件根目錄下。具體代碼在DownloadProgress窗體的ProcDownload方法中處理。
if (!string.IsNullOrEmpty(tempFolderPath))
{
oldPath = Path.Combine(CommonUnitity.SystemBinUrl, file.FileName);
newPath = Path.Combine(CommonUnitity.SystemBinUrl + ConstFile.TEMPFOLDERNAME + tempUrlPath, file.FileName);
}
4、 文件下載移動復制后合適啟動應用程序
在更新程序里添加finally代碼,啟動windows服務、啟動主程序、關閉更新程序(UpdateApplication)。
void InitAndCheck()
{
bool bHasError = false;
IAutoUpdater autoUpdater = new AutoUpdater();
try
{
autoUpdater.Update();
}
catch (WebException exp)
{
MessageBox.Show("Can not find the specified resource");
bHasError = true;
}
catch (XmlException exp)
{
bHasError = true;
MessageBox.Show("Download the upgrade file error");
}
catch (NotSupportedException exp)
{
bHasError = true;
MessageBox.Show("Upgrade address configuration error");
}
catch (ArgumentException exp)
{
bHasError = true;
MessageBox.Show("Download the upgrade file error");
}
catch (Exception exp)
{
bHasError = true;
MessageBox.Show("An error occurred during the upgrade process");
}
finally
{
if (bHasError == true)
{
try
{
autoUpdater.RollBack();
}
catch (Exception)
{
}
}
ServiceProcess serviceProcess = new ServiceProcess();
serviceProcess.StartService();
OperProcess oper = new OperProcess();
oper.StartProcess();
}
}
5、 當Web站點出現新增文件時沒有同步到本地配置文件。
看源代碼是發現修改本地AutoUpdater.config是基於本地UpdateFileList,只需要把Web端新增文件信息添加到該List中,序列化本地config時就會同步。
LocalFile tempFile = null;
foreach (RemoteFile file in listRemotFile.Values)
{
downloadList.Add(new DownloadFileInfo(file.Url, file.Path, file.LastVer, file.Size));
//把遠程服務器新增文件加入config.UpdateFileList, 修改本地AutoUpdater.config時使用。
tempFile = new LocalFile(file.Path, file.LastVer, file.Size);
config.UpdateFileList.Add(tempFile);
if (file.NeedRestart)
bNeedRestart = true;
}
6、添加windows服務處理類
public class ServiceProcess
{
#region 判斷window服務是否啟動
/// <summary>
/// 判斷某個Windows服務是否啟動
/// </summary>
/// <returns></returns>
private bool IsServiceStart(string serviceName)
{
ServiceController psc = new ServiceController(serviceName);
bool bStartStatus = false;
try
{
if (!psc.Status.Equals(ServiceControllerStatus.Stopped))
{
bStartStatus = true;
}
return bStartStatus;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
#endregion
#region 啟動服務
private bool StartService(string serviceName)
{
bool flag = true;
if (ServiceIsExisted(serviceName))
{
System.ServiceProcess.ServiceController service = new System.ServiceProcess.ServiceController(serviceName);
if (service.Status != System.ServiceProcess.ServiceControllerStatus.Running && service.Status != System.ServiceProcess.ServiceControllerStatus.StartPending)
{
service.Start();
for (int i = 0; i < 60; i++)
{
service.Refresh();
System.Threading.Thread.Sleep(1000);
if (service.Status == System.ServiceProcess.ServiceControllerStatus.Running)
{
break;
}
if (i == 59)
{
flag = false;
}
}
}
}
return flag;
}
#endregion
#region 停止服務
private bool StopService(string serviceName)
{
bool flag = true;
if (ServiceIsExisted(serviceName))
{
System.ServiceProcess.ServiceController service = new System.ServiceProcess.ServiceController(serviceName);
if (service.Status == System.ServiceProcess.ServiceControllerStatus.Running)
{
service.Stop();
for (int i = 0; i < 60; i++)
{
service.Refresh();
System.Threading.Thread.Sleep(1000);
if (service.Status == System.ServiceProcess.ServiceControllerStatus.Stopped)
{
break;
}
if (i == 59)
{
flag = false;
}
}
}
}
return flag;
}
#endregion
#region 判斷window服務是否存在
private bool ServiceIsExisted(string serviceName)
{
ServiceController[] services = ServiceController.GetServices();
foreach (ServiceController s in services)
{
if (s.ServiceName == serviceName)
{
return true;
}
}
return false;
}
#endregion
#region 安裝服務
private void InstallService(IDictionary stateSaver, string filepath)
{
try
{
ServiceController service = new ServiceController("ServiceName");
if (!ServiceIsExisted("ServiceName"))
{
//Install Service
AssemblyInstaller myAssemblyInstaller = new AssemblyInstaller();
myAssemblyInstaller.UseNewContext = true;
myAssemblyInstaller.Path = filepath;
myAssemblyInstaller.Install(stateSaver);
myAssemblyInstaller.Commit(stateSaver);
myAssemblyInstaller.Dispose();
//--Start Service
service.Start();
}
else
{
if (service.Status != System.ServiceProcess.ServiceControllerStatus.Running && service.Status != System.ServiceProcess.ServiceControllerStatus.StartPending)
{
service.Start();
}
}
}
catch (Exception ex)
{
}
}
#endregion
#region 卸載windows服務
private void UnInstallService(string filepath)
{
try
{
if (ServiceIsExisted("ServiceName"))
{
//UnInstall Service
AssemblyInstaller myAssemblyInstaller = new AssemblyInstaller();
myAssemblyInstaller.UseNewContext = true;
myAssemblyInstaller.Path = filepath;
myAssemblyInstaller.Uninstall(null);
myAssemblyInstaller.Dispose();
}
}
catch (Exception ex)
{
}
}
#endregion
public void StopService()
{
if (ServiceIsExisted("MyService"))
{
if (IsServiceStart("MyService"))
{
this.StopService("MyService");
}
}
}
public void StartService()
{
if (ServiceIsExisted("MyService"))
{
if (!IsServiceStart("MyService"))
{
this.StartService("MyService");
}
}
}
}
總結
當有任何一個功能需要開發時先要弄懂其原理,並且能反復調試網上找來的源碼,反復調試后能明白其中的原理才能更好的應用。初拿到這個功能時並不明白就想去集成,結果花了時間還沒弄明白怎么使用。 再次感謝聖殿騎士和逆水寒龍的文章給我的啟示。
Demo下載 提取碼99a9
