Winform在線更新


引言

       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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM