)
WinUI 双引擎架构解析Dxaml 层与 Core 层的 Peer 对象通信机制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本文基于 dxamlvscore.md 整理并深入扩展。WinUI 代码库在架构上被清晰地切分为Dxaml 层公开、面向开发者与Core 层内部、贴近系统两大部分每个控件类型实际上由一对 Peer对等 对象组成。阅读本文后你将掌握这两层各自的职责边界、Xaml 实例的构成方式以及 Dxaml → Core 与 Core → Dxaml 两条方向上全部四种通信手段GetHandle、CoreImports/pinvokes、DxamlCore::GetPeer、DxamlServices::GetPeer与FxCallbacks并理解这些设计的历史由来与当前演进方向。背景Silverlight 留下的遗产理解 Dxaml 与 Core 的分层首先要回到 WinUI 的历史源头——Silverlight。Silverlight 是一个浏览器插件可托管在 Windows 或 Mac 上 IE、FireFox、Chrome 的网页中。最初整个 Silverlight 都是用 C 编写的位于我们今天依然称之为 core 的目录Silverlight 的 DLL 名为agcore.dll。后来在它之上增加了一个用 C# 编写的层名为System.Windows有时也被称作framework——它的设计初衷是作为 agcore 之上的一个框架层且未来可能还会有其他框架。Core 层实现了诸如DependencyObject、DependencyProperty、布局、输入、动画和渲染等基础能力。最初它只支持Canvas、TextBlock、Shapes、Animations和Storyboard这些类型。Control与Templating随后也被加入 Core但真正的控件如Button、ListBox则实现在System.Xaml中。因此形成了控件框架在 Core控件本体在System.Windows的格局随着时间推移出于性能原因部分代码又从System.Windows下沉到 Core例如Button如今就是 Core 中的CButton。原始的 Core 层通过CDependencyObject简称 CDO以 JS 对象形式暴露给浏览器因此 Core 中的一切对象都是 CDO而System.Windows类型只暴露给 .NET。例如从 JS 中能看到ItemsControl但只有 .NET 中才能看到ListBox。为了在 .NET 中访问这些类型System.Windows为每个 Core 类型都提供了 C# 包装——比如System.Windows里的Control类就是 Core 中CControl的简单包装。到了 Windows 8 时代Core 层被移植到 Windows 上System.Windows层则被用 C 重写这段重写的代码全部进入了今天的 DXaml 目录dxaml/。这套新的 C Dxaml 代码遵循了当时更现代的命名规范这正是为什么它的类没有像 Core 那样带 C 前缀——除此之外两层代码中还有很多能看出年代差异的痕迹。Xaml Core一个 Xaml 实例由两个对象构成Xaml core在这里不是指 Xaml 代码中的 Core层而是由两个对象共同组成的组合体且每个对象分别属于一个层Dxaml 层的DXamlCore对象Core 层的CCoreServices对象。两者合起来构成一个Xaml 实例。由于 WinUI 是单线程框架每个线程对应一个 Xaml 实例。这一事实是理解整个框架调度、资源访问与对象生命周期的基础所有 UI 对象都归属到所属线程的 Xaml 实例之上跨线程访问必须遵循调度Dispatcher规则。Peer 对象每个控件都是一对对象每个控件类型都由两个对象组成一个 Dxaml 层对象 一个 Core 层对象。以UIElement为例层类型可见性Dxaml 层UIElement公开面向开发者Core 层CUIElement内部Xaml 实现细节在继承关系上Dxaml 层的所有 Xaml 类型都是DependencyObjectDO的子类Core 层的所有类型都是CDependencyObjectCDO的子类更完整的对象旅程可参考 Journey of a control。这两个对象互称peer对等对象Dxaml 对象的 peer 是它的 Core 对象反之亦然。设计上有一条铁律两个 peer 不能直接对话指令流向永远是 Dxaml 层 → Core 层Core 层对象不能直接调用其 Dxaml 层 peer。这一设计源于历史Silverlight 时代 Dxaml 层当时是System.Windows与 Core 层位于不同的 DLL只是后来才合并进单一 DLLMicrosoft.UI.Xaml.dll。那为什么合并之后依然保留这一隔离因为若在 Core 层直接 include Dxaml 层的头文件会牵连引入大量头文件并可能造成循环引用。在没有良好重构策略的前提下两层之间的任何对话都通过抽象层进行。下面分别介绍两个方向上的全部通信方式。Dxaml 层到 Core 层的通信通信流向Dxaml → Core这个方向的通信相对直接主要有两种实现方式。方式一GetHandle()将公开类型强转为DependencyObject甚至UIElement然后调用GetHandle()即可拿到 Core 层的 peer 指针// 用法 // 假设 someType 是 UIElement 的子类 // 将其强转为 UIElement 后调用 GetHandle 即可拿到其 Core peer 对象 CUIElement* foo someType.CastUIElement()-GetHandle(); // 内部实现上UIElement::GetHandle 在 DependencyObject 基类层面完成操作 // 再将结果重新强转回来。以下是其定义 CUIElement* UIElement::GetHandle() const { auto result DependencyObject::GetHandle(); ASSERT(!result || result-OfTypeByIndexKnownTypeIndex::UIElement()); return static_castCUIElement*(result); }注意GetHandle()内部通过OfTypeByIndexKnownTypeIndex::UIElement()做了类型校验保证返回的 Core 对象确实是CUIElement或其派生类型随后用static_cast完成强转。方式二CoreImports.cpp即历史上的pinvokes.cpp并非所有 Dxaml 到 Core 的调用都通过GetHandle实现。在 Silverlight 时代从System.Windows调用 Core 属于托管到原生的跨越必须通过pinvoke因此存在一个pinvokes.cs文件集中存放所有对 Core 的调用。这段代码被移植到 C 后成为现在的pinvokes.cpp已改名为 CoreImports.cpp大量跨层调用都经由它完成。例如 Dxaml 层调用 Core 中的CUIElement::Arrange时就是通过CoreImports.cpp里的UIElement_Arrange方法中转。值得特别关注的是文档中的明确论断由于该边界DLL 边界已不存在这种间接层没有存在的理由应当被移除。也就是说CoreImports.cpp属于可清理的历史包袱是当前代码库中一个已知的重构方向读者在阅读或贡献代码时可留意这一点。Core 层到 Dxaml 层的通信通信流向Core → Dxaml这个方向的通信要绕一些弯子因为 Core 层不能直接引用 Dxaml 层的头文件。目前共有三种抽象手段。手段一DxamlCore::GetCurrent()与DxamlCore::GetPeer如果调用代码本身就存在于 Dxaml 层虽然它操作的是一个 Core 对象那么通过DxamlCore::GetCurrent()可以直接拿到DxamlCore实例再调用其GetPeer获取 Core 对象的 Dxaml peer。以下是来自代码库的实际片段// 代码库中的真实片段 CUIElement* publicRootVisual xamlRoot.CastXamlRoot()-GetVisualTreeNoRef()-GetPublicRootVisual(); ctl::ComPtrDependencyObject peer; DxamlCore::GetCurrent()-GetPeer(publicRootVisual, peer);这里从XamlRoot的视觉树取出公共根视觉对象publicRootVisual一个 Core 层CUIElement再借助DxamlCore::GetCurrent()-GetPeer拿到它的 Dxaml 层 peerDependencyObject并存入ComPtr以安全托管生命周期。在 Dxaml 层代码中这是获取 peer 的最直接途径。手段二DxamlServices::GetPeer如果调用代码存在于 Core 层它没有任何办法调用DxamlCore实例——因为在 Core 层 includeDxamlCore头文件需要大量重构。为此DxamlServices持有一个IDxamlCore接口DxamlCore实现该接口的实例Core 层通过调用该接口上的函数来与DxamlCore实例通信。GetPeer/TryGetPeer函数正是 Core 对象获取其 Dxaml peer 的入口。前缀Try表示允许失败失败时返回错误而非断言的版本// 定义 _Check_return_ HRESULT DxamlServices::GetPeer( _In_ CDependencyObject* pDO, _Outptr_ DependencyObject** ppObject) // 用法 CDependencyObject *foo; DirectUI::DependencyObject* DO{}; DirectUI::DxamlServices::GetPeer(foo, DO); // 返回的 DO 需强转为具体子类型 auto DxamlLayerObject static_castbarType*(DO);GetPeer()接收一个CDependencyObject*返回一个DependencyObject*之后调用方再将其强转为所需的 Dxaml 层具体类型。由于返回值可放入ComPtr可以毫不费力地享受智能指针带来的生命周期管理相关约定见 WinUI Pointers。注意_Check_return_标注要求调用方必须检查 HRESULT 返回值这是 Core 层代码的典型契约。手段三FxCallbacksFxCallbacks是又一个抽象文件用于处理某些需要从 Core 调用 Dxaml 层的常见动作位于 dxaml/xcp/dxaml/lib/FxCallbacks.cpp。与pinvokes.cpp/CoreImports.cpp类似FxCallbacks同样是 Silverlight 时代代码的移植——只不过方向相反它对应的是当年从 Core 反向调用System.Windows的那批函数即一个装满reverse pinvoke的文件。换句话说pinvokes系列管正向跨层FxCallbacks管反向跨层两者共同构成了跨层调用的双向中继站。通信机制速查表方向机制适用场景关键文件Dxaml → CoreGetHandle()直接取 Core peerUIElement/DependencyObject基类实现Dxaml → CoreCoreImports.cpp原pinvokes.cpp批量中转跨层函数调用如UIElement_Arrangedxaml/xcp/core/dll/CoreImports.cppCore → DxamlDxamlCore::GetCurrent()-GetPeer()Dxaml 层代码中为 Core 对象取 peerdxaml/xcp/dxaml/lib/DXamlCore.hCore → DxamlDxamlServices::GetPeer/TryGetPeerCore 层代码中取 Dxaml peerDxamlServices持有IDxamlCore接口实例Core → DxamlFxCallbacks常见的反向跨层动作中转dxaml/xcp/dxaml/lib/FxCallbacks.cpp总结与阅读建议Dxaml/Core 双层的 Peer 架构是 WinUI 最核心的底层设计之一它决定了对象所有权、线程模型与跨层调用约定。把握以下要点即可建立清晰心智模型一个 Xaml 实例 DXamlCoreCCoreServices每线程一份每个控件 Dxaml 对象 Core peer 对象指令只能从 Dxaml 流向 Core正向通信走GetHandle()或CoreImports.cpp其中CoreImports是文档明确标记应被移除的历史间接层反向通信通过DxamlCore::GetPeerDxaml 层代码或DxamlServices::GetPeerCore 层代码与FxCallbacks完成。如需继续深入推荐结合 Journey of a control了解 DO/CDO 的完整对象生命周期、pointers.md掌握 WinUI 的指针/引用计数约定并可直接在仓库的 dxaml/xcp/dxaml/lib/ 与 dxaml/xcp/core/ 目录中对照阅读上述源码验证每一处跨层调用的真实形态。【免费下载链接】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),仅供参考