C++/WinRT 入门

重要

有关设置 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.hmain.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.hstdafx.h。 将该名称重命名为 pch.h. 如果你有一个 stdafx.cpp 文件,请将该文件重命名为 pch.cpp。 将项目属性 C/C++预编译标头预编译标头 设置为 创建(/Yc),并将预编译头文件 设置为 pch.h

查找并将所有 #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 框架进行互作。 本文档介绍了所有这些情况。 开始的好地方是 XAML 控件,将 C++/WinRT 属性 绑定到使用 C++/WinRT的 XAML 自定义(模板化)控件。

以 C++/WinRT 编写的示例应用

请参阅 “在哪里可以找到C++/WinRT 示例应用?

重要 API