using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.IO; using System.Net; using System.Globalization; using System.Xml; using System.Diagnostics; using System.Runtime.InteropServices; using System.Xml.Linq; using System.Security.Cryptography; namespace AutoUpdateEx { public partial class FormUpdate : Form { private WebClient downWebClient = new WebClient(); /// /// 消息处理类申明 /// private static AutoUpdateEx.lib.ClassMessage classMsg = new AutoUpdateEx.lib.ClassMessage(); /// /// 进程检查类申明 /// private static AutoUpdateEx.lib.ClassCheckProIsRun classCPIR = new AutoUpdateEx.lib.ClassCheckProIsRun(); /// /// 日志类申明 /// private static AutoUpdateEx.lib.ClassLog classLog = new AutoUpdateEx.lib.ClassLog(); /// /// 是否有更新,默认没有更新 /// public bool boolUpdateFalg = false; /// /// 更新服务器的URL /// private static string strUpdateURL; /// /// 所有文件大小 /// private static long SIZE; /// /// 文件总数 /// private static int COUNT; /// /// 文件名数组 /// private static string[] strArrayFileNames; /// /// 已更新文件数 /// private static int NUM; /// /// 已更新文件大小 /// private static long UPSIZE; /// /// 当前更新文件名 /// private static string strCurFileName; /// /// 当前更新的目录 /// private static string strCurPath; /// /// 当前文件大小 /// private static long lngCurFileSize; /// /// 本地update.xml的路径 /// private static string strUpdateXmlPath = Application.StartupPath + @"/update.xml"; /// /// 服务端updatelist.xml的URL地址 /// private static string strUpdateListXmlPath = "UpdateServer/UpdateList.xml"; /// /// 服务端更新文件的URL地址 /// private static string strUpdateFilesUrl = "UpdateServer/UpdateFiles"; /// /// 服务端updatelist.xml的更新日期 /// private static string strTheUpdateDate; /// /// 更新文件临时保存的路径 /// private static string strUpdateFilesTmpPath = Application.StartupPath + @"\UpdateFiles\"; /// /// 主程序的文件名 /// private static string strMainProFileName = CommonHelper.strApplicationName; /// /// 主程序目录 /// //private static string strMainProDir = new DirectoryInfo(Application.StartupPath).Parent.FullName; private static string strMainProDir = new DirectoryInfo(Application.StartupPath).FullName; /// /// 备份目录 /// private static string strBackupPath = strMainProDir + @"\backup\"; /// /// 主程序的路径 /// private static string strMainProPath = strMainProDir + "\\" + strMainProFileName; /// /// 获取MD5 /// /// /// private static string GetMD5(string s) { try { FileStream file = new FileStream(s, FileMode.Open); MD5 md5 = new MD5CryptoServiceProvider(); byte[] retval = md5.ComputeHash(file); file.Close(); StringBuilder sc = new StringBuilder(); for (int i = 0; i < retval.Length; i++) { sc.Append(retval[i].ToString("x2")); } return sc.ToString(); } catch (Exception ex) { return ""; } } public FormUpdate() { InitializeComponent(); this.ActiveControl = btnUpdate; btnUpdate.Focus(); //430, 333 } private void FormUpdate_Load(object sender, EventArgs e) { //提示关掉所有主程序 //if(classCPIR.checkProcessForProName(strMainProFileName)) //{ // classMsg.messageInfoBox("进程中检测到主程序正在运行,请先关闭才可更新。" + strMainProFileName); // return; //} checkUpdate(); btnUpdate.Focus(); } /// /// 检测是否有更新 /// private void checkUpdate() { strUpdateURL = getConfigValue(strUpdateXmlPath, "Url"); //读取本地xml中配置的更新服务器的URL string strLastUpdateDate = getConfigValue(strUpdateXmlPath, "UpDate"); //读取本地xml中配置的最近一次更新日期 if (strUpdateURL.Substring(strUpdateURL.Length - 1) != "/") //如果配置的xml中URL没带最后一个反斜杠,则加一下,防止出错 strUpdateURL += "/"; strTheUpdateDate = getTheLastUpdateTime(strUpdateURL); //获得更新服务器端的此次更新日期 if (!String.IsNullOrEmpty(strTheUpdateDate) && !String.IsNullOrEmpty(strLastUpdateDate)) //日期都不为空 { if (DateTime.Compare( Convert.ToDateTime(strTheUpdateDate, CultureInfo.InvariantCulture), Convert.ToDateTime(strLastUpdateDate, CultureInfo.InvariantCulture)) > 0) //字符转日期,并比较日期大小 { boolUpdateFalg = true; //本次更新日期 大于 最近一次更新日期,有更新,修改更新标记 } } } /// /// 开始更新 /// private void updaterStart() { float tempf; //this.progressBarDownFile.Maximum = 808380; //委托下载数据时事件 this.downWebClient.DownloadProgressChanged += delegate(object wcsender, DownloadProgressChangedEventArgs ex) { this.labelDownload.Text = String.Format( CultureInfo.InvariantCulture, "正在下载:{0} [ {1}/{2} ]", strCurFileName, convertSize(ex.BytesReceived), convertSize(ex.TotalBytesToReceive)); lngCurFileSize = ex.TotalBytesToReceive; tempf = ((float)(UPSIZE + ex.BytesReceived) / SIZE); this.progressBarState.Value = Convert.ToInt32(tempf * 100); this.progressBarDownFile.Value = ex.ProgressPercentage; }; //委托下载完成时事件 this.downWebClient.DownloadFileCompleted += delegate(object wcsender, AsyncCompletedEventArgs ex) { if (ex.Error != null) { classMsg.messageInfoBox(ex.Error.Message); //classLog.WriteLog(AutoUpdateEx.lib.ClassLog.LogType.Error, "Source:{" + ex.Error.Source + "}" + " StackTrace:{" + ex.Error.StackTrace + "}" + " Message:{" + ex.Error.Message + "}"); } else { //创建备份日期+更新文件目录 if (!Directory.Exists(strBackupPath + strTheUpdateDate + strCurPath)) //路径是否存在 { createMultipleFolders(strBackupPath, strTheUpdateDate + strCurPath); //创建多层目录 } try { //备份 if (File.Exists(strMainProDir + strCurFileName)) { if (File.Exists(strBackupPath + strTheUpdateDate + strCurFileName)) File.Delete(strBackupPath + strTheUpdateDate + strCurFileName); File.Move(strMainProDir + strCurFileName, strBackupPath + strTheUpdateDate + strCurFileName); if (File.Exists(strBackupPath + strTheUpdateDate + strCurFileName)) File.Delete(strMainProDir + strCurFileName); } //将更新文件从临时存放路径移动到正式路径中 File.Move(Application.StartupPath + @"\UpdateFiles\" + strCurFileName, strMainProDir + strCurFileName); } catch (IOException ioex) { classMsg.messageInfoBox(ioex.Message + strCurFileName); //已更新过,再更新报这个文件已存在;或者未备份成功的话更新会出故障 classLog.WriteLog(AutoUpdateEx.lib.ClassLog.LogType.Error, "Source:{" + ioex.Source + "}" + " StackTrace:{" + ioex.StackTrace + "}" + " Message:{" + ioex.Message + "}"); } UPSIZE += lngCurFileSize; if (strArrayFileNames.Length > NUM) { downloadFile(NUM); } else { setConfigValue(strUpdateXmlPath, "UpDate", strTheUpdateDate); classMsg.messageInfoBox("更新完成!"); updaterClose(); } } }; SIZE = getUpdateSize(strUpdateURL + strUpdateListXmlPath); if (SIZE == 0) //判断要更新的文件是否有大小,如果为0字节则结束更新 updaterClose(); NUM = 0; UPSIZE = 0; updateList(); if (strArrayFileNames != null) downloadFile(0); } void downWebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs ex) { float tempf; this.labelDownload.Text = String.Format( CultureInfo.InvariantCulture, "正在下载:{0} [ {1}/{2} ]", strCurFileName, convertSize(ex.BytesReceived), convertSize(ex.TotalBytesToReceive)); lngCurFileSize = ex.TotalBytesToReceive; tempf = ((float)(UPSIZE + ex.BytesReceived) / SIZE); this.progressBarState.Value = Convert.ToInt32(tempf * 100); this.progressBarDownFile.Value = ex.ProgressPercentage; } /// /// 获取更新文件大小统计 /// /// 更新文件数据XML /// 返回值 private static long getUpdateSize(string filePath) { long len = 0; try { WebClient wc = new WebClient(); Stream sm = wc.OpenRead(filePath); XmlTextReader xr = new XmlTextReader(sm); while (xr.Read()) { if (xr.Name == "UpdateSize") { len = Convert.ToInt64(xr.GetAttribute("Size"), CultureInfo.InvariantCulture); break; } } xr.Close(); sm.Close(); } catch (WebException ex) { classMsg.messageInfoBox(ex.Message); classLog.WriteLog(AutoUpdateEx.lib.ClassLog.LogType.Error, "Source:{" + ex.Source + "}" + " StackTrace:{" + ex.StackTrace + "}" + " Message:{" + ex.Message + "}"); } return len; } /// /// 获取文件列表并赋给全局变量 /// private static void updateList() { string xmlPath = strUpdateURL + strUpdateListXmlPath; WebClient wc = new WebClient(); DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; try { StringBuilder sb = new StringBuilder(); Stream sm = wc.OpenRead(xmlPath); XDocument oXDoc = XDocument.Load(xmlPath); System.Xml.Linq.XElement xeRoot = oXDoc.Root; System.Xml.Linq.XElement xele = xeRoot.Element("UpdateFileList"); foreach (var ele in xele.Elements()) { if (sb.Length == 0) { sb.Append(ele.Value); } else { sb.Append("," + ele.Value); } } strArrayFileNames = sb.ToString().Split(','); //赋给全局变量 //sm.Close(); } catch (WebException ex) { classMsg.messageInfoBox(ex.Message); classLog.WriteLog(AutoUpdateEx.lib.ClassLog.LogType.Error, "Source:{" + ex.Source + "}" + " StackTrace:{" + ex.StackTrace + "}" + " Message:{" + ex.Message + "}"); } } /// /// 下载文件 /// /// 下载序号 private void downloadFile(int arry) { try { NUM++; //全局变量来记录下载的文件序号 strCurFileName = strArrayFileNames[arry]; string strFileName = strCurFileName.Substring(strCurFileName.LastIndexOf("\\") + 1); //截取文件名 strCurPath = strCurFileName.Substring(0, strCurFileName.LastIndexOf("\\") + 1); //截取文件路径 if (!Directory.Exists(strUpdateFilesTmpPath + strCurPath)) //路径是否存在 { createMultipleFolders(strUpdateFilesTmpPath, strCurPath); //创建多层目录 } this.labelState.Text = String.Format( CultureInfo.InvariantCulture, "更新进度 {0}/{1} [ {2} ]", NUM, COUNT, convertSize(SIZE)); //this.progressBarDownFile.Value = 0; this.downWebClient.DownloadFileAsync(new Uri(strUpdateURL + strUpdateFilesUrl + strCurFileName.Replace('\\', '/')), strUpdateFilesTmpPath + strCurFileName); } catch (WebException ex) { classMsg.messageInfoBox(ex.Message); classLog.WriteLog(AutoUpdateEx.lib.ClassLog.LogType.Error, "Source:{" + ex.Source + "}" + " StackTrace:{" + ex.StackTrace + "}" + " Message:{" + ex.Message + "}"); } } /// /// 创建多层文件夹 /// /// 弹出文件浏览窗口选中的符文件路径 /// 需要创建的多层文件夹路径 private void createMultipleFolders(string strParentPath, string strPath) { try { if (strParentPath != "") { Directory.CreateDirectory(strParentPath + @strPath); } } catch (IOException ex) { classMsg.messageInfoBox("路径存在非法字符" + ex.Message); classLog.WriteLog(AutoUpdateEx.lib.ClassLog.LogType.Error, "Source:{" + ex.Source + "}" + " StackTrace:{" + ex.StackTrace + "}" + " Message:{" + ex.Message + "}"); } } /// /// 转换字节大小 /// /// 输入字节数 /// 返回值 private static string convertSize(long byteSize) { string str = ""; float tempf = (float)byteSize; if (tempf / 1024 > 1) { if ((tempf / 1024) / 1024 > 1) { str = ((tempf / 1024) / 1024).ToString("##0.00", CultureInfo.InvariantCulture) + "MB"; } else { str = (tempf / 1024).ToString("##0.00", CultureInfo.InvariantCulture) + "KB"; } } else { str = tempf.ToString(CultureInfo.InvariantCulture) + "B"; } return str; } /// /// 关闭程序 /// private static void updaterClose() { try { if (classCPIR.checkProcess(strMainProPath)) { classMsg.messageInfoBox("更新程序" + strMainProFileName + "已打开!"); } else { //启动程序 Process.Start(strMainProPath); } } catch (Win32Exception ex) { classMsg.messageInfoBox(ex.Message); //主程序未更新成功或者被误删掉,再更新一遍 classLog.WriteLog(AutoUpdateEx.lib.ClassLog.LogType.Error, "Source:{" + ex.Source + "}" + " StackTrace:{" + ex.StackTrace + "}" + " Message:{" + ex.Message + "}"); } Application.Exit(); //退出程序 } /// /// 读取update.xml /// /// update.xml文件的路径 /// "key"的值 /// 返回"value"的值 internal static string getConfigValue(string path, string appKey) { XmlDocument xDoc = new XmlDocument(); XmlNode xNode; XmlElement xElem = null; try { xDoc.Load(path); xNode = xDoc.SelectSingleNode("//appSettings"); xElem = (XmlElement)xNode.SelectSingleNode("//add[@key=\"" + appKey + "\"]"); } catch (XmlException ex) { classMsg.messageInfoBox(ex.Message); classLog.WriteLog(AutoUpdateEx.lib.ClassLog.LogType.Error, "Source:{" + ex.Source + "}" + " StackTrace:{" + ex.StackTrace + "}" + " Message:{" + ex.Message + "}"); } if (xElem != null) return xElem.GetAttribute("value"); else return ""; } /// /// 程序更新完后,要更新update.xml /// /// update.xml文件的路径 /// "key"的值 /// "value"的值 internal static void setConfigValue(string strXmlPath, string appKey, string appValue) { XmlDocument xDoc = new XmlDocument(); try { xDoc.Load(strXmlPath); XmlNode xNode; XmlElement xElem1; XmlElement xElem2; xNode = xDoc.SelectSingleNode("//appSettings"); xElem1 = (XmlElement)xNode.SelectSingleNode("//add[@key=\"" + appKey + "\"]"); if (xElem1 != null) xElem1.SetAttribute("value", appValue); else { xElem2 = xDoc.CreateElement("add"); xElem2.SetAttribute("key", appKey); xElem2.SetAttribute("value", appValue); xNode.AppendChild(xElem2); } xDoc.Save(strXmlPath); } catch (XmlException ex) { classMsg.messageInfoBox(ex.Message); classLog.WriteLog(AutoUpdateEx.lib.ClassLog.LogType.Error, "Source:{" + ex.Source + "}" + " StackTrace:{" + ex.StackTrace + "}" + " Message:{" + ex.Message + "}"); } } /// /// 获取服务器端软件的更新日期 /// /// 服务器地址 /// 返回日期 private static string getTheLastUpdateTime(string Dir) { string LastUpdateTime = ""; string AutoUpdaterFileName = Dir + strUpdateListXmlPath; try { WebClient wc = new WebClient(); Stream sm = wc.OpenRead(AutoUpdaterFileName); XmlTextReader xml = new XmlTextReader(sm); while (xml.Read()) { if (xml.Name == "UpdateTime") { LastUpdateTime = xml.GetAttribute("Date"); break; } } xml.Close(); sm.Close(); } catch (WebException ex) { classMsg.messageInfoBox(ex.Message); classLog.WriteLog(AutoUpdateEx.lib.ClassLog.LogType.Error, "Source:{" + ex.Source + "}" + " StackTrace:{" + ex.StackTrace + "}" + " Message:{" + ex.Message + "}"); } return LastUpdateTime; } private void btnCancel_Click(object sender, EventArgs e) { try { updaterClose(); } catch { } } public void btnUpdate_Click(object sender, EventArgs e) { //提示关掉所有主程序 if (classCPIR.checkProcessForProName(strMainProFileName)) { classMsg.messageInfoBox("进程中检测到主程序正在运行,请先关闭才可更新。" + strMainProFileName); return; } if (boolUpdateFalg) //是否有更新 { updaterStart(); //有更新 } else { classMsg.messageInfoBox("已经是最新版本!"); updaterClose(); //默认无更新 } } private void FormUpdate_KeyDown(object sender, KeyEventArgs e) { } private void FormUpdate_KeyPress(object sender, KeyPressEventArgs e) { } private void FormUpdate_KeyUp(object sender, KeyEventArgs e) { //if (e.KeyCode == Keys.Enter) //{ // btnUpdate_Click(null, null); //} //else if (e.KeyCode == Keys.Escape) //{ // updaterClose(); //} //else //{ // MessageBox.Show("请按Enter确定更新,Esc取消更新!"); //} } } }