mRemoteNG 双轨部署方案全解析:Framework-Dependent 与 Self-Contained 构建实战
2026/9/23 14:14:33 网站建设 项目流程

mRemoteNG 双轨部署方案全解析:Framework-Dependent 与 Self-Contained 构建实战

【免费下载链接】mRemoteNGmRemoteNG is the next generation of mRemote, open source, tabbed, multi-protocol, remote connections manager.项目地址: https://gitcode.com/gh_mirrors/mr/mRemoteNG

mRemoteNG 是开源的多协议远程连接管理器,其 .NET 10 时代引入的两种部署形态——框架依赖版(Framework-Dependent,简称 FD)与自包含版(Self-Contained,简称 SC)——直接决定了产物大小、运行时依赖与分发策略。本文以仓库根目录下的 DEPLOYMENT_OPTIONS.md 为核心骨架,结合 mRemoteNG.csproj、ProgramRoot.cs 与 CI 工作流等源码证据,完整讲解两种部署类型的差异、本地构建命令、编译常量机制、自动发布流程以及验证与迁移方法。读完本文,你将能独立构建、验证并发布 x64 / ARM64 双架构、FD / SC 双形态的 mRemoteNG 安装包。

一、两种部署类型的定位与取舍

mRemoteNG 的发布产物通过文件后缀区分:-FD.zip表示框架依赖版,-SC.zip表示自包含版。两者的本质区别在于是否把 .NET 运行时打包进产物

维度Framework-Dependent(FD)Self-Contained(SC)
文件后缀-FD.zip-SC.zip
体积约 15~25 MB约 80~150 MB
前置条件用户需自行安装 .NET 10 Desktop Runtime无,运行时已内置
启动行为检查 .NET 运行时与 Visual C++ Redistributable,缺失时提示下载不做运行时检查(运行时已打包)
典型场景面向可接受安装前置依赖的标准用户U 盘便携、受限环境、零安装配置需求

FD 版体积小、更新快,且可与系统上其他 .NET 应用共享运行时,适合作为标准发行版;SC 版虽然体积接近百 MB,但"解压即用",无需任何前置环境准备,是便携部署的唯一选择。从 mRemoteNG.csproj 可以看到,工程显式声明了Configurations>Debug;Release;Release Self-Contained;Deploy to github四种配置,其中Release Self-Contained就是为自包含构建单独设计的编译配置,印证了双轨发布是官方一等公民能力。

体积差异的根源:SC 版额外携带了整个 .NET 10 运行时,约 80 MB 的固定开销,这是文档中"Self-contained includes entire .NET 10 runtime (~80 MB overhead)"的直接来源。

二、本地构建:Framework-Dependent 版

FD 构建走传统的 MSBuild 路径,使用msbuild直接编译解决方案:

# x64 msbuild mRemoteNG.sln -p:Configuration=Release -p:Platform=x64 # ARM64 msbuild mRemoteNG.sln -p:Configuration=Release -p:Platform=ARM64

此命令会以Release配置编译 mRemoteNG.sln(仓库根目录)下的全部工程。对应到 mRemoteNG.csproj,Release|x64Release|arm64两个属性组不定义任何特殊编译常量、不设置RuntimeIdentifier,输出分别落入bin\x64\Release\bin\arm64\Release\,产物即纯框架依赖形态。

三、本地构建:Self-Contained 版

SC 构建需要dotnet publish指定运行时标识(RID)并开启自包含开关:

# x64 dotnet publish mRemoteNG\mRemoteNG.csproj ` --configuration Release ` --runtime win-x64 ` --self-contained true ` -p:Platform=x64 ` -p:PublishSingleFile=false ` -p:PublishReadyToRun=true ` -p:DefineConstants="SELF_CONTAINED" # ARM64 dotnet publish mRemoteNG\mRemoteNG.csproj ` --configuration Release ` --runtime win-arm64 ` --self-contained true ` -p:Platform=ARM64 ` -p:PublishSingleFile=false ` -p:PublishReadyToRun=true ` -p:DefineConstants="SELF_CONTAINED"

参数逐一说明:

  • --runtime win-x64 / win-arm64:目标运行时标识,与 mRemoteNG.csproj 中声明的RuntimeIdentifiers>win-x64;win-arm64对应;
  • --self-contained true:将 .NET 10 运行时整体打包;
  • -p:PublishSingleFile=false:保持文件分离。这一选择与 mRemoteNG 的插件机制强相关——工程中通过CopyPackageAssembliesToSubFolderTarget(见 mRemoteNG.csproj)把 NuGet 依赖程序集统一落到Assemblies\子目录,由 ProgramRoot.cs 中的OnAssemblyResolve事件按需加载,单文件发布会破坏这种"程序集延迟解析"布局;
  • -p:PublishReadyToRun=true:启用 ReadyToRun(R2R)预编译,将 IL 预编译为原生代码,缩短冷启动时间,代价是体积略有增加;
  • -p:DefineConstants="SELF_CONTAINED":定义编译常量(详见第五节)。

另一种等效方式:工程内置的 Release Self-Contained 配置

除了命令行传参,仓库还内置了完整的自包含构建配置。在 mRemoteNG.csproj 中,Release Self-Contained|x64/arm64属性组已经写死了SelfContained=trueRuntimeIdentifier=win-x64/win-arm64PublishReadyToRun=trueDefineConstants=PORTABLE,因此直接执行:

msbuild mRemoteNG\mRemoteNG.csproj -p:Configuration="Release Self-Contained" -p:Platform=x64

即可触发构建。更关键的是,工程末尾的PublishAfterBuildTarget(mRemoteNG.csproj)会在该配置构建完成后自动级联执行Publish目标,并把临时编译目录清理掉,最终发布产物落在bin\x64\Publish Self-Contained\(x64)与bin\arm64\Publish Self-Contained\(arm64)。CI 工作流正是复用了这套配置(见第六节)。

四、启动时的运行时检查:源码级实现

FD 版与 SC 版在启动行为上的差异,体现在 ProgramRoot.cs 的MainAsync入口中。实际实现比文档中描述的#if直接裁剪要更精细——代码通过ShouldSkipNativeRuntimeChecks(args)统一决策,自包含构建(IsPortableBuild为 true)直接跳过整个检查块:

private static Task MainAsync(string[] args) { AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve; if (!ShouldSkipNativeRuntimeChecks(args)) { // Runtime checks only needed for framework-dependent deployments // Self-contained builds include the runtime, so no check is needed // Note: .NET runtime check is not needed here — the .NET host (apphost) // natively displays a missing-runtime dialog with a download link. var checkFail = false; // Checking Visual C++ Redistributable version if (VCppRuntimeCheck.GetInstalledVcRedistVersions() == null || VCppRuntimeCheck.GetInstalledVcRedistVersions().Count == 0) { // 弹出 Download/Cancel 对话框,引导用户下载 vc_redist.x64.exe // ... checkFail = true; } if (checkFail) { Environment.Exit(0); } } // ... }

这里有两点值得注意的源码细节:

  1. .NET 运行时缺失的提示并非由应用代码完成:源码注释明确指出,apphost(.NET 生成的应用程序宿主)在运行时缺失时会原生弹出带下载链接的对话框,因此MainAsync只需关心Visual C++ Redistributable是否安装;
  2. VC++ 运行时的检测逻辑:位于 VCppRuntimeCheck.cs,它遍历注册表SOFTWARE\Microsoft\VisualStudioSOFTWARE\WOW6432Node\Microsoft\VisualStudio14.0 ~ 17.3各版本的VC\Runtimes\x64键,检查Installed值是否为 1;一旦检测到缺失,应用会弹出含下载链接的对话框,并在用户确认后通过Process.Start打开https://aka.ms/vs/17/release/vc_redist.x64.exe下载页,随后Environment.Exit(0)退出。

检查的绕过机制

ProgramRoot.cs 的ShouldSkipNativeRuntimeChecks提供了三套跳过途径,对运维和排障很有价值:

  • 便携构建IsPortableBuild(即#if PORTABLE常量,见 ProgramRoot.cs)为 true 时直接跳过;
  • 命令行开关:启动参数携带--skip-runtime-checks(不区分大小写)时跳过;
  • 环境变量:设置MREMOTENG_SKIP_RUNTIME_CHECKS=1(或true/TRUE)时跳过。

这些分支均有单元测试覆盖:mRemoteNGTests/App/ProgramRootTests.cs中的ShouldSkipNativeRuntimeChecks_WhenCliSwitchIsProvided_ReturnsTrueShouldSkipNativeRuntimeChecks_WhenEnvironmentRequestsBypass_ReturnsTrue以及验证无任何绕过配置时行为等于ProgramRoot.IsPortableBuild的测试,完整对应上述三条路径。

关于编译常量的一点勘误

DEPLOYMENT_OPTIONS.md 中提到以SELF_CONTAINED常量配合#if !SELF_CONTAINED裁剪运行时检查代码;从当前仓库的实际代码看,自包含裁剪实际由PORTABLE常量承担(mRemoteNG.csproj 中Release Self-Contained配置定义的是DefineConstants=PORTABLEProgramRoot.IsPortableBuild同样检测PORTABLE)。可以推断SELF_CONTAINED是文档撰写时构想的命名,实际落地沿用了历史遗留的PORTABLE符号,两者功能等价——编译进自包含产物的二进制中不再包含 VC++ 运行时检查逻辑。

五、PORTABLE 符号对相关模块的连锁影响

PORTABLE不只是控制启动检查,它还在全仓库多处条件编译,直接影响自包含版的运行行为:

  • 更新机制(AppUpdater.cs):非便携版(#if !PORTABLE)下载更新后保存到系统临时目录并静默安装 MSI;便携版则弹出SaveFileDialog让用户选择保存位置,由用户手动替换文件;
  • 更新源(UpdateChannelInfo.cs):定义了update-portable.txtpreview-update-portable.txtnightly-update-portable.txt等独立的便携版更新通道文件;
  • 配置读写DockPanelLayoutLoader.csExternalAppsLoader.csChooseProvider.cs中均有#if !PORTABLE/#if PORTABLE分支,决定界面布局、外部工具清单与数据源选择器在便携形态下的行为;
  • UI 呈现FrmAbout.cs[Conditional("PORTABLE")]方法会在"关于"窗口标注便携版身份。

可见,从"免安装"这一需求出发,PORTABLE贯穿了更新、配置与 UI 三条链路,SC 版不是简单的"FD 版加运行时",而是一套经过条件编译裁切的完整便携形态。

六、GitHub Actions:一次构建四种产物的自动化流水线

仓库 .github/workflows/Build_mR-NB.yml 是文档中提到的多部署工作流(工作流nameBuild_and_Release_mR-NB_MultiDeploy)。它通过矩阵策略一次产出四种变体:

变体产物命名(示例)runner
x64 Framework-DependentmRemoteNG-YYYYMMDD-vX.X.X-NB-XXXX-x64-FD.zipwindows-2025-vs2026
x64 Self-ContainedmRemoteNG-YYYYMMDD-vX.X.X-NB-XXXX-x64-SC.zipwindows-2025-vs2026
ARM64 Framework-DependentmRemoteNG-YYYYMMDD-vX.X.X-NB-XXXX-arm64-FD.zipwindows-11-vs2026-arm
ARM64 Self-ContainedmRemoteNG-YYYYMMDD-vX.X.X-NB-XXXX-arm64-SC.zipwindows-11-vs2026-arm

触发条件

工作流仅在两种情况下运行(见 Build_mR-NB.yml 的if表达式):

  • 推送触发:向v1.78.2-dev分支推送,且最新提交信息包含NB release
  • 手动触发:通过workflow_dispatch手动运行(带release_flag输入,默认true)。

流水线关键步骤

  1. 检出代码actions/checkout@v7);
  2. 自动发现解决方案:递归查找.sln.slnx文件,兼容新老格式;
  3. 安装 .NET 10 SDKactions/setup-dotnet@v6)并配置 MSBuild(microsoft/setup-msbuild@v3,按矩阵选择 msbuild 架构);
  4. 解析 MSBuild 环境:定位msbuild.exe、SDK 基础路径,写入MSBuildSDKsPathDOTNET_ROOT等环境变量,保证 MSBuild 能找到正确的 .NET SDK;
  5. T4 模板转换:安装dotnet-t4工具,将 AssemblyInfo.tt 按矩阵平台转换生成AssemblyInfo.cs(版本号即来自此文件);
  6. NuGet 恢复:FD 用Release配置恢复;SC 用Release Self-Contained配置并追加-p:SelfContained=true -p:RuntimeIdentifier=win-{arch} -p:PublishReadyToRun=true恢复,且缓存键按架构与部署类型区分;
  7. 构建:FD 走msbuild /p:Configuration=Release;SC 走msbuild /p:Configuration="Release Self-Contained"(同时把PublishDir指到bin\{Platform}\{arch}\Release);
  8. 生成发布信息:从AssemblyInfo.cs正则提取AssemblyVersion得到版本与构建号,拼出 zip 名与 tag(格式yyyyMMdd-vX.X.X-NB-(build)),并抓取最近一次提交信息;
  9. 提取 CHANGELOG:自动截取 CHANGELOG.md 最新版本段作为发布说明正文;
  10. 压缩与上传Compress-Archive打包整个输出目录为 zip,作为 artifact 上传(保留 1 天)。

合并发布

NB-Build-and-Release完成后,Create-Combined-Release作业汇总四个 artifact,调用softprops/action-gh-release创建单一 GitHub Release,tag 与名称统一(如mRemoteNG vX.X.X NB (build)),发布说明中明确标注:

  • Framework-Dependent (FD):需安装 .NET 10 Runtime,体积约 15~25 MB,文件为*-FD.zip
  • Self-Contained (SC):便携免安装,体积约 80~150 MB,文件为*-SC.zip

并以prerelease: true发布,正文附带 CHANGELOG 与最近提交信息。整个流水线在仓库侧即可"一次触发、四包齐发、合并发布",无需手工处理各平台产物。

七、如何选择:用户视角与分发视角

面向终端用户

选 Framework-Dependent(FD),如果:

  • 不介意一次性安装 .NET 10 Desktop Runtime;
  • 希望下载体积小(15~25 MB);
  • 机器上已运行多个 .NET 应用,愿意共享同一运行时。

选 Self-Contained(SC),如果:

  • 希望零安装、零配置,解压即用;
  • 需要 U 盘等便携介质携带(受限环境、无管理员权限的机器);
  • 不想处理任何前置依赖。

面向分发者

  • FD 作为默认/推荐选项:体积小、更新发布快、与系统共享运行时,适合大多数常规用户;
  • SC 作为便携替代:覆盖特殊场景(隔离网络、临时工作站、企业脱管设备),两种形态互补而不是互斥。当前 CI 工作流的"四个包合并到一个 Release"设计正是这一策略的落地。

八、发布前验证清单

文档给出了两组手工验收步骤,分别验证 FD 与 SC 的运行时处理逻辑:

Framework-Dependent 版:

  1. 卸载 .NET 10 Runtime(若已安装);
  2. 运行mRemoteNG.exe
  3. 应弹出 .NET 10 Runtime 下载提示(由 apphost 原生触发);
  4. 安装运行时后再次启动,确认应用正常进入主界面。

Self-Contained 版:

  1. 卸载 .NET 10 Runtime(若已安装);
  2. 运行mRemoteNG.exe
  3. 应直接启动、无任何运行时检查提示;
  4. 验证完整功能(连接、配置读写、插件加载等)。

结合第五节可知,SC 版还应顺带验证便携更新流程(保存对话框)与便携配置路径是否按预期工作,因为PORTABLE符号同时裁切了这两部分逻辑。

九、从旧工作流迁移

仓库同时保留了旧版单工作流。若此前一直使用Build_and_Release_mR-NB.yml发布单一版本,迁移到多部署方案只需三步:

  1. 重命名或删除旧工作流Build_and_Release_mR-NB.yml(仓库当前实际路径为 .github/workflows/Build_mR-NB.yml,其name已是Build_and_Release_mR-NB_MultiDeploy,可直接复用);
  2. 将新工作流重命名为Build_and_Release_mR-NB.yml(若需要保持既有触发配置的命名习惯);
  3. 提交并推送,提交信息中包含NB release以触发四变体构建。

迁移后,Release 页将从"单个 zip"变为"x64/ARM64 × FD/SC 四个 zip 加清晰说明"的结构,用户可按需取用。

十、总结

mRemoteNG 的双轨部署是一套"工程配置 + 条件编译 + CI 矩阵"三层配合的完整方案:工程层通过Release Self-Contained配置固化自包含参数(mRemoteNG.csproj),代码层通过PORTABLE常量统一裁切运行时检查、更新与配置行为(ProgramRoot.cs、AppUpdater.cs),流水线层通过矩阵策略一次产出四种产物并合并发布(Build_mR-NB.yml)。无论是本地手动构建 FD/SC 包,还是维护自动化发布,理解这三层之间的联动关系都是关键。按本文第七、八节的策略与清单执行,即可稳定交付满足不同用户群体需求的 mRemoteNG 发行版。

【免费下载链接】mRemoteNGmRemoteNG is the next generation of mRemote, open source, tabbed, multi-protocol, remote connections manager.项目地址: https://gitcode.com/gh_mirrors/mr/mRemoteNG

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询