尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

winget-cli 中 UndockedRegFreeWinRT 免注册 WinRT 的集成实践与源码剖析

winget-cli 中 UndockedRegFreeWinRT 免注册 WinRT 的集成实践与源码剖析 包管理器CLI【免费下载链接】winget-cliWinGet is the Windows Package Manager. This project includes a CLI (Command Line Interface), PowerShell modules, and a COM (Component Object Model) API (Application Programming Interface).项目地址https://gitcode.com/gh_mirrors/wi/winget-cli点击查看免费下载WinGetWindows Package Manager是一个典型的未打包unpackaged桌面应用它以传统 Win32 进程方式运行却又需要调用大量 WinRT 组件如Microsoft.Management.Deployment、Microsoft.Management.Configuration。为了让这些组件在不安装 MSIX 包、不做系统级注册的前提下被进程内加载winget-cli 引入了来自 microsoft/xlang 的 UndockedRegFreeWinRT 组件并通过git subtree将其固化在仓库中。本文以 src/Xlang/README.md 为主线完整还原该组件的引入方式、工程集成步骤并结合 dllmain.cpp、catalog.cpp、typeresolution.cpp 等源码讲清免注册 WinRT 激活背后的实现原理。读完本文你将掌握 WinGet 是如何把第三方 WinRT 组件嵌入自身二进制目录完成激活以及如何在类似项目中复刻这套 git subtree 集成流程。一、为什么 WinGet 需要 UndockedRegFreeWinRTWinRTWindows Runtime组件传统上有两种激活方式一是通过 MSIX 打包注册manifest 声明 activatableClass系统根据包图解析二是通过注册表注册激活工厂。而 WinGet 的实际情况是主程序winget.exe是未打包应用运行环境里没有 MSIX 包图可依赖它需要进程内激活WinGetServer等组件暴露的 WinRT 类同时Microsoft.Management.Deployment.OutOfProc、Microsoft.Management.Configuration.OutOfProc等进程外服务器也需要以未注册形式加载 WinRT 类型元数据。UndockedRegFreeWinRT 正是为解决未打包场景下的免注册 WinRT 激活而生的运行时组件。它通过 Windows 提供的 Detours 钩子技术拦截进程内RoActivateInstance、RoGetActivationFactory、RoGetMetaDataFile、RoResolveNamespace四个关键 API把 WinRT 类型解析和激活重定向到应用自身目录旁的 DLL WinMD 元数据 嵌入 manifest从而让未打包应用像打包应用一样无缝使用 WinRT。二、以 git subtree 方式引入上游代码src/Xlang/README.md明确说明UndockedRegFreeWinRT目录内的代码来自 microsoft/xlang此处仅作为上游来源说明文章不输出外部链接禁止直接修改该目录下的任何代码。它是用git subtree命令引入的固定指向一个具体提交git subtree add --prefixsrc/Xlang/UndockedRegFreeWinRT https://github.com/microsoft/xlang cfe510d0d2b07484fea2c6d77163de017738c100 --squash这一命令把上游仓库中对应提交的代码压缩--squash成一个提交合入当前仓库目录挂载点为src/Xlang/UndockedRegFreeWinRT。文档同时给出两条维护约定后续对 subtree 的 PR 更新不应再 squash以便保留与上游的映射关系便于后续git subtree pull同步该子树内容应视为外来代码WinGet 自身的改动应集中在外层工程文件而不是碰子树内部实现。从当前仓库实际布局看子树根目录为 src/Xlang/UndockedRegFreeWinRT其下src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/目录中包含catalog.cpp、dllmain.cpp、typeresolution.cpp、winrtact.def、UndockedRegFreeWinRT.vcxproj等核心文件另有mwinrtact/mwinrtact.cs提供 C# 侧初始化入口与 README 中仅保留 UndockedRegFreeWinRT 文件夹所需内容、删除测试代码的描述完全吻合。三、把工程接入 WinGet 解决方案README 指出集成采用默认项目文件即让UndockedRegFreeWinRT自带的 MSBuild 工程直接作为 WinGet 解决方案的一部分参与编译两个关键文件为UndockedRegFreeWinRT\UndockedRegFreeWinRT\UndockedRegFreeWinRT.vcxprojUndockedRegFreeWinRT\UndockedRegFreeWinRT\UndockedRegFreeWinRT.vcxproj.filters对应到当前仓库的完整相对路径是 UndockedRegFreeWinRT.vcxproj 与 UndockedRegFreeWinRT.vcxproj.filters。3.1 工程文件的集成痕迹从 vcxproj 源码可以看到 WinGet 侧做了哪些定制这些正是 README 中Modify the vcxproj file to follow settings from other project files in the WinGet solution第 3 步的落地体现平台与配置矩阵支持Debug、Release、ReleaseStatic三种配置覆盖Win32、x64、ARM64三种平台Win32在BuildPlatform中映射为x86输出命名统一三种配置下TargetName均为winrtact最终产物为winrtact.dll同时Release/ReleaseStatic开启SpectreMitigationSpectre 缓解、ControlFlowGuardCFG与/debug:full与 WinGet 其他工程的安全基线保持一致vcpkg 集成通过$(ProjectDir)\..\..\..\..\..\vcpkg.props引入仓库根级 vcpkg 配置对应 vcpkg.props并声明VcpkgEnableManifesttrue依赖清单见 packages.configMicrosoft.Windows.ImplementationLibrary1.0.250325.1即 WIL链接依赖链接comsuppw.lib、shlwapi.lib、xmllite.lib、runtimeobject.lib、Pathcch.lib、Rometadata.lib、Rpcrt4.lib等库并通过ModuleDefinitionFile指定 winrtact.def 控制导出符号。3.2 与 WinGetServer 的源码级联动值得注意的工程细节是vcxproj 除了编译子树自身的catalog.cpp、dllmain.cpp、typeresolution.cpp外还把WinGetServer的客户端代码一并编入ClCompile Include..\..\..\..\..\WinGetServer\WinGetServerManualActivation_Client.cpp / ClCompile Include..\..\..\..\..\WinGetServer\WinGetServer_c.c / ClCompile Include..\..\..\..\..\WinGetServer\Utils.cpp /对应源码位于 src/WinGetServer。而 winrtact.def 导出的两个符号揭示了它的双重职责LIBRARY winrtact EXPORTS winrtact_Initialize WinGetServerManualActivation_CreateInstancewinrtact_Initialize免注册 WinRT 运行时的初始化入口C# 侧通过 mwinrtact.cs 以[DllImport(winrtact.dll)]方式 P/Invoke 调用WinGetServerManualActivation_CreateInstanceWinGetServer 的手动激活工厂函数配合 WinGetServerManualActivation_Client.cpp 中基于命名管道\pipe\WinGetServerManualActivation_SID见该文件第 48-60 行的 RPC 绑定逻辑实现跨进程的按需激活。四、核心原理四条 Detours 钩子重定向 WinRT 激活winrtact.dll的核心实现位于 dllmain.cpp。DllMain在DLL_PROCESS_ATTACH时依次调用InstallHooks()与ExtRoLoadCatalog()第 446-467 行前者安装钩子、后者加载目录manifest信息。4.1 钩子安装与原始函数保存InstallHooks()第 327-342 行使用 Detours 事务机制先DetourRestoreAfterWith()再DetourTransactionBegin、DetourUpdateThread随后把四个 API 的原始指针保存到TrueRoActivateInstance等静态变量并DetourAttach到各自的 Detour 实现最后DetourTransactionCommit提交。RemoveHooks()第 344-354 行在进程分离时对称地DetourDetach保证钩子的生命周期与 DLL 加载/卸载严格对应。拦截的四个 API 及其职责如下被拦截 APIDetour 实现作用RoActivateInstanceRoActivateInstanceDetour拦截 WinRT 类实例激活RoGetActivationFactoryRoGetActivationFactoryDetour拦截激活工厂获取RoGetMetaDataFileRoGetMetaDataFileDetour拦截类型元数据WinMD解析RoResolveNamespaceRoResolveNamespaceDetour拦截命名空间到元数据文件的解析4.2 激活位置判定与跨单元Apartment调度RoActivateInstanceDetour与RoGetActivationFactoryDetour共享同一个逻辑核心GetActivationLocation()第 151-186 行它先从 catalog.cpp 的WinRTGetThreadingModel查询该 activatableClass 声明的线程模型both/sta/mta再结合当前线程的 COM apartment 类型决定ThreadingType_BOTH在当前 apartment 直接激活ThreadingType_STA若当前为 MTA 则返回RO_E_UNSUPPORTED_FROM_MTA否则在当前 apartment 激活ThreadingType_MTA若当前非 MTA则通过CoGetDefaultContext(APTTYPE_MTA, ...)取得 MTA 默认上下文用IContextCallback::ContextCallback把激活调用调度到 MTA 中执行再经CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream把接口跨线程封送回来第 207-234 行。其中EnsureMTAInitialized()第 113-149 行解决了一个具体的 COM 兼容性 bug注释第 106-112 行说明 MTA 上下文回调中存在缺陷导致 COM 无法自动初始化 MTA remoting因此该函数特意在线程池中另起一个未经CoInitialize的线程利用隐式 MTA 语义调用CoGetObjectContext完成 MTA 的隐式初始化。两个 Detour 都保留了一个重要回退逻辑当目录中查不到该类型REGDB_E_CLASSNOTREG时直接转调真实的TrueRoActivateInstance/TrueRoGetActivationFactory即兜底回退到系统原生的 WinRT 激活路径保证对Windows.*内置类型的行为完全不受影响。4.3 元数据解析与命名空间回退RoGetMetaDataFileDetour第 280-294 行优先调用WinRTGetMetadataFilecatalog.cpp 中实现负责在应用目录旁查找第三方 WinMD仅当失败且不是RO_E_METADATA_NAME_IS_NAMESPACE命名空间名是合法结果不可回退时才回退到真实 API。RoResolveNamespaceDetour第 296-325 行则把进程 exe 目录与winrtact.dll自身目录作为额外的包图目录传给真实RoResolveNamespace让命名空间解析优先命中应用本地目录失败后再用系统传入的windowsMetaDataDir与packageGraphDirs重试。两个目录均由 typeresolution.cpp 的GetProcessExeDir/GetProcessDllDir提供二者都用InitOnceExecuteOnce保证只计算一次并全局缓存。五、目录Catalog加载manifest 驱动的类型注册表ExtRoLoadCatalog()dllmain.cpp 第 356-444 行负责在进程启动阶段加载 manifest用GetModuleFileNameW取当前 exe 路径通过CreateActCtxW创建激活上下文ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID资源名为MAKEINTRESOURCEW(1)用QueryActCtxW枚举激活上下文中的每个 assemblyAssemblyDetailedInformationInActivationContext取得lpAssemblyManifestPath对每个 manifest 路径调用LoadManifestFromPath完成解析注册。catalog.cpp中的 manifest 解析逻辑支持两种形态SxS 独立 manifest 文件LoadFromSxSManifest直接调用WinRTLoadComponentFromFilePath用SHCreateStreamOnFileEx打开文件流嵌入资源 manifestLoadFromEmbeddedManifest用LoadLibraryExW(..., LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)以数据文件方式加载模块依次尝试RT_MANIFEST资源 1 号与 2 号取出 XML 后走WinRTLoadComponentFromString内存解析。两种途径最终都汇入ParseRootManifestFromXmlReaderInput用 XMLLiteIXmlReader逐元素扫描命中file节点后进入ParseFileTag再在file内解析每个activatableClass元素读取name、threadingModelsta/mta/both与xmlns三个属性构造component记录并写入全局g_types映射表unordered_mapwstring, shared_ptrcomponent。对重复的 activatableClass解析会返回ERROR_SXS_DUPLICATE_ACTIVATABLE_CLASS第 309-314 行。component::LoadModule第 52-68 行体现了激活时的懒加载策略首次使用时才以LOAD_WITH_ALTERED_SEARCH_PATH加载模块并从模块中GetProcAddress(DllGetActivationFactory)取得激活工厂入口随后GetActivationFactory完成DllGetActivationFactoryQueryInterface的标准激活序列。注释第 34-35 行特别说明此处有意不做类工厂缓存因为上层已有缓存层。六、类型解析在应用目录旁查找 WinMDtyperesolution.cpp 实现了对第三方 WinRT 类型的元数据查找入口为ResolveThirdPartyType第 471-533 行其查找顺序是通过GetCurrentPackageInfo(PACKAGE_FILTER_HEAD | PACKAGE_FILTER_DIRECT | PACKAGE_FILTER_IS_IN_RELATED_SET)判断当前进程是否为未打包应用返回APPMODEL_ERROR_NO_PACKAGE若是未打包应用先在exe 所在目录旁查找FindTypeInDirectoryWithNormalization找不到时保持兼容性语义仍返回APPMODEL_ERROR_NO_PACKAGE而非 not found第 499-505 行注释解释了这一兼容行为若 exe 目录旁找不到再尝试winrtact.dll 自身所在目录。FindTypeInDirectory第 255-420 行体现了完整的类型名归约算法对类型SomeNamespace.B.C按SomeNamespace.B.C.WinMD→SomeNamespace.B.WinMD→SomeNamespace.WinMD的顺序逐级向上缩短命名空间前缀探测仍找不到时再用METADATA_FILE_SEARCH_FORMAT%s%s*.winmd通配匹配目录下以该前缀开头的所有 WinMD逐个检查其中是否含有以该名称为前缀的命名空间。FindTypeInMetaDataFile第 106-253 行使用IMetaDataImport2::FindTypeDefByName定位类型并校验该类型确为 WinRT 类型IsTdWindowsRuntime若找到的是公开但非 WinRT 类型返回RO_E_INVALID_METADATA_FILE说明 WinMD 损坏若是私有类型则视为未找到只有确认为命名空间时才返回RO_E_METADATA_NAME_IS_NAMESPACE。此外该类还内置一个MetaDataImportersLRUCache第 538-741 行以 LRU 策略缓存IMetaDataImport2实例与文件路径映射避免每次解析都重复OpenScope打开 WinMD从而降低高频类型解析时的开销。七、三步集成流程原文档步骤详解src/Xlang/README.md给出了创建 VS 工程文件时的三步操作结合仓库现状可还原为可执行的集成清单把 UndockedRegFreeWinRT 工程加入 WinGet 解决方案在 src/AppInstallerCLI.sln 中引用UndockedRegFreeWinRT.vcxproj使其参与解决方案级构建与 NuGet 还原vcxproj 末尾的EnsureNuGetPackageBuildImportsTarget 会校验Microsoft.Windows.ImplementationLibrary包是否已还原裁剪不需要的文件仅保留UndockedRegFreeWinRT文件夹内的运行时代码删除上游自带的其他模块与该文件夹内的测试代码——对应 README 第 2 步Remove code and files not needed by WinGet project对齐工程设置按 WinGet 解决方案中其他工程的标准修改 vcxproj包括配置/平台矩阵、TargetNamewinrtact、Spectre/CFG 缓解选项、vcpkg manifest 集成、OutDir输出布局$(SolutionDir)$(BuildPlatform)\$(Configuration)\$(ProjectName)\等即 README 第 3 步。集成完成后winget.exe启动时通过winrtact.dll完成钩子安装与目录加载C# 侧如 PowerShell 模块中的 WinRT 调用则可通过Microsoft.Windows.UndockedRegFreeWinrt.Initialize()见 mwinrtact.cs主动触发初始化WinGetServer 的手动激活则由WinGetServerManualActivation_CreateInstance经命名管道 RPC 按需拉起见 WinGetServerManualActivation_Client.cpp。八、维护约定与注意事项子树不可直接改动src/Xlang/UndockedRegFreeWinRT目录内的代码是上游 xlang 的快照PR 应通过git subtree机制与上游同步不要手工编辑子树内实现文件更新不 squashREADME 明确要求后续 subtree 更新 PR 不压缩提交以保留与上游提交cfe510d0d2b07484fea2c6d77163de017738c100的可追溯映射回退语义是正确性关键四个 Detour 均保留了对系统原生 API 的回退路径任何对第三方类型解析逻辑的改动都必须维持这一回退否则会破坏Windows.*内置类型的正常解析目录查找顺序敏感类型解析优先 exe 目录、其次 DLL 目录发布产物必须把第三方 WinMD 与对应 DLL 放在与winget.exe/winrtact.dll相同的目录下才能被免注册机制命中线程模型声明必须准确manifest 中activatableClass的threadingModel直接决定激活是否跨 apartment 调度声明错误可能导致RO_E_UNSUPPORTED_FROM_MTA或在错误 apartment 中创建对象。结语通过git subtree引入的 UndockedRegFreeWinRT是 WinGet 在未打包进程 进程内 WinRT 激活这一矛盾约束下的关键工程答案Detours 钩子接管激活入口manifest 驱动的目录注册表提供类型-模块映射目录旁 WinMD 解析补齐元数据三者配合让 winget.exe 无需 MSIX 包图即可流畅调用 WinRT 组件。本文涉及的工程文件UndockedRegFreeWinRT.vcxproj、运行时实现dllmain.cpp、catalog.cpp、typeresolution.cpp以及 src/Xlang/README.md 中记录的集成流程共同构成了一份可复用的免注册 WinRT 运行时接入指南对需要在未打包应用中承载 WinRT 组件的项目具有直接的参考价值。赞分享包管理器CLI【免费下载链接】winget-cliWinGet is the Windows Package Manager. This project includes a CLI (Command Line Interface), PowerShell modules, and a COM (Component Object Model) API (Application Programming Interface).项目地址https://gitcode.com/gh_mirrors/wi/winget-cli点击查看免费下载相关推荐create-t3-app 中的 Tailwind CSS 实战指南从 utility-first 到 CLI 集成与源码剖析create t3 app 中的 Tailwind CSS 实战指南从 utility first 到 CLI 集成与源码剖析 Tailwind CSS 是开发工具CLI代码生成BambuStudio 中的 MCUT 网格布尔引擎源码剖析、能力清单与集成实践BambuStudio 中的 MCUT 网格布尔引擎源码剖析、能力清单与集成实践 导读 MCUTMesh CUTting是一套以 C 风格 API 暴露的桌面应用3D渲染图形学智能硬件Dubbo 注册中心模块源码剖析从 RegistryService 到 RegistryDirectory 的服务注册、订阅与发现机制Dubbo 注册中心模块源码剖析从 RegistryService 到 RegistryDirectory 的服务注册、订阅与发现机制 本文基于 Dubbo注文档教程技术博客知识库上一篇探索Eloquent JSON Relations优雅处理JSON关系的利器下一篇【亲测免费】 推荐开源项目通用随机森林Generalized Random Forests创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表