次の方法で共有


C++/WinRT で API を使用する

このトピックでは、 C++/WinRT API が Windows の一部であるか、サードパーティコンポーネント ベンダーによって実装されているか、自分で実装されているかに関係なく、使用する方法について説明します。

Von Bedeutung

このトピックのコード例は短く、簡単に試すことができるように、新しい Windows コンソール アプリケーション (C++/WinRT) プロジェクトを作成し、コードをコピーして貼り付けることで再現できます。 ただし、このようなパッケージ化されていないアプリから任意のカスタム (サード パーティ製) Windows ランタイム型を使用することはできません。 その方法で利用できるのは Windows 型のみです。

コンソール アプリからカスタム (サード パーティ製) の Windows ランタイム型を使用するには、使用されるカスタム型の登録を解決できるように、アプリに パッケージ ID を 付与する必要があります。 詳細については、「 Windows アプリケーション パッケージ プロジェクト」を参照してください。

または、空のアプリ (C++/WinRT)、コア アプリ (C++/WinRT)、または Windows ランタイム コンポーネント (C++/WinRT) プロジェクト テンプレートから新しいプロジェクトを作成します。 これらのアプリの種類には、 既にパッケージ ID があります

API が Windows 名前空間にある場合

これは、Windows ランタイム API を使用する最も一般的なケースです。 メタデータで定義されている Windows 名前空間のすべての型について、C++/WinRT は C++に対応する同等の型 ( 投影型と呼ばれます) を定義します。 投影型の完全修飾名は Windows 型と同じですが、C++ 構文を使用して C++ winrt 名前空間に配置されます。 たとえば、 Windows::Foundation::Uriwinrt::Windows::Foundation::Uri として C++/WinRT に投影されます。

簡単なコード例を次に示します。 次のコード例を コピーして、Windows コンソール アプリケーション (C++/WinRT) プロジェクトのメイン ソース コード ファイルに直接貼り付ける場合は、最初にプロジェクトのプロパティで プリコンパイル済みヘッダーを使用しない を設定します。

// main.cpp
#include <winrt/Windows.Foundation.h>

using namespace winrt;
using namespace Windows::Foundation;

int main()
{
    winrt::init_apartment();
    Uri contosoUri{ L"http://www.contoso.com" };
    Uri combinedUri = contosoUri.CombineUri(L"products");
}

含まれるヘッダー winrt/Windows.Foundation.h は SDK の一部であり、フォルダー %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt\内にあります。 そのフォルダー内のヘッダーには、C++/WinRT に投影された Windows 名前空間の型が含まれています。 この例では、winrt/Windows.Foundation.hwinrt::Windows::Foundation::Uriが含まれています。これは、ランタイム クラス Windows::Foundation::Uriの投影型です。

ヒント

Windows 名前空間の型を使用する場合は常に、その名前空間に対応する C++/WinRT ヘッダーを含めます。 using namespace ディレクティブは省略可能ですが、便利です。

上記のコード例では、C++/WinRT を初期化した後、公開されているコンストラクター (この例では Uri(String)) のいずれかを使用して、winrt::Windows::Foundation::Uri 投影型の値をスタック割り当てます。 このため、最も一般的なユース ケースでは、通常、必要な操作はこれで済むのです。 C++/WinRT 投影型の値を取得すると、同じメンバーがすべて含まれるため、実際の Windows ランタイム型のインスタンスであるかのように扱うことができます。

実際には、その投影値はプロキシです。これは基本的にバッキング オブジェクトへのスマート ポインターにすぎません。 投影値のコンストラクターは RoActivateInstance を呼び出して、バッキング Windows ランタイム クラス (この場合は Windows.Foundation.Uri) のインスタンスを作成し、そのオブジェクトの既定のインターフェイスを新しい投影値内に格納します。 次に示すように、投影された値のメンバーへの呼び出しは、実際にはスマート ポインターを介してバッキング オブジェクトに委任します。これは、状態の変更が発生する場所です。

投影された Windows::Foundation::Uri 型

contosoUri値がスコープ外になると、その値は破棄され、既定のインターフェイスへの参照が解放されます。 その参照がバッキング Windows ランタイム Windows.Foundation.Uri オブジェクトへの最後の参照である場合、バッキング オブジェクトも破棄されます。

ヒント

投影型は、その API を使用するための Windows ランタイム型のラッパーです。 たとえば、 投影されたインターフェイス は、Windows ランタイム インターフェイスのラッパーです。

C++/WinRT プロジェクション ヘッダー

C++/WinRT から Windows 名前空間 API を使用するには、 %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt フォルダーのヘッダーを含めます。 使用する各名前空間に対応するヘッダーを含める必要があります。

たとえば、 Windows::Security::Cryptography::Certificates 名前空間では、同等の C++/WinRT 型定義が winrt/Windows.Security.Cryptography.Certificates.hに存在します。 このヘッダーを含めると、 Windows::Security::Cryptography::Certificates 名前空間内のすべての型にアクセスできます。

1 つの名前空間ヘッダーに関連する名前空間ヘッダーの一部が含まれる場合がありますが、この実装の詳細に依存しないでください。 使用する名前空間のヘッダーを明示的に含めます。

たとえば、 Certificate::GetCertificateBlob メソッドは Windows::Storage::Streams::IBuffer インターフェイスを 返します。 Certificate::GetCertificateBlob メソッドを呼び出す前に、返された winrt/Windows.Storage.Streams.h を確実に受信して操作できるように、名前空間ヘッダー ファイルを含める必要があります。

その名前空間で型を使用する前に必要な名前空間ヘッダーを含め忘れることは、ビルド エラーの一般的な原因です。

オブジェクト、インターフェイス、または ABI 経由でメンバーにアクセスする

C++/WinRT プロジェクションでは、Windows ランタイム クラスのランタイム表現は、基になる ABI インターフェイスに過ぎません。 ただし、便宜上、作成者が意図した方法でクラスに対してコードを記述できます。 たとえば、UriToString メソッドは、それがクラスのメソッドであるかのように呼び出すことができます (実際には、内部では、別の IStringable インターフェイスのメソッドです)。

WINRT_ASSERT はマクロ定義であり、_ASSERTEに展開されます。

Uri contosoUri{ L"http://www.contoso.com" };
WINRT_ASSERT(contosoUri.ToString() == L"http://www.contoso.com/"); // QueryInterface is called at this point.

この利便性は、適切なインターフェイスのクエリを使用して実現されます。 しかし、あなたは常に制御しています。 IStringable インターフェイスを自分で取得して直接使用することで、少しのパフォーマンスのためにその利便性を少し与えることができます。 次のコード例では、実行時に (1 回限りのクエリを使用して) 実際の IStringable インターフェイス ポインターを取得します。 その後、ToString の呼び出しは直接行われ、QueryInterfaceはそれ以上呼び出されることは避けられます。

...
IStringable stringable = contosoUri; // One-off QueryInterface.
WINRT_ASSERT(stringable.ToString() == L"http://www.contoso.com/");

同じインターフェイスで複数のメソッドを呼び出すことがわかっている場合は、この手法を選択できます。

なお、ABI レベルでメンバーにアクセスする場合は、アクセスできます。 次のコード例は、 C++/WinRT と ABI の間の相互運用の詳細とコード例を示しています。

#include <Windows.Foundation.h>
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
using namespace winrt::Windows::Foundation;

int main()
{
    winrt::init_apartment();
    Uri contosoUri{ L"http://www.contoso.com" };

    int port{ contosoUri.Port() }; // Access the Port "property" accessor via C++/WinRT.

    winrt::com_ptr<ABI::Windows::Foundation::IUriRuntimeClass> abiUri{
        contosoUri.as<ABI::Windows::Foundation::IUriRuntimeClass>() };
    HRESULT hr = abiUri->get_Port(&port); // Access the get_Port ABI function.
}

初期化の遅延

C++/WinRT では、投影された各型に特別な C++/WinRT std::nullptr_t コンストラクターがあります。 このコンストラクターを除き、既定のコンストラクターを含むすべての投影型コンストラクターによって、バッキング Windows ランタイム オブジェクトが作成され、そのオブジェクトへのスマート ポインターが提供されます。 そのため、その規則は、初期化されていないローカル変数、初期化されていないグローバル変数、初期化されていないメンバー変数など、既定のコンストラクターが使用される任意の場所に適用されます。

一方で、投影された型の変数を作成する際に、バックエンドの Windows ランタイム オブジェクトをすぐに作成しないようにすることができます。その作業を後回しにしたい場合に、この方法を使用できます。 その特殊な C++/WinRT std::nullptr_t コンストラクター (C++/WinRT プロジェクションがすべてのランタイム クラスに挿入する) を使用して、変数またはフィールドを宣言します。 この特殊なコンストラクターは、次のコード例の m_gamerPicBuffer と共に使用します。

#include <winrt/Windows.Storage.Streams.h>
using namespace winrt::Windows::Storage::Streams;

#define MAX_IMAGE_SIZE 1024

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

private:
    Buffer m_gamerPicBuffer{ nullptr };
};

int main()
{
    winrt::init_apartment();
    Sample s;
    // ...
    s.DelayedInit();
}

投影された型のすべてのコンストラクター std::nullptr_t コンストラクターを除き、バッキング Windows ランタイム オブジェクトが作成されます。 std::nullptr_t コンストラクターは基本的に no-opです。 投影されたオブジェクトは、後で初期化されることを想定しています。 そのため、ランタイム クラスに既定のコンストラクターがあるかどうかに関係なく、この手法を使用して効率的な遅延初期化を行うことができます。

この考慮事項は、ベクトルやマップなど、既定のコンストラクターを呼び出す他の場所に影響します。 このコード例を検討する際には、空のアプリ (C++/WinRT) プロジェクトが必要になります。

std::map<int, TextBlock> lookup;
lookup[2] = value;

割り当てによって新しい TextBlock が作成され、すぐに value で上書きされます。 解決策を次に示します。

std::map<int, TextBlock> lookup;
lookup.insert_or_assign(2, value);

既定のコンストラクターがコレクションに与える影響も参照してください。

間違って初期化を遅延しない

std::nullptr_t コンストラクターを誤って呼び出さないように注意してください。 コンパイラの競合解決は、ファクトリ コンストラクターよりも優先されます。 たとえば、これら 2 つのランタイム クラス定義について考えてみましょう。

// GiftBox.idl
runtimeclass GiftBox
{
    GiftBox();
}

// Gift.idl
runtimeclass Gift
{
    Gift(GiftBox giftBox); // You can create a gift inside a box.
}

例えば、ボックス内にない ギフト を作成するとしたら、それは初期化されていない ギフトボックスで構築された ギフト になるでしょう。 まず、間違った 方法を見てみましょう。 私たちは、GiftBoxを受け取る Gift コンストラクターが存在することを知っています。 しかし、(以下のように、均一な初期化を介して Gift コンストラクターを呼び出す) GiftBox に null を渡したければ、必要な結果 得られない

// These are *not* what you intended. Doing it in one of these two ways
// actually *doesn't* create the intended backing Windows Runtime Gift object;
// only an empty smart pointer.

Gift gift{ nullptr };
auto gift{ Gift(nullptr) };

あなたがここで得るものは、初期化されていない ギフトです. 初期化されていない GiftBoxでは、ギフト を受け取れません。 これを行う正しい 方法 次に示します。

// Doing it in one of these two ways creates an initialized
// Gift with an uninitialized GiftBox.

Gift gift{ GiftBox{ nullptr } };
auto gift{ Gift(GiftBox{ nullptr }) };

正しくない例では、遅延初期化コンストラクターを優先して、nullptr リテラルを渡すと解決されます。 ファクトリ コンストラクターを優先して解決するには、パラメーターの型が GiftBox である必要があります。 正しい例に示すように、GiftBoxに対して明示的に遅延初期化を渡すというオプションがまだあります。

この次の例 正しいです。パラメーターには型 GiftBox があり、std::nullptr_tされていないためです。

GiftBox giftBox{ nullptr };
Gift gift{ giftBox }; // Calls factory constructor.

あいまいさが生じるのは、nullptr リテラルを渡す場合だけです。

間違ってコピーによる構築をしないでください。

この注意は、上記の「 間違って初期化を遅延しない 」セクションで説明したものと似ています。

遅延初期化コンストラクターに加えて、C++/WinRT プロジェクションでは、すべてのランタイム クラスにコピー コンストラクターも挿入されます。 これは、構築されるオブジェクトと同じ型を受け入れる単一パラメーター コンストラクターです。 結果のスマート ポインターは、コンストラクター パラメーターによって指されているのと同じバッキング Windows ランタイム オブジェクトを指します。 結果は、同じバッキング オブジェクトを指す 2 つのスマート ポインター オブジェクトです。

コード例で使用するランタイム クラス定義を次に示します。

// GiftBox.idl
runtimeclass GiftBox
{
    GiftBox(GiftBox biggerBox); // You can place a box inside a bigger box.
}

たとえば、より大きな GiftBox 内に GiftBox を作成するとします。

GiftBox bigBox{ ... };

// These are *not* what you intended. Doing it in one of these two ways
// copies bigBox's backing-object-pointer into smallBox.
// The result is that smallBox == bigBox.

GiftBox smallBox{ bigBox };
auto smallBox{ GiftBox(bigBox) };

これを行う 正しい 方法は、アクティブ化ファクトリを明示的に呼び出す方法です。

GiftBox bigBox{ ... };

// These two ways call the activation factory explicitly.

GiftBox smallBox{
    winrt::get_activation_factory<GiftBox, IGiftBoxFactory>().CreateInstance(bigBox) };
auto smallBox{
    winrt::get_activation_factory<GiftBox, IGiftBoxFactory>().CreateInstance(bigBox) };

API が Windows ランタイム コンポーネントに実装されている場合

このセクションでは、コンポーネントを自分で作成したか、ベンダーが作成したかを適用します。

C++/WinRT Visual Studio 拡張機能 (VSIX) と NuGet パッケージ (プロジェクト テンプレートとビルド サポートを提供) のインストールと使用については、Visual Studio での C++/WinRTのサポート 参照してください。

アプリケーション プロジェクトで、Windows ランタイム コンポーネントの Windows ランタイム メタデータ (.winmd) ファイルを参照し、ビルドします。 ビルド中、 cppwinrt.exe ツールは、コンポーネントの API サーフェスを完全に記述 (または プロジェクト) する標準の C++ ライブラリを生成します。 つまり、生成されたライブラリには、コンポーネントの投影型が含まれます。

次に、Windows 名前空間の型と同様に、ヘッダーを含め、そのコンストラクターのいずれかを使用して投影型を構築します。 アプリケーション プロジェクトのスタートアップ コードによってランタイム クラスが登録され、投影された型のコンストラクターが RoActivateInstance を呼び出して、参照先コンポーネントからランタイム クラスをアクティブ化します。

#include <winrt/ThermometerWRC.h>

struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
    ThermometerWRC::Thermometer thermometer;
    ...
};

Windows ランタイム コンポーネントに実装されている API の使用方法の詳細、コード、チュートリアルについては、「C++/WinRT を使用した Windows ランタイム コンポーネントの と、C++/WinRTでのイベントの作成 に関するページを参照してください。

使用しているプロジェクトに API が実装されている場合

このセクションのコード例は、 トピック XAML コントロールから取得し、C++/WinRT プロパティにバインドします。 詳細、コード、およびランタイム クラスを使用する同じプロジェクトに実装されているランタイム クラスを使用するチュートリアルについては、そのトピックを参照してください。

XAML UI から使用される型は、XAML と同じプロジェクト内にある場合でも、ランタイム クラスである必要があります。 このシナリオでは、ランタイム クラスの Windows ランタイム メタデータ (.winmd) から投影型を生成します。 ここでもヘッダーを含めますが、ランタイム クラスのインスタンスを構築する C++/WinRT バージョン 1.0 またはバージョン 2.0 の方法のいずれかを選択できます。 バージョン 1.0 のメソッドでは、の後に winrt::makeが使用されます。バージョン 2.0 のメソッドは、として知られる均一構築です。 それぞれを順番に見てみましょう。

を使用した構築は winrt::make です。

少なくともそのパターンに精通することをお勧めしますので、既定の (C++/WinRT バージョン 1.0) メソッドから始めましょう。 投影された型は、その std::nullptr_t コンストラクターを使用して構築します。 このコンストラクターは初期化を実行しないため、次に winrt::make ヘルパー関数を使用してインスタンスに値を割り当て、必要なコンストラクター引数を渡す必要があります。 使用しているコードと同じプロジェクトに実装されたランタイム クラスは、登録する必要も、Windows ランタイム/COM のアクティブ化を介してインスタンス化する必要もありません。

XAML コントロール を参照し、完全な手順については、 の C++/WinRT プロパティにバインドしてください。 このセクションでは、そのチュートリアルの抜粋を示します。

// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        BookstoreViewModel MainViewModel{ get; };
    }
}

// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
    ...
    private:
        Bookstore::BookstoreViewModel m_mainViewModel{ nullptr };
};
...

// MainPage.cpp
...
#include "BookstoreViewModel.h"

MainPage::MainPage()
{
    m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();
    ...
}

均一な構造

C++/WinRT バージョン 2.0 以降では、均一な構築 と呼ばれる最適化された形式の構築が可能です (C++/WinRT 2.0の ニュースと変更点を参照)。

XAML コントロール を参照し、完全な手順については、 の C++/WinRT プロパティにバインドしてください。 このセクションでは、そのチュートリアルの抜粋を示します。

winrt::makeではなく、均一コンストラクションを使用するには、アクティベーションファクトリーが必要です。 これを生成する良い方法は、IDL にコンストラクターを追加することです。

// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        MainPage();
        BookstoreViewModel MainViewModel{ get; };
    }
}

次に、次に示すように、 MainPage.h で 1 つの手順で m_mainViewModel を宣言して初期化します。

// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
    ...
    private:
        Bookstore::BookstoreViewModel m_mainViewModel;
        ...
    };
}
...

次に、MainPage.cpp コンストラクターでは、コードを m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();する必要はありません。

均一な構築とコード例の詳細については、「均一な構築へのオプトイン」および「直接実装アクセス」を参照してください。

投影された型とインターフェイスのインスタンス化と返し

あなたのプロジェクトで予測される型やインターフェースの一例を次に示します。 投影された型 (この例の型など) はツールによって生成され、自分で作成するものではないことに注意してください。

struct MyRuntimeClass : MyProject::IMyRuntimeClass, impl::require<MyRuntimeClass,
    Windows::Foundation::IStringable, Windows::Foundation::IClosable>

MyRuntimeClass は投影型です。投影インターフェイスには、IMyRuntimeClassIStringable、そして IClosableがあります。 このトピックでは、投影型をインスタンス化するさまざまな方法について説明しました。 例として MyRuntimeClass を使用したリマインダーと概要を次に示します。

// The runtime class is implemented in another compilation unit (it's either a Windows API,
// or it's implemented in a second- or third-party component).
MyProject::MyRuntimeClass myrc1;

// The runtime class is implemented in the same compilation unit.
MyProject::MyRuntimeClass myrc2{ nullptr };
myrc2 = winrt::make<MyProject::implementation::MyRuntimeClass>();
  • 投影された型のすべてのインターフェイスのメンバーにアクセスできます。
  • 投影された型を呼び出し元に返すことができます。
  • 投影型とインターフェイスは 、winrt::Windows::Foundation::IUnknown から派生します。 そのため、投影された型またはインターフェイスとして を IUnknown::as に呼び出すことで、他の投影されたインターフェイスを照会できます。これらのインターフェイスは使用することも、呼び出し元に返すことも可能です。 as メンバー関数は QueryInterface と同様に機能します。
void f(MyProject::MyRuntimeClass const& myrc)
{
    myrc.ToString();
    myrc.Close();
    IClosable iclosable = myrc.as<IClosable>();
    iclosable.Close();
}

アクティベーション工場

C++/WinRT オブジェクトを作成する便利で直接的な方法は次のとおりです。

using namespace winrt::Windows::Globalization::NumberFormatting;
...
CurrencyFormatter currency{ L"USD" };

ただし、アクティベーションファクトリーを自分で作成し、それを使って必要な時にオブジェクトを作成したい場合があります。 winrt::get_activation_factory 関数テンプレートを使用する方法を示す例をいくつか次に示します。

using namespace winrt::Windows::Globalization::NumberFormatting;
...
auto factory = winrt::get_activation_factory<CurrencyFormatter, ICurrencyFormatterFactory>();
CurrencyFormatter currency = factory.CreateCurrencyFormatterCode(L"USD");
using namespace winrt::Windows::Foundation;
...
auto factory = winrt::get_activation_factory<Uri, IUriRuntimeClassFactory>();
Uri uri = factory.CreateUri(L"http://www.contoso.com");

上記の 2 つの例のクラスは、Windows 名前空間の型です。 この次の例では、 ThermometerWRC::Thermometer は Windows ランタイム コンポーネントに実装されたカスタム型です。

auto factory = winrt::get_activation_factory<ThermometerWRC::Thermometer>();
ThermometerWRC::Thermometer thermometer = factory.ActivateInstance<ThermometerWRC::Thermometer>();

メンバー/型のあいまいさ

メンバー関数の名前が型と同じ場合、あいまいさがあります。 メンバー関数の C++ 非修飾名参照の規則により、名前空間を検索する前にクラスが検索されます。 置換エラーはエラーではありません (SFINAE) ルールは適用されません (関数テンプレートのオーバーロード解決中に適用されます)。 したがって、クラス内の名前が意味をなさない場合、コンパイラはより良い一致を探し続けず、単にエラーを報告します。

struct MyPage : Page
{
    void DoWork()
    {
        // This doesn't compile. You get the error
        // "'winrt::Windows::Foundation::IUnknown::as':
        // no matching overloaded function found".
        auto style{ Application::Current().Resources().
            Lookup(L"MyStyle").as<Style>() };
    }
}

上記では、コンパイラは、として IUnknown::: するテンプレート パラメーターとして、FrameworkElement.Style() (C++/WinRT ではメンバー関数) を渡していると考えています。 解決策は、名前Styleを Windows::UI::Xaml::Style 型として解釈するように強制することです。

struct MyPage : Page
{
    void DoWork()
    {
        // One option is to fully-qualify it.
        auto style{ Application::Current().Resources().
            Lookup(L"MyStyle").as<Windows::UI::Xaml::Style>() };

        // Another is to force it to be interpreted as a struct name.
        auto style{ Application::Current().Resources().
            Lookup(L"MyStyle").as<struct Style>() };

        // If you have "using namespace Windows::UI;", then this is sufficient.
        auto style{ Application::Current().Resources().
            Lookup(L"MyStyle").as<Xaml::Style>() };

        // Or you can force it to be resolved in the global namespace (into which
        // you imported the Windows::UI::Xaml namespace when you did
        // "using namespace Windows::UI::Xaml;".
        auto style = Application::Current().Resources().
            Lookup(L"MyStyle").as<::Style>();
    }
}

修飾されていない名前の検索では、名前の後に ::が続く場合は特別な例外があります。その場合、関数、変数、および列挙型の値は無視されます。 これにより、次のようなことができます。

struct MyPage : Page
{
    void DoSomething()
    {
        Visibility(Visibility::Collapsed); // No ambiguity here (special exception).
    }
}

Visibility() の呼び出しは、UIElement.Visibility の メンバー関数名として解決されます。 ただし、パラメーター Visibility::CollapsedVisibility::という単語に従うので、メソッド名は無視され、コンパイラは列挙型クラスを検索します。

重要な API