创建 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. 浏览器中js怎么将图片下载而不是直接打开

    网上找了好多方法都是不能用的,经过试验在Chrome中都是直接打开. 经过自己的摸索,找到了一套能用的解决方案 var database = "data:image/jpg;base64,& ...

  2. JUL 日志框架

    1.JUL 简介 JUL 全称 Java Util Logging,位于java.util.logging.Logger 包.它是 java 原生的日志框架,使用时无需另外引用第三方的类库,相对其他的 ...

  3. DDoS防护方式以及产品

    导航: 这里将一个案例事项按照流程进行了整合,这样查看起来比较清晰.部分资料来自于Cloudflare 1.DDoS介绍 2.常用DDoS攻击 3.DDoS防护方式以及产品 4.Cloudflare ...

  4. 流程自动化RPA,Power Automate Desktop系列 - 创建WPF程序安装包及升级包

    一.背景 之前写过的几个WPF小工具,每次发布都需要给它打安装包和升级包,涉及到一些系列繁琐的手工操作,有了Power Automate Desktop,于是便寻思着能不能做成一个自动化的流来使用. ...

  5. Java程序设计(2021春)——第一章续笔记与思考

    Java程序设计(2021春)--第一章续笔记与思考 目录 Java程序设计(2021春)--第一章续笔记与思考 Java数据类型 基本数据类型 引用类型 基本数据类型--整数类型的细节 基本数据类型 ...

  6. spring 5 webflux异常处理

    序 本文主要研究一下spring 5 webflux的异常处理 maven <dependency> <groupId>org.springframework.boot< ...

  7. 22 shell组命令与子进程

    1.组命令 2.子进程 2.1 什么是子进程 2.2 创建子进程 2.3 子进程总结 3.如何检测子shell与子进程 1.组命令 组命令,就是将多个命令划分为一组,或者看成一个整体. 用法 区别 S ...

  8. PHP常见的设计模式

    设计模式六大原则 开放封闭原则:一个软件实体如类.模块和函数应该对扩展开放,对修改关闭. 里氏替换原则:所有引用基类的地方必须能透明地使用其子类的对象. 依赖倒置原则:高层模块不应该依赖低层模块,二者 ...

  9. XCTF 进阶区 CAT

    这题脑洞是真的大,讲道理 看到这个,先尝试了一下命令拼接,发现字符被过滤了应该.fuzz一下看看,有哪些字符还没被过滤了 import requests dictory=["!", ...

  10. 打开设置windows10内置linux功能-启用linux子系统

    第一步设置开发者模式 步骤:windows+s打开娜娜,输入设置,并点击. 点击更新与安全 点击开发者选项,选择开发者模型,弹出的对话框选确定之后等待安装完毕. 第二步:安装linux 点击确定后等待 ...