【手把手实战教学】基于C#和.NET Framework的WinForms开发教程系列(5)版本自增
系列目录
(1)Visual Studio 2026 中创建、运行、发布应用
(2)开机自启
(3)自动定时更新
(4)后台运行
(5)版本自增
文章目录
前言
开发环境:
IDE:Visual Studio 2026
语言:C#
框架:.NET Framework 4.5
UI:WinForms
在前几篇文章中,我们完成了开机自启、定时更新和后台运行等核心功能,程序已经能够稳定地在后台执行日常任务。当软件逐渐成型并准备对外发布时,版本号管理就成了不可回避的问题。一个清晰、可追溯的版本号不仅能帮助我们定位不同版本的问题,也能让用户清楚地知道自己使用的是哪个版本。
一些教程会建议修改
AssemblyInfo.cs文件,将AssemblyVersion设置为"1.0.*",从而实现每次编译自动变化。
但在实际测试中,我发现 Visual Studio 会直接报错:
既然微软不再允许这种“偷懒”方式,那我们就自己动手,借助 Visual Studio 的生成事件来实现一个更灵活、可定制的版本自增方案。
更自由的是,我们还可以顺便满足一个特殊需求——跳过含有数字“4”的版本号。
一、为什么需要版本自增?
在软件开发过程中,版本号扮演着非常重要的角色:
- 程序内部调用:在“关于”页面显示当前版本,或者供自动更新组件进行版本比较,判断是否需要升级。
- 安装包和 EXE 文件重命名:每次生成 Release 版本后,自动复制一个带版本号的 EXE 文件,如
MyApp_1.0.5.66.exe,方便区分和管理不同时期的交付物。
基于此,我们采用如下方案:
- 手动维护主版本号和次版本号(如
1.0.5),第四位修订号在每次生成时自动 +1。 - 所有版本号记录在项目
Version\version.txt文件中,每行一个历史版本,最新版本位于最后一行。 - 生成前事件自动读取最新版本号,生成一个
BuildVersion.cs文件,供代码内部使用。 - 生成后事件完成版本号递增、复制文件、跳过“4”等任务。
- 至于
AssemblyInfo.cs中的版本号,保持1.0.0.0不变,毕竟很多商业软件都是这么做的,我们也不必纠结。
二、实现原理
Visual Studio 提供了“生成事件”功能,可以在编译前后执行我们自定义的命令行脚本。通过编写批处理脚本(.bat 或直接内联命令),我们可以:
- 读取文本文件中的版本号。
- 使用
echo和重定向动态生成 C# 源代码文件。 - 通过
copy命令重命名和备份 EXE。 - 利用简单的字符串匹配和跳转实现“跳过数字 4”的逻辑。
整个过程完全自动化,无需额外安装任何工具,非常适合 WinForms 项目。
三、实现步骤
1. 准备版本号记录文件
在项目根目录下新建一个 Version 文件夹,并在其中创建一个 version.txt 文件。文件内容按行书写,每一行是一个完整的版本号(格式:x.x.x.x),后面加空格可以填写一些版本关键信息,例如:
1.0.0.0 验证码登录,主体框架
1.0.0.1 去掉登录,下载公式
1.0.0.2 修复下载公式后桌面没有
1.0.0.3 修复找不到资源
1.0.0.4 增加刷新桌面
1.0.0.5 修复刷新桌面
...
最新版本号放在最后一行。

2. 配置预生成事件:自动生成 BuildVersion.cs
右键点击项目 → 属性 → “生成事件”标签页。

在“生成前事件命令行”中输入以下脚本:
:: 不显示命令
@echo off
setlocal enabledelayedexpansion
:: 版本文件路径
set "versionFile=$(ProjectDir)Version\version.txt"
if not exist "%versionFile%" ( echo 错误: 找不到版本文件 & exit /b 1 )
:: 读取最后一行有效版本(先取空格前部分,再提取数字段)
for /F "tokens=1 delims= " %%a in ('type "%versionFile%" ^| findstr /r "^[0-9]"') do set "raw=%%a"
:: 提取纯净版本号(只保留 数字.数字.数字.数字)
for /F "tokens=1-4 delims=." %%a in ("%raw%") do (
set "version=%%a.%%b.%%c.%%d"
)
:: 如果提取失败,设置默认值
if "%version%"=="" set "version=0.0.0.0"
:: 确保 Version 目录存在
if not exist "$(ProjectDir)Version" mkdir "$(ProjectDir)Version"
:: 生成 BuildVersion.cs 文件
(
echo // 此文件由预生成事件自动生成,请勿手动修改
echo namespace SupportResistance
echo {
echo /// ^<summary^>
echo /// 存储从 version.txt 读取的当前版本(编译时嵌入)
echo /// ^</summary^>
echo public static class BuildVersion
echo {
echo /// ^<summary^>
echo /// 当前应用程序版本(格式 x.x.x.x)
echo /// ^</summary^>
echo public static readonly string Version = "%version%";
echo }
echo }
) > "$(ProjectDir)Version\BuildVersion.cs"
echo 预生成事件完成,当前版本:%version%
这段脚本会读取 version.txt 最后一行的有效版本号,然后生成一个 BuildVersion.cs 文件,其中定义了一个静态类 BuildVersion,它的静态属性 Version 就是当前版本字符串。编译时,该文件会自动参与编译,我们就可以在代码中通过 BuildVersion.Version 获取版本号。

3. 配置生成后事件:递增版本号 + 复制文件
同样在“生成事件”标签页,在“生成后事件命令行”中输入以下脚本:
:: 不显示命令
@echo off
:: 启用 “延迟变量扩展”
setlocal enabledelayedexpansion
:: ===================== 配置 =====================
set "versionFile=$(ProjectDir)Version\version.txt"
:: ===================== 读取最后一行有效版本 =====================
for /F "tokens=1 delims= " %%a in ('type "%versionFile%" ^| findstr /r "^[0-9]"') do (
set "version=%%a"
)
:: ===================== 复制带版本号的EXE =====================
copy /Y "$(TargetPath)" "$(TargetDir)$(TargetName)_!version!.exe"
:: ===================== 自动版本+1,并跳过包含数字4的版本 =====================
:: 提取版本号的四个部分
for /F "tokens=1-4 delims=." %%a in ("!version!") do (
set "major=%%a"
set "minor=%%b"
set "build=%%c"
set "revision=%%d"
)
:nextVersion
set /a newRevision=revision + 1
set "newVer=!major!.!minor!.!build!.!newRevision!"
:: 检查新版本号中是否包含数字4(findstr 找到则 errorlevel 为 0)
echo !newVer! | findstr "4" >nul
if not errorlevel 1 (
:: 包含4,继续递增
set "revision=!newRevision!"
goto nextVersion
)
:: ===================== 追加新版本到文件末尾 =====================
echo !newVer! >> "%versionFile%"
:: ===================== 自动打开文件夹 =====================
start explorer "$(TargetDir)"
:: 完成
echo 版本升级完成:!version! → !newVer!
:: 强制返回成功,避免因 errorlevel 非零导致生成失败
exit /b 0
脚本执行流程:
- 读取当前构建对应的版本号(
version),例如1.0.5.65。 - 将编译好的 EXE 复制为
MyApp_1.0.5.65.exe(实际文件名会根据项目名称变化)。 - 将修订号 65 加 1 得到 66,组合成
1.0.5.66,并检查字符串中是否含有'4'。 - 如果新版本号中包含
'4'(例如1.0.5.74),则跳转到:nextVersion继续递增,直到不含'4'为止(如1.0.5.75)。 - 将最终的新版本号追加到
version.txt末尾。 - 自动打开输出文件夹,方便查看生成的文件。

4. 在代码中使用版本号
生成后的 BuildVersion.cs 内容类似于:
// 此文件由预生成事件自动生成,请勿手动修改
namespace SupportResistance
{
public static class BuildVersion
{
public static readonly string Version = "1.0.5.65";
}
}
在程序的“关于”页面或初始化自动更新组件时,可以这样使用:
lblVersionValue.Text = $"版本号:v{BuildVersion.Version}";
Version currentVersion = new Version(BuildVersion.Version);
AutoUpdater.InstalledVersion = currentVersion;
5. 跳过数字“4”的实现细节
在批处理中,我们通过 findstr "4" 检查字符串是否包含字符 '4'。如果 findstr 找到了匹配项,它返回 errorlevel 0;否则返回 errorlevel 1。利用这一特性,我们使用 if not errorlevel 1 来判断是否包含,若包含则继续递增。这个小功能虽然只是“迷信”,但也在团队内部获得了笑声,算是一个有趣的彩蛋。
四、测试验证
-
首次生成:清理解决方案后,选择 Release 模式并生成项目。查看输出目录,应该看到一个带当前版本号的 EXE 文件(如
MyApp_1.0.5.65.exe),同时version.txt末尾新增了一行递增后的版本号。 -
再次生成:再次生成 Release,复制出的 EXE 文件名版本号会比上次 +1,且不会出现含数字 4 的版本。

-
查看 BuildVersion.cs:打开
Version\BuildVersion.cs,其Version字符串应与生成时读取的版本一致。 -
界面显示:运行程序,进入“关于我们”页面,版本号显示正确。
-
跳过测试:手动在
version.txt最后添加一个如1.0.5.73的行,然后生成 Release,检查新追加的版本号是否为1.0.5.75(跳过了74)。
五、注意事项
- 文件编码:
version.txt建议使用 UTF-8 无 BOM 编码,避免批处理读取时出现乱码。 - 生成事件脚本的调试:如果脚本执行失败,可以在 Visual Studio 的输出窗口中查看详细错误信息。批处理中的
echo命令也会输出到输出窗口,方便定位问题。 - 权限问题:如果项目位于系统保护目录(如 Program Files),可能需要以管理员权限运行 VS,但通常项目放在用户目录下不会有此问题。
六、总结
通过本文,我们掌握了使用 Visual Studio 生成事件实现版本号自动递增的方法,同时解决了 AssemblyVersion 通配符失效的问题。这个方案完全依赖 VS 自身功能,无需第三方工具,而且可以根据需求随意定制(比如跳过特定数字)。结合之前几篇文章,我们的工具已经具备了完整的自动化构建、版本管理和运行时更新数据能力。
后记
后续想到,虽然
AssemblyInfo.cs中的版本号不会自增,但是可以通过生成前事件动态调整,不过观察了一些工具软件,大部分右键属性的版本号都是1.0.0.0,版本号加到文件名中就可以。
喜欢的点个关注吧><!祝你永无bug~
/*
_ooOoo_
o8888888o
88" . "88
(| -_- |)
O\ = /O
____/`---'\____
.' \\| |// `.
/ \\||| : |||// \
/ _||||| -:- |||||- \
| | \\\ - /// | |
| \_| ''\---/'' | |
\ .-\__ `-` ___/-. /
___`. .' /--.--\ `. . __
."" '< `.___\_<|>_/___.' >'"".
| | : `- \`.;`\ _ /`;.`/ - ` : | |
\ \ `-. \_ __\ /__ _/ .-` / /
======`-.____`-.___\_____/___.-`____.-'======
`=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
佛祖保佑 永无BUG
*/


235

被折叠的 条评论
为什么被折叠?



