从 C++/CX 移动到 C++/WinRTMove to C++/WinRT from C++/CX

本主题是系列文章中的第一篇,介绍如何将 C++/CX 项目中的源代码移植到 C++/WinRT 中的等效项。This topic is the first in a series describing how you can port the source code in your C++/CX project to its equivalent in C++/WinRT.

如果你的项目还使用 Windows 运行时 C++ 模板库 (WRL) 类型,请参阅从 WRL 迁移到 C++/WinRTIf your project is also using Windows Runtime C++ Template Library (WRL) types, then see Move to C++/WinRT from WRL.

移植策略Strategies for porting

值得注意的是,从 C++/CX 到 C++/WinRT 的移植通常很简单,但从并行模式库 (PPL) 任务迁移到协同程序的情况例外。It's worth knowing that porting from C++/CX to C++/WinRT is generally straightforward, with the one exception of moving from Parallel Patterns Library (PPL) tasks to coroutines. 其模型不同。The models are different. 从 PPL 任务到协同程序没有自然的一对一映射,也没有适用于所有情况的机械移植代码的简单方法。There isn't a natural one-to-one mapping from PPL tasks to coroutines, and there's no simple way to mechanically port the code that works for all cases. 有关移植的这一特定方面的帮助信息,以及在这两个模型之间进行互操作的选项,请参阅实现 C++/WinRT 与 C++/CX 之间的异步和互操作For help with this specific aspect of porting, and your options for interoperating between the two models, see Asynchrony, and interop between C++/WinRT and C++/CX.

开发团队通常会报告说,一旦克服了移植异步代码的障碍,其余的移植工作大部分是机械式的。Development teams routinely report that once they're over the hurdle of porting their asynchronous code, the remainder of the porting work is largely mechanical.

一次性移植Porting in one pass

如果你能够一次性移植整个项目,那么只需要阅读本主题即可获取所需的信息(无需阅读本主题后面的互操作主题)。If you're in a position to be able to port your entire project in one pass, then you'll need only this topic for the info you need (and you won't need the interop topics that follow this one). 建议首先使用 C++/WinRT 项目模板之一在 Visual Studio 中创建一个新项目(请参阅 Visual Studio 对 C++/WinRT 的支持)。We recommend that you begin by creating a new project in Visual Studio using one of the C++/WinRT project templates (see Visual Studio support for C++/WinRT). 然后将源代码文件移入该新项目,与此同时,将所有 C++/CX 源代码移植到 C++/WinRT 中。Then move your source code files over into that new project, and port all of the C++/CX source code to C++/WinRT as you do so.

或者,如果你希望在现有的 C++/CX 项目中进行移植工作,则需要向其添加 C++/WinRT 支持。Alternatively, if you'd prefer to do the porting work in your existing C++/CX project, then you'll need to add C++/WinRT support to it. 采用 C++/CX 项目并添加 C++/WinRT 支持中介绍了执行此操作的步骤。The steps that you follow to do that are described in Taking a C++/CX project and adding C++/WinRT support. 完成移植之时,便已将原来的纯 C++/CX 项目变成了纯 C++/WinRT 项目。By the time you're done porting, you'll have turned what was a pure C++/CX project into a pure C++/WinRT project.

备注

如果你有 Windows 运行时组件项目,则一次性移植是你唯一的选项。If you have a Windows Runtime component project, then porting in one pass is your only option. 用 C++ 编写的 Windows 运行时组件项目包含的要么全部是 C++/CX 源代码,要么全部是 C++/WinRT 源代码。A Windows Runtime component project written in C++ must contain either all C++/CX source code, or all C++/WinRT source code. 它们不能在此项目类型中共存。They can't coexist in this project type.

逐步移植项目Porting a project gradually

除了前面部分中提到的 Windows 运行时组件项目以外,如果代码库的大小或复杂性使得有必要逐步移植项目,则需要一个移植过程,在此过程中的某段时间,C++/CX 和 C++/WinRT 代码将在同一项目中并存。With the exception of Windows Runtime component projects, as mentioned in the previous section, if the size or complexity of your codebase makes it necessary to port your project gradually, then you'll need a porting process in which for a time C++/CX and C++/WinRT code exists side by side in the same project. 除了阅读本主题,还应参阅实现 C++/WinRT 与 C++/CX 之间的互操作实现 C++/WinRT 与 C++/CX 之间的异步和互操作In addition to reading this topic, also see Interop between C++/WinRT and C++/CX and Asynchrony, and interop between C++/WinRT and C++/CX. 这些主题提供了一些信息和代码示例,演示如何在这两种语言投影之间进行互操作。Those topics provide info and code examples showing how to interoperate between the two language projections.

若要使项目为逐步移植过程做好准备,一种选择是向 C++/CX 项目添加 C++/WinRT 支持。To get a project ready for a gradual porting process, one option is to add C++/WinRT support to your C++/CX project. 采用 C++/CX 项目并添加 C++/WinRT 支持中介绍了执行此操作的步骤。The steps that you follow to do that are described in Taking a C++/CX project and adding C++/WinRT support. 然后,你就可以从这里逐步进行移植。You can then port gradually from there.

另一种选择是使用 C++/WinRT 项目模板之一在 Visual Studio 中创建一个新项目(请参阅 Visual Studio 对 C++/WinRT 的支持)。Another option is to create a new project in Visual Studio using one of the C++/WinRT project templates (see Visual Studio support for C++/WinRT). 然后向该项目添加 C++/CX 支持。And then add C++/CX support to that project. 采用 C++/WinRT 项目并添加 C++/CX 支持中介绍了执行此操作的步骤。The steps that you follow to do that are described in Taking a C++/WinRT project and adding C++/CX support. 然后,你可以开始将源代码移入其中,与此同时,将一些 C++/CX 源代码移植到 C++/WinRT 中。You can then start moving your source code over into that, and porting some of the C++/CX source code to C++/WinRT as you do so.

无论是哪种情况,都需要在 C++/WinRT 代码与尚未移植的任何 C++/CX 代码之间进行互操作(双向)。In either case, you'll interoperate (both ways) between your C++/WinRT code and any C++/CX code that you haven't yet ported.

备注

C++/CX 和 Windows SDK 都在根命名空间 Windows 中声明类型。Both C++/CX and the Windows SDK declare types in the root namespace Windows. 投影到 C++/WinRT 的 Windows 类型具有与 Windows 类型相同的完全限定名称,但放置于 C++ winrt 命名空间中。A Windows type projected into C++/WinRT has the same fully-qualified name as the Windows type, but it's placed in the C++ winrt namespace. 这些不同的命名空间可让你按照自己的节奏从 C++/CX 移植到 C++/WinRT。These distinct namespaces let you port from C++/CX to C++/WinRT at your own pace.

逐步移植 XAML 项目Porting a XAML project gradually

重要

对于使用 XAML 的项目,无论何时,均要求所有 XAML 页面类型要么完全是 C++/CX,要么完全是 C++/WinRT。For a project that uses XAML, at any given time all of your XAML page types need to be either entirely C++/CX or entirely C++/WinRT. 你仍可以在同一项目中 XAML 页面类型以外的位置(在模型和视图模型中以及其他位置)混合使用 C++/CX 和 C++/WinRT。You can still mix C++/CX and C++/WinRT outside of XAML page types within the same project (in your models and viewmodels, and elsewhere).

对于这种情况,我们建议的工作流是创建一个新的 C++/WinRT 项目并从 C++/CX 项目复制源代码和标记。For this scenario, the workflow that we recommend is to create a new C++/WinRT project and copy source code and markup over from the C++/CX project. 只要所有 XAML 页面类型都是 C++/WinRT,就可以使用“项目”>“添加新项...”>“Visual C++” > “空白页(C++/WinRT)”来添加新 XAML 页面。As long as all of your XAML page types are C++/WinRT, then you can add new XAML pages with Project > Add New Item... > Visual C++ > Blank Page (C++/WinRT).

或者,可以在移植 XAML C++/CX 项目时使用 Windows 运行时组件 (WRC) 从中提出代码。Alternatively, you can use a Windows Runtime component (WRC) to factor code out of the XAML C++/CX project as you port it.

  • 可以创建一个新的 C++/CX WRC 项目,将尽可能多的 C++/CX 代码移入该项目,然后将 XAML 项目更改为 C++/WinRT。You could create a new C++/CX WRC project, move as much C++/CX code as you can into that project, and then change the XAML project to C++/WinRT.
  • 或者,可以创建一个新的 C++/WinRT WRC 项目,将 XAML 项目保留为 C++/CX,然后开始将 C++/CX 移植到 C++/WinRT,将所得到的代码移出 XAML 项目并移入组件项目。Or you could create a new C++/WinRT WRC project, leave the XAML project as C++/CX, and begin porting C++/CX to C++/WinRT and moving the resulting code out of the XAML project and into the component project.
  • 还可以让 C++/CX 组件项目以及 C++/WinRT 组件项目处于同一个解决方案中,从应用程序项目引用两者,然后逐渐从一个项目移植到另一个项目。You could also have a C++/CX component project alongside a C++/WinRT component project within the same solution, reference both of them from your application project, and gradually port from one to the other. 同样,请参阅实现 C++/WinRT 与 C++/CX 之间的互操作,了解有关在同一个项目中使用这两种语言投影的更多详细信息。Again, see Interop between C++/WinRT and C++/CX for more details on using the two language projections in the same project.

将 C++/CX 项目移植到 C++/WinRT 的第一步First steps in porting a C++/CX project to C++/WinRT

无论使用哪种移植策略(一次性移植或逐步移植),第一步都是准备要移植的项目。No matter what your porting strategy will be (porting in one pass, or porting gradually), your first step is to prepare your project for porting. 下面回顾了移植策略中所述的内容,其中涉及你将要开始使用的项目类型以及如何对其进行设置。Here's a recap of what we described in Strategies for porting in terms of the kind of project you'll be starting with, and how to set it up.

  • 一次性移植Porting in one pass. 使用 C++/WinRT 项目模板之一在 Visual Studio 中创建一个新项目。Create a new project in Visual Studio using one of the C++/WinRT project templates. 将文件从 C++/CX 项目移入该新项目,然后移植 C++/CX 源代码。Move the files from your C++/CX project into that new project, and port the C++/CX source code.
  • 逐步移植非 XAML 项目Porting a non-XAML project gradually. 可以选择向 C++/CX 项目添加 C++/WinRT 支持(请参阅采用 C++/CX 项目并添加 C++/WinRT 支持),然后逐步进行移植。You can choose to add C++/WinRT support to your C++/CX project (see Taking a C++/CX project and adding C++/WinRT support), and port gradually. 也可以选择创建一个新的 C++/WinRT 项目并向其添加 C++/CX 支持(请参阅采用 C++/WinRT 项目并添加 C++/CX 支持),移入文件并逐步进行移植。Or you can choose to create a new C++/WinRT project and add C++/CX support to that (see Taking a C++/WinRT project and adding C++/CX support), move files over, and port gradually.
  • 逐步移植 XAML 项目Porting a XAML project gradually. 创建一个新的 C++/WinRT 项目,移入文件并逐步进行移植。Create a new C++/WinRT project, move files over, and port gradually. 无论何时,均要求 XAML 页面类型要么完全是 C++/WinRT,要么完全是 C++/CX。At any given time your XAML page types must be either all C++/WinRT or all C++/CX.

无论选择哪种移植策略,本主题的其余部分均适用。The rest of this topic applies no matter which porting strategy you choose. 它包含将源代码从 C++/CX 移植到 C++/WinRT 所涉及的技术细节的目录。It contains a catalog of technical details involved in porting source code from C++/CX to C++/WinRT. 如果要进行逐步移植,则可能还需要查看实现 C++/WinRT 与 C++/CX 之间的互操作实现 C++/WinRT 与 C++/CX 之间的异步和互操作If you're porting gradually, then you'll likely also want to see Interop between C++/WinRT and C++/CX and Asynchrony, and interop between C++/WinRT and C++/CX.

文件命名约定File-naming conventions

XAML 标记文件XAML markup files

文件原始格式File origin C++/CXC++/CX C++/WinRTC++/WinRT
开发人员 XAML 文件Developer XAML files MyPage.xamlMyPage.xaml
MyPage.xaml.hMyPage.xaml.h
MyPage.xaml.cppMyPage.xaml.cpp
MyPage.xamlMyPage.xaml
MyPage.hMyPage.h
MyPage.cppMyPage.cpp
MyPage.idl(见下)MyPage.idl (see below)
生成的 XAML 文件Generated XAML files MyPage.xaml.g.hMyPage.xaml.g.h
MyPage.xaml.g.hppMyPage.xaml.g.hpp
MyPage.xaml.g.hMyPage.xaml.g.h
MyPage.xaml.g.hppMyPage.xaml.g.hpp
MyPage.g.hMyPage.g.h

请注意,C++/WinRT 会从 *.h*.cpp 文件名中删除 .xamlNotice that C++/WinRT removes the .xaml from the *.h and *.cpp file names.

C++/WinRT 添加了一个额外的开发人员文件,即 Midl 文件 (.idl)C++/WinRT adds an additional developer file, the Midl file (.idl). C++/CX 在内部自动生成此文件,并将其添加到每个公开的和受保护的成员。C++/CX autogenerates this file internally, adding to it every public and protected member. 在 C++/WinRT 中,你自行创作并添加此文件。In C++/WinRT, you add and author the file yourself. 如需更多详细信息、代码示例以及 IDL 创作演练,请参阅 XAML 控件;绑定到 C++/WinRT 属性For more details, code examples, and a walkthrough of authoring IDL, see XAML controls; bind to a C++/WinRT property.

另请参阅将运行时类重构到 Midl 文件 (.idl) 中Also see Factoring runtime classes into Midl files (.idl)

运行时类Runtime classes

C++/CX 不对头文件的名称施加限制;通常会将多个运行时类定义置于单个头文件中,小型类尤其如此。C++/CX doesn't impose restrictions on the names of your header files; it's common to put multiple runtime class definitions into a single header file, especially for small classes. 但是,C++/WinRT 要求每个运行时类将自己的头文件以类名称来命名。But C++/WinRT requires that each runtime class has its own header file named after the class name.

C++/CXC++/CX C++/WinRTC++/WinRT
Common.hCommon.h
ref class A { ... }
ref class B { ... }
Common.idlCommon.idl
runtimeclass A { ... }
runtimeclass B { ... }
A.hA.h
namespace implements {
  struct A { ... };
}
B.hB.h
namespace implements {
  struct B { ... };
}

在 C++/CX 中,使用不同命名方式的头文件进行 XAML 自定义控制的做法不太常见(但仍属合法)。Less common (but still legal) in C++/CX is to use differently-named header files for XAML custom controls. 需根据类名来重命名这些头文件。You'll need to rename these header file to match the class name.

C++/CXC++/CX C++/WinRTC++/WinRT
A.xamlA.xaml
<Page x:Class="LongNameForA" ...>
A.xamlA.xaml
<Page x:Class="LongNameForA" ...>
A.hA.h
partial ref class LongNameForA { ... }
LongNameForA.hLongNameForA.h
namespace implements {
  struct LongNameForA { ... };
}

头文件要求Header file requirements

C++/CX 不要求你包括任何特殊的头文件,因为它会在内部根据 .winmd 文件自动生成头文件。C++/CX doesn't require you to include any special header files, because it internally autogenerates header files from .winmd files. 在 C++/CX 中,通常会对按名称使用的命名空间使用 using 指令。It's common in C++/CX to use using directives for namespaces that you consume by name.

using namespace Windows::Media::Playback;

String^ NameOfFirstVideoTrack(MediaPlaybackItem^ item)
{
    return item->VideoTracks->GetAt(0)->Name;
}

可以通过 using namespace Windows::Media::Playback 指令在不使用命名空间前缀的情况下编写 MediaPlaybackItemThe using namespace Windows::Media::Playback directive lets us write MediaPlaybackItem without a namespace prefix. 我们还接触了 Windows.Media.Core 命名空间,因为 item->VideoTracks->GetAt(0) 返回 Windows.Media.Core.VideoTrackWe also touched the Windows.Media.Core namespace, because item->VideoTracks->GetAt(0) returns a Windows.Media.Core.VideoTrack. 但是,我们不需要在任何位置键入 VideoTrack 这个名称,因此不需要 using Windows.Media.Core 指令。But we didn't have to type the name VideoTrack anywhere, so we didn't need a using Windows.Media.Core directive.

但是,C++/WinRT 要求你针对每个所使用的命名空间包括一个头文件,即使不为其命名。But C++/WinRT requires you to include a header file corresponding to each namespace that you consume, even if you don't name it.

#include <winrt/Windows.Media.Playback.h>
#include <winrt/Windows.Media.Core.h> // !!This is important!!

using namespace winrt;
using namespace Windows::Media::Playback;

winrt::hstring NameOfFirstVideoTrack(MediaPlaybackItem const& item)
{
    return item.VideoTracks().GetAt(0).Name();
}

另一方面,即使 MediaPlaybackItem.AudioTracksChanged 事件的类型为 TypedEventHandler<MediaPlaybackItem, Windows.Foundation.Collections.IVectorChangedEventArgs>,我们也不需包括 winrt/Windows.Foundation.Collections.h,因为我们并未使用该事件 。On the other hand, even though the MediaPlaybackItem.AudioTracksChanged event is of type TypedEventHandler<MediaPlaybackItem, Windows.Foundation.Collections.IVectorChangedEventArgs>, we don't need to include winrt/Windows.Foundation.Collections.h because we didn't use that event.

C++/WinRT 还要求你针对 XAML 标记所使用的命名空间包括相应的头文件。C++/WinRT also requires you to include header files for namespaces that are consumed by XAML markup.

<!-- MainPage.xaml -->
<Rectangle Height="400"/>

使用 Rectangle 类意味着你必须添加以下 include。Using the Rectangle class means that you have to add this include.

// MainPage.h
#include <winrt/Windows.UI.Xaml.Shapes.h>

如果忘记了头文件,则仍可正常编译,但会出现链接器错误,因为缺少 consume_ 类。If you forget a header file, then everything will compile okay but you'll get linker errors because the consume_ classes are missing.

参数传递Parameter-passing

编写 C++/CX 源代码时,在顶帽 (^) 引用时,你将 C++/CX 类型作为函数参数传递。When writing C++/CX source code, you pass C++/CX types as function parameters as hat (^) references.

void LogPresenceRecord(PresenceRecord^ record);

在 C++/WinRT 中,对于同步函数,默认情况下应该使用 const& 参数。In C++/WinRT, for synchronous functions, you should use const& parameters by default. 这将避免复制和互锁开销。That will avoid copies and interlocked overhead. 但你的协同程序应使用按值传递来确保它们按值捕获,并避免生命周期问题(更多详细信息,请参阅利用 C++/WinRT 实现的并发和异步操作)。But your coroutines should use pass-by-value to ensure that they capture by value and avoid lifetime issues (for more details, see Concurrency and asynchronous operations with C++/WinRT).

void LogPresenceRecord(PresenceRecord const& record);
IASyncAction LogPresenceRecordAsync(PresenceRecord const record);

C++/WinRT 对象根本上是一个保留支持 Windows 运行时对象的接口指针的值。A C++/WinRT object is fundamentally a value that holds an interface pointer to the backing Windows Runtime object. 在复制 C++/WinRT 对象时,编译器复制封装的接口指针,从而递增其引用计数。When you copy a C++/WinRT object, the compiler copies the encapsulated interface pointer, incrementing its reference count. 副本的最终销毁涉及递减引用计数。Eventual destruction of the copy involves decrementing the reference count. 因此,仅在必要时产生复制开销。So, only incur the overhead of a copy when necessary.

变量和字段引用Variable and field references

编写 C++/CX 源代码时,你使用顶帽 (^) 变量引用 Windows 运行时对象,使用箭头 (->) 运算符来取消引用顶帽变量。When writing C++/CX source code, you use hat (^) variables to reference Windows Runtime objects, and the arrow (->) operator to dereference a hat variable.

IVectorView<User^>^ userList = User::Users;

if (userList != nullptr)
{
    for (UINT32 iUser = 0; iUser < userList->Size; ++iUser)
    ...

移植到等效 C++/WinRT 代码时,可以通过删除顶帽,并将箭头运算符 (->) 更改为点运算符 (.),来完成大量工作。When porting to the equivalent C++/WinRT code, you can get a long way by removing the hats, and changing the arrow operator (->) to the dot operator (.). C++/WinRT 投影类型是值,而不是指针。C++/WinRT projected types are values, and not pointers.

IVectorView<User> userList = User::Users();

if (userList != nullptr)
{
    for (UINT32 iUser = 0; iUser < userList.Size(); ++iUser)
    ...

C++/CX 顶帽引用的默认构造函数会将它初始化为 null。The default constructor for a C++/CX hat reference initializes it to null. 下面是一个 C++/CX 代码示例,我们在其中创建一个具有正确类型,但是未初始化的变量/字段。Here's a C++/CX code example in which we create a variable/field of the correct type, but one that's uninitialized. 换句话说,它最初不引用 TextBlock;我们打算在以后分配引用。In other words, it doesn't initially refer to a TextBlock; we intend to assign a reference later.

TextBlock^ textBlock;

class MyClass
{
    TextBlock^ textBlock;
};

有关 C++/WinRT 中的等效项,请参阅延迟初始化For the equivalent in C++/WinRT, see Delayed initialization.

“属性”Properties

C++/CX 语言扩展包括属性概念。The C++/CX language extensions include the concept of properties. 编写 C++/CX 源代码时,你可以像访问字段那样访问属性。When writing C++/CX source code, you can access a property as if it were a field. 标准 C++ 没有属性概念,因此,在 C++/WinRT 中,你调用获取和设置函数。Standard C++ does not have the concept of a property so, in C++/WinRT, you call get and set functions.

在随后的示例中,XboxUserId、UserState、PresenceDeviceRecords 和 Size 全部都是属性。In the examples that follow, XboxUserId, UserState, PresenceDeviceRecords, and Size are all properties.

从属性检索值Retrieving a value from a property

下面介绍如何在 C++/CX 中获取属性值。Here's how you get a property value in C++/CX.

void Sample::LogPresenceRecord(PresenceRecord^ record)
{
    auto id = record->XboxUserId;
    auto state = record->UserState;
    auto size = record->PresenceDeviceRecords->Size;
}

等效的 C++/WinRT 源代码调用与属性同名但没有参数的函数。The equivalent C++/WinRT source code calls a function with the same name as the property, but with no parameters.

void Sample::LogPresenceRecord(PresenceRecord const& record)
{
    auto id = record.XboxUserId();
    auto state = record.UserState();
    auto size = record.PresenceDeviceRecords().Size();
}

请注意,PresenceDeviceRecords 函数返回其本身具有 Size 函数的 Windows 运行时对象。Note that the PresenceDeviceRecords function returns a Windows Runtime object that itself has a Size function. 由于返回的对象也是 C++/WinRT 投影类型,因此我们使用点运算符调用 Size 来取消引用。As the returned object is also a C++/WinRT projected type, we dereference using the dot operator to call Size.

将属性设置为新值Setting a property to a new value

将属性设置为新值遵循类似模式。Setting a property to a new value follows a similar pattern. 首先,在 C++/CX 中。First, in C++/CX.

record->UserState = newValue;

若要在 C++/WinRT 执行同等操作,调用与属性同名的函数,并传递参数。To do the equivalent in C++/WinRT, you call a function with the same name as the property, and pass an argument.

record.UserState(newValue);

创建类的实例Creating an instance of a class

你通过 C++/CX 对象的句柄来处理它,通常称为顶帽 (^) 引用。You work with a C++/CX object via a handle to it, commonly known as a hat (^) reference. 通过 ref new 关键字创建新对象,这反过来会调用 RoActivateInstance 来激活运行时类的新实例。You create a new object via the ref new keyword, which in turn calls RoActivateInstance to activate a new instance of the runtime class.

using namespace Windows::Storage::Streams;

class Sample
{
private:
    Buffer^ m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
};

C++/WinRT 对象是一个值;因此你可以在堆栈上进行分配,或作为对象字段分配。A C++/WinRT object is a value; so you can allocate it on the stack, or as a field of an object. 切勿使用 ref new(或 new)来分配 C++/WinRT 对象。You never use ref new (nor new) to allocate a C++/WinRT object. 在后台,仍然在调用 RoActivateInstance。Behind the scenes, RoActivateInstance is still being called.

using namespace winrt::Windows::Storage::Streams;

struct Sample
{
private:
    Buffer m_gamerPicBuffer{ MAX_IMAGE_SIZE };
};

如果初始化资源的成本很高,通常可以推迟资源的初始化,直到实际需要时再执行。If a resource is expensive to initialize, then it's common to delay initialization of it until it's actually needed. 如前所述,C++/CX 顶帽引用的默认构造函数会将它初始化为 null。As already mentioned, the default constructor for a C++/CX hat reference initializes it to null.

using namespace Windows::Storage::Streams;

class Sample
{
public:
    void DelayedInit()
    {
        // Allocate the actual buffer.
        m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
    }

private:
    Buffer^ m_gamerPicBuffer;
};

移植到 C++/WinRT 的同一个代码。The same code ported to C++/WinRT. 请注意 std::nullptr_t 构造函数的使用。Note the use of the std::nullptr_t constructor. 有关该构造函数的详细信息,请参阅延迟初始化For more info about that constructor, see Delayed initialization.

using namespace winrt::Windows::Storage::Streams;

struct Sample
{
    void DelayedInit()
    {
        // Allocate the actual buffer.
        m_gamerPicBuffer = Buffer(MAX_IMAGE_SIZE);
    }

private:
    Buffer m_gamerPicBuffer{ nullptr };
};

默认构造函数如何影响集合How the default constructor affects collections

C++ 集合类型使用默认构造函数,这可能导致意外的对象构造。C++ collection types use the default constructor, which can result in unintended object construction.

方案Scenario C++/CXC++/CX C++/WinRT(不正确)C++/WinRT (incorrect) C++/WinRT(正确)C++/WinRT (correct)
本地变量,一开始为空Local variable, initially empty TextBox^ textBox; TextBox textBox; // Creates a TextBox! TextBox textBox{ nullptr };
成员变量,一开始为空Member variable, initially empty class C {
  TextBox^ textBox;
};
class C {
  TextBox textBox; // Creates a TextBox!
};
class C {
  TextBox textbox{ nullptr };
};
全局变量,一开始为空Global variable, initially empty TextBox^ g_textBox; TextBox g_textBox; // Creates a TextBox! TextBox g_textBox{ nullptr };
空引用的矢量Vector of empty references std::vector<TextBox^> boxes(10); // Creates 10 TextBox objects!
std::vector<TextBox> boxes(10);
std::vector<TextBox> boxes(10, nullptr);
在 map 中设置值Set a value in a map std::map<int, TextBox^> boxes;
boxes[2] = value;
std::map<int, TextBox> boxes;
// Creates a TextBox at 2,
// then overwrites it!
boxes[2] = value;
std::map<int, TextBox> boxes;
boxes.insert_or_assign(2, value);
空引用的数组Array of empty references TextBox^ boxes[2]; // Creates 2 TextBox objects!
TextBox boxes[2];
TextBox boxes[2] = { nullptr, nullptr };
配对Pair std::pair<TextBox^, String^> p; // Creates a TextBox!
std::pair<TextBox, String> p;
std::pair<TextBox, String> p{ nullptr, nullptr };

有关空引用集合的详细信息More about collections of empty references

只要在 C++/CX 中有一个 Platform::Array^ (参见 移植 Platform::Array^),即可将其移植到 C++/WinRT 中的 std::vector(事实上,可以将其移植到任何邻近的容器),而不是将其作为数组保留。Whenever you have a Platform::Array^ (see Port Platform::Array^) in C++/CX, you have the choice to port that to a std::vector in C++/WinRT (in fact, any contiguous container) rather than leave it as an array. 选择 std::vector 有多种优势。There are advantages to choosing std::vector.

例如,尽管存在用于创建固定大小的空引用矢量(参见上表)的速记,但没有用于创建空引用数组的速记。For example, while there is shorthand for creating a fixed-sized vector of empty references (see table above), there's no such shorthand for creating an array of empty references. 必须针对数组中的每个元素重复执行 nullptrYou have to repeat nullptr for each element in an array. 如果构造的数目太少,则会默认构造额外的。If you have too few, then the extras will be default-constructed.

对于矢量,可以在初始化时使用空引用填充它(如上表所示),也可以借助如下所示的代码在初始化后使用空引用填充它。For a vector, you can fill it with empty references at initialization (as in the table above), or you can fill it with empty references post-initialization with code such as this.

std::vector<TextBox> boxes(10); // 10 default-constructed TextBoxes.
boxes.resize(10, nullptr); // 10 empty references.

有关 std::map 示例的详细信息More about the std::map example

std::map[] 下标运算符的行为如下所示。The [] subscript operator for std::map behaves like this.

  • 如果在 map 中发现此键,则返回现有值(可以将其覆盖)的引用。If the key is found in the map, return a reference to the existing value (which you can overwrite).
  • 如果未在 map 中发现此键,则在包含此键(在可移动的情况下已经移动)的 map 中创建新条目和默认构造的值,并返回此值(可以随后将其覆盖)的引用。If the key isn't found in the map, then create a new entry in the map consisting of the key (moved, if movable) and a default-constructed value, and return a reference to the value (which you can then overwrite).

换言之,[] 运算符始终在 map 中创建一个条目。In other words, the [] operator always creates an entry in the map. 这不同于 C#、Java 和 JavaScript。This is different from C#, Java, and JavaScript.

从运行时基类转换为派生类Converting from a base runtime class to a derived one

通常有一个已知引用派生类型对象的基类引用。It's common to have a reference-to-base that you know refers to an object of a derived type. 在 C++/CX 中,使用 dynamic_cast 将基类引用强制转换为派生类引用。In C++/CX, you use dynamic_cast to cast the reference-to-base into a reference-to-derived. dynamic_cast 实际上只是对 QueryInterface 的隐藏调用。The dynamic_cast is really just a hidden call to QueryInterface. 下面是一个典型示例 — 你要处理依赖属性更改事件,并且要从 DependencyObject 强制转换回拥有依赖属性的实际类型。Here's a typical example—you're handling a dependency property changed event, and you want to cast from DependencyObject back to the actual type that owns the dependency property.

void BgLabelControl::OnLabelChanged(Windows::UI::Xaml::DependencyObject^ d, Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ e)
{
    BgLabelControl^ theControl{ dynamic_cast<BgLabelControl^>(d) };

    if (theControl != nullptr)
    {
        // succeeded ...
    }
}

等效 C++/WinRT 代码将 dynamic_cast 替换为对 IUnknown::try_as 函数的调用,该函数会封装 QueryInterface。The equivalent C++/WinRT code replaces the dynamic_cast with a call to the IUnknown::try_as function, which encapsulates QueryInterface. 还可以选择改为调用 IUnknown::as,这会在未返回对所需接口(具有所请求的类型的默认接口)的查询时引发异常。You also have the option to call IUnknown::as, instead, which throws an exception if querying for the required interface (the default interface of the type you're requesting) is not returned. 下面是 C++/WinRT 代码示例。Here's a C++/WinRT code example.

void BgLabelControl::OnLabelChanged(Windows::UI::Xaml::DependencyObject const& d, Windows::UI::Xaml::DependencyPropertyChangedEventArgs const& e)
{
    if (BgLabelControlApp::BgLabelControl theControl{ d.try_as<BgLabelControlApp::BgLabelControl>() })
    {
        // succeeded ...
    }

    try
    {
        BgLabelControlApp::BgLabelControl theControl{ d.as<BgLabelControlApp::BgLabelControl>() };
        // succeeded ...
    }
    catch (winrt::hresult_no_interface const&)
    {
        // failed ...
    }
}

派生类Derived classes

若要从运行时类派生,基类必须是可组合类。In order to derive from a runtime class, the base class must be composable. C++/CX 不需要你执行任何特殊步骤即可将类变为可组合类,但 C++/WinRT 需要。C++/CX doesn't require that you take any special steps to make your classes composable, but C++/WinRT does. 请使用 unsealed 关键字来指示你希望将类用作基类。You use the unsealed keyword to indicate that you want your class to be usable as a base class.

unsealed runtimeclass BasePage : Windows.UI.Xaml.Controls.Page
{
    ...
}
runtimeclass DerivedPage : BasePage
{
    ...
}

在实现标头类中,在包括为派生类自动生成的标头之前,必须包括基类标头文件。In your implementation header class, you must include the base class header file before you include the autogenerated header for the derived class. 否则会出现“将此类型用作表达式非法”之类的错误。Otherwise you'll get errors such as "Illegal use of this type as an expression".

// DerivedPage.h
#include "BasePage.h"       // This comes first.
#include "DerivedPage.g.h"  // Otherwise this header file will produce an error.

namespace winrt::MyNamespace::implementation
{
    struct DerivedPage : DerivedPageT<DerivedPage>
    {
        ...
    }
}

通过代理进行事件处理Event-handling with a delegate

下面介绍了在 C++/CX 中处理事件的典型示例,在本例中将 lambda 函数用作代理。Here's a typical example of handling an event in C++/CX, using a lambda function as a delegate in this case.

auto token = myButton->Click += ref new RoutedEventHandler([=](Platform::Object^ sender, RoutedEventArgs^ args)
{
    // Handle the event.
    // Note: locals are captured by value, not reference, since this handler is delayed.
});

它是 C++/WinRT 中的等效项。This is the equivalent in C++/WinRT.

auto token = myButton().Click([=](IInspectable const& sender, RoutedEventArgs const& args)
{
    // Handle the event.
    // Note: locals are captured by value, not reference, since this handler is delayed.
});

不使用 lambda 函数,你可以选择作为自由函数或指向成员函数的指针实现代理。Instead of a lambda function, you can choose to implement your delegate as a free function, or as a pointer-to-member-function. 有关详细信息,请参阅使用 C++/WinRT 中的代理来处理事件For more info, see Handle events by using delegates in C++/WinRT.

如果你正在从内部使用(不是跨二进制文件)事件和代理的 C++/CX 基本代码移植,winrt::delegate 将帮助你复制 C++/WinRT 中的这个模式。If you're porting from a C++/CX codebase where events and delegates are used internally (not across binaries), then winrt::delegate will help you to replicate that pattern in C++/WinRT. 另请参阅项目中的参数化委托、简单信号和回调Also see Parameterized delegates, simple signals, and callbacks within a project.

撤销代理Revoking a delegate

在 C++/CX 中,使用 -= 运算符来撤销之前的事件注册。In C++/CX you use the -= operator to revoke a prior event registration.

myButton->Click -= token;

它是 C++/WinRT 中的等效项。This is the equivalent in C++/WinRT.

myButton().Click(token);

有关详细信息和选项,请参阅撤销已注册的代理For more info and options, see Revoke a registered delegate.

装箱和取消装箱Boxing and unboxing

C++/CX 自动将标量装箱到对象中。C++/CX automatically boxes scalars into objects. C++/WinRT 要求你显式调用 winrt::box_value 函数。C++/WinRT requires you to call the winrt::box_value function explicitly. 两种语言都要求你以显式方式取消装箱。Both languages require you to unbox explicitly. 请参阅使用 C++/WinRT 装箱和取消装箱See Boxing and unboxing with C++/WinRT.

在下面的表中,我们将使用这些定义。In the tables that follows, we'll use these definitions.

C++/CXC++/CX C++/WinRTC++/WinRT
int i; int i;
String^ s; winrt::hstring s;
Object^ o; IInspectable o;
操作Operation C++/CXC++/CX C++/WinRTC++/WinRT
装箱Boxing o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
取消装箱Unboxing i = (int)o;
s = (String^)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

如果尝试取消值类型的 null 指针的装箱,C++/CX 和 C# 会引发异常。C++/CX and C# raise exceptions if you try to unbox a null pointer to a value type. C++/WinRT 将其视为编程错误,因此会崩溃。C++/WinRT considers this a programming error, and it crashes. 在 C++/WinRT 中,请使用 winrt::unbox_value_or 函数来处理对象类型不符合预期的情况。In C++/WinRT, use the winrt::unbox_value_or function if you want to handle the case where the object is not of the type that you thought it was.

方案Scenario C++/CXC++/CX C++/WinRTC++/WinRT
取消已知整数的装箱Unbox a known integer i = (int)o; i = unbox_value<int>(o);
如果 o 为 nullIf o is null Platform::NullReferenceException 崩溃Crash
如果 o 不是装箱的整数If o is not a boxed int Platform::InvalidCastException 崩溃Crash
取消整数的装箱,在为 null 的情况下使用回退;任何其他情况则崩溃Unbox int, use fallback if null; crash if anything else i = o ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
尽可能取消整数的装箱;在任何其他情况下使用回退Unbox int if possible; use fallback for anything else auto box = dynamic_cast<IBox<int>^>(o);
i = box ? box->Value : fallback;
i = unbox_value_or<int>(o, fallback);

将字符串装箱和取消装箱Boxing and unboxing a string

字符串在某些情况下是值类型,在另一些情况下是引用类型。A string is in some ways a value type, and in other ways a reference type. C++/CX 和 C++/WinRT 对待字符串的方式有所不同。C++/CX and C++/WinRT treat strings differently.

ABI 类型 HSTRING 是一个指向引用计数字符串的指针。The ABI type HSTRING is a pointer to a reference-counted string. 但是,它并非派生自 IInspectable,因此从技术上来说它不是一个对象。But it doesn't derive from IInspectable, so it's not technically an object. 另外,null HSTRING 表示空字符串。Furthermore, a null HSTRING represents the empty string. 将并非派生自 IInspectable 的项装箱时,需将其包装到 IReference<T> 中,而 Windows 运行时会以 PropertyValue 对象的形式提供标准实现(自定义类型以 PropertyType::OtherType 形式报告) 。Boxing of things not derived from IInspectable is done by wrapping them inside an IReference<T>, and the Windows Runtime provides a standard implementation in the form of the PropertyValue object (custom types are reported as PropertyType::OtherType).

C++/CX 将 Windows 运行时字符串表示为引用类型,而 C++/WinRT 则将字符串投影为值类型。C++/CX represents a Windows Runtime string as a reference type; while C++/WinRT projects a string as a value type. 这意味着装箱的 null 字符串可能有不同的表示形式,具体取决于你所采用的方法。This means that a boxed null string can have different representations depending how you got there.

另外,C++/CX 允许取消引用 null String^ ,在这种情况下,其行为类似于字符串 ""Furthermore, C++/CX allows you to dereference a null String^, in which case it behaves like the string "".

行为Behavior C++/CXC++/CX C++/WinRTC++/WinRT
声明Declarations Object^ o;
String^ s;
IInspectable o;
hstring s;
字符串类型类别String type category 引用类型Reference type 值类型Value type
null HSTRING 投影方式null HSTRING projects as (String^)nullptr hstring{}
null 和 "" 是否相同?Are null and "" identical? Yes Yes
null 的有效性Validity of null s = nullptr;
s->Length == 0(有效)s->Length == 0 (valid)
s = hstring{};
s.size() == 0(有效)s.size() == 0 (valid)
如果将 null 字符串分配给对象If you assign null string to object o = (String^)nullptr;
o == nullptr
o = box_value(hstring{});
o != nullptr
如果将 "" 分配给对象If you assign "" to object o = "";
o == nullptr
o = box_value(hstring{L""});
o != nullptr

基本装箱和取消装箱。Basic boxing and unboxing.

操作Operation C++/CXC++/CX C++/WinRTC++/WinRT
将字符串装箱Box a string o = s;
空字符串变为 nullptr。Empty string becomes nullptr.
o = box_value(s);
空字符串变为非 null 对象。Empty string becomes non-null object.
取消已知字符串的装箱Unbox a known string s = (String^)o;
Null 对象变为空字符串。Null object becomes empty string.
如果不是字符串,则引发 InvalidCastException。InvalidCastException if not a string.
s = unbox_value<hstring>(o);
Null 对象崩溃。Null object crashes.
如果不是字符串,则崩溃。Crash if not a string.
将可能的字符串取消装箱Unbox a possible string s = dynamic_cast<String^>(o);
Null 对象或非字符串变为空字符串。Null object or non-string becomes empty string.
s = unbox_value_or<hstring>(o, fallback);
Null 或非字符串变为 fallback。Null or non-string becomes fallback.
空字符串被保留。Empty string preserved.

并发和异步操作Concurrency and asynchronous operations

并行模式库 (PPL)(例如 concurrency::task)已更新,现在支持 C++/CX 顶帽引用。The Parallel Patterns Library (PPL) (concurrency::task, for example) was updated to support C++/CX hat references.

对于 C++/WinRT,应改用协同程序和 co_awaitFor C++/WinRT, you should use coroutines and co_await instead. 详细信息和代码示例,请参阅利用 C++/WinRT 实现的并发和异步运算For more info, and code examples, see Concurrency and asynchronous operations with C++/WinRT.

使用 XAML 标记中的对象Consuming objects from XAML markup

在 C++/CX 项目中,可以使用 XAML 标记中的专用成员和命名元素。In a C++/CX project, you can consume private members and named elements from XAML markup. 但在 C++/WinRT 中,以 XAML {x:Bind} 标记扩展形式使用的所有实体必须在 IDL 中以公开方式公开。But in C++/WinRT, all entities consumed by using the XAML {x:Bind} markup extension must be exposed publicly in IDL.

另外,绑定到布尔值时,在 C++/CX 中会显示 truefalse,但在 C++/WinRT 中会显示 Windows.Foundation.IReference`1<Boolean>。Also, binding to a Boolean displays true or false in C++/CX, but it shows Windows.Foundation.IReference`1<Boolean> in C++/WinRT.

有关详细信息和代码示例,请参阅使用标记中的对象For more info, and code examples, see Consuming objects from markup.

将 C++/CX 平台类型映射到 C++/WinRT 类型Mapping C++/CX Platform types to C++/WinRT types

C++/CX 在平台命名空间中提供了多个数据类型。C++/CX provides several data types in the Platform namespace. 这些类型不是标准的 C++,因此只能在启用 Windows 运行时语言扩展(Visual Studio 项目属性“C/C++” > “常规” > “使用 Windows 运行时扩展” > “是(/ZW)”)的情况下使用。These types are not standard C++, so you can only use them when you enable Windows Runtime language extensions (Visual Studio project property C/C++ > General > Consume Windows Runtime Extension > Yes (/ZW)). 下表帮助你从平台类型移植到 C++/WinRT 中的等效项。The table below helps you port from Platform types to their equivalents in C++/WinRT. 完成后,由于 C++/WinRT 是标准 C++,因此你可以关闭 /ZW 选项。Once you've done that, since C++/WinRT is standard C++, you can turn off the /ZW option.

C++/CXC++/CX C++/WinRTC++/WinRT
Platform::Agile^Platform::Agile^ winrt::agile_refwinrt::agile_ref
Platform::Array^Platform::Array^ 请参阅移植 Platform::Array^See Port Platform::Array^
Platform::Exception^Platform::Exception^ winrt::hresult_errorwinrt::hresult_error
Platform::InvalidArgumentException^Platform::InvalidArgumentException^ winrt::hresult_invalid_argumentwinrt::hresult_invalid_argument
Platform::Object^Platform::Object^ winrt::Windows::Foundation::IInspectablewinrt::Windows::Foundation::IInspectable
Platform::String^Platform::String^ winrt::hstringwinrt::hstring

将 Platform::Agile^ 移植到 winrt::agile_refPort Platform::Agile^ to winrt::agile_ref

C++/CX 中的 Platform::Agile^ 类型表示可以从任何线程访问的 Windows 运行时类。The Platform::Agile^ type in C++/CX represents a Windows Runtime class that can be accessed from any thread. C++/WinRT 的等效项是 winrt::agile_refThe C++/WinRT equivalent is winrt::agile_ref.

在 C++/CX 中。In C++/CX.

Platform::Agile<Windows::UI::Core::CoreWindow> m_window;

在 C++/WinRT 中。In C++/WinRT.

winrt::agile_ref<Windows::UI::Core::CoreWindow> m_window;

移植 Platform::Array^Port Platform::Array^

在 C++/CX 要求使用数组的情况下,C++/WinRT 允许使用任何相邻的容器。In cases where C++/CX requires you to use an array, C++/WinRT allows you to use any contiguous container. 请参阅 默认构造函数如何影响集合,了解为何可以使用 std::vectorSee How the default constructor affects collections for a reason why std::vector is a good choice.

因此,只要在 C++/CX 中有 Platform::Array^ ,移植选项中就会包括使用初始值设定项列表、std::arraystd::vector 的选项。So, whenever you have a Platform::Array^ in C++/CX, your porting options include using an initializer list, a std::array, or a std::vector. 有关详细信息和代码示例,请参阅标准初始值设定项列表标准数组和矢量For more info, and code examples, see Standard initializer lists and Standard arrays and vectors.

将 Platform::Exception^ 移植到 winrt::hresult_errorPort Platform::Exception^ to winrt::hresult_error

当 Windows 运行时 API 返回非 S_OK HRESULT 时,Platform::Exception^ 类型在 C++/CX 中生成。The Platform::Exception^ type is produced in C++/CX when a Windows Runtime API returns a non S_OK HRESULT. C++/WinRT 的等效项是 winrt::hresult_errorThe C++/WinRT equivalent is winrt::hresult_error.

若要移植到 C++/WinRT,将使用 Platform::Exception^ 的所有代码更改为使用 winrt::hresult_error。To port to C++/WinRT, change all code that uses Platform::Exception^ to use winrt::hresult_error.

在 C++/CX 中。In C++/CX.

catch (Platform::Exception^ ex)

在 C++/WinRT 中。In C++/WinRT.

catch (winrt::hresult_error const& ex)

C++/WinRT 提供这些异常类。C++/WinRT provides these exception classes.

例外类型Exception type 基类Base class HRESULTHRESULT
winrt::hresult_errorwinrt::hresult_error 调用 hresult_error::to_abicall hresult_error::to_abi
winrt::hresult_access_deniedwinrt::hresult_access_denied winrt::hresult_errorwinrt::hresult_error E_ACCESSDENIEDE_ACCESSDENIED
winrt::hresult_canceledwinrt::hresult_canceled winrt::hresult_errorwinrt::hresult_error ERROR_CANCELLEDERROR_CANCELLED
winrt::hresult_changed_statewinrt::hresult_changed_state winrt::hresult_errorwinrt::hresult_error E_CHANGED_STATEE_CHANGED_STATE
winrt::hresult_class_not_availablewinrt::hresult_class_not_available winrt::hresult_errorwinrt::hresult_error CLASS_E_CLASSNOTAVAILABLECLASS_E_CLASSNOTAVAILABLE
winrt::hresult_illegal_delegate_assignmentwinrt::hresult_illegal_delegate_assignment winrt::hresult_errorwinrt::hresult_error E_ILLEGAL_DELEGATE_ASSIGNMENTE_ILLEGAL_DELEGATE_ASSIGNMENT
winrt::hresult_illegal_method_callwinrt::hresult_illegal_method_call winrt::hresult_errorwinrt::hresult_error E_ILLEGAL_METHOD_CALLE_ILLEGAL_METHOD_CALL
winrt::hresult_illegal_state_changewinrt::hresult_illegal_state_change winrt::hresult_errorwinrt::hresult_error E_ILLEGAL_STATE_CHANGEE_ILLEGAL_STATE_CHANGE
winrt::hresult_invalid_argumentwinrt::hresult_invalid_argument winrt::hresult_errorwinrt::hresult_error E_INVALIDARGE_INVALIDARG
winrt::hresult_no_interfacewinrt::hresult_no_interface winrt::hresult_errorwinrt::hresult_error E_NOINTERFACEE_NOINTERFACE
winrt::hresult_not_implementedwinrt::hresult_not_implemented winrt::hresult_errorwinrt::hresult_error E_NOTIMPLE_NOTIMPL
winrt::hresult_out_of_boundswinrt::hresult_out_of_bounds winrt::hresult_errorwinrt::hresult_error E_BOUNDSE_BOUNDS
winrt::hresult_wrong_threadwinrt::hresult_wrong_thread winrt::hresult_errorwinrt::hresult_error RPC_E_WRONG_THREADRPC_E_WRONG_THREAD

请注意,每个类(通过 hresult_error 基类)均提供 to_abi 函数,其返回错误 HRESULT,并提供 message 函数,其返回该 HRESULT 的字符串表示形式。Note that each class (via the hresult_error base class) provides a to_abi function, which returns the HRESULT of the error, and a message function, which returns the string representation of that HRESULT.

下面是在 C++/CX 中抛出异常的示例。Here's an example of throwing an exception in C++/CX.

throw ref new Platform::InvalidArgumentException(L"A valid User is required");

以及 C++/WinRT 中的等效项。And the equivalent in C++/WinRT.

throw winrt::hresult_invalid_argument{ L"A valid User is required" };

将 Platform::Object^ 移植到 winrt::Windows::Foundation::IInspectablePort Platform::Object^ to winrt::Windows::Foundation::IInspectable

与所有 C++/WinRT 类型一样,winrt::Windows::Foundation::IInspectable 属于值类型。Like all C++/WinRT types, winrt::Windows::Foundation::IInspectable is a value type. 下面介绍如何初始化类型为 null 的变量。Here's how you initialize a variable of that type to null.

winrt::Windows::Foundation::IInspectable var{ nullptr };

从 Platform::String^ 移植到 winrt::hstringPort Platform::String^ to winrt::hstring

Platform::String^ 等同于 Windows 运行时 HSTRING ABI 类型。Platform::String^ is equivalent to the Windows Runtime HSTRING ABI type. 对于 C++/WinRT,等效项是 winrt::hstringFor C++/WinRT, the equivalent is winrt::hstring. 但使用 C++/WinRT,你可以使用 C++ 标准库宽字符串类型(如 std::wstring)和/或宽字符串文字调用 Windows 运行时 API。But with C++/WinRT, you can call Windows Runtime APIs using C++ Standard Library wide string types such as std::wstring, and/or wide string literals. 有关更多详细信息和代码示例,请参阅 C++/WinRT 中的字符串处理For more details, and code examples, see String handling in C++/WinRT.

通过 C++/CX,你可以访问 Platform::String::Data 属性来作为 C 样式 const wchar_t*_ 数组检索字符串(例如,将其传递到 _std::wcout)。With C++/CX, you can access the Platform::String::Data property to retrieve the string as a C-style *const wchar_t*_ array (for example, to pass it to _* std::wcout**).

auto var{ titleRecord->TitleName->Data() };

使用 C++/WinRT 也一样,你可以使用 hstring::c_str 函数获取 null 结尾的 C 样式字符串版本,就像从 std::wstring 获取的一样。To do the same with C++/WinRT, you can use the hstring::c_str function to get a null-terminated C-style string version, just as you can from std::wstring.

auto var{ titleRecord.TitleName().c_str() };

在实现获取或返回字符串的 API 时,通常要将使用 Platform::String^ 的任何 C++/CX 代码更改为使用 winrt::hstring。When it comes to implementing APIs that take or return strings, you typically change any C++/CX code that uses Platform::String^ to use winrt::hstring instead.

下面是获取字符串的 C++/CX API 的示例。Here's an example of a C++/CX API that takes a string.

void LogWrapLine(Platform::String^ str);

对于 C++/WinRT,你可以像这样在 MIDL 3.0 中声明该 API。For C++/WinRT you could declare that API in MIDL 3.0 like this.

// LogType.idl
void LogWrapLine(String str);

C++/WinRT 工具链随后将为你生成源代码,如下所示。The C++/WinRT toolchain will then generate source code for you that looks like this.

void LogWrapLine(winrt::hstring const& str);

ToString()ToString()

C++/CX 类型提供 Object::ToString 方法。C++/CX types provide the Object::ToString method.

int i{ 2 };
auto s{ i.ToString() }; // s is a Platform::String^ with value L"2".

C++/ WinRT 不直接提供此工具,不过可以转为使用替代方法。C++/WinRT doesn't directly provide this facility, but you can turn to alternatives.

int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".

C++/WinRT 也支持 winrt::to_hstring,但仅限数目有限的一些类型。C++/WinRT also supports winrt::to_hstring for a limited number of types. 对于任何其他需要字符串化的类型,你需要添加重载。You'll need to add overloads for any additional types you want to stringify.

LanguageLanguage 将整数字符串化Stringify int 将枚举字符串化Stringify enum
C++/CXC++/CX String^ result = "hello, " + intValue.ToString(); String^ result = "status: " + status.ToString();
C++/WinRTC++/WinRT hstring result = L"hello, " + to_hstring(intValue); // must define overload (see below)
hstring result = L"status: " + to_hstring(status);

如果将枚举字符串化,则需提供 winrt::to_hstring 的实现。In the case of stringifying an enum, you will need to provide the implementation of winrt::to_hstring.

namespace winrt
{
    hstring to_hstring(StatusEnum status)
    {
        switch (status)
        {
        case StatusEnum::Success: return L"Success";
        case StatusEnum::AccessDenied: return L"AccessDenied";
        case StatusEnum::DisabledByPolicy: return L"DisabledByPolicy";
        default: return to_hstring(static_cast<int>(status));
        }
    }
}

这些字符串化通常通过数据绑定来隐式使用。These stringifications are often consumed implicitly by data binding.

<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>

这些绑定会对被绑定属性执行 winrt::to_hstringThese bindings will perform winrt::to_hstring of the bound property. 至于第二个示例 (StatusEnum),则必须提供你自己的 winrt::to_hstring 重载,否则会出现编译器错误。In the case of the second example (the StatusEnum), you must provide your own overload of winrt::to_hstring, otherwise you'll get a compiler error.

字符串生成String-building

C++/CX 和 C++/WinRT 按照标准的 std::wstringstream 来生成字符串。C++/CX and C++/WinRT defer to the standard std::wstringstream for string building.

操作Operation C++/CXC++/CX C++/WinRTC++/WinRT
追加字符串,保留 nullAppend string, preserving nulls stream.print(s->Data(), s->Length); stream << std::wstring_view{ s };
追加字符串,在遇到第一个 null 时停止Append string, stop on first null stream << s->Data(); stream << s.c_str();
提取结果Extract result ws = stream.str(); ws = stream.str();

更多示例More examples

在下面的示例中,ws 是一个类型为 std::wstring 的变量。In the examples below, ws is a variable of type std::wstring. 另外,虽然 C++/CX 可以根据 8 位字符串来构造 Platform::String,C++/WinRT 并不这样做。Also, while C++/CX can construct a Platform::String from an 8-bit string, C++/WinRT doesn't do that.

操作Operation C++/CXC++/CX C++/WinRTC++/WinRT
根据文本构造字符串Construct string from literal String^ s = "hello";
String^ s = L"hello";
// winrt::hstring s{ "hello" }; // Doesn't compile
winrt::hstring s{ L"hello" };
std::wstring 进行转换,保留 nullConvert from std::wstring, preserving nulls String^ s = ref new String(ws.c_str(),
  (uint32_t)ws.size());
winrt::hstring s{ ws };
s = winrt::hstring(ws);
// s = ws; // Doesn't compile
std::wstring 进行转换,在遇到第一个 null 时停止Convert from std::wstring, stop on first null String^ s = ref new String(ws.c_str()); winrt::hstring s{ ws.c_str() };
s = winrt::hstring(ws.c_str());
// s = ws.c_str(); // Doesn't compile
转换为 std::wstring,保留 nullConvert to std::wstring, preserving nulls std::wstring ws{ s->Data(), s->Length };
ws = std::wstring(s>Data(), s->Length);
std::wstring ws{ s };
ws = s;
转换为 std::wstring,在遇到一个 null 时停止Convert to std::wstring, stop on first null std::wstring ws{ s->Data() };
ws = s->Data();
std::wstring ws{ s.c_str() };
ws = s.c_str();
将文本传递给方法Pass literal to method Method("hello");
Method(L"hello");
// Method("hello"); // Doesn't compile
Method(L"hello");
std::wstring 传递给方法Pass std::wstring to method Method(ref new String(ws.c_str(),
  (uint32_t)ws.size()); // Stops on first null
Method(ws);
// param::winrt::hstring accepts std::wstring_view

重要的 APIImportant APIs