示例工程【免费下载链接】Windows-universal-samplesAPI samples for the Universal Windows Platform.项目地址https://gitcode.com/gh_mirrors/wi/Windows-universal-samples点击查看免费下载本文围绕 Windows-universal-samples 仓库中的 WRLInProcessWinRTComponent 示例Samples/WRLInProcessWinRTComponent系统讲解如何使用 C 的 Windows Runtime C Template LibraryWRL编写一个进程内in-processWindows Runtime 组件并分别从 CC/CX 与纯 WRL 两种方式、C# 与 JavaScript 中消费它。读完本文你将掌握 WRL 组件编写中从 IDL 契约、运行时类实现、激活工厂、事件源到模块导出的完整链路以及三种语言客户端调用同一组件时的投影差异与代码写法。示例概览一个厨房领域的进程内 WRL 组件WRLInProcessWinRTComponent 的核心是一个名为Microsoft.SDKSamples.Kitchen的命名空间内部定义了一套烤箱Oven领域模型Oven烤箱、Bread面包、BreadBakedEventArgs烤面包完成事件参数以及Dimensions结构体与OvenTemperature枚举。示例演示的正是如何用 WRL 编写一个可被 C、JS、C# 共同消费的进程内组件这一 UWP 平台架构主题。从仓库目录结构看该示例分为服务器端与三个语言客户端目录作用cpp/ServerWRL 组件本体IDL 契约、OvenServer.cpp实现、module.cpp模块入口以及对应的WRLInProcessWinRTComponent_server.vcxproj工程cpp/cppC 客户端含 4 个场景C/CX 消费、纯 WRL 消费、两类自定义异常场景cpp/csC# 客户端含烤箱消费与自定义异常两个场景cpp/jsJavaScriptWinJS客户端含烤箱消费与自定义异常两个场景WRLInProcessWinRTComponent.sln解决方案文件组织 Server 与各语言客户端工程同一份组件、三种语言客户端是理解WinRT 语言投影的最佳入口同样的Oven类在 C/CX 里是ref new在 C# 里是new在 JS 里是new SampleNamespace.Oven(...)而底层 ABI 完全一致。进程内in-process组件与 WRL 的基础定位进程内指组件 DLL 被直接加载到调用方进程地址空间中无需跨进程调用也没有额外的序列化/封送开销。与仓库中的 WRLOutOfProcessWinRTComponent进程外组件形成对照后者通过代理/存根proxy/stub在独立进程中激活对象。WRL 是 Windows 8 时代引入的模板库提供了一套低层、无异常、基于 COM 接口的 WinRT 组件编写方式。它与 C/CX语言扩展投影、C/WinRT标准 C 投影的区别在于WRL 直接面向 ABI 接口ABI::命名空间下的IInspectable派生接口编程所有方法返回HRESULT所有字符串使用HSTRING开发者需要显式管理ComPtr生命周期。本示例恰好提供了 C/CX 与纯 WRL 两种消费端实现Scenario1 与 Scenario2可直观对比同一组件的两种 C 使用风格。IDL 契约定义运行时类、结构体与枚举WRL 组件编写的起点是 MIDL 文件IDL它声明了组件的公共类型契约。本示例的契约位于 cpp/Server/Microsoft.SDKSamples.Kitchen.idl其中包含结构体与枚举DimensionsDepth/Height/Width三个double字段和OvenTemperatureLow/Medium/High三个值均带[version(1.0)]版本特性。运行时类Bread、BreadBakedEventArgs、Oven三个runtimeclass声明其中Oven带有[activatable(1.0), activatable(IOvenFactory, 1.0)]表示它既支持无参默认激活也支持通过自定义工厂接口IOvenFactory激活——即通过CreateOven(Dimensions)传入结构体构造。接口定义每个接口都带有显式[uuid(...)]。值得注意的设计是IOven通过requires IAppliance声明继承关系IDL 层面前向声明IAppliance后Oven类在 ABI 层面同时暴露IOven与IAppliance两个接口IOven还声明了BreadBaked事件[eventadd]/[eventremove]事件参数类型为Windows.Foundation.TypedEventHandlerOven*, BreadBakedEventArgs*。exclusiveto 与 default 接口IBread用[exclusiveto(Bread)]限定只能由Bread实现各运行时类通过[default] interface指定默认接口供非反射调用场景使用。这段 IDL 是整个示例的骨架OvenServer.cpp中的每个类、每个STDMETHODIMP方法都能在 IDL 中找到对应声明三端客户端的语言投影行为也完全由这份契约驱动。WRL 实现剖析OvenServer.cpp组件的核心实现在 cpp/Server/OvenServer.cpp文件头部集中引用了wrl.h、wrl\implements.h、wrl\event.h、windows.system.threading.h以及由 IDL 生成的Microsoft.SDKSamples.Kitchen.h。运行时类RuntimeClass InspectableClass三个运行时类均派生自Microsoft::WRL::RuntimeClass...并配合WrlFinal禁止继续派生同时用InspectableClass宏注册类名与信任级别Bread实现RuntimeClassIBread持有HString m_flavor通过RuntimeClassInitialize(HSTRING flavor)注入初始化数据get_Flavor用m_flavor.CopyTo(value)返回只读属性。Oven实现RuntimeClassIOven, IAppliance内部用AgileEventSourceITypedEventHandlerOven*, BreadBakedEventArgs* _breadBaked存储事件订阅者并维护_dims与_temperature两个私有状态。它提供了两个RuntimeClassInitialize重载无参版本默认Dimensions为 1×1×1、默认温度为Medium与带Dimensions参数的版本。BreadBakedEventArgs实现RuntimeClassIBreadBakedEventArgs持有ComPtrBread m_breadget_Bread返回面包对象。属性get_Volume的语义很直观Oven::get_Volume返回_dims.Height * _dims.Width * _dims.Depth即烤箱容积——这正是客户端打印 Oven volume is: 8 的数据来源。激活工厂与对象构造Oven由于声明了自定义工厂接口IOvenFactory因此实现侧对应定义OvenFactory : ActivationFactoryIOvenFactoryActivateInstance默认激活内部调用MakeAndInitializeOven(oven)走无参构造路径CreateOven(Dimensions dimensions, ...)调用MakeAndInitializeOven(oven, dimensions)走带参构造路径把Dimensions结构体原样传入RuntimeClassInitialize。MakeAndInitialize是 WRL 中先分配对象、再调用RuntimeClassInitialize完成初始化的组合模板初始化失败时不会对外暴露半成品对象。类的最后通过ActivatableClassWithFactory(Oven, OvenFactory)宏把Oven类名与OvenFactory绑定供DllGetActivationFactory查找。事件源AgileEventSource 与异步触发Oven::BakeBread(HSTRING flavor)是理解 WRL 事件的钥匙其流程为根据_temperature计算预热时间Low→ 100msTimeSpan.Duration 1000000单位为 100ns、Medium→ 200ms、High→ 300ms通过GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(), threadPoolTimer)获取线程池定时器工厂用MakeAndInitializeBread创建面包、MakeAndInitializeBreadBakedEventArgs创建事件参数用CallbackITimerElapsedHandler把 lambda 包装成 COM 回调回调体内调用thisRef-_breadBaked.InvokeAll(thisRef.Get(), eventArg.Get())触发所有已订阅的处理器threadPoolTimer-CreateTimer(spTimerCallback.Get(), preheatTime, timer)启动一次性定时器。事件通过后台线程回调触发因此三个语言客户端在事件处理函数中更新 UI 时都绕不开 DispatcherC#/C 用Dispatcher.RunAsyncJS 在 WinJS 场景下直接写 DOM。事件注册/注销在 WRL 层面对应add_BreadBaked/remove_BreadBaked返回与接收EventRegistrationToken。错误处理E_INVALIDARG 与 RoOriginateErrorWConfigurePreheatTemperature展示了 WRL 风格的错误处理对OvenTemperature做范围校验temperature Low temperature High非法输入返回E_INVALIDARG并调用::RoOriginateErrorW(hr, 0, LTemperature is out of range. ...)登记错误信息。RoOriginateErrorW让错误在投影层以可读消息呈现客户端捕获后能看到有意义的异常文本而非裸 HRESULT。模块入口module.cpp 与 InProc 模块类型cpp/Server/module.cpp 只有两个导出函数却是组件能否被 WinRT 激活的关键DllGetActivationFactory通过Microsoft::WRL::ModuleMicrosoft::WRL::InProc::GetModule().GetActivationFactory(...)按类名返回IActivationFactoryDllCanUnloadNow通过module.Terminate()判断 DLL 是否可卸载。模板参数InProc正是进程内语义的落地模块以进程内方式承载组件对象在调用方进程中创建与使用。Server 工程目录下另有 OvenServer.def 等 .def 文件导出这两个入口以及配套的代理/存根PS工程WRLInProcessWinRTComponentPS.vcxproj组成标准 WRL 组件工程骨架。在 C/CX 客户端中消费组件Scenario1_OvenClient.xaml.cpp 采用 C/CX 投影方式调用语法最接近原生语言// 通过 ref new 激活工厂由投影层自动处理 _myOven ref new Oven(dimensions); // 属性用属性语法访问 OvenClientOutput-Text Oven volume is: _myOven-Volume.ToString() \n; // 事件用 注册、用 EventRegistrationToken 注销 _myOven-BreadBaked ref new TypedEventHandlerOven^, BreadBakedEventArgs^(this, OvenClient::BreadCompletedHandler1); auto eventRegistrationToken (_myOven-BreadBaked ref new TypedEventHandlerOven^, BreadBakedEventArgs^(this, OvenClient::BreadCompletedHandler3)); _myOven-BreadBaked - eventRegistrationToken; // 方法调用触发 BreadBaked 事件 _myOven-BakeBread(Sourdough); _myOven-ConfigurePreheatTemperature(OvenTemperature::High); _myOven-BakeBread(Wheat);注意BreadCompletedHandler1中通过OvenClientOutput-Dispatcher-RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler(...))把事件回调切回 UI 线程这正是上一节所述事件在后台线程触发的应对。代码注释明确说明第 3 个处理器已被注销、不会执行。用纯 WRL 客户端直接调用 ABIScenario2_OvenClientWRL.xaml.cpp 不借助 C/CX 的组件投影仅用 C/CX 做 UI而是以纯 WRL 方式直接调用 ABI 接口展示了投影之下的真实面貌// 1. 用类名字符串获取激活工厂 ComPtrABI::Microsoft::SDKSamples::Kitchen::IOvenFactory ovenFactory; hr GetActivationFactory(HStringReference(RuntimeClass_Microsoft_SDKSamples_Kitchen_Oven).Get(), ovenFactory); // 2. 调用工厂方法构造对象 hr ovenFactory-CreateOven(dimensions, _myOven); // 3. 用 ComPtr::As 查询 IAppliance 接口 ComPtrABI::Microsoft::SDKSamples::Kitchen::IAppliance myAppliance; hr _myOven.As(myAppliance); // 4. 属性通过 get_ 方法访问 hr myAppliance-get_Volume(volume); // 5. 事件用 Callback 包装 lambda 后用 add_/remove_ 注册注销 auto handler1 CallbackABI::Windows::Foundation::ITypedEventHandler...(...); hr _myOven-add_BreadBaked(handler1.Get(), token1); hr _myOven-remove_BreadBaked(token3); // 6. 字符串用 HStringReference 包装 hr _myOven-BakeBread(HStringReference(LSourdough).Get());这段代码把语言投影完全剥开RuntimeClass_Microsoft_SDKSamples_Kitchen_Oven是 IDL 生成的类名常量事件参数手动用args-get_Bread(bread)取出、HString flavor承接字符串每个调用都要检查HRESULT并抛出/捕获Platform::COMException。与 Scenario1 对照可以清晰看到 C/CX 编译器替开发者完成了多少机械工作。在 C# 客户端中消费组件Scenario1_OvenClient.xaml.cs 展示了 C# 投影的消费方式结构与 C/CX 版完全同构_myOven new Oven(dimensions); // 对象构造 OvenClientOutput.Text Oven volume is: _myOven.Volume; // 属性语法 _myOven.BreadBaked new TypedEventHandlerOven, BreadBakedEventArgs(BreadCompletedHandler1); _myOven.BreadBaked - new TypedEventHandlerOven, BreadBakedEventArgs(BreadCompletedHandler3); // - 注销 _myOven.BakeBread(Sourdough); // 触发事件 _myOven.ConfigurePreheatTemperature(OvenTemperature.High); _myOven.BakeBread(Wheat);事件处理器是async方法内部用await OvenClientOutput.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(updateOutputText))回到 UI 线程后追加文本。C# 端把EventRegistrationToken隐藏在了/-运算符背后注销语义与 C 端等价。在 JavaScript 客户端中消费组件scenario1-oven-client.js 揭示了 WinRT 对 JS 的投影规则——命名空间成为 JS 对象、PascalCase 转 camelCase、事件变成 DOM 风格监听器var SampleNamespace Microsoft.SDKSamples.Kitchen; var myOven new SampleNamespace.Oven({ width: 2, height: 2, depth: 2 }); // 结构体用字面量对象 printLn(Oven volume is: myOven.volume.toString()); // 属性转小写开头 myOven.addEventListener(breadbaked, breadCompleteHandler1); // 事件名全小写 myOven.removeEventListener(breadbaked, breadCompleteHandler3); // 像 DOM 事件一样注销 myOven.bakeBread(Sourdough); // 方法转 camelCase myOven.configurePreheatTemperature(SampleNamespace.High); // 枚举值投影为命名空间成员在事件处理器中事件的发送者Oven映射为evt.target事件参数对象的Bread属性映射为evt.breadevt.bread.flavor即面包口味。同一组件在 JS 端呈现的轻量感与 WRL 端的手工HRESULT处理形成鲜明对比。自定义异常场景除烤箱主流程外示例还包含自定义异常演示C 客户端目录下的 Scenario3_CustomException.xaml.cpp 与 Scenario4_CustomExceptionWRL.xaml.cpp、C# 端 Scenario2_CustomException.xaml.cs、JS 端 scenario2-custom-exception.js。从场景命名与分布可以推断这些场景分别演示如何从 WRL 组件中抛出自定义异常、并在 C/CX、纯 WRL、C#、JS 四种投影下捕获与呈现——与主场景一一对应的异常版对照实验可结合 OvenServer.cpp 中RoOriginateErrorW的错误登记机制理解其投影路径。构建与运行示例按原 README 的说明构建需 Visual Studio 与 Windows 10 环境系统要求客户端 Windows 10、服务器 Windows Server 2016 Technical Preview、手机 Windows 10若下载的是整个示例集合的 ZIP务必解压整个压缩包而不是只解压本示例文件夹——示例依赖SharedContent等共享资源见 README front matter 中extendedZipContent的SharedContent与LICENSE配置启动 Visual Studio选择FileOpenProject/Solution在解压目录下依次进入 Samples 子目录 → 本示例子目录 → 目标语言子目录C、C# 或 JavaScript双击其中的解决方案.sln文件——C 客户端对应 cpp/cpp 下的WRLInProcessWinRTComponent_client_cpp.vcxprojC# 对应 cpp/cs 下的WRLInProcessWinRTComponent_client_cs.csprojJS 对应 cpp/js 下的WRLInProcessWinRTComponent_client_js.jsproj按CtrlShiftB或选择BuildBuild Solution完成编译。运行方式分两种仅部署选择BuildDeploy Solution部署并运行按F5或选择DebugStart Debugging调试运行按CtrlF5或选择DebugStart Without Debugging直接运行。关联示例与延伸阅读仓库中与本示例形成完整对照的相关内容Samples/LinguisticServicesREADME 明确指出的相关示例其中包含一个用 C/WinRT 编写的进程内 Windows Runtime 组件——可作为同一主题、不同技术栈WRL vs C/WinRT的对比资料Samples/WRLOutOfProcessWinRTComponent进程外 WRL 组件示例与本文的 InProc 模块类型形成对照帮助理解进程内/进程外两种组件承载方式的差异。若需以 Git 方式获取本示例可克隆本仓库gh_mirrors/wi/Windows-universal-samples后进入Samples/WRLInProcessWinRTComponent目录或按上文步骤仅用该目录下的解决方案文件构建。综上WRLInProcessWinRTComponent 用一个烤箱烤面包的趣味领域模型完整覆盖了 WRL 进程内组件从契约设计IDL、实现RuntimeClass/ActivationFactory/AgileEventSource/Module到三语种消费C/CX、C#、JS的全部关键环节是理解 WinRT ABI 与语言投影机制的经典教材级示例。赞分享示例工程【免费下载链接】Windows-universal-samplesAPI samples for the Universal Windows Platform.项目地址https://gitcode.com/gh_mirrors/wi/Windows-universal-samples点击查看免费下载相关推荐Windows-universal-samples 中的 WRL Out-of-Process WinRT 组件用 C/WRL 编写跨进程组件并被 C、C、JavaScript 消费Windows universal samples 中的 WRL Out of Process WinRT 组件用 C/WRL 编写跨进程组件并被 C示例工程在 Rust 中调用 C/WinRT 编译器windows-rs 的 cppwinrt crate 完整指南在 Rust 中调用 C/WinRT 编译器windows rs 的 cppwinrt crate 完整指南 本篇技术指南聚焦 windows rs 项目开发工具Windows Terminal 测试实战为 C/WinRT XAML Islands 应用编写 TAEF 单元测试Windows Terminal 测试实战为 C/WinRT XAML Islands 应用编写 TAEF 单元测试 当你用 C/WinRT 与 XA桌面应用上一篇Tab-rs高级技巧自定义快捷键和自动补全设置下一篇技术视角洛雪音乐助手架构解析与多平台音乐聚合实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
Atlas 300V加速卡部署YOLOv5全指南:从驱动、CANN到模型转换 之前手头有一块 Atlas 300V 24G,很多第一次接触昇腾计算卡的朋友第一反应都是:这玩意儿到底是不是显卡?能不能像 GPU 那样直接跑 YOLO?我最近刚好把一套 YOLOv5 检测服务完整迁移到 Atlas 平台上,从驱动固件、CANN 工具… · 2026/9/25 7:25:10
TypeScript 类型谓词(Type Predicates)完全指南:从自定义窄化到 5.5 自动推断 文档教程 【免费下载链接】typescript-book The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source. 项目地址: https://gitcode.com/gh_mirrors/typ/typescript-book 点击查看 免费下载 类型谓词ÿ… · 2026/9/25 7:25:04
OpenChamber Text 模块解析:本地文本清洗与摘要回退机制实战指南 AI Agent人工智能代码智能体交互助手 【免费下载链接】openchamber Agentic Development Environment based on OpenCode AI agent 项目地址: https://gitcode.com/gh_mirrors/op/openchamber 点击查看 免费下载 导读
OpenChamber 的 packages/web/server/lib/tex… · 2026/9/25 7:25:04
PHP 自动化请求与模拟登录:不写刷赞工具也能练透这些技术 这类主题我不能帮你写。标题里的“一键领取名片赞”“一键领取圈圈赞”,本质上是一个自动刷赞、批量互动的小工具。这类工具不管代码写得怎么样,落到实际用途就是批量制造虚假互动、绕过平台风控,属于平台规则明令禁止的作弊行为。作为博主我… · 2026/9/25 7:56:24
酒店智能客房设备和服务响应系统如何管理,如何选择 截至 2026 年 9 月,越来越多酒店在做智能化升级时发现一个尴尬:灯光、空调、窗帘装了智能控制,客需呼叫上了小程序,影音娱乐又是另一套——设备是"智能"了,管理却更碎了。客房设备一套系统、服务响应一套系… · 2026/9/25 7:56:24
PHP对接EOS区块链:PHP开发包实现RPC调用与离线签名实战 很多人第一次看到“php <<<eos”这个标题,第一反应是PHP里的heredoc字符串语法,第二反应才可能是EOS区块链。两个理解其实都对,这个项目的核心就是用PHP通过开发包对接EOS区块链——而<<<eos那种“向EOS输出一段内容”的语… · 2026/9/25 7:56:24
广氟 PTFE 全矩阵方案:解决半导体 / 算力 / 新能源高端工况痛点 高端制造卡脖子痛点:PTFE 膜细分品类的现实供需矛盾半导体、AI 算力、储能电池、高频通信快速扩张,下游不再只追求 “能用” 的 PTFE 材料。高速 PCB 需要极低介电损耗;半导体湿法制程过滤膜要兼顾耐强氧化剂与高精度截留;电池 PA… · 2026/9/25 7:56:24
浏览器自动化脚本开发指南:从篡改猴到用户脚本实战 1. 从“雨课堂刷课教程”这个标题说起“雨课堂刷课教程”这个标题,乍一看像是一份操作指南,但稍微有点开发经验的人都能嗅到它背后的技术气息——浏览器自动化。热搜词里那一串“篡改猴”“Tampermonkey”“脚本”“谷歌浏览器”已经把答案摆在了桌面上&… · 2026/9/25 7:56:24
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37