C++/WinRT 入门

Important

有关为 C++/WinRT 开发设置Visual Studio(包括安装和使用 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++>语言>一致性模式:否(如果 /permissive- 出现在项目属性 C/C++>语言>命令行中的“其他选项”下,请将其删除)。

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>

使用默认项目设置时,包含的头文件来自位于文件夹 %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt 中的 Windows SDK。 Visual Studio在其 IncludePath 宏中包含该路径。 但是,Windows SDK 没有严格的依赖关系,因为项目(通过cppwinrt.exe工具)将相同的标头生成到项目的 $(GeneratedFilesDir) 文件夹中。 如果无法在其他位置找到这些文件夹,或者更改项目设置,则会从该文件夹中加载它们。

这些头文件包含投射到 C++/WinRT 的 Windows API。 换句话说,对于每种 Windows 类型,C++/WinRT 都定义了一个对 C++ 友好的对应类型(称为投影类型)。 投影类型与 Windows 类型具有相同的完全限定名,但它位于 C++ winrt 命名空间中。 将这些 #include 指令放入预编译头中可缩短增量生成时间。

Important

每当想要从Windows命名空间中使用类型时,都必须#include使用相应的 C++/WinRT Windows命名空间头文件,如上所示。 相应的标头是与类型命名空间同名的标头。 例如,若要对 Windows::Foundation::Collections::P ropertySet 运行时类使用 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 模板函数枚举 Items。 每当像这样遍历 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 Desktop>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++ 语言标准设置为 ISO C++17 标准版(/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 总括库。 本部分介绍满足链接器要求的三种方法。

第一个选项是添加到Visual Studio项目的所有 C++/WinRT MSBuild 属性和目标。 为此,请在项目中安装Microsoft.Windows.CppWinRT NuGet 包。 在 Visual Studio 中打开该项目,单击 Project>管理 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