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

 界面设计:

 

当然,也可以扩展为从网站下载或者轮循新版本。

 

posted @ 2018-10-19 19:07  ROTA  阅读(774)  评论(0编辑  收藏  举报