/****************************** * 说明:下载进度窗口 * 创建人:龚宇超 * 创建日期:2018-02-09 * 修改人: * 修改日期: * 修改备注: * 版本:1.0.0.0 ******************************/ using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.IO; using System.Threading; using System.Net; namespace Lskj.AutoUpdate { /// /// 下载进度窗口 /// public partial class FrmProgress : Form { /// /// 是否下载完成 /// private bool isFinished = false; /// /// 下载总数 /// private long total = 0; /// /// 当前下载的总数 /// private long nDownloadedTotal = 0; /// /// 下载文件列表 /// private List downloadFileList = new List(); /// /// 所有文件列表 /// private List allFileList = null; private ManualResetEvent evtDownload = null; private ManualResetEvent evtPerDonwload = null; private WebClient clientDownload = null; private AutoUpdater autoUpdater = null; private bool isUserCanceled = false; private bool isClosingByCode = false; private delegate void SetProcessBarCallBack(int current, int total); private delegate void ExitCallBack(bool success); private delegate void ShowCurrentDownloadFileNameCallBack(string name); public bool IsUserCanceled { get { return isUserCanceled; } } public FrmProgress(List downloadFileListTemp, AutoUpdater apd) { InitializeComponent(); this.autoUpdater = apd; this.downloadFileList = downloadFileListTemp; this.allFileList = downloadFileListTemp.Select(x => new DownloadFileInfo(x.DownloadUrl, x.FileName, x.Version, x.Size, x.LastVersion)).ToList(); //foreach (DownloadFileInfo item in downloadFileListTemp) //{ // // 本地临时文件夹中不存在此文件则下载. // string tempUrlPath = CommonUnitity.GetFolderUrl(item); // tempUrlPath = Path.Combine(CommonUnitity.SystemBinUrl + ConstModel.TEMP_FOLDER_NAME + tempUrlPath, item.FileName); // if (!File.Exists(tempUrlPath)) // { // this.downloadFileList.Add(item); // } //} this.lblAll.Text = string.Format("总进度(0/{0})", this.downloadFileList.Count); } /// /// 说明:窗口加载 /// 创建人:龚宇超 /// 创建日期:2018-02-23 /// 修改人: /// 修改日期: /// 修改备注: /// 版本:1.0 /// /// The sender. /// The instance containing the event data. private void OnFormLoad(object sender, EventArgs e) { evtDownload = new ManualResetEvent(true); evtDownload.Reset(); ThreadPool.QueueUserWorkItem(new WaitCallback(this.ProcDownload)); } /// /// 说明: /// 创建人:龚宇超 /// 创建日期:2018-02-23 /// 修改人: /// 修改日期: /// 修改备注: /// 版本:1.0 /// /// The sender. /// The instance containing the event data. private void OnFormClosing(object sender, FormClosingEventArgs e) { if (isClosingByCode) { ReleaseDownloadWait(); return; } if (!isFinished && !isUserCanceled) { if (DialogResult.Yes != MessageBox.Show(ConstModel.UPDATE_GIVING_UP, ConstModel.LS_TITLE_NAME, MessageBoxButtons.YesNo, MessageBoxIcon.Question)) { e.Cancel = true; return; } CancelUpdate(true); } ReleaseDownloadWait(); } /// /// 说明:下载文件 /// 创建人:龚宇超 /// 创建日期:2018-02-23 /// 修改人: /// 修改日期: /// 修改备注: /// 版本:1.0 /// /// The o. private void ProcDownload(object o) { string tempFolderPath = Path.Combine(CommonUnitity.SystemBinUrl, ConstModel.TEMP_FOLDER_NAME); if (!Directory.Exists(tempFolderPath)) { Directory.CreateDirectory(tempFolderPath); } evtPerDonwload = new ManualResetEvent(false); foreach (DownloadFileInfo file in this.downloadFileList) { total += file.Size; } int count = this.downloadFileList.Count; while (!evtDownload.WaitOne(0, false)) { if (isUserCanceled) return; try { if (this.downloadFileList.Count == 0) break; this.lblAll.Text = string.Format("总进度({0}/{1})", count - this.downloadFileList.Count, count); DownloadFileInfo file = this.downloadFileList[0]; //Debug.WriteLine(String.Format("Start Download:{0}", file.FileName)); this.ShowCurrentDownloadFileName(file.FileName); //Download clientDownload = new WebClient(); //Added the function to support proxy //clientDownload.Proxy = System.Net.WebProxy.GetDefaultProxy(); clientDownload.Proxy = WebRequest.GetSystemWebProxy(); clientDownload.Proxy.Credentials = CredentialCache.DefaultCredentials; clientDownload.Credentials = System.Net.CredentialCache.DefaultCredentials; //End added clientDownload.DownloadProgressChanged += (object sender, DownloadProgressChangedEventArgs e) => { try { //this.Text = "接收:" + e.BytesReceived.ToString() + " 字节数:" + progressBarCurrent.Maximum.ToString(); progressBarCurrent.Value = (int)e.BytesReceived / progressBarCurrent.Maximum; progressBarTotal.Value = (int)((nDownloadedTotal + e.BytesReceived) * 100 / total); this.SetProcessBar(e.ProgressPercentage, (int)((nDownloadedTotal + e.BytesReceived) * 100 / total)); } catch (Exception ex) { //MessageBox.Show(ex.Message); //log the error message,you can use the application's log code } }; clientDownload.DownloadFileCompleted += (object sender, AsyncCompletedEventArgs e) => { try { if (isUserCanceled || e.Cancelled) { evtPerDonwload.Set(); return; } if (e.Error != null) throw e.Error; DealWithDownloadErrors(); DownloadFileInfo dfile = e.UserState as DownloadFileInfo; nDownloadedTotal += dfile.Size; this.SetProcessBar(0, (int)(nDownloadedTotal * 100 / total)); evtPerDonwload.Set(); } catch (Exception exp) { if (!isUserCanceled) { DownloadFileInfo dfile = e.UserState as DownloadFileInfo; MessageBox.Show(BuildDownloadErrorMessage(dfile, exp), ConstModel.LS_TITLE_NAME, MessageBoxButtons.OK, MessageBoxIcon.Warning); evtPerDonwload.Set(); ShowErrorAndRestartApplication(); } //log the error message,you can use the application's log code } }; evtPerDonwload.Reset(); //Download the folder file string tempFolderPath1 = CommonUnitity.GetFolderUrl(file); if (!string.IsNullOrEmpty(tempFolderPath1)) { tempFolderPath = Path.Combine(CommonUnitity.SystemBinUrl, ConstModel.TEMP_FOLDER_NAME); tempFolderPath += tempFolderPath1; } else { tempFolderPath = Path.Combine(CommonUnitity.SystemBinUrl, ConstModel.TEMP_FOLDER_NAME); } clientDownload.DownloadFileAsync(new Uri(file.DownloadUrl), Path.Combine(tempFolderPath, file.FileName), file); //Wait for the download complete evtPerDonwload.WaitOne(); if (isUserCanceled) return; clientDownload.Dispose(); clientDownload = null; //Remove the downloaded files this.downloadFileList.Remove(file); } catch (Exception e) { if (!isUserCanceled) { DownloadFileInfo file = this.downloadFileList.Count > 0 ? this.downloadFileList[0] : null; MessageBox.Show(BuildDownloadErrorMessage(file, e), ConstModel.LS_TITLE_NAME, MessageBoxButtons.OK, MessageBoxIcon.Warning); ShowErrorAndRestartApplication(); } //throw; } } if (isUserCanceled) return; //When the files have not downloaded,return. if (downloadFileList.Count > 0) { return; } //Test network and deal with errors if there have DealWithDownloadErrors(); //Debug.WriteLine("All Downloaded"); bool replaceSuccess = true; foreach (DownloadFileInfo file in this.allFileList) { string tempUrlPath = CommonUnitity.GetFolderUrl(file); string oldPath = string.Empty; string newPath = string.Empty; try { if (!string.IsNullOrEmpty(tempUrlPath)) { oldPath = Path.Combine(CommonUnitity.SystemBinUrl + tempUrlPath.Substring(1), file.FileName); newPath = Path.Combine(CommonUnitity.SystemBinUrl + ConstModel.TEMP_FOLDER_NAME + tempUrlPath, file.FileName); } else { oldPath = Path.Combine(CommonUnitity.SystemBinUrl, file.FileName); newPath = Path.Combine(CommonUnitity.SystemBinUrl + ConstModel.TEMP_FOLDER_NAME, file.FileName); } //just deal with the problem which the files EndsWith xml can not download System.IO.FileInfo f = new FileInfo(newPath); if (!file.Size.ToString().Equals(f.Length.ToString()) && !file.FileName.ToString().EndsWith(".xml")) { MessageBox.Show($"文件大小与服务器不匹配 "+ file.FileName); ShowErrorAndRestartApplication(); } //Added for dealing with the config file download errors string newfilepath = string.Empty; if (newPath.Substring(newPath.LastIndexOf(".") + 1).Equals(ConstModel.CONFIG_FILE_KEY)) { if (System.IO.File.Exists(newPath)) { if (newPath.EndsWith("_")) { newfilepath = newPath; newPath = newPath.Substring(0, newPath.Length - 1); oldPath = oldPath.Substring(0, oldPath.Length - 1); } File.Move(newfilepath, newPath); } } //End added if (File.Exists(oldPath)) { MoveFolderToOld(oldPath, newPath); } else { //Edit for config_ file if (!string.IsNullOrEmpty(tempUrlPath)) { if (!Directory.Exists(CommonUnitity.SystemBinUrl + tempUrlPath.Substring(1))) { Directory.CreateDirectory(CommonUnitity.SystemBinUrl + tempUrlPath.Substring(1)); MoveFolderToOld(oldPath, newPath); } else { MoveFolderToOld(oldPath, newPath); } } else { MoveFolderToOld(oldPath, newPath); } } } catch (Exception exp) { replaceSuccess = false; MessageBox.Show("oldPath:" + oldPath + "\r\n" + "newPath:" + newPath + "\r\n" + exp.Message); break; //log the error message,you can use the application's log code } } //After dealed with all files, clear the data this.allFileList.Clear(); if (this.downloadFileList.Count == 0 && replaceSuccess) Exit(true); else Exit(false); evtDownload.Set(); } /// /// 说明:移动文件 /// 创建人:龚宇超 /// 创建日期:2018-02-23 /// 修改人: /// 修改日期: /// 修改备注: /// 版本:1.0 /// /// The old path. /// The new path. private void MoveFolderToOld(string oldPath, string newPath) { Exception lastException = null; for (int i = 0; i < 10; i++) { try { if (File.Exists(oldPath + ".old")) System.IO.File.SetAttributes(oldPath + ".old", System.IO.FileAttributes.Normal); if (File.Exists(oldPath)) System.IO.File.SetAttributes(oldPath, System.IO.FileAttributes.Normal); if (File.Exists(oldPath + ".old")) File.Delete(oldPath + ".old"); if (File.Exists(oldPath)) File.Move(oldPath, oldPath + ".old"); File.Move(newPath, oldPath); return; } catch (IOException ex) { lastException = ex; Thread.Sleep(500); } catch (UnauthorizedAccessException ex) { lastException = ex; Thread.Sleep(500); } } throw new IOException("文件可能仍被其他进程占用,重试后仍无法替换:" + oldPath, lastException); } /// /// 说明:显示当前下载文件 /// 创建人:龚宇超 /// 创建日期:2018-02-23 /// 修改人: /// 修改日期: /// 修改备注: /// 版本:1.0 /// /// The name. private void ShowCurrentDownloadFileName(string name) { if (this.labelCurrentItem.InvokeRequired) { ShowCurrentDownloadFileNameCallBack cb = new ShowCurrentDownloadFileNameCallBack(ShowCurrentDownloadFileName); this.Invoke(cb, new object[] { name }); this.labelCurrentItem.Text = "正在更新:" + name; } else { this.labelCurrentItem.Text = "正在更新:" + name; } } /// /// 说明:显示当前进度 /// 创建人:龚宇超 /// 创建日期:2018-02-23 /// 修改人: /// 修改日期: /// 修改备注: /// 版本:1.0 /// /// The current. /// The total. private void SetProcessBar(int current, int total) { if (this.progressBarCurrent.InvokeRequired) { SetProcessBarCallBack cb = new SetProcessBarCallBack(SetProcessBar); this.Invoke(cb, new object[] { current, total }); } else { this.progressBarCurrent.Value = current; this.progressBarTotal.Value = total; } } /// /// 说明:退出升级程序 /// 创建人:龚宇超 /// 创建日期:2018-02-23 /// 修改人: /// 修改日期: /// 修改备注: /// 版本:1.0 /// /// if set to true [success]. private void Exit(bool success) { if (this.InvokeRequired) { ExitCallBack cb = new ExitCallBack(Exit); this.Invoke(cb, new object[] { success }); } else { this.isClosingByCode = true; this.isFinished = success; this.DialogResult = success ? DialogResult.OK : DialogResult.Cancel; this.Close(); } } /// /// 说明:取消 /// 创建人:龚宇超 /// 创建日期:2018-02-23 /// 修改人: /// 修改日期: /// 修改备注: /// 版本:1.0 /// /// The sender. /// The instance containing the event data. private void OnCancel(object sender, EventArgs e) { if (isUserCanceled) return; if (!isFinished && DialogResult.Yes == MessageBox.Show(ConstModel.UPDATE_GIVING_UP, ConstModel.LS_TITLE_NAME, MessageBoxButtons.YesNo, MessageBoxIcon.Question)) { CancelUpdate(true); Exit(false); } } /// /// 说明:下载失败 /// 创建人:龚宇超 /// 创建日期:2018-02-23 /// 修改人: /// 修改日期: /// 修改备注: /// 版本:1.0 /// private void DealWithDownloadErrors() { try { //Test Network is OK or not. Config config = new Config().LoadConfig(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConstModel.AUTOUPDATE_CONFIG)); WebClient client = new WebClient(); client.DownloadString(config.ServerUrl); } catch (Exception e) { //MessageBox.Show(e.Message); //log the error message,you can use the application's log code ShowErrorAndRestartApplication(); } } /// /// 将WebClient下载异常转换为用户可理解的提示。 /// /// 下载文件信息。 /// 下载异常。 /// 友好的错误提示。 private string BuildDownloadErrorMessage(DownloadFileInfo file, Exception ex) { StringBuilder message = new StringBuilder(); message.AppendLine("更新文件下载失败。"); if (file != null) { message.AppendLine("文件:" + file.FileName); message.AppendLine("地址:" + file.DownloadUrl); } message.AppendLine("原因:" + GetFriendlyDownloadExceptionMessage(ex)); message.AppendLine(); message.AppendLine("请检查网络是否正常、更新服务器是否可访问,或关闭防火墙/杀毒软件拦截后重试。"); return message.ToString(); } /// /// 获取下载异常的具体原因。 /// /// 异常对象。 /// 异常原因说明。 private string GetFriendlyDownloadExceptionMessage(Exception ex) { WebException webException = ex as WebException; if (webException == null && ex != null) { webException = ex.InnerException as WebException; } if (webException != null) { HttpWebResponse response = webException.Response as HttpWebResponse; if (response != null) { if (response.StatusCode == HttpStatusCode.ProxyAuthenticationRequired) { return "代理服务器需要认证,请检查系统代理账号或网络代理设置。"; } if (response.StatusCode == HttpStatusCode.NotFound) { return "服务器上找不到该更新文件,请确认更新包是否已上传完整。"; } if (response.StatusCode == HttpStatusCode.Forbidden || response.StatusCode == HttpStatusCode.Unauthorized) { return "服务器拒绝访问该更新文件,请检查文件权限或更新地址配置。"; } return string.Format("服务器返回 {0} {1},请确认更新文件是否存在或服务器权限是否正确。", (int)response.StatusCode, response.StatusDescription); } switch (webException.Status) { case WebExceptionStatus.NameResolutionFailure: return "无法解析更新服务器地址,请检查DNS、网络连接或服务器地址配置。"; case WebExceptionStatus.ConnectFailure: return "无法连接更新服务器,请检查网络、服务器端口或防火墙设置。"; case WebExceptionStatus.Timeout: return "连接更新服务器超时,可能是网络不稳定或服务器响应过慢。"; case WebExceptionStatus.ProxyNameResolutionFailure: return "代理服务器地址解析失败,请检查系统代理设置。"; case WebExceptionStatus.TrustFailure: case WebExceptionStatus.SecureChannelFailure: return "安全连接失败,请检查服务器证书或客户端系统时间。"; case WebExceptionStatus.ConnectionClosed: return "下载过程中连接被关闭,可能是网络中断或服务器主动断开。"; case WebExceptionStatus.ProtocolError: return "服务器返回了错误响应,请确认更新地址和文件权限。"; default: return webException.Message; } } if (ex is UriFormatException) { return "更新文件地址格式不正确。"; } if (ex is UnauthorizedAccessException) { return "没有写入更新文件的权限,请以管理员身份运行或检查目录权限。"; } if (ex is IOException) { return "写入更新文件失败,可能是文件被占用或磁盘空间不足。"; } return ex != null ? ex.Message : "未知错误。"; } /// /// 说明:重新下载 /// 创建人:龚宇超 /// 创建日期:2018-02-23 /// 修改人: /// 修改日期: /// 修改备注: /// 版本:1.0 /// private void ShowErrorAndRestartApplication() { DialogResult dlg = MessageBox.Show(ConstModel.UPDATE_CONTINUE, ConstModel.LS_TITLE_NAME, MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (dlg == DialogResult.Yes) CommonUnitity.RestartApplication(); else { if (clientDownload != null) clientDownload.CancelAsync(); ReleaseDownloadWait(); autoUpdater.RollBack(); Exit(false); } } private void CancelUpdate(bool rollback) { isUserCanceled = true; if (clientDownload != null) clientDownload.CancelAsync(); ReleaseDownloadWait(); if (rollback) autoUpdater.RollBack(true); } private void ReleaseDownloadWait() { if (evtDownload != null) evtDownload.Set(); if (evtPerDonwload != null) evtPerDonwload.Set(); } } }