次の方法で共有


C++/CX から C++/WinRT への移行

このトピックは、C++/CX プロジェクトのソース コードを、C++/WinRTで同等のプロジェクトに移植する方法を説明するシリーズの最初のトピックです。

プロジェクトで Windows ランタイム C++ テンプレート ライブラリ (WRL) 型も使用している場合は、「WRLから C++/WinRT に移動する」参照してください。

移植の戦略

C++/CX から C++/WinRT への移植は一般的に簡単です。ただし、並列パターン ライブラリ (PPL) タスクからコルーチンへの移行は例外です。 モデルは異なります。 PPL タスクからコルーチンへの自然な 1 対 1 のマッピングはなく、すべてのケースで動作するコードを機械的に移植する簡単な方法はありません。 移植のこの特定の側面と、2 つのモデル間の相互運用のオプションについては、「非同期の と、C++/WinRT と C++/CX間の相互運用」を参照してください。

開発チームは、非同期コードの移植のハードルを超えると、移植作業の残りの部分は主に機械的であると日常的に報告しています。

1 つのパスでの移植

プロジェクト全体を 1 回のパスで移植できる立場にある場合は、必要な情報に対してのみこのトピックが必要になります (このトピックに続く 相互運用 トピックは必要ありません)。 まず、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 ランタイム コンポーネント プロジェクトがある場合は、1 回のパスでの移植のみが唯一のオプションです。 C++ で記述された Windows ランタイム コンポーネント プロジェクトには、すべての C++/CX ソース コードまたはすべての C++/WinRT ソース コードが含まれている必要があります。 この種類のプロジェクトでは共存できません。

プロジェクトを段階的に移植する

前のセクションで説明したように、Windows ランタイム コンポーネント プロジェクトを除き、コードベースのサイズや複雑さがプロジェクトを徐々に移植する必要がある場合は、C++/CX と C++/WinRT コードが同じプロジェクトに並べて存在する移植プロセスが必要になります。 このトピックの読み方に加えて、C++/WinRT と C++/CX と Asynchrony の間の相互運用、および C++/WinRT と C++/CX間の相互運用の も参照してください。 これらのトピックでは、2 つの言語プロジェクション間で相互運用する方法を示す情報とコード例を提供します。

段階的な移植プロセスに備えてプロジェクトを準備するには、C++/WinRT のサポートを C++/CX プロジェクトに追加する方法があります。 これを行う手順については、「C++/CX プロジェクトの作成と C++/WinRT サポートの追加」で説明されています。 その後、そこから徐々に移植できます。

もう 1 つのオプションは、C++/WinRT プロジェクト テンプレートのいずれかを使用して Visual Studio で新しいプロジェクトを作成することです (C++/WinRTの Visual Studio のサポート 参照)。 その後、そのプロジェクトに 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 プロジェクトを段階的に移植する

Von Bedeutung

XAML を使用するプロジェクトの場合、すべての XAML ページ型は、常に完全に C++/CX または完全に C++/WinRT である必要があります。 C++/CX と C++/WinRT は、同じプロジェクト内 (モデルやビューモデルなど) 内の XAML ページ型の 外でも混在させることができます。

このシナリオでは、新しい C++/WinRT プロジェクトを作成し、C++/CX プロジェクトからソース コードとマークアップをコピーすることをお勧めします。 すべての XAML ページの種類が C++/WinRT である限り、Project>新しい項目の追加...>Visual C++>空白ページ (C++/WinRT)を使用して新しい XAML ページを追加できます。

または、Windows ランタイム コンポーネント (WRC) を使用して、移植時に XAML C++/CX プロジェクトからコードを要素化することもできます。

  • 新しい C++/CX WRC プロジェクトを作成し、できるだけ多くの C++/CX コードをそのプロジェクトに移動してから、XAML プロジェクトを C++/WinRT に変更できます。
  • または、新しい C++/WinRT WRC プロジェクトを作成し、XAML プロジェクトを C++/CX のままにして、C++/CX を C++/WinRT に移植し、結果のコードを XAML プロジェクトからコンポーネント プロジェクトに移動することもできます。
  • また、C++/CX コンポーネント プロジェクトと C++/WinRT コンポーネント プロジェクトを同じソリューション内に配置し、両方をアプリケーション プロジェクトから参照し、徐々に一方から他方に移植することもできます。 ここでも、同じプロジェクトで 2 つの言語プロジェクションを使用する方法の詳細については、C++/WinRT と C++/CX 間の相互運用の を参照してください。

C++/CX プロジェクトを C++/WinRT に移植する最初の手順

移植戦略 (1 回のパスでの移植、または段階的な移植) に関係なく、最初の手順は、移植用にプロジェクトを準備することです。 開始するプロジェクトの種類と設定方法の観点から、 を移植するための 戦略で説明した内容の要約を次に示します。

  • 一回で移植します。 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 の Asynchrony との相互運用も参考にすると良いでしょう。

ファイルの名前付け規則

XAML マークアップ ファイル

ファイルの配信元 C++/CX C++/WinRT
Developer 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 では、.xaml*.h ファイル名から *.cpp が削除されます。

C++/WinRT は、Midl ファイル (.idl)追加の開発者ファイルを追加します。 C++/CX は、このファイルを自動生成し、各パブリックおよび保護されたメンバーを追加します。 C++/WinRT では、ファイルを自分で追加して作成します。 IDL の作成の詳細、コード例、およびチュートリアルについては、XAML コントロール 参照してください。C++/WinRT プロパティにバインドします。

ランタイム クラスを Midl ファイル (.idl) に分解する も参照してください。

ランタイム クラス

C++/CX では、ヘッダー ファイルの名前に制限はありません。複数のランタイム クラス定義を 1 つのヘッダー ファイル (特に小規模なクラスの場合) に配置するのが一般的です。 ただし、C++/WinRT では、各ランタイム クラスに、クラス名にちなんだ独自のヘッダー ファイルが必要です。

C++/CX C++/WinRT
Common.h
ref class A { ... }
ref class B { ... }
Common.idl
runtimeclass A { ... }
runtimeclass B { ... }
A.h
namespace implements {
  struct A { ... };
}
B.h
namespace implements {
  struct B { ... };
}

C++/CX では、XAML カスタム コントロールに異なる名前のヘッダー ファイルを使用することはあまり一般的ではありません (ただし、まだ有効です)。 クラス名と一致するように、これらのヘッダー ファイルの名前を変更する必要があります。

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

ヘッダー ファイルの要件

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.VideoTrackを返すので、 名前空間にも触れました。 しかし、VideoTrack 名前をどこにも入力する必要はなかったので、 ディレクティブは必要ありませんでした。

ただし、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 クラスを使用すると、このインクルードを追加する必要があります。

// 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 ソース コードを記述するときは、Hat (^) 変数を使用して 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 Hat 参照の既定のコンストラクターは、それを null に初期化します。 正しい型の変数/フィールドを作成する C++/CX コード例を次に示しますが、初期化されていません。 つまり、最初は TextBlockを参照していません。後で参照を割り当てる予定です。

TextBlock^ textBlock;

class MyClass
{
    TextBlock^ textBlock;
};

C++/WinRT での同等の機能については、「遅延初期化を参照してください。

特性

C++/CX 言語拡張機能には、プロパティの概念が含まれています。 C++/CX ソース コードを記述する場合は、フィールドであるかのようにプロパティにアクセスできます。 標準 C++ にはプロパティの概念がないため、C++/WinRT では get 関数と set 関数を呼び出します。

次の例では、XboxUserIdUserStatePresenceDeviceRecords、および 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 オブジェクトは、そのハンドル (一般に帽子 (^) 参照と呼ばれます) を介して操作します。 キーワードを使用して新しいオブジェクトを作成し、RoActivateInstance 呼び出してランタイム クラスの新しいインスタンスをアクティブ化します。

using namespace Windows::Storage::Streams;

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

C++/WinRT オブジェクトは値です。スタックに割り当てることもできますし、オブジェクトのフィールドとして割り当てることもできます。 (または ) を使用して C++/WinRT オブジェクトを割り当てる 。 幕の裏で、RoActivateInstance はまだ呼び出されています。

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

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

リソースの初期化にコストがかかる場合は、実際に必要になるまで初期化を遅らせるのが一般的です。 既に説明したように、C++/CX Hat 参照の既定のコンストラクターは 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 (正しい)
ローカル変数(最初は空) TextBox^ textBox; TextBox textBox; // Creates a TextBox! TextBox textBox{ nullptr };
メンバー変数 (最初は空) 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);
マップで値を設定する 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^ (Port 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 例の詳細

[] 添字演算子は、次のように動作します。

  • キーがマップ内に見つかった場合は、既存の値への参照を返します (上書きできます)。
  • キーがマップに見つからない場合は、キー (移動可能な場合は移動可能) で構成される新しいエントリをマップに作成し、既定で構築された値し、値への参照を返します (上書きできます)。

つまり、[] 演算子は常にマップにエントリを作成します。 これは、C#、Java、JavaScript とは異なります。

基本ランタイム クラスから派生クラスへの変換

ある型が派生型のオブジェクトに関していることが分かっているベース参照が存在するのは一般的によくあります。 C++/CX では、 を使用して、参照から派生 に キャストします。 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 コードは、 を、QueryInterfaceカプセル化する IUnknown::try_as 関数の呼び出しに置き換えます。 また、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 ...
    }

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

派生クラス

ランタイム クラスから派生するには、基底クラスは 構成可能である必要があります。 C++/CX では、クラスを構成可能にするために特別な手順を踏む必要はありませんが、C++/WinRT では特別な手順が必要です。 封印解除キーワード を使用して、クラスが基底クラスとして利用可能であることを示します。

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 でイベントを処理する一般的な例を次に示します。

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

ラムダ関数の代わりに、デリゲートを無料の関数として実装するか、メンバー関数へのポインターとして実装するかを選択できます。 詳細については、「C++/WinRTでデリゲートを使用してイベントを処理する」を参照してください。

イベントとデリゲートが (バイナリ間ではなく) 内部的に使用される C++/CX コードベースから移植する場合は、winrt::d elegate を すると、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
int i; int i;
String^ s; winrt::hstring s;
Object^ o; IInspectable o;
オペレーション C++/CX C++/WinRT
ボクシング o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
開封の儀 i = (int)o;
s = (String^)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

値型への null ポインターのボックス化を解除しようとすると、C++/CX と C# で例外が発生します。 C++/WinRT では、これがプログラミング エラーと見なされ、クラッシュします。 C++/WinRT では、オブジェクトが思った型ではない場合に処理する場合は、winrt::unbox_value_or 関数を使用します。

シナリオ C++/CX C++/WinRT
既知の整数をアンボックスする i = (int)o; i = unbox_value<int>(o);
o が null の場合 Platform::NullReferenceException クラッシュ
o がボックス化された int でない場合 Platform::InvalidCastException クラッシュ
int をボックス化解除し、null の場合はフォールバックを使用します。それ以外の場合はクラッシュする i = o ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
可能な場合は int のボックスを解除します。それ以外の場合はフォールバックを使用する 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 から派生していないものボックス化は、IReferenceT内でラップすることによって行われます。Windows ランタイムは、PropertyValue オブジェクトの形式で標準実装を提供します (カスタム型は PropertyType::OtherTypeとして報告されます)。

C++/CX は、参照型として Windows ランタイム文字列を表します。一方、C++/WinRT は文字列を値型として投影します。 つまり、ボックス化された null 文字列は、取得方法に応じて異なる表現を持つことができます。

さらに、C++/CX を使用すると、null String^を逆参照できます。この場合、文字列 ""のように動作します。

行動 C++/CX C++/WinRT
宣言 Object^ o;
String^ s;
IInspectable o;
hstring s;
文字列型カテゴリ 参照の種類 値の種類
HSTRING はプロジェクトとして null となる (String^)nullptr hstring{}
null と "" は同じですか? イエス イエス
null の有効性 s = nullptr;
s->Length == 0 (有効)
s = hstring{};
s.size() == 0 (有効)
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
文字列をボックス化する o = s;
空の文字列が nullptr になります。
o = box_value(s);
空の文字列は null 以外のオブジェクトになります。
既知の文字列のボックスを解除する 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 または非文字列値がフォールバックに適用されます。
空の文字列は保持されます。

並行性と非同期処理

並列パターン ライブラリ (PPL) (concurrency::taskなど) が、C++/CX Hat 参照をサポートするように更新されました。

C++/WinRT の場合は、代わりにコルーチンと co_await を使用する必要があります。 詳細とコード例については、「C++/WinRTを使用したコンカレンシーと非同期操作の 」を参照してください。

XAML マークアップからのオブジェクトの利用

C++/CX プロジェクトでは、XAML マークアップからプライベート メンバーと名前付き要素を使用できます。 ただし、C++/WinRT では、XAML {x:Bind} マークアップ拡張 を使用して使用されるすべてのエンティティを IDL でパブリックに公開する必要があります。

また、ブール値にバインドすると、C++/CX では または が表示されますが、C++/WinRT では Windows.Foundation.IReference'1Boolean 表示されます。

詳細とコード例については、「マークアップからオブジェクトを使用する」を参照してください。

C++/CX プラットフォームの 型を C++/WinRT 型にマッピングする

C++/CX は、Platform 名前空間に複数のデータ型を提供します。 これらの型は標準の C++ ではないため、Windows ランタイム言語拡張機能を有効にした場合にのみ使用できます (Visual Studio プロジェクト プロパティ C/C++>General>Windows ランタイム拡張機能の使用>はい (/ZW))。 次の表は、Platform 型から C++/WinRT の同等の型に移植するのに役立ちます。 これを行うと、C++/WinRT は標準の C++ であるため、/ZW オプションをオフにすることができます。

C++/CX C++/WinRT
Platform::Agile^ winrt::agile_ref
Platform::Array^ ポート Platform::Array^ を参照してください
プラットフォーム::例外^ winrt::hresult_error
Platform::InvalidArgumentException^ winrt::hresult_invalid_argument
Platform::Object^ winrt::Windows::Foundation::IInspectable
Platform::String^ winrt::hstring

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;

Port Platform::Array^

C++/CX で配列を使用する必要がある場合、C++/WinRT では連続する任意のコンテナーを使用できます。 std::vector が適切な理由 、既定のコンストラクターがコレクション に与える影響 参照してください。

したがって、C++/CX で Platform::Array^ がある場合は常に、初期化子リスト、std::array、または std::vectorを使用する移植オプションが含まれます。 詳細とコード例については、「標準初期化子リスト および Standard 配列とベクターを参照してください。

Port Platform::Exception^から winrt::hresult_error へポートする

Platform::Exception^ 型は、Windows ランタイム API が非S_OK HRESULT を返すときに 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_error call hresult_error::to_abi
winrt::hresult_access_denied winrt::hresult_error E_ACCESSDENIED (アクセス拒否)
winrt::hresult_canceled winrt::hresult_error エラー_キャンセル
winrt::hresult_changed_state を する winrt::hresult_error 状態変更_済み
winrt::hresult_class_not_available を する winrt::hresult_error CLASS_E_CLASSNOTAVAILABLE(クラスが利用できません)
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 基底クラスを介して) には、エラーの HRESULT を返す to_abi 関数と、その 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 };

Port Platform::String^winrt::hstring にする

Platform::String^ は、Windows ランタイム HSTRING ABI 型と同じです。 C++/WinRT の場合、同等の winrt::hstringです。 ただし、C++/WinRT では、std::wstring、ワイド文字列リテラルなどの C++ 標準ライブラリのワイド文字列型を使用して Windows ランタイム API を呼び出すことができます。 詳細とコード例については、「C++/WinRTでの 文字列処理」を参照してください。

C++/CX を使用すると、Platform::String::D ata プロパティにアクセスして、文字列を C スタイルの const wchar_t* 配列として取得できます (たとえば、std::wcoutに渡します)。

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

C++/WinRT で同じ操作を行うには、の hstring::c_str 関数を使用して、std::wstringからできるのと同様に、null で終わる C スタイルの文字列バージョン 取得できます。

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もサポートされています。 追加の型を文字列化するために、オーバーロードを追加する必要があります。

言語 整数を文字列に変換する 列挙型を文字列化する
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 を実行します。 2 番目の例 (StatusEnum) の場合は、winrt::to_hstringの独自のオーバーロード 指定する必要があります。そうしないと、コンパイラ エラーが発生します。

文字列構築

C++/CX と C++/WinRT は文字列の構築のために標準ライブラリの std::wstringstream を利用します。

オペレーション C++/CX C++/WinRT
文字列を追加し、null を保持する stream.print(s->Data(), s->Length); stream << std::wstring_view{ s };
文字列を追加し、最初の null で停止する stream << s->Data(); stream << s.c_str();
結果の抽出 ws = stream.str(); ws = stream.str();

その他の例

次の例では、ws は std::wstring型の変数です。 また、C++/CX は 8 ビット文字列から Platform::String を構築できますが、C++/WinRT ではこれを行いません。

オペレーション 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 compile
std::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
null 値を保持しつつ std::wstringに変換する 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