一 实现程序更新思路
程序实现自我升级,一般有两种方式:
1. 独立的更新程序
开发一个独立的更新程序如Update.exe,用于检查主程序是否有新版本,并下载和安装新版本。
实现步骤:
- 主程序启动完后,调用一下更新程序。
- 更新程序检查版本不需要更新就退出否则执行更新。
- 更新程序检查主程序的版本号。
- 如果主程序的版本号低于服务器上的版本号,则下载新版本。
- 下载完成后,更新程序将新版本安装到主程序的目录中(一般要备份旧版的)。
- 更新程序重启主程序。
优点:
- 更新过程对用户透明,用户无需手动操作。
- 可以实现增量更新,只下载更新的文件,节省流量。
缺点:
- 更新程序需要访问服务器,如果服务器不可访问,则无法更新。
2. 主程序自带更新功能
将更新功能集成到主程序中,无需开发额外的更新程序。
实现步骤如下:
- 主程序启动时,向服务器发送请求,检查是否有新版本。
- 如果有新版本,则服务器返回新版本的版本号和下载地址。
- 主程序下载新版本。
- 下载完成后,主程序将新版本安装到自己的目录中。
- 主程序重启自身。
优点:
- 开发简单,在更新过程中无需考虑与主程序通讯。
缺点:
- 每次更新都需要下载整个程序,比较耗流量,在更新后不能很好实现自我重启,需要用脚本或者其它方式。
二 程序更新代码(独立的更新程序)
1 主程序增加调用更新程序代码:
public void CheckForUpdates()
{
// 检查是否存在更新程序
if (File.Exists("Update.exe"))
{
// 启动更新程序
try
{
Process.Start("Update.exe");
}
catch (Exception e)
{
}
}
}
2 update.exe完整代码:
//这部分用于隐藏窗体或控制台,一般静默更新,重启才提示。
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_HIDE = 0;
String processName = "xxx平台";
String mainFilePath = "xxx平台.exe";
String infaceVersion = "";
String updateUrl = "http://192.168.22.100:1888/pc/getVersionInfo";
String downloadUrlExe = "http://192.168.22.144:8904/common/downloadAppFile/2666c50a-6e1c-4a8a-b8f6-c1a5002d4ca0.exe";
String downloadFileName = "test.exe";
public Form1()
{
InitializeComponent();
// 获取控制台窗口句柄
IntPtr hWnd = GetConsoleWindow();
// 隐藏控制台窗口
ShowWindow(hWnd, SW_HIDE);
}
private void Form1_Load(object sender, EventArgs e)
{
Update();
}
//更新程序
public void Update()
{
// 获取主程序的版本号
String version = GetVersion(mainFilePath);
// 检查是否有新版本
String newVersion = GetVersionFromWeb();
if (newVersion != null)
{
// 如果有新版本,则下载新版本
if (CompareVersion(newVersion, version) == 1)
{
Log("本地版本:"+version);
Log("符合更新条件,开始更新…");
Log("开始下载…");
if (DownloadNewVersion())
{
// 安装新版本
Log("开始更新程序…");
InstallNewVersion();
Log("启动主程序…");
// 重启主程序
Process.Start(mainFilePath);
Log("启动主程序ok");
}
else
{
Log("因下载环节问题终止更新操作!");
}
}
else
{
Log("没有的新条件,退出");
}
}
else
{
Log("获取服务器版本失败!");
}
Log("更新程序退出.");
Environment.Exit(0);
}
/// <summary>
/// 下载新版本并验证版本号
/// </summary>
/// <returns></returns>
private Boolean DownloadNewVersion()
{
WebClient wcClient = new WebClient();
// 下载文件并保存到指定位置
WebClient client = new WebClient();
Log("获取下载url: "+ downloadUrlExe);
byte[] data = client.DownloadData(downloadUrlExe);
Log("下载文件大小[" + data.Length/1024 + " kb]");
String tempPath = "./" + downloadFileName;
// 将字节数组保存到文件
File.WriteAllBytes(tempPath, data);
Log("保存位置 " + tempPath);
//验证版本 是否与接口一致
String version = GetVersion(tempPath);
bool vaildVersion = version.Equals(infaceVersion);
Log("验证已下载文件版本("+version+")与 接口版本("+infaceVersion+"): " + vaildVersion);
return vaildVersion;
}
/// <summary>
/// 安装
/// </summary>
private void InstallNewVersion()
{
Log("开始关闭主程序…");
Process[] ppp= Process.GetProcessesByName(processName);
if (ppp.Length > 0)
{
MessageBox.Show("正在执行升级,重启远程鉴定平台。");
try
{
for (int i = 0; i < ppp.Length; i++)
{
Log("结束进程:" + ppp[i].ProcessName);
ppp[i].Kill();
}
}
catch(Exception ex)
{
Log("结束进程:" +ex.Message);
}
}
Log("备份主程序…");
if (!Directory.Exists("./bak"))
{
Directory.CreateDirectory("./bak");
}
DateTime currentDateAndTime = DateTime.Now;
String time= currentDateAndTime.ToString("yyyyMMddHHmmss");
String bakPath = "./bak/" + mainFilePath + "." + time;
File.Copy(mainFilePath,bakPath,true);
Log("备份主程序完成。");
int waitTimeMilliseconds = 1000; // 5秒
Thread.Sleep(waitTimeMilliseconds);
File.Delete(mainFilePath);
Log("删除旧版程序OK。 ");
File.Copy(downloadFileName,mainFilePath);
Log("更新文件OK。 ");
File.Delete(downloadFileName);
Log("删除下载文件OK。 ");
}
private String GetVersionFromWeb()
{
Log("准备获取服务器版本号…");
String json =request(updateUrl);
//{"msg":"操作成功","code":200,"data":{"id":589,"versionCode":3,"versionName":"1.0.0.1","updateContent":"test","fileName":"xxx平台.exe","saveName":"2666c50a-6e1c-4a8a-b8f6-c1a5002d4ca0.exe","fileSuffix":".exe","fileSize":"107KB","url":"http://192.168.22.144:8904/common/downloadAppFile/2666c50a-6e1c-4a8a-b8f6-c1a5002d4ca0.exe","type":2,"uploadTime":"2024-03-26 10:17:29"}}
JsonElement element = JsonDocument.Parse(json).RootElement;
infaceVersion = element.GetProperty("data").GetProperty("versionName").GetString();
Log("获取服务器版本号:" + infaceVersion);
downloadUrlExe = element.GetProperty("data").GetProperty("url").GetString();
Log("获取服务器下载URL:" + downloadUrlExe);
downloadFileName = element.GetProperty("data").GetProperty("saveName").GetString();
Log("获取服务器下载文件名称:" + downloadFileName);
return infaceVersion;
}
/// <summary>
/// 日记记录
/// </summary>
/// <param name="v"></param>
private void Log(string v)
{
//DateTime currentDateAndTime = DateTime.Now;
//richTextBox1.AppendText(currentDateAndTime.ToString() +" - "+ v+"\n");
String filePath = "./update.log";
try
{
using (StreamWriter writer = new StreamWriter(filePath, true))
{
string logEntry = $"{DateTime.Now} - {v}";
writer.WriteLine(logEntry);
}
}
catch (Exception ex)
{
// 记录异常信息
Console.WriteLine("日志记录失败:" + ex.Message);
}
}
/// <summary>
/// http请求
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static string request(string url)
{
using (WebClient client = new WebClient())
{
return client.DownloadString(url);
}
}
/// <summary>
/// 获取文件版本号
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
private String GetVersion(string path)
{
richTextBox1.AppendText("准备获取本地版本号……:" + path+"\n");
// 获取文件版本信息
FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(path);
// 获取文件版本号
string fileVersion = fileVersionInfo.FileVersion;
richTextBox1.AppendText("获取本地版本号:" + fileVersion + "\n");
return fileVersion;
}
/// <summary>
/// 比较软件的版本号
/// </summary>
/// <param name="version1"></param>
/// <param name="version2"></param>
/// <returns></returns>
public static int CompareVersion(string version1, string version2)
{
string[] parts1 = version1.Split('.');
string[] parts2 = version2.Split('.');
for (int i = 0; i < parts1.Length && i < parts2.Length; i++)
{
int v1 = int.Parse(parts1[i]);
int v2 = int.Parse(parts2[i]);
if (v1 > v2)
{
return 1;
}
else if (v1 < v2)
{
return -1;
}
}
return 0;
}
执行后的更新日记