WinUI (microsoft-ui-xaml) 调试实践:从本地构建到多部署配置下的私有二进制替换
【免费下载链接】microsoft-ui-xamlWinUI: a modern UI framework with a rich set of controls and styles to build dynamic and high-performing Windows applications.项目地址: https://gitcode.com/GitHub_Trending/mi/microsoft-ui-xaml
本文基于仓库中 docs/debugging/debugging.md 整理,聚焦 WinUI 仓库私有位(private bits)调试的核心工作流:如何用根目录的 Build.cmd 完成一次完整的本地构建,以及如何在你自己的 VS 应用、Store 部署应用等不同部署形态下,把本地编译出的 WinUI 二进制替换或重定向进目标应用,从而让调试器加载你自己的代码与 PDB。读完后,你将掌握.localDLL 重定向、takeown/icacls直接覆盖、PSExec 提权替换三种实战方案及其各自的适用边界。
一、前提:先用 build.cmd 完成一次完整构建
所有调试动作的前提是“手上有一份自己的产品二进制”。原文档明确要求:
You first need to perform a full build using the
build.cmdscript in the root of the repo.
Build.cmd 是仓库根目录的一键构建入口。从 Build.cmd 的 usage 部分可以看到它支持的构建目标(target):
| 目标 | 含义 |
|---|---|
prodtest(默认) | 构建产品代码 + 测试代码(不含 samples) |
product | 只构建产品代码(prodtest的子集) |
mux | 只构建Microsoft.UI.Xaml.dll(product的子集) |
test | 只构建测试(prodtest的子集) |
samples | 构建示例应用 |
all | 产品 + 测试 + samples 全部构建 |
常用选项(同样来自 Build.cmd 的帮助文本):
/restore:附加 NuGet restore;/c:构建前清理 bin/obj/temp/packaging 目录;/muxfinal:设置MUXFinalRelease=true,模拟正式发布构建(关闭实验性功能);/b、/m:控制 MSBuild 并行实例数(后台模式 2 个 / 每核心 1 个);/fake:只打印将要执行的命令,不实际构建;/version <ver>:覆盖WinUIVersion属性,默认为3.0.0-dev(见 Build.cmd 中的_version初始化)。
构建产物落在哪里:BuildOutput 约定
后文反复出现的“把 dll 复制到应用安装目录”,其源目录就是构建输出。从 eng/folderpaths.props 可以确认产物路径的构成:
ArtifactsRoot默认为$(ProjectRoot)BuildOutput\,即 Build.cmd 生成的所有二进制统一汇入BuildOutput\;ProductBinplaceDestinationPath为$(CurrentEnvironmentSubDir)\Product,而CurrentEnvironmentSubDir是$(MUXOutputPlatform)$(MUXOutputConfiguration)的拼接。
这正是原文档示例路径BuildOutput\bin\x86chk\Product\的来源:x86是平台(NuGet 兼容命名),chk是 Debug 配置。也就是说,Debug x86 构建出的产品 dll(含 PDB)最终都会被 binplace 到BuildOutput\bin\x86chk\Product\,Debug x64 对应amd64chk等组合。替换二进制时务必保证架构(x86/amd64)一致——原文档对此有明确提醒。
配套文档:构建示例应用与新建测试应用
原文档在 Manual Testing 一节给出了三条延伸路径(以下链接已转换为仓库根目录相对路径):
- 构建/调试仓库内任意示例应用(含 WinUI Gallery):docs/building/building-sample-apps.md。其中说明了示例应用既能针对本地 WinUI 组件包构建,也能针对已发布的
Microsoft.WindowsAppSDKNuGet 包构建,通过scripts\buildSample <AppName> <version>指定版本; - 在仓库内创建一个使用本地位的新测试应用:docs/building/building-new-repo-app.md;
- 在仓库外创建自己的 VS 应用并指向本地构建的快速内循环(ad-hoc)工作流:docs/ad-hoc-testing-of-local-build-with-fast-inner-loop.md。该文档给出了完整的
nuget.config配置方式,让外部项目直接引用本地构建产出的组件包。
另外,Build.cmd 在完成产品构建后会调用 pack.component.cmd 生成一个本地 mock 组件包(本地开发版命名为Microsoft.WindowsAppSDK.WinUI.3.0.0-dev.nupkg)。仓库 Samples 目录下的示例应用正是通过 Samples/WinUIPackageReference.props 引用该包(版本取$(WinUIVersion)),从而把本地构建输出注入到示例应用里。
二、在 xaml 仓库内部测试私有位
这是最简单的情形。原文档指出:如果你在 xaml 仓库中工作,Samples\下的示例应用总是从BuildOutput拾取 dll。你只需要按常规方式构建一次 xaml 仓库,然后重新构建任意一个示例应用即可。
这与 Build.cmd 的流程一致:build.cmd先构建 XAML 编译器前置工程(XamlCompilerPrerequisites.sln),再依次构建Microsoft.UI.Xaml.sln与 controls/MUXControls.sln,最后samples目标调用 buildsamples.cmd 把示例应用链接到BuildOutput中的本地位上。整个链条中示例应用与产品二进制共享同一份BuildOutput,不需要任何手动复制。
三、在 VS 应用(仓库外项目)中覆盖 WinUI 二进制
如果你写了自己的应用(不在仓库 Samples 目录里),做法非常直接:把更新后的 WinUI 位复制到应用的安装位置,而安装位置就在应用项目目录下。
原文档给出的示例:如果你的应用位于c:\repos\MyApp且构建 Debug,WinUI 位会位于形如
c:\repos\MyApp\MyApp (Package)\bin\x86\Debug\AppX\MyApp的目录中。通常 VS 的构建输出窗口会在某处显示该位置;你也可以用scripts\find-appx脚本来定位应用安装位置。该脚本对应仓库中的 scripts/find-appx.ps1,其实现是对get-appxpackage的结果按包全名和InstallLocation做子串匹配:
get-appxpackage | Where-Object { ($_.PackageFullName.ToLower().Contains($searchString)) -or ($_.InstallLocation -ne $null -and $_.InstallLocation.ToLower().Contains($searchString)) }所以scripts\find-appx WinUIGallery这样的调用即可找到 WinUI Gallery 的InstallLocation。
例外:应用走 Framework Package 时
原文档特别强调:如果你的 VS 应用是通过 Framework Package 方式消费 WinUI(而不是自包含打包),简单复制 AppX 目录里的 dll 可能不生效——此时应改用下文“.local与 DLL 重定向”方案,按步骤 1–3a/3b 执行并把你的二进制放入 3b 指定的位置。这种场景在“缺陷只在 Framework Package 部署形态下复现、自包含部署形态下不复现”时尤其有用,因为自包含打包无法覆盖 Framework Package 中的系统级二进制。
四、在 Store 部署(第三方)应用中覆盖 WinUI 二进制
对于你自己构建的应用,在 .csproj 里把 WinUI 设为自包含通常更简单也更安全:
<!-- .csproj of the app --> <PropertyGroup> ... <!-- Set this property to point to your local WinUI details repo --> <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained> </PropertyGroup>但面对 Store 上或第三方安装的包,你无法修改其项目文件,就需要下面的三种手段。
4.1 .local 与 DLL 重定向(推荐,可复用于开发场景)
该方式不是覆盖系统二进制,而是利用 DLL 查找顺序让加载器“先找到你的副本”。它同样适用于开发场景:例如本地构建过MUX.dll后,F5 部署示例应用时也会顺带部署一份私有副本。
原理:带包身份(package identity)的应用进程在 DLL Search Order 中多出一个“第 0 步”——pkgdir\microsoft.system.package.metadata\Application.Local重定向目录,前提是在注册表开启DevOverrideEnable。这是 Windows 8 应 shell 开发者的要求加入的机制,专门用于快速测试打补丁的 DLL。
原文档给出的操作步骤(完整保留):
设置
DevOverrideEnable注册表键:REG ADD "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options" /v DevOverrideEnable /t REG_DWORD /d 1注意:该设置需要重启才生效。
在目标包的已安装位置下创建
microsoft.system.package.metadata\Application.Local目录:MD "C:\Program Files\WindowsApps\Microsoft.WindowsCalculator_11.2206.0.0_x64__8wekyb3d8bbwe\microsoft.system.package.metadata\Application.Local"把你的覆盖 DLL 复制到该位置:
COPY C:\PrivateLocalBuild\shell32.dll "C:\Program Files\WindowsApps\Microsoft.WindowsCalculator_11.2206.0.0_x64__8wekyb3d8bbwe\microsoft.system.package.metadata\Application.Local\"(把
shell32.dll换成你本地构建的Microsoft.UI.Xaml.dll等产品 dll 即可。)对非打包(unpackaged)应用,可以在 exe 旁边创建同名
.exe目录放入本地二进制。例如为explorer.exe创建c:\windows\explorer.exe.local,其中任何 DLL 都会替代系统二进制。
原文档附带的两条重要注释:
NOTE: This Application.Local trick applies to all packages no matter their source or install location. Long as the process has package identity and
DevOverrideEnableis set, the Loader looks here before anywhere else (even before APIsets!)
NOTE: If deploying a packaged app through Visual Studio, you will still need to follow step 4 below too, to create a
.exefolder withinAppxfolder but copy dlls to location mentioned in step 3 only. You can useModulesview within VS to check the location of loadedMicrosoft.ui.xaml.dllto verify if the trick worked.
即:通过 VS 部署打包应用时,仍需要在Appx文件夹中创建一个.exe目录,但 dll 只复制到步骤 3 的位置;最后用 VS 的 Modules 视图确认Microsoft.UI.Xaml.dll的实际加载路径来完成验证。
4.2 takeown + icacls(直接覆盖应用 DLL)
该方式直接改写已安装应用的 DLL。以替换从 Store 安装的 WinUI Gallery 所用的 DLL 为例,原文档给出的完整命令(以管理员身份打开 PowerShell):
cd $(get-appxpackage Microsoft.WinUIGallery).InstallLocation takeown /f * icacls * /grant:r administrators:f copy <product directory>*.dll其中<product directory>即你的构建产物目录,原文档举例:
copy <repo-root>\BuildOutput\bin\x86chk\Product\*.dll要点:
- 架构必须匹配(x86/amd64),对应第一节中
BuildOutput\bin\<arch><config>\Product的目录约定; - 可以用
scripts\find-appx定位应用安装位置,例如scripts\find-appx WinUIGallery,然后cd $(scripts\find-appx WinUIGallery).InstallLocation; - 原文档明确警告:该方式无法用于替换 Framework Package(即
...\WindowsApps\Microsoft.WindowsAppRuntime*)的内容,此类场景应使用 4.1 的.local与 DLL 重定向方案。
4.3 PSExec(SYSTEM 提权替换)
另一种手段是借助 SysInternals PSExec:在提权命令提示符中执行PSExec.exe -s cmd,进入 SYSTEM 账户的命令行,从而拥有对WindowsApps目录执行xcopy等文件操作的权限。
原文档对此方案的定性是:unsupported,可能引发问题(例如 Windows Defender 开始告警),“This option comes with large responsibility and improper usage can corrupt windows installation”——使用不当可能损坏 Windows 安装。因此应把它作为最后手段。
4.4 三种方案如何选择
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 仓库内 Samples 示例应用 | 直接构建 | 示例应用自动从BuildOutput拾取本地位 |
| 自己写的 VS 自包含应用 | 复制 dll 到AppX\MyApp或设WindowsAppSDKSelfContained | 无权限障碍,最直接 |
| 自己写的 VS 应用(Framework Package 形态) | .local重定向 | 无法用自包含打包替换二进制 |
| Store/第三方打包应用 | .local重定向 或 takeown+icacls | 重定向不改原文件、可随时撤除;直接覆盖更彻底但破坏原包 |
| Framework Package(WindowsAppRuntime) | 仅.local重定向 | 原文档明确 takeown 方式对其无效 |
| 非打包应用 | exe 旁建.exe目录 | 加载器优先 exe 目录 |
五、延伸:更深入的诊断技巧与测试体系
docs/debugging/debugging.md 本身是入口性文档,它把两类更细的内容外链了出去(转换为仓库根相对路径):
- docs/debugging/debugging-tips.md:VS 断点动作(Trace)串联事件链、
ntdll.dll!LdrpDebugFlags的 Loader Snaps 排查模块加载失败、winui.natvis调试器可视化、dbgsrv 跨机调试、Time Travel Debugging、测试内存泄漏定位(XcpCheckLeaks断点 +dps/dqs转储分配栈)、CEventManager::Raise上的事件循环条件断点等; - docs/testing/testing-FAQ.md 与 docs/testing/test-system-overview.md:通用测试调试指引,例如测试基础设施支持的
/p:WaitForDebugger运行时参数(让 TAEF 测试宿主等待调试器附加后再执行)。
需要说明的是,原文档目录中还列有“Testing changes to Lifted IXP (including the FrameworkUDK)”与“Testing changes to WinUI Details”两个主题,但当前仓库的该文档中这两节尚无正文内容,属于待补全的占位条目,本文不对其展开。
六、小结
围绕 docs/debugging/debugging.md 的核心脉络可以归纳为一条主线:先构建,再让目标进程加载你的位。
build.cmd全量构建(默认prodtest),产品位 binplace 到BuildOutput\bin\<arch><config>\Product(见 eng/folderpaths.props);- 仓库内样本:零配置,自动拾取
BuildOutput; - VS 自包含应用:复制位到
AppX\<AppName>,用 scripts/find-appx.ps1 定位; - Store/第三方/框架包场景:
DevOverrideEnable+Application.Local重定向为首选,takeown+icacls 用于直接覆盖,PSExec 仅作为不受支持的兜底。
掌握这套“构建—部署形态—二进制替换”的映射关系,就是在 WinUI 仓库中调试私有位的基本功;更细的诊断手段(加载日志、时间旅行调试、测试等待调试器附加)则可继续参照 docs/debugging/debugging-tips.md 与测试 FAQ 深入。
【免费下载链接】microsoft-ui-xamlWinUI: a modern UI framework with a rich set of controls and styles to build dynamic and high-performing Windows applications.项目地址: https://gitcode.com/GitHub_Trending/mi/microsoft-ui-xaml
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考