创建 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. 将Winform和wpf的界面转换为CPF代码用来实现跨平台

    CPF的设计器里带界面代码转换功能,将运行中的Winform或者wpf的程序界面转换为cpf代码,主要转换控件类型和布局,默认支持的是常用的原生控件.不支持Netcore,只支持.Netframewo ...

  2. Django-Auth模块之auth_user表

    一.Auth模块之auth_user表 在创建Django项目之后直接执行数据迁移命令会自动生成许多表. Django在启动之后就可以直接访问admin路由,需要输入用户名和密码,数据参考的就是aut ...

  3. 案例 | 腾讯广告 AMS 的容器化之路

    作者 张煜,15年加入腾讯并从事腾讯广告维护工作.20年开始引导腾讯广告技术团队接入公司的TKEx-teg,从业务的日常痛点并结合腾讯云原生特性来完善腾讯广告自有的容器化解决方案 项目背景 腾讯广告承 ...

  4. Xmanager6 企业版安装

    Xmanager6 企业版安装 链接:https://pan.baidu.com/s/1QZOD0iPd4WbVHBVXIbJ-fw 提取码:ebkl 一.安装教程 1.1 下载解压,双击安装exe主 ...

  5. C# 实现复制Excel内容到DataGridview中

    业务要求:复制:将Excel内容复制到datagridview中 最终效果:复制Excel内容,点击datagridview中的某个单元格,顺着这个单元格自动填充自动增加行.偷懒了,没写填充在选择哪些 ...

  6. 两台主机间docker容器网络互通

    服务器1: 网络172.30.0.0/16 服务器2: 网络172.31.0.0/16 服务器1和服务器2上的docker容器网络之间是无法互通的,如果需要互通,需要做以下配置: 服务器1上执行: i ...

  7. MySQL忘记密码怎么办-MySQL修改密码(亲测可用)

    前言: 最近要用到本地的MySQL,结果把密码忘记了. ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using pas ...

  8. Java:HttpPost 传输Json数据过长使用HttpServletRequest解析

    直接上代码 /** * 测试生成json数据 */ @Test public void synYxGoodsInfoTest() { try { String url = "http://1 ...

  9. Sentinel流控与熔断

    参考: https://thinkwon.blog.csdn.net/article/details/103770879 项目结构 com.guo     ├── guo-sentinel       ...

  10. SpringMVC(5)数据绑定-2

    在SpringMVC(4)数据绑定-1中我们介绍了如何用@RequestParam来绑定数据,下面我们来看一下其它几个数据绑定注解的使用方法. 1.@PathVariable 用来绑定URL模板变量值 ...