1. 程式人生 > >C# Winform下一個熱插拔的MIS/MRP/ERP框架14(自動更新)

C# Winform下一個熱插拔的MIS/MRP/ERP框架14(自動更新)

對於軟體來說,啟用自動更新是非常必要的。

根據軟體的應用場景,我們可以設計不同的更新模型。

目前,IMES框架執行在.Net framework 4.0下面,使用的Win系統版本在Win7,域內管控,平時業務調整也不是很頻繁。

所以,我的更新很粗放,就是刪除舊檔案,拷貝新檔案:

1、更新檔案放置在檔案伺服器一個公共目錄下:\\SV001\Public\Update ;

2、僅在使用者登入時檢測更新(或者在系統介面點選“更新”按鈕手動更新);

3、根據業務變更可以指定更新某一個檔案、一個資料夾、或者所有檔案;

4、軟體是否要更新,簡單的由一個文字檔案的最後修改時間來判斷;

5、可以保留某些本地生成和下載的配置/檔案;

完整程式碼:

    public partial class Fm19Update : Form
    {
        /// <summary>
        /// 這個程式會單獨執行,因此不要引用其他DLL
        /// </summary>
        public Fm19Update()
        {
            InitializeComponent();
            FormBorderStyle = FormBorderStyle.None;
            StartPosition 
= FormStartPosition.CenterScreen; } #region 宣告 [DllImport("user32.dll")] public static extern bool ReleaseCapture();//拖動無窗體的控制元件 [DllImport("user32.dll")] public static extern bool SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam); public
const int WM_SYSCOMMAND = 0x0112; public const int SC_MOVE = 0xF010; public const int HTCAPTION = 0x0002; /// <summary> /// 更新程式來源路徑(更新檔案一般放在所有客戶端可訪問的網路伺服器上) /// </summary> private string UpdatePath = string.Empty; /// <summary> /// 更新內容(可使用\\,*.*,0;dir1\*.dll,0分割要更新的區塊,所有檔案全部更新使用[\\,*.*,1] /// </summary> private string UpdateContent = string.Empty; /// <summary> /// 更新內容清單,由UpdateContent轉換而來. /// </summary> private string[] UpdateList; /// <summary> /// 要更新的檔案個數,由各更新區域加總 /// </summary> private static int UpdateFilesCount = 0; #endregion /// <summary> /// 延時函式, 如使用Thread.Sleep()會停止響應 /// </summary> /// <param name="delayTime"></param> /// <returns></returns> private static bool Delay(int delayTime) { DateTime now = DateTime.Now; int s; do { TimeSpan spand = DateTime.Now - now; s = spand.Seconds; Application.DoEvents(); } while (s < delayTime); return true; } /// <summary> /// 顯示當前作業狀態 /// </summary> /// <param name="tStatus"></param> private void SetUpdateStatus(string tStatus) { LabCurStatus.Text = tStatus; Refresh(); } private void CmdQuit_Click(object sender, EventArgs e) { Application.Exit(); } private void Fm19Update_MouseDown(object sender, MouseEventArgs e) { ReleaseCapture(); //呼叫移動無窗體控制元件函式 SendMessage(Handle, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0); } private void Fm19Update_Shown(object sender, EventArgs e) { SetUpdateStatus("Checking progress status..."); Refresh(); Delay(2); //這裡的檢測其實是一個假象,僅僅延時了2秒,以等待主程式結束執行。 SetUpdateStatus("Checking progress status...Finished"); Refresh(); DoUpdate(); //開始進行更新 } #region 更新作業區域 /// <summary> /// 當本地更新檔案的最後修改時間與更新路徑下的更新檔案不同,則認為需要更新。 /// </summary> private void DoUpdate() { #region 判斷來源與本地資料夾狀態 string localPath = AppDomain.CurrentDomain.BaseDirectory; localPath = localPath.Substring(0, localPath.Length - 1); //從本地更新歷史檔案讀取遠端更新路徑 string[] allLines = File.ReadAllLines(localPath + @"\UpdateLog.txt"); string remotePath = string.Empty; foreach (string line in allLines) { if (line.IndexOf("UpdatePath=") >= 0) { remotePath = line.Substring(line.IndexOf("UpdatePath=") + 11).Trim(); break; } } if (localPath.ToUpper() == remotePath.ToUpper()) { MessageBox.Show("更新路徑不能與來源路徑相同!", "警告...", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } FileInfo fiUL = new FileInfo(localPath + @"\UpdateLog.txt"); FileInfo fiUR = new FileInfo(remotePath + @"\UpdateLog.txt"); if (fiUL.LastWriteTime == fiUR.LastWriteTime) { SetUpdateStatus("系統沒有更新,操作終止!"); return; } SetUpdateStatus("正在連線到目標資料夾......"); if (!Directory.Exists(remotePath)) { SetUpdateStatus("目標資料夾:" + remotePath + " 不存在,請聯絡系統管理員!"); return; } SetUpdateStatus("正在連線到目標資料夾......成功!"); //從遠端讀取本次更新內容 string[] allLinesRmt = File.ReadAllLines(remotePath + @"\UpdateLog.txt"); foreach (string line in allLinesRmt) { if (line.IndexOf("UpdateContent=") >= 0) { UpdateContent = line.Substring(line.IndexOf("UpdateContent=") + 14); break; } } #endregion #region 計算檔案數量並設定百分比 if (!string.IsNullOrEmpty(UpdateContent)) { UpdateList = UpdateContent.Split(';'); } else { UpdateList = (@"\\,*.*,1").Split(';'); //更新內容設定不正確,則設定為更新所有檔案. } UpdateFilesCount = 0; foreach (string ul in UpdateList) { string[] updContent = ul.Split(','); string updDirPath = string.Empty; string updFilesPattern = "*.*"; int updWithSubDir = 0; if (updContent.Length > 0) { updDirPath = updContent[0]; updDirPath = remotePath + @"\" + updDirPath.Replace("\\", ""); } if (updContent.Length > 1) { updFilesPattern = updContent[1]; } if (updContent.Length > 2) { updWithSubDir = Convert.ToInt16(updContent[2]); } bool withSubDir = updWithSubDir > 0; if (!string.IsNullOrEmpty(updContent[0].Trim())) { UpdateFilesCount += CountAllFiles(updDirPath, updFilesPattern, withSubDir); } } //所有更新進度根據更新區域切割百分比 ProgMain.Value = 0; ProgMain.Step = 1; ProgMain.Maximum = UpdateFilesCount + 5; for (int i = 0; i < 6; i++) { ProgMain.PerformStep(); LabProgStep.Text = (ProgMain.Value * 100 / ProgMain.Maximum).ToString() + "%"; } #endregion #region 開始刪除和複製檔案(會刪除目錄下的所有檔案,再重新複製,這樣可以清除掉某些過期的檔案) foreach (string ul in UpdateList) { string[] updContent = ul.Split(','); string localUpdDirPath = string.Empty; string remoteUpdDirPath = string.Empty; string updFilesPattern = "*.*"; int updWithSubDir = 0; if (updContent.Length > 0) { localUpdDirPath = localPath + @"\" + updContent[0].Replace("\\", ""); remoteUpdDirPath = remotePath + @"\" + updContent[0].Replace("\\", ""); } if (updContent.Length > 1) { updFilesPattern = updContent[1]; } if (updContent.Length > 2) { updWithSubDir = Convert.ToInt16(updContent[2]); } bool withSubDir = updWithSubDir > 0; SetUpdateStatus("正在更新" + (updContent[0] == @"\\" ? "根目錄" : updContent[0]) + "下的檔案......"); if (!string.IsNullOrEmpty(updContent[0].Trim())) { CopyFilesTo(remoteUpdDirPath, updFilesPattern, localUpdDirPath, withSubDir); } } #endregion //更新完成後重新啟動主程式 string mainEXE = AppDomain.CurrentDomain.BaseDirectory + @"B20.exe"; Process.Start(mainEXE); Application.Exit(); } /// <summary> /// 統計符合篩選條件的所有檔案數量。(已排除A19.exe和LocalFile目錄) /// </summary> /// <param name="fullPath">完整目錄名稱</param> /// <param name="sPattern">篩選條件(如*.xls)</param> /// <param name="withSubDir">是否包含子目錄</param> /// <returns></returns> private int CountAllFiles(string fullPath, string sPattern, bool withSubDir = false) { SearchOption searchOption = withSubDir ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; var files = Directory.EnumerateFiles(fullPath, sPattern, searchOption) .Where(s => !s.Contains("A19.exe") && !s.Contains("Custom.cfg")); return files.Count(); } /// <summary> /// 從A資料夾拷貝檔案到B資料夾下面 /// </summary> /// <param name="remoteSourcePath">檔案所在目錄(@"C:\A\A")</param> /// <param name="localSavePath">儲存的目標目錄(@"C:\B\B")</param> /// <returns>返回:true-拷貝成功;false:拷貝失敗</returns> public bool CopyFilesTo(string remoteSourcePath, string sPattern, string localSavePath, bool withSubDir = false) { try { SearchOption searchOption = withSubDir ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; var files = Directory.EnumerateFiles(remoteSourcePath, sPattern, searchOption) .Where(s => !s.Contains("A19.exe")); if (withSubDir) { //包含子目錄更新,先刪除當前目錄下的所有檔案,再重新複製本目錄,這樣可以清除某些過期檔案 SetUpdateStatus("正在清除舊檔案......"); DelectDir(localSavePath, withSubDir); //如果包含子目錄 ,則先根據遠端的目錄結構全部檢查/建立好. var dirs = Directory.EnumerateDirectories(remoteSourcePath, "*.*", searchOption); foreach (string dir in dirs) { string locTagDir = dir.Replace(remoteSourcePath, localSavePath); if (!Directory.Exists(locTagDir)) { Directory.CreateDirectory(locTagDir); } } } if (files.Count() > 0) { foreach (string fileName in files) { //採用覆蓋模式,但要保留一些本地自定義檔案 if (fileName == (remoteSourcePath + @"Config\Custom.cfg")) { if (!File.Exists(fileName.Replace(remoteSourcePath, localSavePath))) { File.Copy(fileName, fileName.Replace(remoteSourcePath, localSavePath), true); } } else { File.Copy(fileName, fileName.Replace(remoteSourcePath, localSavePath), true); } ProgMain.PerformStep(); LabProgStep.Text = (ProgMain.Value * 100 / ProgMain.Maximum).ToString() + "%"; } } LabCurStatus.Text = "正在複製檔案......完成!"; Refresh(); } catch (Exception ex) { MessageBox.Show(ex.Message); return false; } return true; } /// <summary> /// 刪除本目錄下除更新程式及LocalFile目錄(儲存本地生成的檔案)以外的所有檔案 /// </summary> /// <param name="tagPath"></param> public static void DelectDir(string tagPath, bool delSub = false) { try { DirectoryInfo dir = new DirectoryInfo(tagPath); FileSystemInfo[] fileinfo = dir.GetFileSystemInfos(); //返回目錄中所有檔案和子目錄 foreach (FileSystemInfo i in fileinfo) { if (i is DirectoryInfo) //判斷是否資料夾 { if (delSub) { if ((i.FullName != (tagPath + "LocalFiles")) && (i.FullName != (tagPath + "Config"))) { DirectoryInfo subdir = new DirectoryInfo(i.FullName); subdir.Delete(true); //刪除子目錄和檔案 } } } else { if ((i.Name != "A19.exe") && (i.FullName != tagPath + @"Config\Custom.cfg")) { File.Delete(i.FullName); //刪除指定檔案 } } } } catch (Exception e) { throw e; } } #endregion }
Fm19Update.cs

 介面設計:

 

當然,也可以擴充套件為從網站下載或者輪循新版本。