添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

本主题是系列文章中的第一篇,介绍如何将 C++/CX 项目中的源代码移植到 C++/WinRT 中的等效项。

如果你的项目还使用 Windows 运行时 C++ 模板库 (WRL) 类型,请参阅 从 WRL 迁移到 C++/WinRT

值得注意的是,从 C++/CX 到 C++/WinRT 的移植通常很简单,但从 并行模式库 (PPL) 任务迁移到协同程序的情况例外。 其模型不同。 从 PPL 任务到协同程序没有自然的一对一映射,也没有适用于所有情况的机械移植代码的简单方法。 有关移植的这一特定方面的帮助信息,以及在这两个模型之间进行互操作的选项,请参阅 实现 C++/WinRT 与 C++/CX 之间的异步和互操作

开发团队通常会报告说,一旦克服了移植异步代码的障碍,其余的移植工作大部分是机械式的。

一次性移植

如果你能够一次性移植整个项目,那么只需要阅读本主题即可获取所需的信息(无需阅读本主题后面的互操作主题)。 建议首先使用 C++/WinRT 项目模板之一在 Visual Studio 中创建一个新项目(请参阅 Visual Studio 对 C++/WinRT 的支持 )。 然后将源代码文件移入该新项目,与此同时,将所有 C++/CX 源代码移植到 C++/WinRT 中。

或者,如果你希望在现有的 C++/CX 项目中进行移植工作,则需要向其添加 C++/WinRT 支持。 采用 C++/CX 项目并添加 C++/WinRT 支持 中介绍了执行此操作的步骤。 完成移植之时,便已将原来的纯 C++/CX 项目变成了纯 C++/WinRT 项目。

如果你有 Windows 运行时组件项目,则一次性移植是你唯一的选项。 用 C++ 编写的 Windows 运行时组件项目包含的要么全部是 C++/CX 源代码,要么全部是 C++/WinRT 源代码。 它们不能在此项目类型中共存。

逐步移植项目

除了前面部分中提到的 Windows 运行时组件项目以外,如果代码库的大小或复杂性使得有必要逐步移植项目,则需要一个移植过程,在此过程中的某段时间,C++/CX 和 C++/WinRT 代码将在同一项目中并存。 除了阅读本主题,还应参阅 实现 C++/WinRT 与 C++/CX 之间的互操作 实现 C++/WinRT 与 C++/CX 之间的异步和互操作 。 这些主题提供了一些信息和代码示例,演示如何在这两种语言投影之间进行互操作。

若要使项目为逐步移植过程做好准备,一种选择是向 C++/CX 项目添加 C++/WinRT 支持。 采用 C++/CX 项目并添加 C++/WinRT 支持 中介绍了执行此操作的步骤。 然后,你就可以从这里逐步进行移植。

另一种选择是使用 C++/WinRT 项目模板之一在 Visual Studio 中创建一个新项目(请参阅 Visual Studio 对 C++/WinRT 的支持 )。 然后向该项目添加 C++/CX 支持。 采用 C++/WinRT 项目并添加 C++/CX 支持 中介绍了执行此操作的步骤。 然后,你可以开始将源代码移入其中,与此同时,将一些 C++/CX 源代码移植到 C++/WinRT 中。

无论是哪种情况,都需要在 C++/WinRT 代码与尚未移植的任何 C++/CX 代码之间进行互操作(双向)。

C++/CX 和 Windows SDK 都在根命名空间 Windows 中声明类型。 投影到 C++/WinRT 的 Windows 类型具有与 Windows 类型相同的完全限定名称,但放置于 C++ winrt 命名空间中。 这些不同的命名空间可让你按照自己的节奏从 C++/CX 移植到 C++/WinRT。

逐步移植 XAML 项目

对于使用 XAML 的项目,无论何时,均要求所有 XAML 页面类型要么完全是 C++/CX,要么完全是 C++/WinRT。 你仍可以在同一项目中 XAML 页面类型以外的位置(在模型和视图模型中以及其他位置)混合使用 C++/CX 和 C++/WinRT。

对于这种情况,我们建议的工作流是创建一个新的 C++/WinRT 项目并从 C++/CX 项目复制源代码和标记。 只要所有 XAML 页面类型都是 C++/WinRT,就可以使用“项目”>“添加新项...”>“Visual C++”>“空白页(C++/WinRT)”来添加新 XAML 页面。

或者,可以在移植 XAML C++/CX 项目时使用 Windows 运行时组件 (WRC) 从中提出代码。

  • 可以创建一个新的 C++/CX WRC 项目,将尽可能多的 C++/CX 代码移入该项目,然后将 XAML 项目更改为 C++/WinRT。
  • 或者,可以创建一个新的 C++/WinRT WRC 项目,将 XAML 项目保留为 C++/CX,然后开始将 C++/CX 移植到 C++/WinRT,将所得到的代码移出 XAML 项目并移入组件项目。
  • 还可以让 C++/CX 组件项目以及 C++/WinRT 组件项目处于同一个解决方案中,从应用程序项目引用两者,然后逐渐从一个项目移植到另一个项目。 同样,请参阅 实现 C++/WinRT 与 C++/CX 之间的互操作 ,了解有关在同一个项目中使用这两种语言投影的更多详细信息。
  • 将 C++/CX 项目移植到 C++/WinRT 的第一步

    无论使用哪种移植策略(一次性移植或逐步移植),第一步都是准备要移植的项目。 下面回顾了 移植策略 中所述的内容,其中涉及你将要开始使用的项目类型以及如何对其进行设置。

  • 一次性移植 。 使用 C++/WinRT 项目模板之一在 Visual Studio 中创建一个新项目。 将文件从 C++/CX 项目移入该新项目,然后移植 C++/CX 源代码。
  • 逐步移植非 XAML 项目 。 可以选择向 C++/CX 项目添加 C++/WinRT 支持(请参阅 采用 C++/CX 项目并添加 C++/WinRT 支持 ),然后逐步进行移植。 也可以选择创建一个新的 C++/WinRT 项目并向其添加 C++/CX 支持(请参阅 采用 C++/WinRT 项目并添加 C++/CX 支持 ),移入文件并逐步进行移植。
  • 逐步移植 XAML 项目 。 创建一个新的 C++/WinRT 项目,移入文件并逐步进行移植。 无论何时,均要求 XAML 页面类型要么完全是 C++/WinRT,要么完全是 C++/CX。
  • 无论选择哪种移植策略,本主题的其余部分均适用。 它包含将源代码从 C++/CX 移植到 C++/WinRT 所涉及的技术细节的目录。 如果要进行逐步移植,则可能还需要查看 实现 C++/WinRT 与 C++/CX 之间的互操作 实现 C++/WinRT 与 C++/CX 之间的异步和互操作

    文件命名约定

    XAML 标记文件

    文件原始格式 C++/CX C++/WinRT 开发人员 XAML 文件 MyPage.xaml
    MyPage.xaml.h
    MyPage.xaml.cpp MyPage.xaml
    MyPage.h
    MyPage.cpp
    MyPage.idl(见下) 生成的 XAML 文件 MyPage.xaml.g.h
    MyPage.xaml.g.hpp MyPage.xaml.g.h
    MyPage.xaml.g.hpp
    MyPage.g.h

    请注意,C++/WinRT 会从 *.h *.cpp 文件名中删除 .xaml

    C++/WinRT 添加了一个额外的开发人员文件,即 Midl 文件 (.idl) 。 C++/CX 在内部自动生成此文件,并将其添加到每个公开的和受保护的成员。 在 C++/WinRT 中,你自行创作并添加此文件。 如需更多详细信息、代码示例以及 IDL 创作演练,请参阅 XAML 控件;绑定到 C++/WinRT 属性

    另请参阅 将运行时类重构到 Midl 文件 (.idl) 中

    C++/CX 不对头文件的名称施加限制;通常会将多个运行时类定义置于单个头文件中,小型类尤其如此。 但是,C++/WinRT 要求每个运行时类将自己的头文件以类名称来命名。

    C++/CX C++/WinRT

    头文件要求

    C++/CX 不要求你包括任何特殊的头文件,因为它会在内部根据 .winmd 文件自动生成头文件。 在 C++/CX 中,通常会对按名称使用的命名空间使用 using 指令。

    using namespace Windows::Media::Playback;
    String^ NameOfFirstVideoTrack(MediaPlaybackItem^ item)
        return item->VideoTracks->GetAt(0)->Name;
    

    可以通过 using namespace Windows::Media::Playback 指令在不使用命名空间前缀的情况下编写 MediaPlaybackItem。 我们还接触了 Windows.Media.Core 命名空间,因为 item->VideoTracks->GetAt(0) 返回 Windows.Media.Core.VideoTrack。 但是,我们不需要在任何位置键入 VideoTrack 这个名称,因此不需要 using Windows.Media.Core 指令。

    但是,C++/WinRT 要求你针对每个所使用的命名空间包括一个头文件,即使不为其命名。

    #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,因为并未使用该事件。

    C++/WinRT 还要求你针对 XAML 标记所使用的命名空间包括相应的头文件。

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

    使用 Rectangle 类意味着你必须添加以下 include。

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

    如果忘记了头文件,则仍可正常编译,但会出现链接器错误,因为缺少 consume_ 类。

    编写 C++/CX 源代码时,在顶帽 (^) 引用时,你将 C++/CX 类型作为函数参数传递。

    void LogPresenceRecord(PresenceRecord^ record);
    

    在 C++/WinRT 中,对于同步函数,默认情况下应该使用 const& 参数。 这将避免复制和互锁开销。 但你的协同程序应使用按值传递来确保它们按值捕获,并避免生命周期问题(更多详细信息,请参阅利用 C++/WinRT 实现的并发和异步操作)。

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

    C++/WinRT 对象根本上是一个保留支持 Windows 运行时对象的接口指针的值。 在复制 C++/WinRT 对象时,编译器复制封装的接口指针,从而递增其引用计数。 副本的最终销毁涉及递减引用计数。 因此,仅在必要时产生复制开销。

    变量和字段引用

    编写 C++/CX 源代码时,你使用顶帽 (^) 变量引用 Windows 运行时对象,使用箭头 (->) 运算符来取消引用顶帽变量。

    IVectorView<User^>^ userList = User::Users;
    if (userList != nullptr)
        for (UINT32 iUser = 0; iUser < userList->Size; ++iUser)
    

    移植到等效 C++/WinRT 代码时,可以通过删除顶帽,并将箭头运算符 (->) 更改为点运算符 (.),来完成大量工作。 C++/WinRT 投影类型是值,而不是指针。

    IVectorView<User> userList = User::Users();
    if (userList != nullptr)
        for (UINT32 iUser = 0; iUser < userList.Size(); ++iUser)
    

    C++/CX 顶帽引用的默认构造函数会将它初始化为 null。 下面是一个 C++/CX 代码示例,我们在其中创建一个具有正确类型,但是未初始化的变量/字段。 换句话说,它最初不引用 TextBlock;我们打算在以后分配引用。

    TextBlock^ textBlock;
    class MyClass
        TextBlock^ textBlock;
    

    有关 C++/WinRT 中的等效项,请参阅延迟初始化

    C++/CX 语言扩展包括属性概念。 编写 C++/CX 源代码时,你可以像访问字段那样访问属性。 标准 C++ 没有属性概念,因此,在 C++/WinRT 中,你调用获取和设置函数。

    在随后的示例中,XboxUserId、UserState、PresenceDeviceRecords 和 Size 全部都是属性。

    从属性检索值

    下面介绍如何在 C++/CX 中获取属性值。

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

    等效的 C++/WinRT 源代码调用与属性同名但没有参数的函数。

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

    请注意,PresenceDeviceRecords 函数返回其本身具有 Size 函数的 Windows 运行时对象。 由于返回的对象也是 C++/WinRT 投影类型,因此我们使用点运算符调用 Size 来取消引用。

    将属性设置为新值

    将属性设置为新值遵循类似模式。 首先,在 C++/CX 中。

    record->UserState = newValue;
    

    若要在 C++/WinRT 执行同等操作,调用与属性同名的函数,并传递参数。

    record.UserState(newValue);
    

    创建类的实例

    你通过 C++/CX 对象的句柄来处理它,通常称为顶帽 (^) 引用。 通过 ref new 关键字创建新对象,这反过来会调用 RoActivateInstance 来激活运行时类的新实例。

    using namespace Windows::Storage::Streams;
    class Sample
    private:
        Buffer^ m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
    

    C++/WinRT 对象是一个值;因此你可以在堆栈上进行分配,或作为对象字段分配。 切勿使用 ref new(或 new)来分配 C++/WinRT 对象。 在后台,仍然在调用 RoActivateInstance。

    using namespace winrt::Windows::Storage::Streams;
    struct Sample
    private:
        Buffer m_gamerPicBuffer{ MAX_IMAGE_SIZE };
    

    如果初始化资源的成本很高,通常可以推迟资源的初始化,直到实际需要时再执行。 如前所述,C++/CX 顶帽引用的默认构造函数会将它初始化为 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 的同一个代码。 请注意 std::nullptr_t 构造函数的使用。 有关该构造函数的详细信息,请参阅延迟初始化

    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 };
    

    默认构造函数如何影响集合

    C++ 集合类型使用默认构造函数,这可能导致意外的对象构造。

    C++/CX C++/WinRT(不正确) C++/WinRT(正确) 成员变量,一开始为空 class C {
      TextBox^ textBox;
    }; class C {
      TextBox textBox; // Creates a TextBox!
    }; class C {
      TextBox textbox{ nullptr };
    }; 全局变量,一开始为空 TextBox^ g_textBox; TextBox g_textBox; // Creates a TextBox! TextBox g_textBox{ nullptr }; 空引用的矢量 std::vector<TextBox^> boxes(10); // Creates 10 TextBox objects!
    std::vector<TextBox> boxes(10); std::vector<TextBox> boxes(10, nullptr); 在 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); 空引用的数组 TextBox^ boxes[2]; // Creates 2 TextBox objects!
    TextBox boxes[2]; TextBox boxes[2] = { nullptr, nullptr }; std::pair<TextBox^, String^> p; // Creates a TextBox!
    std::pair<TextBox, String> p; std::pair<TextBox, String> p{ nullptr, nullptr };

    有关空引用集合的详细信息

    只要在 C++/CX 中有一个 Platform::Array(参见移植 Platform::Array),即可将其移植到 C++/WinRT 中的 std::vector(事实上,可以将其移植到任何邻近的容器),而不是将其作为数组保留。 选择 std::vector 有多种优势。

    例如,尽管存在用于创建固定大小的空引用矢量(参见上表)的速记,但没有用于创建空引用数组的速记。 必须针对数组中的每个元素重复执行 nullptr。 如果构造的数目太少,则会默认构造额外的。

    对于矢量,可以在初始化时使用空引用填充它(如上表所示),也可以借助如下所示的代码在初始化后使用空引用填充它。

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

    有关 std::map 示例的详细信息

    std::map[] 下标运算符的行为如下所示。

  • 如果在 map 中发现此键,则返回现有值(可以将其覆盖)的引用。
  • 如果未在 map 中发现此键,则在包含此键(在可移动的情况下已经移动)的 map 中创建新条目和默认构造的值,并返回此值(可以随后将其覆盖)的引用。
  • 换言之,[] 运算符始终在 map 中创建一个条目。 这不同于 C#、Java 和 JavaScript。

    从运行时基类转换为派生类

    通常有一个已知引用派生类型对象的基类引用。 在 C++/CX 中,使用 dynamic_cast 将基类引用强制转换为派生类引用。 dynamic_cast 实际上只是对 QueryInterface 的隐藏调用。 下面是一个典型示例:你要处理依赖属性更改事件,并且要从 DependencyObject 强制转换回拥有依赖属性的实际类型。

    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。 还可以选择改为调用 IUnknown::as,这会在未返回对所需接口(具有所请求的类型的默认接口)的查询时引发异常。 下面是 C++/WinRT 代码示例。

    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 ...
            BgLabelControlApp::BgLabelControl theControl{ d.as<BgLabelControlApp::BgLabelControl>() };
            // succeeded ...
        catch (winrt::hresult_no_interface const&)
            // failed ...
    

    若要从运行时类派生,基类必须是可组合类。 C++/CX 不需要你执行任何特殊步骤即可将类变为可组合类,但 C++/WinRT 需要。 请使用 unsealed 关键字来指示你希望将类用作基类。

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

    在实现标头类中,在包括为派生类自动生成的标头之前,必须包括基类标头文件。 否则会出现“将此类型用作表达式非法”之类的错误。

    // 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>
    

    通过代理进行事件处理

    下面介绍了在 C++/CX 中处理事件的典型示例,在本例中将 lambda 函数用作代理。

    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 中的等效项。

    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 函数,你可以选择作为自由函数或指向成员函数的指针实现代理。 有关详细信息,请参阅使用 C++/WinRT 中的代理来处理事件

    如果你正在从内部使用(不是跨二进制文件)事件和代理的 C++/CX 基本代码移植,winrt::delegate 将帮助你复制 C++/WinRT 中的这个模式。 另请参阅项目中的参数化委托、简单信号和回调

    在 C++/CX 中,使用 -= 运算符来撤销之前的事件注册。

    myButton->Click -= token;
    

    它是 C++/WinRT 中的等效项。

    myButton().Click(token);
    

    有关详细信息和选项,请参阅撤销已注册的代理

    装箱和取消装箱

    C++/CX 自动将标量装箱到对象中。 C++/WinRT 要求你显式调用 winrt::box_value 函数。 两种语言都要求你以显式方式取消装箱。 请参阅使用 C++/WinRT 装箱和取消装箱

    在下面的表中,我们将使用这些定义。

    C++/CX C++/WinRT 取消整数的装箱,在为 null 的情况下使用回退;任何其他情况则崩溃 i = o ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback; 尽可能取消整数的装箱;在任何其他情况下使用回退 auto box = dynamic_cast<IBox<int>^>(o);
    i = box ? box->Value : fallback; i = unbox_value_or<int>(o, fallback);

    将字符串装箱和取消装箱

    字符串在某些情况下是值类型,在另一些情况下是引用类型。 C++/CX 和 C++/WinRT 对待字符串的方式有所不同。

    ABI 类型 HSTRING 是一个指向引用计数字符串的指针。 但是,它并非派生自 IInspectable,因此从技术上来说它不是一个对象。 另外,null HSTRING 表示空字符串。 将并非派生自 IInspectable 的项装箱时,需将其包装到 IReference<T> 中,而 Windows 运行时会以 PropertyValue 对象的形式提供标准实现(自定义类型以 PropertyType::OtherType 形式报告)。

    C++/CX 将 Windows 运行时字符串表示为引用类型,而 C++/WinRT 则将字符串投影为值类型。 这意味着装箱的 null 字符串可能有不同的表示形式,具体取决于你所采用的方法。

    另外,C++/CX 允许取消引用 null String^ ,在这种情况下,其行为类似于字符串 ""

    C++/CX C++/WinRT 如果将 null 字符串分配给对象 o = (String^)nullptr;
    o == nullptr o = box_value(hstring{});
    o != nullptr 如果将 "" 分配给对象 o = "";
    o == nullptr o = box_value(hstring{L""});
    o != nullptr

    基本装箱和取消装箱。

    C++/CX C++/WinRT 取消已知字符串的装箱 s = (String^)o;
    Null 对象变为空字符串。
    如果不是字符串,则引发 InvalidCastException。 s = unbox_value<hstring>(o);
    Null 对象崩溃。
    如果不是字符串,则崩溃。 将可能的字符串取消装箱 s = dynamic_cast<String^>(o);
    Null 对象或非字符串变为空字符串。 s = unbox_value_or<hstring>(o, fallback);
    Null 或非字符串变为 fallback。
    空字符串被保留。

    并发和异步操作

    并行模式库 (PPL)(例如 concurrency::task)已更新,现在支持 C++/CX 顶帽引用。

    对于 C++/WinRT,应改用协同程序和 co_await。 详细信息和代码示例,请参阅利用 C++/WinRT 实现的并发和异步运算

    使用 XAML 标记中的对象

    在 C++/CX 项目中,可以使用 XAML 标记中的专用成员和命名元素。 但在 C++/WinRT 中,以 XAML {x:Bind} 标记扩展形式使用的所有实体必须在 IDL 中以公开方式公开。

    另外,绑定到布尔值时,在 C++/CX 中会显示 truefalse,但在 C++/WinRT 中会显示 Windows.Foundation.IReference`1<布尔值>

    有关详细信息和代码示例,请参阅使用标记中的对象

    将 C++/CX 平台类型映射到 C++/WinRT 类型

    C++/CX 在平台命名空间中提供了多个数据类型。 这些类型不是标准的 C++,因此只能在启用 Windows 运行时语言扩展(Visual Studio 项目属性“C/C++”>“常规”>“使用 Windows 运行时扩展”>“是(/ZW)”)的情况下使用。 下表帮助你从平台类型移植到 C++/WinRT 中的等效项。 完成后,由于 C++/WinRT 是标准 C++,因此你可以关闭 /ZW 选项。

    C++/CX C++/WinRT

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

    C++/CX 中的 Platform::Agile^ 类型表示可以从任何线程访问的 Windows 运行时类。 C++/WinRT 的等效项是 winrt::agile_ref

    在 C++/CX 中。

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

    在 C++/WinRT 中。

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

    移植 Platform::Array

    在 C++/CX 要求使用数组的情况下,C++/WinRT 允许使用任何相邻的容器。 请参阅默认构造函数如何影响集合,了解为何可以使用 std::vector

    因此,只要在 C++/CX 中有 Platform::Array^,移植选项中就会包括使用初始值设定项列表、std::array 或 std::vector 的选项。 有关详细信息和代码示例,请参阅标准初始值设定项列表标准数组和矢量

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

    当 Windows 运行时 API 返回非 S_OK HRESULT 时,Platform::Exception^ 类型在 C++/CX 中生成。 C++/WinRT 的等效项是 winrt::hresult_error

    若要移植到 C++/WinRT,将使用 Platform::Exception^ 的所有代码更改为使用 winrt::hresult_error。

    在 C++/CX 中。

    catch (Platform::Exception^ ex)
    

    在 C++/WinRT 中。

    catch (winrt::hresult_error const& ex)
    

    C++/WinRT 提供这些异常类。

    HRESULT winrt::hresult_illegal_delegate_assignment winrt::hresult_error E_ILLEGAL_DELEGATE_ASSIGNMENT winrt::hresult_illegal_method_call winrt::hresult_error E_ILLEGAL_METHOD_CALL winrt::hresult_illegal_state_change winrt::hresult_error E_ILLEGAL_STATE_CHANGE winrt::hresult_invalid_argument winrt::hresult_error E_INVALIDARG winrt::hresult_no_interface winrt::hresult_error E_NOINTERFACE winrt::hresult_not_implemented winrt::hresult_error E_NOTIMPL winrt::hresult_out_of_bounds winrt::hresult_error E_BOUNDS winrt::hresult_wrong_thread winrt::hresult_error RPC_E_WRONG_THREAD

    请注意,每个类(通过 hresult_error 基类)均提供 to_abi 函数,其返回错误 HRESULT,并提供 message 函数,其返回该 HRESULT 的字符串表示形式。

    下面是在 C++/CX 中抛出异常的示例。

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

    以及 C++/WinRT 中的等效项。

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

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

    与所有 C++/WinRT 类型一样,winrt::Windows::Foundation::IInspectable 属于值类型。 下面介绍如何初始化类型为 null 的变量。

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

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

    Platform::String^ 等同于 Windows 运行时 HSTRING ABI 类型。 对于 C++/WinRT,等效项是 winrt::hstring。 但使用 C++/WinRT,你可以使用 C++ 标准库宽字符串类型(如 std::wstring)和/或宽字符串文字调用 Windows 运行时 API。 有关更多详细信息和代码示例,请参阅 C++/WinRT 中的字符串处理

    通过 C++/CX,你可以访问 Platform::String::Data 属性来作为 C 样式 wchar_t* 数组检索字符串(例如,将其传递到 std::wcout)。

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

    使用 C++/WinRT 也一样,你可以使用 hstring::c_str 函数获取 null 结尾的 C 样式字符串版本,就像从 std::wstring 获取的一样。

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

    在实现获取或返回字符串的 API 时,通常要将使用 Platform::String^ 的任何 C++/CX 代码更改为使用 winrt::hstring。

    下面是获取字符串的 C++/CX API 的示例。

    void LogWrapLine(Platform::String^ str);
    

    对于 C++/WinRT,你可以像这样在 MIDL 3.0 中声明该 API。

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

    C++/WinRT 工具链随后将为你生成源代码,如下所示。

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

    ToString()

    C++/CX 类型提供 Object::ToString 方法。

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

    C++/ WinRT 不直接提供此工具,不过可以转为使用替代方法。

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

    C++/WinRT 也支持 winrt::to_hstring,但仅限数目有限的一些类型。 对于任何其他需要字符串化的类型,你需要添加重载。

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

    如果将枚举字符串化,则需提供 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));
    

    这些字符串化通常通过数据绑定来隐式使用。

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

    这些绑定会对被绑定属性执行 winrt::to_hstring。 至于第二个示例 (StatusEnum),则必须提供你自己的 winrt::to_hstring 重载,否则会出现编译器错误。

    字符串生成

    C++/CX 和 C++/WinRT 按照标准的 std::wstringstream 来生成字符串。

    C++/CX C++/WinRT 根据文本构造字符串 String^ s = "hello";
    String^ s = L"hello"; // winrt::hstring s{ "hello" }; // Doesn't compile
    winrt::hstring s{ L"hello" };std::wstring 进行转换,保留 null String^ s = ref new String(ws.c_str(),
      (uint32_t)ws.size()); winrt::hstring s{ ws };
    s = winrt::hstring(ws);
    // s = ws; // Doesn't compilestd::wstring 进行转换,在遇到第一个 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,保留 null std::wstring ws{ s->Data(), s->Length };
    ws = std::wstring(s>Data(), s->Length); std::wstring ws{ s };
    ws = s; 转换为 std::wstring,在遇到一个 null 时停止 std::wstring ws{ s->Data() };
    ws = s->Data(); std::wstring ws{ s.c_str() };
    ws = s.c_str(); 将文本传递给方法 Method("hello");
    Method(L"hello"); // Method("hello"); // Doesn't compile
    Method(L"hello");std::wstring 传递给方法 Method(ref new String(ws.c_str(),
      (uint32_t)ws.size()); // Stops on first null Method(ws);
    // param::winrt::hstring accepts std::wstring_view

    重要的 API

  • winrt::delegate 结构模板
  • winrt::hresult_error 结构
  • winrt::hstring 结构
  • winrt 命名空间
  • C++/CX
  • 在 C++/WinRT 中创作事件
  • 利用 C++/WinRT 实现的并发和异步操作
  • 通过 C++/WinRT 使用 API
  • 在 C++/WinRT 中使用委托处理事件
  • 实现 C++/WinRT 与 C++/CX 之间的互操作
  • 实现 C++/WinRT 与 C++/CX 之间的异步和互操作
  • Microsoft 接口定义语言 3.0 参考
  • 从 WRL 移动到 C++/WinRT
  • C++/WinRT 中的字符串处理
  •