重要
有关设置 Visual Studio 进行 C++/WinRT 开发的信息(包括安装和使用 C++/WinRT Visual Studio 扩展(VSIX)和 NuGet 包(它们同时提供项目模板和生成支持),请参阅 Visual Studio 对 C++/WinRT的支持。
若要快速使用 C++/WinRT,本主题将基于新的 Windows 控制台应用程序(C++/WinRT) 项目演练一个简单的代码示例。 本主题还演示如何向 Windows 桌面应用程序项目添加C++/WinRT 支持。
注释
虽然我们建议你使用最新版本的 Visual Studio 和 Windows SDK 进行开发, 如果你使用的是 Visual Studio 2017(版本 15.8.0 或更高版本),并且面向 Windows SDK 版本 10.0.17134.0(Windows 10 版本 1803),则新创建的 C++/WinRT 项目可能无法编译并出现错误“错误 C3861: 'from_abi': 找不到标识符“,并且存在源自 base.h 的其他错误。 解决方案是使用更新(更符合标准)的 Windows SDK 版本,或设置项目属性 C/C++>语言>一致性模式:否(另外,如果在项目属性 C/C++>> 中的 附加选项下出现 /permissive-,则将其删除)。
C++/WinRT 快速入门
创建一个新的 Windows 控制台应用程序(C++/WinRT) 项目。
修改 pch.h
和 main.cpp
使之如下所示。
// pch.h
#pragma once
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Web.Syndication.h>
#include <iostream>
// main.cpp
#include "pch.h"
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Syndication;
int main()
{
winrt::init_apartment();
Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
SyndicationClient syndicationClient;
syndicationClient.SetRequestHeader(L"User-Agent", L"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)");
SyndicationFeed syndicationFeed = syndicationClient.RetrieveFeedAsync(rssFeedUri).get();
for (const SyndicationItem syndicationItem : syndicationFeed.Items())
{
winrt::hstring titleAsHstring = syndicationItem.Title().Text();
// A workaround to remove the trademark symbol from the title string, because it causes issues in this case.
std::wstring titleAsStdWstring{ titleAsHstring.c_str() };
titleAsStdWstring.erase(remove(titleAsStdWstring.begin(), titleAsStdWstring.end(), L'™'), titleAsStdWstring.end());
titleAsHstring = titleAsStdWstring;
std::wcout << titleAsHstring.c_str() << std::endl;
}
}
让我们逐条取下上面的简短代码示例,并说明每个部分发生的情况。
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Web.Syndication.h>
使用默认项目设置时,包含的标头来自 Windows SDK,位于文件夹%WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt
中。 Visual Studio 在其 IncludePath 宏中包含该路径。 但是,Windows SDK 没有严格的依赖关系,因为项目(通过 cppwinrt.exe
工具)将相同的标头生成到项目的 $(GeneratedFilesDir) 文件夹中。 如果无法在其他位置找到这些文件夹,或者更改项目设置,则会从该文件夹中加载它们。
标题包含映射到 C++/WinRT 的 Windows API。 换句话说,对于每个 Windows 类型,C++/WinRT 定义了适合 C++ 的等价类型(称为 投影类型)。 投影类型与 Windows 类型具有相同的完全限定名称,但它放置在 C++ winrt 命名空间中。 将这些内容放入预编译标头可减少增量构建时间。
重要
每当想要从 Windows 命名空间使用类型时,都必须 #include
相应的 C++/WinRT Windows 命名空间头文件,如上所示。 相应的 标头是与类型命名空间同名的标头。 例如,若要对 Windows::Foundation::Collections::PropertySet 运行时类使用 C++/WinRT 投影,请包含 winrt/Windows.Foundation.Collections.h
头文件。
C++/WinRT 投影标头通常自动包含相关的命名空间头文件。 例如, winrt/Windows.Foundation.Collections.h
包括 winrt/Windows.Foundation.h
. 但是你不应该依赖这种行为,因为这是一个随时间变化的实现细节。 必须显式包含所需的任何标头。
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Syndication;
using namespace
指令是可选的,但很方便。 上面针对此类指令显示的模式(允许对 winrt 命名空间中的任何内容进行非限定的名称查找)适用于开始新项目,C++/WinRT 是在该项目中使用的唯一语言投影。 另一方面,如果要将 C++/WinRT 代码与 C++/CX 和/或 SDK 应用程序二进制接口(ABI)代码混合使用(无论是从中移植,还是与其中一个或两个模型进行互操作),请参阅以下主题:C++/WinRT 与 C++/CX之间的互操作,从 C++/CX 迁移到 C++/WinRT,以及 C++/WinRT 与 ABI之间的互操作。
winrt::init_apartment();
调用 winrt::init_apartment 会在 Windows 运行时中初始化线程;默认情况下,它是在多线程单元中。 该调用还会初始化 COM。
Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
SyndicationClient syndicationClient;
在堆栈中分配两个对象:它们表示 Windows 博客的 URI 和一个分发客户端。 我们使用简单的宽字符字面量构造 URI(请参阅 C++/WinRT 中的 字符串处理,了解字符串操作的更多方法)。
SyndicationFeed syndicationFeed = syndicationClient.RetrieveFeedAsync(rssFeedUri).get();
SyndicationClient::RetrieveFeedAsync 是异步 Windows 运行时函数的示例。 代码示例从 RetrieveFeedAsync接收一个异步操作对象,并在该对象上调用 get 方法,以阻止调用线程并等待结果(在本例中是一个订阅源)。 有关并发和非阻塞技术的详细信息,请参阅 C++/WinRT 中的并发和异步操作。
for (const SyndicationItem syndicationItem : syndicationFeed.Items()) { ... }
SyndicationFeed.Items 是一个范围,由从 开始 返回的迭代器定义,结束 函数(或其常量、反向和常量反向变体)。 因此,您可以使用范围型 语句或 for
模板函数来枚举 项。 每当您遍历 Windows 运行时集合时,如下所示,您需要执行 #include <winrt/Windows.Foundation.Collections.h>
。
winrt::hstring titleAsHstring = syndicationItem.Title().Text();
// Omitted: there's a little bit of extra work here to remove the trademark symbol from the title text.
std::wcout << titleAsHstring.c_str() << std::endl;
获取提要的标题文本,以 winrt::hstring 对象形式(更多详细信息参见 C++/WinRT 中的 字符串处理)。 然后,通过 c_str 函数输出 hstring,该函数反映与C++标准库字符串一起使用的模式。
如你所看到的,C++/WinRT 鼓励新式和类式的C++表达式,例如 syndicationItem.Title().Text()
。 这是与传统 COM 编程不同的、更简洁的编程风格。 无需直接初始化 COM,也不需要使用 COM 指针。
也不需要处理 HRESULT 返回代码。 C++/WinRT 将错误 HRESULT 转换为异常,例如 winrt::hresult-error 和,以实现自然和现代的编程风格。 有关错误处理和代码示例的详细信息,请参阅 C++/WinRT 的错误处理。
修改 Windows 桌面应用程序项目以添加C++/WinRT 支持
某些桌面项目(例如 Visual Studio 中的 WinUI 3 模板)内置了 C++/WinRT 支持。
但本部分介绍如何向可能拥有的任何 Windows 桌面应用程序项目添加C++/WinRT 支持。 如果没有现有的 Windows 桌面应用程序项目,可以先创建一个项目,然后按照这些步骤作。 例如,打开 Visual Studio 并创建 Visual C++>Windows 桌面Windows 桌面>应用程序项目。
可以选择安装 C++/WinRT Visual Studio 扩展 (VSIX) 和 NuGet 包。 有关详细信息,请参阅 Visual Studio 对 C++/WinRT 的支持。
设置项目属性
转到项目属性“常规”“Windows SDK 版本”,然后选择“所有配置”和“所有平台”。> 确保 Windows SDK 版本 设置为 10.0.17134.0(Windows 10 版本 1803)或更高版本。
请确认您未受到为什么我的新项目无法编译?的问题影响。
由于 C++/WinRT 使用 C++17 标准中的功能,因此将项目属性 C/C++>Language>C++ Language Standard 设置为 ISO C++17 Standard(/std:c++17)。
预编译标头
默认项目模板会为你创建一个预编译标头,名字为framework.h
或stdafx.h
。 将该名称重命名为 pch.h
. 如果你有一个 stdafx.cpp
文件,请将该文件重命名为 pch.cpp
。 将项目属性
查找并将所有 #include "framework.h"
(或 #include "stdafx.h"
)替换为 #include "pch.h"
。
在 pch.h
中,包括 winrt/base.h
。
// pch.h
...
#include <winrt/base.h>
链接
C++/WinRT 语言投影依赖于某些独立的 Windows 运行时函数和入口点,这些函数需要链接到 WindowsApp.lib 伞库。 本部分介绍三种满足链接器的方式。
第一个选项是将所有C++/WinRT MSBuild 属性和目标添加到您的 Visual Studio 项目中。 为此,请将 Microsoft.Windows.CppWinRT NuGet 包 安装到项目中。 在 Visual Studio 中打开项目,单击“项目>管理 NuGet 包...”>浏览,在搜索框中键入或粘贴 Microsoft.Windows.CppWinRT,选择搜索结果中的项,然后单击 安装 安装该项目的包。
你还可以使用项目链接设置来显式链接 WindowsApp.lib
。 或者,您可以在源代码中(例如,在 pch.h
中)进行此操作。
#pragma comment(lib, "windowsapp")
现在可以编译和链接,并将 C++/WinRT 代码添加到项目(例如,类似于上面的 A C++/WinRT 快速入门 部分所示的代码)。
C++/WinRT 的三个主要方案
当您使用并熟悉 C++/WinRT,继续阅读其余文档内容时,您可能会注意到有三个主要场景,如以下部分所述。
使用 Windows API 和类型
换句话说, 使用或 调用 API。 例如,进行 API 调用以使用蓝牙进行通信;流式传输和演示视频;与 Windows shell 集成;等等。 C++/WinRT 完全且毫不妥协地支持此类方案。 有关详细信息,请参阅 通过 C++/WinRT调用 API。
编写 Windows API 和类型
换句话说,生成 API 和类型。 例如,生成上述部分所述的 API 类型;或图形 API;存储和文件系统 API;网络 API 等。 有关详细信息,请参阅 使用 C++/WinRT开发 API。
使用 C++/WinRT 创作 API 比使用 API 更为复杂,因为必须先使用 IDL 来定义 API 的形状,然后才能实现 API。 在 XAML 控件中执行该操作的演练:绑定到 C++/WinRT 属性。
XAML 应用程序
此方案与在 XAML UI 框架上生成应用程序和控件有关。 在 XAML 应用程序中工作相当于使用和创作的组合。 但是,由于 XAML 是当今 Windows 上占主导地位的 UI 框架,并且它对 Windows 运行时的影响与该框架成比例,因此它应具有自己的方案类别。
请注意,XAML 最适合支持反射功能的编程语言。 在 C++/WinRT 中,有时必须执行一些额外的工作才能与 XAML 框架进行互作。 本文档介绍了所有这些情况。 开始的好地方是
以 C++/WinRT 编写的示例应用
重要 API
- SyndicationClient.RetrieveFeedAsync 方法
- SyndicationFeed.Items 属性
- winrt::hstring 结构
- winrt::hresult-error 结构体
相关主题
- C++/CX
- 使用 C++/WinRT 进行错误处理
- C++/WinRT 与 C++/CX 之间的互操作
- C++/WinRT 与 ABI 之间的互操作
- 从 C++/CX 迁移到 C++/WinRT
- C++/WinRT 中的字符串处理