紫嫣宝贝 發表於 2025-12-2 09:54:00

基于 JSON 配置的 .NET 桌面应用自动更新方案

<h2 id="前言">前言</h2>
<p>在软件开发和维护过程中,程序更新一直是个绕不开的话题。一开始用最简单的方式——让用户手动下载新版本覆盖安装,但随着用户量增加、功能迭代加快,这种方式不仅效率低,还容易出错。一个轻量、可靠、配置灵活的自动更新机制变得尤为重要。</p>
<p>本文介绍一个简单可靠的 Windows 程序自动更新实用小工具,它不追求复杂的功能,而是专注于解决"如何让程序安静又稳妥地完成自我升级"这个问题。</p>
<h2 id="项目介绍">项目介绍</h2>
<p>一个基于 .NET 开发的轻量级程序更新框架,核心由两个部分组成:主程序集成逻辑 + 独立的升级执行程序(Upgrade.exe)。</p>
<p>它的设计思路很简单:每次启动主程序时,先检查是否有新版本;如果有,就调用 Upgrade.exe 完成文件替换,再重新启动主程序。整个过程对用户透明,且通过 JSON 配置驱动,无需硬编码更新地址或文件列表。</p>
<h2 id="项目功能">项目功能</h2>
<p>主要功能围绕"对比—下载—替换—重启"展开</p>
<p>通过本地与服务器上的 <code>UpLoadVersion.json</code> 文件比对版本差异;</p>
<p>自动下载服务器上指定的更新文件(支持任意数量);</p>
<p>支持自动更新(IsAutoUpLoad=true)或手动确认更新;</p>
<p>更新完成后自动启动目标程序(可由配置指定,也可由主程序传入路径);</p>
<p>避免重复检测:升级失败或无更新时生成 <code>NoUpgrade.ini</code>,防止频繁弹窗。</p>
<h2 id="项目特点">项目特点</h2>
<p>这个方案最大的特点是"轻"和"稳"。</p>
<p>它不需要数据库、不需要后台服务,只依赖一个 JSON 文件和一个独立的升级程序。配置完全外置,运维人员只需更新服务器上的 JSON 和文件,就能触发客户端升级。</p>
<p>同时,它巧妙利用环境变量判断是否在 Visual Studio 中调试,避免开发阶段误触发更新。</p>
<p>另外,通过传入主程序路径的方式,使得 <code>ProgrmStartupDir</code> 字段甚至可以留空,提升了灵活性。</p>
<h2 id="项目使用">项目使用</h2>
<p>可以通过 JSON 文件进行更新和打开程序的一个更新程序。</p>
<h3 id="uploadversionjson-配置说明">UpLoadVersion.json 配置说明</h3>
<p><code>UpLoadVersion.json</code> 需要同时放置在程序安装目录和服务器更新目录中,是用于版本对比和更新控制的核心配置文件。</p>
<pre><code class="language-json">{
"UpLoadContent": "更新提示内容",
"UpLoadFileUrl": "更新的地址(带输出目录的网站地址),例:http://localhost:8888/",
"ProgrmStartupDir": "更新完成后需要运行的程序",
"IsAutoUpLoad": true,
"UpLoadFiles": []
}
</code></pre>
<h3 id="字段说明">字段说明</h3>
<ul>
<li>
<p><code>UpLoadContent</code>:更新时向用户展示的提示文本。</p>
</li>
<li>
<p><code>UpLoadFileUrl</code>:服务器上更新文件所在的根 URL,需以 <code>/</code> 结尾。</p>
</li>
<li>
<p><code>ProgrmStartupDir</code>:更新完成后要启动的程序路径(可为空,若启动 Upgrade.exe 时传入主程序路径,则优先使用传入值)。</p>
</li>
<li>
<p><code>IsAutoUpLoad</code>:是否自动更新,设为 <code>true</code> 则无需用户点击确认。</p>
</li>
<li>
<p><code>UpLoadFiles</code>:包含待更新文件信息的数组,每项记录文件名及其 MD5 值,用于比对是否需要下载。</p>
</li>
</ul>
<h3 id="iis-发布配置">IIS 发布配置</h3>
<p>1、添加网站,物理路径指向你的最新程序更新目录。</p>
<p><img src="https://img2024.cnblogs.com/blog/576536/202511/576536-20251124171454763-291890877.png" width="50%" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p>2、若访问时出现 403 错误,请在 IIS 管理器中进入该网站 → 功能视图 → "目录浏览" → 点击右侧"启用"。</p>
<p><img src="https://img2024.cnblogs.com/blog/576536/202511/576536-20251124171545419-930085131.png" width="50%" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p><img src="https://img2024.cnblogs.com/blog/576536/202511/576536-20251124171601706-1561231942.png" width="50%" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<h3 id="生成服务器端-uploadversionjson">生成服务器端 UpLoadVersion.json</h3>
<p>将 <code>UpLoadDemoXmlBuild.exe</code> 复制到待发布的程序目录中,运行后点击"生成"按钮,即可自动生成包含所有文件 MD5 的 <code>UpLoadVersion.json</code>。</p>
<h3 id="c-调用示例">C# 调用示例</h3>
<p>将以下代码放入 <code>Program.cs</code> 或 WPF 应用的 <code>OnStartup</code> 方法中:</p>
<pre><code class="language-csharp">string vsVersion = Environment.GetEnvironmentVariable("VisualStudioVersion");
if (string.IsNullOrEmpty(vsVersion))
{
    string upIni = AppDomain.CurrentDomain.BaseDirectory + "NoUpgrade.ini";
    if (!System.IO.File.Exists(upIni) &amp;&amp; System.IO.File.Exists(AppDomain.CurrentDomain.BaseDirectory + "Upgrade.exe"))
    {
      Process.Start(AppDomain.CurrentDomain.BaseDirectory + "Upgrade.exe",
                      System.Reflection.Assembly.GetExecutingAssembly().Location);
      Environment.Exit(0);
    }
    System.IO.File.Delete(upIni);
}
</code></pre>
<h3 id="逻辑说明">逻辑说明</h3>
<ul>
<li>
<p>通过 <code>VisualStudioVersion</code> 环境变量判断是否处于调试状态,避免开发时触发更新。</p>
</li>
<li>
<p>若存在 <code>NoUpgrade.ini</code>,表示上次检测无更新,跳过本次检查。</p>
</li>
<li>
<p>启动 <code>Upgrade.exe</code> 时传入当前主程序路径,作为更新完成后的启动目标。</p>
</li>
<li>
<p>检测结束后删除 <code>NoUpgrade.ini</code>,确保下次启动仍能检查更新。</p>
</li>
</ul>
<h3 id="测试更新流程">测试更新流程</h3>
<p>1、初始状态下,本地无更新文件。</p>
<p><img src="https://img2024.cnblogs.com/blog/576536/202511/576536-20251124171638860-208521578.png" width="50%" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p>2、运行程序,若检测到服务器版本更新,则弹出提示(若 <code>IsAutoUpLoad=true</code> 则自动开始)。</p>
<p><img src="https://img2024.cnblogs.com/blog/576536/202511/576536-20251124171847027-986290189.png" width="50%" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p>3、下载完成后,<code>Upgrade.exe</code> 自动关闭,并启动配置中指定或传入的主程序。</p>
<p><img src="https://img2024.cnblogs.com/blog/576536/202511/576536-20251124171913671-519190694.png" width="50%" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p>4、更新后,本地的 <code>UpLoadVersion.json</code> 内容与服务器完全一致。</p>
<p><img src="https://img2024.cnblogs.com/blog/576536/202511/576536-20251124171949576-1045394925.png" width="50%" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<h2 id="项目代码">项目代码</h2>
<h3 id="自动更新">自动更新</h3>
<pre><code class="language-csharp">private void UpLoadExcute(object obj)
{
    Thread thread = new Thread(()=&gt;{
      if (obj!=null&amp;&amp;obj is System.Windows.Controls.FlowDocumentScrollViewer)
      {
            var uartDataFlowDocument = obj as System.Windows.Controls.FlowDocumentScrollViewer;
         MsgScrollViewer = uartDataFlowDocument.Template.FindName("PART_ContentHost", uartDataFlowDocument) as System.Windows.Controls.ScrollViewer;
      }
      BtnIsEnabled = false;
      ProgressBarVisiblity = Visibility.Visible;
      BtnName = "正在更新...";
      ContentTitle = "更新信息:";
      UpLoadContent = "\r\n";
      AppendMsg("正在比对本地系统与服务器的版本信息。");
      
      ProgressBarValue = 4;
      AppendMsg("需要更新"+ StaticModel.UpLoads.Count+"个文件!");
      UpLoadDown(StaticModel.UpLoads);
      AppendMsg("正在重新启动程序...");
      Thread.Sleep(2000);
      //升级成功后删除不检测升级的文件
      File.Delete(AppDomain.CurrentDomain.BaseDirectory + "NoUpgrade.ini");
      if (!string.IsNullOrEmpty(StaticModel.MyUpLoadModel.ProgrmStartupDir))
      {
            Process.Start(StaticModel.MyUpLoadModel.ProgrmStartupDir);
            Application.Current.Dispatcher.Invoke(new Action(() =&gt;
            {
                Environment.Exit(0);
            }));
      }
      else {
            AppendMsg("没有找到要启动的程序路径...");
      }
      //BtnIsEnabled = true;
      //BtnName = "立 即 更 新";
    });
    thread.IsBackground = false;
    thread.Start();
}
</code></pre>
<h2 id="项目源码">项目源码</h2>
<p>Gitee:https://gitee.com/sageinqi/UpLoadDemo</p>
<h2 id="总结">总结</h2>
<p>本项目不是一个炫技型的框架,而是一个"解决问题就好"的务实工具。它没有复杂的 UI,也不依赖云服务,却能有效支撑中小型桌面应用的持续交付需求。</p>
<p>对于那些不想引入 Electron autoUpdater 或商业更新服务的 .NET 团队来说,这种基于文件比对 + JSON 配置的方案,既可控又易于维护。更重要的是,它把更新逻辑从主业务中剥离出来,降低了耦合度,也让后续扩展(比如加签名验证、差分更新)成为可能。</p>
<h2 id="关键词">关键词</h2>
<p>自动更新、JSON配置、MD5校验、Upgrade.exe、IIS部署、.NET桌面应用、轻量级、程序升级、文件同步、无侵入</p>
<h2 id="最后">最后</h2>
<p>如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。</p>
<p>也可以加入微信公众号 社区,与其他热爱技术的同行一起交流心得,共同成长!</p>
<p><img src="https://img2024.cnblogs.com/blog/576536/202505/576536-20250527195524293-1794896295.png" width="50%" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p><br><br>
来源:https://www.cnblogs.com/1312mn/p/19264973
頁: [1]
查看完整版本: 基于 JSON 配置的 .NET 桌面应用自动更新方案