C++/WinRT 是 Windows 运行时 (WinRT) API 的完全标准新式 C++17 语言实现,以基于头文件的库的形式实现,旨在为你提供对新式 Windows API 的一流访问。 利用 C++/WinRT,你可以采用任何符合标准的 C++17 编译器创作和使用 Windows 运行时 API。 Windows SDK 包含 C++/WinRT;它已在版本 10.0.17134.0(Windows 10,版本 1803)中引用。
C++ 编程语言适用于企业和独立软件供应商 (ISV) 行业中重视高水平的正确性、质量和性能的应用场合。 例如:系统编程;资源受限的嵌入式和移动系统;游戏和图形;设备驱动程序;以及工业、科学和医疗应用,等等。
从语言的角度来说,C++ 一直专注于创作和使用类型丰富且轻量的抽象。 但是,由于原始的指针、原始的循环、耗时耗力的内存分配以及 C++98 的发布,该语言已经发生了根本性的改变。 新式 C++(从 C++11 起)可以清楚地表达想法,具有简便性和可读性并且引入 bug 可能性低得多。
若要通过 C++ 创作和使用 Windows API,可使用 C++/WinRT。 这是 Microsoft 推荐的用于替代 C++/CX 语言实现和 Windows 运行时 C++ 模板库 (WRL) 的替代品。
当采用 C++/WinRT 时,你将使用标准 C++ 数据类型、算法和关键字。 该实现确实有自己的自定义数据类型,但在大多数情况下,你无需了解它们,因为它们将提供到/自标准类型的相应转换。 这样,你就可以继续使用已用惯的标准 C++ 语言功能和已拥有的源代码。 通过 C++/WinRT,可在任何 C++ 应用程序(从 Win32 到 Windows AppSDK,再到 UWP)中非常轻松地调用 Windows API。
与适用于 Windows 运行时的任何其他语言选择相比,C++/WinRT 的表现更好,生成的二进制文件更小。 它的表现甚至超过了直接使用 ABI 接口手动编写的代码。 这是因为抽象使用了 Visual C++ 编译器能够优化的新式 C++ 习惯用语。 这包括神奇静态变量、空基类、strlen 删除以及最新版本的 Visual C++ 中的很多专门用于改善 C++/WinRT 的性能的更新的优化。
可以通过多种方式将 C++/WinRT 逐步引入项目。 可以使用 Windows 运行时组件,也可以通过 C++/CX 进行互操作。
我们建议使用最新版本的 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++”>“语言”>“命令行”中,请将其删除)。
入门案例
创建一个新的 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>
/*
包含的头文件采用默认项目设置,来自 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。 但你不应依赖此行为,因为它是一个随时间推移而变化的实现细节。 必须显式包含所需的任何头文件。
*/
// main.cpp
#include "pch.h"
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Syndication;
/*
using namespace 指令是可选的,不过这种指令很方便。 上方显示的此类指令的模式(允许查找 winrt 命名空间中任何项目的非限定名称)适用于当你开始新项目且 C++/WinRT 是你在该项目内使用的唯一语言投影的情况。
*/
int main()
{
// 调用 winrt::init_apartment 会初始化 Windows 运行时中(默认在多线程单元中)的线程。
// 该调用还会初始化 COM。
winrt::init_apartment();
// 堆栈分配两个对象:它们表示 Windows 博客的 URI 和联合客户端。
// 我们将使用具有简单的宽字符串参数的 uri;
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)");
// SyndicationClient::RetrieveFeedAsync 是异步 Windows 运行时函数的示例。
// 该代码示例将接收来自 RetrieveFeedAsync 的异步操作对象,然后对该对象调用 get 以阻止调用
// 线程并等待结果(在此例中为联合源)。
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;
}
}