创建 C++ WinRT 组件

通过 Cpp/WinRT 项目模板创建一个 WinRT 组件工程 CppWinrtComponent.vcxproj,主要接口定义如下:

namespace CppWinrtComponent
{
[default_interface]
runtimeclass Class
{
Class();
String GetModule();
}
}

最终该项目 CppWinrtComponent 可以被编译生成两个 WinRT 组件的核心部分:

  • CppWinrtComponent.winmd, 提供接口描述
  • CppWinrtComponent.dll, 负责接口实现,基于 COM 技术

由于该项目 CppWinrtComponent.vcxproj 默认的平台是 Universal Windows,所以它使用的 VC 运行时函数都是 Store 版本,这导致在非 Universal Windows 应用中会出现找不到 VC 运行时库的异常。为了解决这个问题,针对该类型项目,Visual Studio 2019 version 16.9 版本开始支持属性 Windows Desktop Compatible,设置该属性后所有的 VC 运行时函数会被链接到 Deskotp 版本。

该属性可以在 VS 项目属性面板中配置,对应的 vcxproj 中配置如下:

<PropertyGroup>
<DesktopCompatible>true</DesktopCompatible>
</PropertyGroup>

消费 C++ WinRT 组件

本文主要讨论在如下语言、框架中使用该组件:

  • C++ Desktop
  • C++ UWP
  • .Net Framework Desktop
  • .Net5 Desktop

C++ Desktop

使用 VS 的 C++ 项目模板,创建一个空的控制台应用程序 ConsumerCppConsole.vcxproj。

1. 使用 Nuget 包管理器给该项目安装 Microsoft.Windows.CppWinRT

建议安装最新版本,在 Microsoft.Windows.CppWinRT 2.0.200115.8 之后版本中,引入了对 Reg Free 的支持,可以简化 WinRT 组件使用流程,不需要创建额外的 Manifest 文件。

2. 添加对项目 CppWinrtComponent 生成的 CppWinrtComponent.WinMD 的引用

不能直接在 VS 中添加对项目 CppWinrtComponent.vcxproj 的工程引用,但是可以直接添加对文件 CppWinrtComponent.winmd 的引用,在该文件引用的属性中,VS 默认会把"Copy Local" 设置为 True,该操作会导致 CppWinrtComponent.dll 在编译后会被复制到输出目录,这也是期待的行为。

虽然在 VS 的 GUI 中无法直接添加对 CppWinrtComponent.vcxproj 的工程引用,但是可以手动编辑 ConsumerCppConsole.vcxproj 来添加!

...
<ItemGroup>
<ProjectReference Include="..\CppWinrtComponent\CppWinrtComponent.vcxproj">
<Project>{184b97de-746b-47d4-b055-d18f24b57dba}</Project>
</ProjectReference>
</ItemGroup>
...

3. 使用 Nuget 包管理器给该项目安装 Microsoft.VCRTForwarders.140

因为 CppWinrtComponent 是用 C++ 代码编写,必不可少会用到 VC 的运行时库,但是默认情况下,CppWinrtComponent.vcxproj 是一个 Universal Windows 的项目,所以它链接的 VC 运行时库都是 Store 版本,Store 版本 VC 运行时库不随 Windows 分发,所以我们的 Desktop 应用在运行时找不到这些 VC 运行时库。

Microsoft.VCRTForwarders.140 的原理是把所有的 Store 版本的 VC 运行时 API 全部链接到 Desktop 版本。组件如其名,就是一个 Forwarder!

如果 CppWinrtComponent.vcxproj 中的 DesktopCompatible 被打开,则此步骤3不再必要。

4. 调用 CppWinrtComponent 组件

CppWinrt.exe 会解析 CppWinrtComponent.winmd 生成头文件 winrt/CppWinrtComponent.h, 通过该头文件,就可以调用组件 CppWinrtComponent 了。

C++/WinRT UWP

C++/WinRT 有一个 VS 的拓展包,提供了几种基于 C++/WinRT 的项目模板。(https://marketplace.visualstudio.com/items?itemName=CppWinRTTeam.cppwinrt101804264)

用模板创建一个 UWP 项目 ConsumerCppWinrtUWP.vcproj。

1. 添加对项目 CppWinrtComponent 生成的工程引用

因为 ConsumerCppWinrtUWP 和 CppWinrtComponent 都是 Universal Windows 项目,所以可以直接通过 VS 添加对 CppWinrtComponent 的工程引用,CppWinrtComponent 输出的 CppWinrtComponent.winmd 和 CppWinrtComponent.dll 会被复制到 ConsumerCppWinrtUWP 的生成目录。

2. 调用 CppWinrtComponent 组件

CppWinrt.exe 会解析 CppWinrtComponent.winmd 生成头文件 winrt/CppWinrtComponent.h, 通过该头文件,就可以调用组件 CppWinrtComponent 了。

.Net Framework Desktop

在 VS 里面创建一个 .Net Framework console 项目 ConsumerNetFrameworkConsole.csproj,对于使用 Winrt 组件而言,基于.Net Framework 的 WPF 和 WinFrom 应用与 Console 应用没有任何区别。

值得注意的是,微软在 .Net Framework 4.5引入了对 WinRT 的支持。在 .Net5 中移除了对 WinRT 的原生支持。

1. 添加对项目 CppWinrtComponent 生成的工程引用

2. 创建 Reg Free Manimest 文件

C++ WinRT 组件实际是一种进程内(In Process)COM 服务器,使用之前需要向 OS 注册。在 UWP 应用中,Package Manifest 里面可以声明所有用到的 WinRT 组件,在运行时或者安装时,OS 可以根据此文件完成 COM 服务器的注册调用(此处具体细节暂时不清楚,但大致原理应该是这样)。

同样的,针对桌面应用也可以生成一个 manifest 文件,用来描述 WinRT 组件,该文件会被嵌入到桌面应用生成的可执行文件中,在运行时帮助我们完成对 WinRT 组件的加载(COM调用)。

该文件结构如下:

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!--名称 MyApplication.app 无关紧要,确保唯一就可以-->
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<!-- WinRT 组件的 DLL 名称 -->
<file name="CppWinrtComponent.dll">
<!-- 线程模型默认 both(代表 STA 和 MTA),name 是在 WinRT 组件中定义的 "命名空间.接口名称" -->
<activatableClass
name="CppWinrtComponent.Class"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1" />
</file>
</assembly>

可以将该文件任意命名,此处命名为 app.manifest, 然后把它添加到 ConsumerNetFrameworkConsole.csproj 中,为避免出错,选择手动编辑该项目文件,加入如下节点:

<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>

最后编译项目 ConsumerNetFrameworkConsole.csproj,该文件会被嵌入到 ConsumerNetFrameworkConsole.exe 中。所以在分发 ConsumerNetFrameworkConsole.exe 的时候,并不需要将 manifest 文件一起分发。

3. 使用 Nuget 包管理器给该项目安装 Microsoft.VCRTForwarders.140

参考 C++ Desktop 步骤3。

4. 调用 CppWinrtComponent 组件

因为添加了对项目 CppWinrtComponent 的引用,VS 可以从 CppWinrtComponent.winmd 中读取到接口的元数据信息,所以和普通的 .Net 对象一样,可以通过命名空间、类名访问到该 WinRT 组件。

.Net5 Desktop

在 VS 里面创建一个 .Net5 Console 项目 ConsumerNet5Console.csproj。

微软把 WinRT 的支持从 .Net5 中拿掉后,创建了项目 C#/WinRT, 它包含一个 cswinrt.exe, 可以解析 WinRT 组件的 wimmd 文件并创建相应的 C# 代码,这些代码是关于如果通过 COM 的方式访问 WinRT 组件。这些代码和 C++ Desktop 中 CppWinRT 生成的代码非常一致,几乎就是同一份代码 C# 改写版。实际上 C#/WinRT 和 CppWinRT 是用相同的解析器来解析 winmd 文件的。 详情可参考微软开源项目 xlang

1. 创建 .Net5 Library CppWinrtComponentProjection.csproj

因为 C#/WinRT 会创建用来访问 WinRT 组件的代码,为了方便使用这些代码,在这儿创建一个单独的 .Net5 Library 来包装这些代码。

实际上可以略过这一步,直接在项目 ConsumerNet5Console.csproj 上执行以下步骤!

2. 使用 Nuget 包管理器给 CppWinrtComponentProjection.csproj 安装 Microsoft.Windows.CsWinRT

3. 给 CppWinrtComponentProjection.csproj 添加项目 CppWinrtComponent.csproj 的工程引用

C#/WinRT 会检查项目 CppWinrtComponentProjection.csproj 依赖的所有 WinRT 组件,并根据步骤4中指定的 WinRT 组件中的命名空间,为其创建C#访问代码。

4. 添加CsWinRTIncludes

C#/WinRT 会读取该项目属性,它指定了引入的 WinRT 组件中的命名空间,这样才能为这个命名空间生成 C# 访问代码。

需要手动在 CppWinrtComponentProjection.csproj 中添加如下节点:

<PropertyGroup>
<CsWinRTIncludes>CppWinrtComponent</CsWinRTIncludes>
</PropertyGroup>

5. 编译CppWinrtComponentProjection.csproj

编译这个 Library, 在该项目 Obj 目录 obj\x64\Debug\net5.0-windows10.0.19041.0\Generated Files\ 会生成如下四个文件:

  • CppWinrtComponent.cs
  • WinRT.cs
  • WinRT_Interop.cs
  • WinRTEventHelpers.cs

它们就是用来访问 WinRT 组件 CppWinrtComponent 的全部 C# 代码! 同时它们也会被编译成一个 .Net5 Library CppWinrtComponentProjection, 这个 Library 就是所谓的 CppWinrtComponent 的 Projection。

6. 给ConsumerNet5Console.csproj 添加工程引用 CppWinrtComponentProjection.csproj

因为最终需要在 ConsumerNet5Console.csproj 使用 WinRT 组件,所以加入对 projection 项目的引用,该引用是一个标准的 .Net5 Library。

7. 复制 CppWinrtComponent.dll 到输出项目ConsumerNet5Console的输出目录

前6步所作的工作只是用来调用 WinRT 组件,真正的组件服务器是 CppWinrtComponent.Dll,它会在运行时被加载到 ConsumerNet5Console 的进程中。

8. 使用 Nuget 包管理器给该项目 ConsumerNet5Console.csproj 安装 Microsoft.VCRTForwarders.140

9. 调用 CppWinrtComponent 组件

添加完对 CppWinrtComponentProjection 的引用后,就可以直接用 命名空间.类名 访问WinRT 组件 CppWinrtComponent。

示例代码

参考

使用 C++ WinRT 组件的更多相关文章

  1. [UWP] 为WinRT组件创建Nuget包

    Nuget 是 dotnet 开发中必不可少的包管理工具,但不仅仅局限于 dotnet 项目,在 VS 中使用 C++ 开发的时候,也可以使用 Nuget 来引用第三方组件.同样也可以用 Nuget ...

  2. 与WinRT组件进行操作

    1,原理: WinRT是一个新的类库,应用程序可以用它访问操作系统的功能. 在内部,WinRT以组件的形式实现.COM Component Object Model- WinRT使用.net元数据来描 ...

  3. winrt组件库(包括翻书组件)

    http://www.mindscapehq.com/products/metroelements/controls/book-control-for-winrt 点击“down free trial ...

  4. Win10系列:VC++调用自定义组件3

    (3)C++/CX调用WinRT组件 在解决方案资源管理器中右键点击解决方案图标,选择添加一个Visual C++的Windows应用商店的空白应用程序项目,并命名为FileCPP.接着右键点击Fil ...

  5. Win10系列:VC++调用自定义组件2

    (2)C#调用WinRT组件 在解决方案资源管理器中右键点击解决方案图标,选择添加一个Visual C#的Windows应用商店的空白应用程序项目,并命名为FileCS.接着右键点击FileCS项目的 ...

  6. Win10系列:VC++调用自定义组件1

    通过20.9.1小节中的代码和步骤编写了一个名为"FilePickerComponent"的WinRT组件,接下来将在上一小节所新建的项目基础上,继续介绍如何在不同的语言所编写的应 ...

  7. 将 .NET 任务作为 WinRT 异步操作公开

    转自:http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/06/22/net-winrt.aspx 在博文深入探究 Await 和 WinRT ...

  8. windows RT开发笔记:WinRT DLL及其调用研究

    一. 几个概念: WinRT : Windows Runtime, windows运行时.创建Windows运行时(WinRT)是为了在Windows上给用户提供一种流畅且安全的应用体验.WinRT会 ...

  9. C#与C++的发展历程第三 - C#5.0异步编程巅峰

    系列文章目录 1. C#与C++的发展历程第一 - 由C#3.0起 2. C#与C++的发展历程第二 - C#4.0再接再厉 3. C#与C++的发展历程第三 - C#5.0异步编程的巅峰 C#5.0 ...

随机推荐

  1. 上手 Raspberry Pi Pico

    什么是 PICO Raspberry Pi Pico 是树莓派推出的一块基于 Arm Cortex-M0+ 内核的 MCU 的开发板,使用的 MCU 是树莓派自己研发的 Arm Cortex-M0+ ...

  2. Unity中各种查找物体的方法

    本文转自博主:Teng的世界 https://blog.csdn.net/teng_ontheway/article/details/47188141 GameObject.Find().Transf ...

  3. MobileNet系列之MobileNet_v2

    ​ MobileNet系列之MobileNet_v1 Inception系列之Inception_v1 Inception系列之Batch Normalization Inception系列之Ince ...

  4. Java Set HashSet

    import java.util.HashSet; import java.util.Set; /** Set存储特点:数据无序.不可重复 Set接口的实现类: HashSet:Set接口的主要实现类 ...

  5. Python 绘制词云

    文本内容:data(包含很多条文本) 1.分词: import jieba data_cut = data.apply(jieba.lcut) 2.去除停用词: stoplist.txt:链接:htt ...

  6. 8 shell if else

    if 语句的判断条件,从本质上讲,判断的就是命令的退出状态. 语句 语句格式 同一行书写 注意点 用例1 用例2 if 语句 if  conditionthen  statement(s)fi if  ...

  7. 2021/2/5 关于new的一个教训

    千万不要在类构造函数的初始化里new任何东西,这会导致析构函数delete一个野指针!构造函数一定要把所有的指针初始化为nullptr! 以下代码会报错(堆内存崩溃): Integer::Intege ...

  8. MySQL | Xtrabackup 安装

    rpm方式安装 # xtrabackup 2.4.8 安装包 wget https://www.percona.com/downloads/XtraBackup/Percona-XtraBackup- ...

  9. Arduino IDE 2.0 beta安装

    1.在官网(Software | Arduino)下载安装包,此次提供操作系统有:Windows.Linux和macOC系统 2.点击安装包进行安装 3.点击我同意 4.点击下一步 5.选择安装路径( ...

  10. 二、从GitHub浏览Prism示例代码的方式入门WPF下的Prism之Modules的几种加载方式

    这一篇梳理Prism中07示例Module的几种加载方式. 07示例分为了5个,有5种不同的Module加载方式. 我们开始学习加载Modules 观察07-Modules-Appconfig示例 分 ...