C++/WinRT 入门Get started with C++/WinRT

为帮助你快速掌握 C++/WinRT 的用法,本主题将会根据新的 Windows 控制台应用程序 (C++/WinRT) 项目演练一个简单的代码示例。To get you up to speed with using C++/WinRT, this topic walks through a simple code example based on a new Windows Console Application (C++/WinRT) project. 此外,本主题将会介绍如何将 C++/WinRT 支持添加到 Windows 桌面应用程序项目This topic also shows how to add C++/WinRT support to a Windows Desktop application project.

备注

我们建议使用最新版本的 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 的其他错误。While we recommend that you develop with the latest versions of Visual Studio and the Windows SDK, if you're using Visual Studio 2017 (version 15.8.0 or higher), and targeting the Windows SDK version 10.0.17134.0 (Windows 10, version 1803), then a newly created C++/WinRT project may fail to compile with the error "error C3861: 'from_abi': identifier not found", and with other errors originating in base.h. 解决方法是要么面向 Windows SDK 的更高(更相符)版本,要么设置项目属性“C/C++” > “语言” > “一致性模式: 否”(此外,如果 /permissive- 显示在“其他选项”下的项目属性“C/C++” > “语言” > “命令行”中,请将其删除)。The solution is to either target a later (more conformant) version of the Windows SDK, or set project property C/C++ > Language > Conformance mode: No (also, if /permissive- appears in project property C/C++ > Language > Command Line under Additional Options, then delete it).

C++/WinRT 快速入门A C++/WinRT quick-start

备注

有关设置 Visual Studio 以进行 C++/WinRT 部署的信息 — 包括安装和使用 C++/WinRT Visual Studio 扩展 (VSIX) 和 NuGet 包(两者共同提供项目模板,并生成支持)的信息 — 请参阅适用于 C++/WinRT 的 Visual Studio 支持For info about setting up Visual Studio for C++/WinRT development—including installing and using the C++/WinRT Visual Studio Extension (VSIX) and the NuGet package (which together provide project template and build support)—see Visual Studio support for C++/WinRT.

创建一个新的 Windows 控制台应用程序(C++/WinRT) 项目。Create a new Windows Console Application (C++/WinRT) project.

按如下所示编辑 pch.hmain.cppEdit pch.h and main.cpp to look like this.

// 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;
    SyndicationFeed syndicationFeed = syndicationClient.RetrieveFeedAsync(rssFeedUri).get();
    for (const SyndicationItem syndicationItem : syndicationFeed.Items())
    {
        winrt::hstring titleAsHstring = syndicationItem.Title().Text();
        std::wcout << titleAsHstring.c_str() << std::endl;
    }
}

我们将分析上述简短代码示例的每个片段,并解释每个部分的作用。Let's take the short code example above piece by piece, and explain what's going on in each part.

#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Web.Syndication.h>

包含的标头采用默认项目设置,来自 Windows SDK 的 %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt 文件夹。With the default project settings, the included headers come from the Windows SDK, inside the folder%WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt. Visual Studio 将该路径包含在其 IncludePath 宏中。Visual Studio includes that path in its IncludePath macro. 但这些标头并不严格依赖于 Windows SDK,因为项目会(通过 cppwinrt.exe 工具)在项目的 $(GeneratedFilesDir) 文件夹中生成与此相同的标头。But there's no strict dependency on the Windows SDK, because your project (via the cppwinrt.exe tool) generates those same headers into your project's $(GeneratedFilesDir) folder. 如果在其他位置找不到这些标头,或者你更改了项目设置,则会从该文件夹中加载这些标头。They'll be loaded from that folder if they can't be found elsewhere, or if you change your project settings.

这些标头包含投影到 C++/WinRT 的 Windows API。The headers contain Windows APIs projected into C++/WinRT. 换言之,对于每个 Windows 类型,C++/WinRT 都会定义 C++ 友好等效项(称为“投影类型”)。In other words, for each Windows type, C++/WinRT defines a C++-friendly equivalent (called the projected type). 投影类型具有与 Windows 类型相同的完全限定名称,但放置于 C++ winrt 命名空间中。A projected type has the same fully-qualified name as the Windows type, but it's placed in the C++ winrt namespace. 将这些内容放置在预编译标头中将减少增量生成时间。Putting these includes in your precompiled header reduces incremental build times.

重要

如果希望使用来自 Windows 命名空间的类型,必须 #include 对应的 C++/WinRT Windows 命名空间标头文件,如上所示。Whenever you want to use a type from a Windows namespaces, you must #include the corresponding C++/WinRT Windows namespace header file, as shown above. 对应的标头是与该类型的命名空间具有相同名称的标头。The corresponding header is the one with the same name as the type's namespace. 例如,要为 Windows::Foundation::Collections::PropertySet 运行时类使用 C++/WinRT 投影,则应包含 winrt/Windows.Foundation.Collections.h 标头。For example, to use the C++/WinRT projection for the Windows::Foundation::Collections::PropertySet runtime class, include the winrt/Windows.Foundation.Collections.h header.

C++/WinRT 投影标头通常自动包含其父命名空间头文件。It's usual for a C++/WinRT projection header to automatically include its parent namespace header file. 例如,winrt/Windows.Foundation.Collections.h 包含 winrt/Windows.Foundation.hSo, for example, winrt/Windows.Foundation.Collections.h includes winrt/Windows.Foundation.h. 但你不应依赖此行为,因为它是一个随时间推移而变化的实现细节。But you shouldn't rely on this behavior, since it's an implementation detail that changes over time. 必须显式包含所需的任何标头。You must explicitly include any headers that you need.

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Syndication;

using namespace 指令是可选的,不过这种指令很方便。The using namespace directives are optional, but convenient. 上方显示的此类指令的模式(允许查找 winrt 命名空间中任何项目的非限定名称)适用于当你开始新项目且 C++/WinRT 是你在该项目内使用的唯一语言投影的情况。The pattern shown above for such directives (allowing unqualified name lookup for anything in the winrt namespace) is suitable for when you're beginning a new project and C++/WinRT is the only language projection you're using inside of that project. 另一方面,如果你在将 C++/WinRT 代码与 C++/CX 和/或 SDK 应用程序二进制接口 (ABI) 代码混合(从其移植或与其互操作,其中一个模型或全部两个模型),则请参阅主题实现 C++/WinRT 与 C++/CX 之间的互操作从 C++/CX 移动到 C++/WinRT实现 C++/WinRT 与 ABI 之间的互操作If, on the other hand, you're mixing C++/WinRT code with C++/CX and/or SDK application binary interface (ABI) code (you're either porting from, or interoperating with, one or both of those models), then see the topics Interop between C++/WinRT and C++/CX, Move to C++/WinRT from C++/CX, and Interop between C++/WinRT and the ABI.

winrt::init_apartment();

调用 winrt::init_apartment 会初始化 Windows 运行时中(默认在多线程单元中)的线程。The call to winrt::init_apartment initializes the thread in the Windows Runtime; by default, in a multithreaded apartment. 该调用还会初始化 COM。The call also initializes COM.

Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
SyndicationClient syndicationClient;

堆栈分配两个对象:它们表示 Windows 博客的 URI 和联合客户端。Stack-allocate two objects: they represent the uri of the Windows blog, and a syndication client. 我们将使用具有简单的宽字符串参数的 uri(请参阅 C++/WinRT 中的字符串处理了解使用字符串的更多方法)。We construct the uri with a simple wide string literal (see String handling in C++/WinRT for more ways you can work with strings).

SyndicationFeed syndicationFeed = syndicationClient.RetrieveFeedAsync(rssFeedUri).get();

SyndicationClient::RetrieveFeedAsync 是异步 Windows 运行时函数的示例。SyndicationClient::RetrieveFeedAsync is an example of an asynchronous Windows Runtime function. 该代码示例将接收来自 RetrieveFeedAsync 的异步操作对象,然后对该对象调用 get 以阻止调用线程并等待结果(在此例中为联合源)。The code example receives an asynchronous operation object from RetrieveFeedAsync, and it calls get on that object to block the calling thread and wait for the result (which is a syndication feed, in this case). 要获得有关并发的详细信息和了解非阻止性技术,请参阅 C++/WinRT 的并发和异步操作For more about concurrency, and for non-blocking techniques, see Concurrency and asynchronous operations with C++/WinRT.

for (const SyndicationItem syndicationItem : syndicationFeed.Items()) { ... }

SyndicationFeed.Items 是一个范围,由从 beginend 函数(或其常量、反向和常量-反向变体)返回的迭代程序定义。SyndicationFeed.Items is a range, defined by the iterators returned from begin and end functions (or their constant, reverse, and constant-reverse variants). 因此,可以使用基于范围的 for 语句或使用 std::for_each 模板函数枚举Because of this, you can enumerate Items with either a range-based for statement, or with the std::for_each template function. 循环访问此类 Windows 运行时集合时,需要指定 #include <winrt/Windows.Foundation.Collections.h>Whenever you iterate over a Windows Runtime collection like this, you'll need to #include <winrt/Windows.Foundation.Collections.h>.

winrt::hstring titleAsHstring = syndicationItem.Title().Text();
std::wcout << titleAsHstring.c_str() << std::endl;

获取源的标题文本以作为 winrt::hstring 对象(有关更多详细信息,请参阅 C++/WinRT 中的字符串处理)。Gets the feed's title text, as a winrt::hstring object (more details in String handling in C++/WinRT). 然后,hstring 通过 c_str 函数输出,这反映使用 C++ 标准库字符串的模式。The hstring is then output, via the c_str function, which reflects the pattern used with C++ Standard Library strings.

可以看到,C++/WinRT 鼓励使用类似于类的新式 C++ 表达式,例如 syndicationItem.Title().Text()As you can see, C++/WinRT encourages modern, and class-like, C++ expressions such as syndicationItem.Title().Text(). 这是与传统的 COM 编程不同的更简洁的编程风格。This is a different, and cleaner, programming style from traditional COM programming. 无需直接初始化 COM,也无需处理 COM 指针。You don't need to directly initialize COM, nor work with COM pointers.

也不需要处理 HRESULT 返回代码。Nor do you need to handle HRESULT return codes. C++/WinRT 会将错误 HRESULT 转换为异常(如 winrt::hresult-error)以实现自然、现代化的编程风格。C++/WinRT converts error HRESULTs to exceptions such as winrt::hresult-error for a natural and modern programming style. 有关错误处理以及代码示例的详细信息,请参阅 C++/WinRT 的错误处理For more info about error-handling, and code examples, see Error handling with C++/WinRT.

修改 Windows 桌面应用程序项目以添加 C++/WinRT 支持Modify a Windows Desktop application project to add C++/WinRT support

本部分介绍如何将 C++/WinRT 支持添加到 Windows 桌面应用程序项目。This section shows you how you can add C++/WinRT support to a Windows Desktop application project that you might have. 如果你没有 Windows 桌面应用程序项目,可以先遵循以下步骤创建一个。If you don't have an existing Windows Desktop application project, then you can follow along with these steps by first creating one. 例如,打开 Visual Studio 并选择“Visual C++”>“Windows 桌面”>“Windows 桌面应用程序”来创建一个项目。For example, open Visual Studio and create a Visual C++ > Windows Desktop > Windows Desktop Application project.

可以选择性地安装 C++/WinRT Visual Studio 扩展 (VSIX) 和 NuGet 包。You can optionally install the C++/WinRT Visual Studio Extension (VSIX) and the NuGet package. 有关详细信息,请参阅 C++/WinRT 的 Visual Studio 支持For details, see Visual Studio support for C++/WinRT.

设置项目属性Set project properties

转到项目属性“常规”>“Windows SDK 版本”,然后选择“所有配置”和“所有平台”。Go to project property General > Windows SDK Version, and select All Configurations and All Platforms. 确保“Windows SDK 版本”设置为 10.0.17134.0(Windows 10 版本 1803)或更高。Ensure that Windows SDK Version is set to 10.0.17134.0 (Windows 10, version 1803) or greater.

确认你没有遇到为何我的新项目不能编译?的问题。Confirm that you're not affected by Why won't my new project compile?.

由于 C++/WinRT 使用 C++17 标准版中的功能,请将项目属性“C/C++” > “语言” > “C++ 语言标准版”设置为“ISO C++17 标准版(/std:c++17)”。Because C++/WinRT uses features from the C++17 standard, set project property C/C++ > Language > C++ Language Standard to ISO C++17 Standard (/std:c++17).

预编译的标头The precompiled header

默认项目模板将为你创建名为 framework.hstdafx.h 的预编译标头。The default project template creates a precompiled header for you, named either framework.h, or stdafx.h. 请将它重命名为 pch.hRename that to pch.h. 如果已有一个 stdafx.cpp 文件,请将它重命名为 pch.cppIf you have a stdafx.cpp file, then rename that to pch.cpp. 将项目属性“C/C++” > “预编译标头” > “预编译标头”设置为“创建(/Yc)”,将“预编译标头文件”设置为“pch.h”。Set project property C/C++ > Precompiled Headers > Precompiled Header to Create (/Yc), and Precompiled Header File to pch.h.

查找所有 #include "framework.h"(或 #include "stdafx.h")并将其替换为 #include "pch.h"Find and replace all #include "framework.h" (or #include "stdafx.h") with #include "pch.h".

pch.h 中包含 winrt/base.hIn pch.h, include winrt/base.h.

// pch.h
...
#include <winrt/base.h>

链接Linking

C++/WinRT 语言投影依赖于某些 Windows 运行时自由(非成员)函数和入口点,需要链接到 WindowsApp.lib 伞型库。The C++/WinRT language projection depends on certain Windows Runtime free (non-member) functions, and entry points, that require linking to the WindowsApp.lib umbrella library. 本部分介绍满足链接器要求的三种方式。This section describes three ways of satisfying the linker.

第一种做法是将所有 C++/WinRT MSBuild 属性和目标添加到 Visual Studio 项目。The first option is to add to your Visual Studio project all of the C++/WinRT MSBuild properties and targets. 为此,请在项目中安装 Microsoft.Windows.CppWinRT NuGet 包To do this, install the Microsoft.Windows.CppWinRT NuGet package into your project. 在 Visual Studio 中打开项目,然后单击“项目”>“管理 NuGet 包...” >“浏览”,在搜索框中键入或粘贴 Microsoft.Windows.CppWinRT,在搜索结果中选择该项,然后单击“安装”以安装该项目的包。Open the project in Visual Studio, click Project > Manage NuGet Packages... > Browse, type or paste Microsoft.Windows.CppWinRT in the search box, select the item in search results, and then click Install to install the package for that project.

也可以使用项目链接设置来显式链接 WindowsApp.libYou can also use project link settings to explicitly link WindowsApp.lib. 或者,可以在源代码中(例如,在 pch.h 中)按如下所示执行此操作。Or, you can do it in source code (in pch.h, for example) like this.

#pragma comment(lib, "windowsapp")

现在,可以编译、链接 C++/WinRT 代码并将其添加到项目(例如,类似于前面 C++/WinRT 快速入门部分所示的代码)。You can now compile and link, and add C++/WinRT code to your project (for example, code similar to that shown in the A C++/WinRT quick-start section, above).

C++/WinRT 的三大应用方案The three main scenarios for C++/WinRT

在使用和熟悉 C++/WinRT 的过程中,以及在阅读本文档余下内容的过程中,你可能会注意到有三大应用方案,详见后面部分的介绍。As you use and become familiar with C++/WinRT, and work through the rest of the documentation here, you'll likely notice that there are three main scenarios, as described in the following sections.

使用 Windows 运行时 API 和类型Consuming Windows Runtime APIs and types

也就是说,使用或调用 API。In other words, using, or calling APIs. 例如,通过 API 调用使用蓝牙进行通信、流式传输和提供视频、与 Windows shell 集成,等等。For example, making API calls to communicate using Bluetooth; to stream and present video; to integrate with the Windows shell; and so on. C++/WinRT 完全支持此类方案。C++/WinRT fully and uncompromisingly supports this category of scenario. 有关详细信息,请参阅通过 C++/WinRT 使用 APIFor more info, see Consume APIs with C++/WinRT.

创作 Windows 运行时 API 和类型Authoring Windows Runtime APIs and types

也就是说,生成 API 和类型。In other words, producing APIs and types. 例如,生成上一部分介绍的 API 类型、图形 API、存储和文件系统 API、网络 API 等。For example, producing the kinds of APIs described in the section above; or the graphics APIs; the storage and file system APIs; the networking APIs, and so on. 有关详细信息,请参阅使用 C++/WinRT 创作 APIFor more info, see Author APIs with C++/WinRT.

使用 C++/WinRT 创作 API 涉及的事项要稍多于使用 API 的情况,因为你必须使用 IDL 来定义 API 的形状,然后才能实现它。Authoring APIs with C++/WinRT is a little more involved than consuming them, because you must use IDL to define the shape of the API before you can implement it. XAML 控件;绑定到 C++/WinRT 属性中详述了此方面的操作。There's a walkthrough of doing that in XAML controls; bind to a C++/WinRT property.

XAML 应用程序XAML applications

此方案涉及在 XAML UI 框架上构建应用程序和控件。This scenario is about building applications and controls on the XAML UI framework. 在 XAML 应用程序中工作相当于既要使用,又要创作。Working in a XAML application amounts to a combination of consuming and authoring. 但是,由于 XAML 是当今的 Windows 主流 UI 框架,其对 Windows 运行时的影响也同样很大,因此有必要专门设置一个它的应用方案类别。But since XAML is the dominant UI framework on Windows today, and its influence over the Windows Runtime is proportionate to that, it deserves its own category of scenario.

请注意,XAML 最适用于提供反射的编程语言。Be aware that XAML works best with programming languages that offer reflection. 在 C++/WinRT 中,有时需要做一些额外的工作才能与 XAML 框架互操作。In C++/WinRT, you sometimes have to do a little extra work in order to interoperate with the XAML framework. 所有这些情况均在相应文档中进行了介绍。All of those cases are covered in the documentation. 可以从 XAML 控件;绑定到 C++/WinRT 属性XAML 自定义(模板化)控件与 C++/WinRT 着手。Good places to start are XAML controls; bind to a C++/WinRT property and XAML custom (templated) controls with C++/WinRT.

使用 C++/WinRT 编写的示例应用Sample apps written in C++/WinRT

请参阅可在哪里找到 C++/WinRT 示例应用?See Where can I find C++/WinRT sample apps?.

重要的 APIImportant APIs