# UpdateSoon
**Repository Path**: Howesdomo/UpdateSoon
## Basic Information
- **Project Name**: UpdateSoon
- **Description**: 配合发现新版本, 下载新版本zip后使用
- **Primary Language**: C#
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2021-12-16
- **Last Updated**: 2022-02-07
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# UpdateSoon
UpdateSoon -- WPF 程序更新程序
### 测试运行参数
~~~c#
// arg0 arg1 arg2
// 参数解释
// arg0 - 程序名称
// arg1 - gitee发布zip下载地址
// arg2 - 文件夹地址 (zip文件解压后, 拷贝到的程序目录)
// 示例:
"QRCoderCreator.exe" "C:\Users\Howe\AppData\Roaming\HoweSoftware\QRCodeCreator\Downloads\1.0.0.12\QRCodeCreator1.0.0.12.zip" "D:\SC_Github\QRCodeTools\QRCodeCreator\bin\Debug"
~~~
### 配合控件使用
xaml代码
~~~xaml
~~~
csharp代码
~~~c#
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Xml.Linq;
using System.Xml.XPath;
// 引入Assemblies - System.Runtime.Serialization
using System.Runtime.Serialization.Json;
// TODO Step 1 修改命名空间
namespace QRCodeCreator
{
// V 1.0.2 - 2022-02-07 15:50:27
// 搞清楚了弹出 WerFault.exe 的问题, 要先判断 System.Diagnostics.Debugger.IsAttached == true 之后才能, System.Diagnostics.Debugger.Break()
//
// V 1.0.1 - 2022-02-07 14:12:34
// 没有网络时访问 GitXxx 获取最新版本时, 会弹出 WerFault.exe 并提示 XXXX.exe 已停止工作.
// 优化没有网络的表现
///
/// Interaction logic for UcCheckUpdate_Gitee.xaml
///
public partial class UcCheckUpdate_GitXxx : UserControl
{
// TODO Step2 选择 Gitee or Github, 并根据各自的要求修改发行版地址
///
/// Gitee 发行版页面, 返回html信息
///
public const string CheckUpdateUrl = "https://gitee.com/Howesdomo/QRCodeTools/releases";
///
/// Github 发行版 api, 返回json信息
///
public const string CheckUpdateUrl_Github = "https://api.github.com/repos/howesdomo/QRCodeTools/releases/latest";
System.ComponentModel.BackgroundWorker mBgWorker_CheckUpdateGitee { get; set; }
System.ComponentModel.BackgroundWorker mBgWorker_DownloadUpdate { get; set; }
#region [DP] FindUpdateVersionInfo
public static readonly DependencyProperty FindUpdateVersionInfoProperty = DependencyProperty.Register
(
name: "FindUpdateVersionInfo",
propertyType: typeof(string),
ownerType: typeof(UcCheckUpdate_GitXxx),
validateValueCallback: null, // new ValidateValueCallback((toValidate) => { return true; }),
typeMetadata: new PropertyMetadata
(
defaultValue: null,
propertyChangedCallback: null,
coerceValueCallback: null
)
);
public string FindUpdateVersionInfo
{
get { return (string)GetValue(FindUpdateVersionInfoProperty); }
set { SetValue(FindUpdateVersionInfoProperty, value); }
}
#endregion
#region [DP] InfoVisibility
public static readonly DependencyProperty InfoVisibilityProperty = DependencyProperty.Register
(
name: "InfoVisibility",
propertyType: typeof(Visibility),
ownerType: typeof(UcCheckUpdate_GitXxx),
validateValueCallback: null,
typeMetadata: new PropertyMetadata
(
defaultValue: Visibility.Collapsed,
propertyChangedCallback: null,
coerceValueCallback: null
)
);
public Visibility InfoVisibility
{
get { return (Visibility)GetValue(InfoVisibilityProperty); }
set { SetValue(InfoVisibilityProperty, value); }
}
#endregion
#region [DP] ProgressBarVisibility
public static readonly DependencyProperty ProgressBarVisibilityProperty = DependencyProperty.Register
(
name: "ProgressBarVisibility",
propertyType: typeof(Visibility),
ownerType: typeof(UcCheckUpdate_GitXxx),
validateValueCallback: null, // new ValidateValueCallback((toValidate) => { return true; }),
typeMetadata: new PropertyMetadata
(
defaultValue: Visibility.Collapsed,
propertyChangedCallback: null,
coerceValueCallback: null
)
);
public Visibility ProgressBarVisibility
{
get { return (Visibility)GetValue(ProgressBarVisibilityProperty); }
set { SetValue(ProgressBarVisibilityProperty, value); }
}
#endregion
public UcCheckUpdate_GitXxx()
{
InitializeComponent();
this.Loaded += ucLoaded;
this.btnCheckUpdate.Click += btnCheckUpdate_Click;
}
void btnCheckUpdate_Click(object sender, RoutedEventArgs e)
{
checkUpdate();
}
void ucLoaded(object sender, RoutedEventArgs e)
{
checkUpdate();
}
int mRunCheckUpdateCount { get; set; } = 0;
void checkUpdate()
{
if (mBgWorker_CheckUpdateGitee != null && mBgWorker_CheckUpdateGitee.IsBusy == true)
{
return;
}
mBgWorker_CheckUpdateGitee = new System.ComponentModel.BackgroundWorker();
mBgWorker_CheckUpdateGitee.DoWork += (bgSender, bgArgs) =>
{
if (mRunCheckUpdateCount == 0)
{
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
}
bgArgs.Result = CheckUpdateUtils.CheckUpdateGitee(UcCheckUpdate_GitXxx.CheckUpdateUrl);
// bgArgs.Result = CheckUpdateUtils.CheckUpdateGithub(UcCheckUpdate_GitXxx.CheckUpdateUrl_Github);
};
mBgWorker_CheckUpdateGitee.RunWorkerCompleted += (bgSender, bgResult) =>
{
mRunCheckUpdateCount++;
if (bgResult.Error != null)
{
MessageBox.Show(bgResult.Error.Message, "捕获异常");
}
else
{
if (bgResult.Result == null)
{
return; // 1.没有网络; 2.GitXxx网址填写错误
}
this.btnCheckUpdate.Visibility = Visibility.Collapsed;
var hasNewVersion = (bool)bgResult.Result;
if (hasNewVersion == true)
{
this.InfoVisibility = Visibility.Visible;
this.FindUpdateVersionInfo = $"发现新版本: {CheckUpdateUtils.CheckUpdateModel.Version}";
}
}
};
mBgWorker_CheckUpdateGitee.WorkerReportsProgress = true;
mBgWorker_CheckUpdateGitee.ProgressChanged += (bgSender, bgArgs) =>
{
};
mBgWorker_CheckUpdateGitee.RunWorkerAsync(new object[] { });
}
private void Hyperlink_Click(object sender, RoutedEventArgs e)
{
if (this.btnCheckUpdate.Visibility == Visibility.Visible)
{
return;
}
if (mBgWorker_DownloadUpdate != null && mBgWorker_DownloadUpdate.IsBusy == true)
{
return;
}
var dr = MessageBox.Show
(
owner: Application.Current.MainWindow,
messageBoxText: $"{FindUpdateVersionInfo}\r\n确认更新?",
caption: "提示",
button: MessageBoxButton.OKCancel,
icon: MessageBoxImage.Question,
defaultResult: MessageBoxResult.OK
);
if (dr != MessageBoxResult.OK)
{
return;
}
mBgWorker_DownloadUpdate = new System.ComponentModel.BackgroundWorker();
mBgWorker_DownloadUpdate.DoWork += (bgSender, bgArgs) =>
{
CheckUpdateUtils.DownloadUpdate(CheckUpdateUtils.CheckUpdateModel.Version, CheckUpdateUtils.CheckUpdateModel.DownloadAddress);
};
mBgWorker_DownloadUpdate.RunWorkerCompleted += (bgSender, bgResult) =>
{
if (bgResult.Error != null)
{
MessageBox.Show(bgResult.Error.Message, "捕获异常", MessageBoxButton.OK, MessageBoxImage.Error);
}
else
{
if (System.IO.File.Exists(CheckUpdateUtils.CheckUpdateModel.ZipFilePath) == true)
{
if (CheckUpdateUtils.HasAppExit_Handle == true)
{
App.Current.Exit -= CheckUpdateUtils.Current_Exit;
}
this.ProgressBarVisibility = Visibility.Collapsed;
this.InfoVisibility = Visibility.Collapsed;
CheckUpdateUtils.CheckUpdateModel.IsDownloaded = true;
App.Current.Exit += CheckUpdateUtils.Current_Exit;
CheckUpdateUtils.HasAppExit_Handle = true;
}
MessageBox.Show($"新版本{CheckUpdateUtils.CheckUpdateModel.Version}下载完毕,\r\n关闭程序后更新到新版。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
};
mBgWorker_DownloadUpdate.WorkerReportsProgress = true;
mBgWorker_DownloadUpdate.ProgressChanged += (bgSender, bgArgs) =>
{
};
this.FindUpdateVersionInfo = $"下载新版本: {CheckUpdateUtils.CheckUpdateModel.Version}";
this.ProgressBarVisibility = Visibility.Visible;
mBgWorker_DownloadUpdate.RunWorkerAsync(new object[] { });
}
}
public class CheckUpdateUtils
{
///
/// 检查更新数据类
///
public static CheckUpdateModel CheckUpdateModel { get; set; }
///
/// 用于判断有否注册了 Application.Exit 事件
///
public static bool HasAppExit_Handle { get; set; }
public static IWebProxy WebHelper_GetProxy()
{
return null;
}
public static bool? CheckUpdateGitee(string url)
{
try
{
// Util.Web.WebUtils.FixWebRequest(); // 已迁移到 App.Startup 中处理
var request = (HttpWebRequest)WebRequest.Create(url);
request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393";
request.Proxy = WebHelper_GetProxy();
// var response = (HttpWebResponse)await request.GetResponseAsync();
var response = (HttpWebResponse)request.GetResponse();
using (var resultStream = response.GetResponseStream())
{
if (resultStream == null)
return null;
using (var reader = new StreamReader(resultStream))
{
// var result = await reader.ReadToEndAsync();
string html = reader.ReadToEnd();
string latestVersionStr = getVerionStr4Gitee(html); // 获取 gitee 上最新版本号
if (latestVersionStr.IsNullOrEmpty()) { return false; }
var version = Version.Parse(latestVersionStr);
// 比较版本号是否需要更新
if (version <= Assembly.GetExecutingAssembly().GetName().Version) { return false; }
string giteeDownloadUrl = string.Empty;
#region 获取 giteeDownloadUrl
// TODO优化直接获取到 下载地址
var pattern = "(?<=
\n
\n)
";
var a_href_MatchCollection = Regex.Matches(html, pattern);
string a_href = a_href_MatchCollection[0].Value;
string str_href = "href=\"";
string relativeUrl = a_href.Substring(a_href.IndexOf(str_href));
giteeDownloadUrl = relativeUrl.Substring(str_href.Length, relativeUrl.Length - 2 - str_href.Length);
#endregion
#region [弃用]获取 SHA 信息 (由于网站的 SHA 值是指 git 提交的SHA, 并不是下载文件的SHA )
//string SHA = string.Empty;
//var patternSHA = "(?<=
/// 下载更新包
///
///
public static void DownloadUpdate(Version version, string url)
{
string fileName = Path.GetFileName(url);
string saveDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "HoweSoftware", "QRCodeCreator", "Downloads", version.ToString());
if (Directory.Exists(saveDir) == false) { Directory.CreateDirectory(saveDir); }
string savePath = Path.Combine(saveDir, fileName);
if (File.Exists(savePath) == true) { File.Delete(savePath); }
CheckUpdateUtils.CheckUpdateModel.ZipFilePath = savePath;
using (var webClient = new WebClient())
{
webClient.Credentials = CredentialCache.DefaultNetworkCredentials;
webClient.Headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393";
webClient.Proxy = WebHelper_GetProxy();
webClient.DownloadFile(address: new Uri(url), fileName: savePath);
}
}
///
/// 执行 UpdateSoon.exe
/// 1. 解压zip
/// 2. 拷贝zip内所有文件到程序目录
///
public static void InstallUpdate(string zipFilePath)
{
string an = Assembly.GetExecutingAssembly().GetName().Name;
string[] args = new string[3] { an, zipFilePath, Environment.CurrentDirectory };
Util.ProgressUtils.ExcuteProgressWithArguments("UpdateSoon.exe", args.CombineString(" ", false));
}
private static void WebClient_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
string msg = $"downloaded ...";
System.Diagnostics.Debug.WriteLine(msg);
System.Diagnostics.Debugger.Break();
}
static string getVerionStr4Gitee(string html)
{
string r = null;
string tagSymbol = "";
var matchCollection = Regex.Matches(html, tagSymbol);
if (matchCollection.Count <= 0)
{
return r; // 没有版本
}
int start = html.IndexOf("
");
string content = html.Substring(start);
string startContent = "";
string endContent = "";
int startSpan = content.IndexOf(startContent);
int endSpan = content.IndexOf(endContent);
r = content.Substring(startSpan + startContent.Length, endSpan - startSpan - startContent.Length);
return r;
}
}
///
/// 检查更新数据类
///
public class CheckUpdateModel
{
///
/// 发布版在 Gitee / Github 上发现的最新版本
///
public Version Version { get; set; }
///
/// 发布版在 Gitee / Github 下载地址
///
public string DownloadAddress { get; set; }
///
/// 下载zip文件在硬盘的位置
///
public string ZipFilePath { get; set; }
///
/// zip文件下载完毕
///
public bool IsDownloaded { get; set; }
}
}
~~~