/******************************
* 说明:下载进度窗口
* 创建人:龚宇超
* 创建日期: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();
}
}
}