C# WinForm 软件自动升级实现

C# WinForm 软件自动升级实现

WinForm 程序相对 Web 程序功能更强大,但软件更新却相当麻烦,需要到客户端一台一台升级。本文介绍一种通过登录器自动检测并下载新版本的实现思路。

核心思路

程序运行时不能覆盖自身,因此将登录窗口单独做成一个可执行文件(Login.exe)。用户打开登录器后,先从服务器检测是否有新的主程序(MainPro.exe),如果有则后台下载并覆盖旧版本,登录成功后再通过参数将必要信息(用户名、密码等)传递给主程序。

项目结构

项目 说明
MainPro 主程序,接收参数启动
UpLoad 上传工具,供开发者发布新版本
Login 登录器,负责检测更新和启动主程序

主程序接收参数

主程序的 Main 方法改为接收字符串数组参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static string m_UserName, m_Password;

[STAThread]
static void Main(string[] args)
{
if (args.Length == 2)
{
m_UserName = args[0];
m_Password = args[1];
Application.Run(new Form1());
}
else
{
MessageBox.Show("不能从这里启动");
}
}

上传工具

使用 Access 数据库存放版本信息,表结构:

字段 类型 说明
序号 长整型 主键
版本号 长整型 当前版本
文件名称 字符 对应的文件名
文件内容 OLE 对象 文件二进制数据

确定按钮的核心逻辑:读取本地文件 → 写入数据库 → 更新版本号。

登录器自动更新

1. 注册表保存本地版本

用注册表记录当前主程序的版本号,避免每次重复下载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private string ReadInfo(string keyName)
{
RegistryKey software = Registry.LocalMachine.OpenSubKey("Software", true);
RegistryKey company = software.OpenSubKey("MyCompany");
if (company == null) return "";
RegistryKey product = company.OpenSubKey("MyProduct");
if (product == null) return "";
return product.GetValue(keyName)?.ToString() ?? "";
}

private void WriteInfo(string keyName, string keyValue)
{
RegistryKey software = Registry.LocalMachine.OpenSubKey("Software", true);
RegistryKey company = software.CreateSubKey("MyCompany");
RegistryKey product = company.CreateSubKey("MyProduct");
product.SetValue(keyName, keyValue);
}

2. 后台检测更新

窗口加载时启动后台线程,不阻塞界面:

1
2
3
4
5
private void Form1_Load(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(GetInfo));
thread.Start();
}

GetInfo 函数负责:查询用户名列表 → 检查服务器版本号 → 与本地注册表版本比较 → 新版本则下载并覆盖 → 启用登录按钮。

3. 启动主程序

登录验证通过后,用 Process.Start 带参数启动主程序,然后关闭登录窗口:

1
2
3
4
string filename = Application.StartupPath + "\\MainPro.exe";
string arg = username + " " + password;
System.Diagnostics.Process.Start(filename, arg);
this.Close();

公共代码复用

三个项目都用到数据库连接等重复代码,可以提取到 MyTools.cs 中作为链接文件共享:

  • 在项目里「添加现有项」→ 选择 MyTools.cs → 点「打开」按钮旁的下拉箭头 → 选择「链接文件」
  • 这样不会生成副本,修改一处即可全局生效

网络版/单机版切换

通过预编译指令兼容 Access 和 SQL Server:

1
2
3
4
5
6
7
#define NETWORKVERSION  // 注释掉则为单机版

#if NETWORKVERSION
using System.Data.SqlClient;
#else
using System.Data.OleDb;
#endif

查询时通过 #if 分支使用不同的 Command/DataReader 类型,其余业务逻辑完全统一。

小结

这套方案将更新逻辑前置到登录器,既解决了”运行时不能覆盖自身”的问题,又对用户透明。扩展后还可用于更新资源文件、配置文件等。登录器本身的更新可以在主程序关闭时完成。