C++/WinRT での API の作成Author APIs with C++/WinRT

このトピックでは、直接的または間接的に winrt::implements 基本構造体を使用して、C++/WinRT API を作成する方法を示します。This topic shows how to author C++/WinRT APIs by using the winrt::implements base struct, either directly or indirectly. このコンテキストで作成者の同義語は、生成、または実装です。Synonyms for author in this context are produce, or implement. このトピックでは、C++/WinRT で API を実装するために、次のシナリオをこの順序で説明します。This topic covers the following scenarios for implementing APIs on a C++/WinRT type, in this order.

注意

このトピックでは Windows ランタイム コンポーネントについて説明しますが、C++/WinRT のコンテキストについてだけです。This topic touches on the subject of Windows Runtime Components, but only in the context of C++/WinRT. すべての Windows ランタイム言語を対象とする Windows ランタイム コンポーネントに関するコンテンツをお探しの場合は、「Windows ランタイム コンポーネント」をご覧ください。If you're looking for content about Windows Runtime components that covers all Windows Runtime languages, then see Windows Runtime components.

  • Windows ランタイム クラス (ランタイム クラス) は作成しません。アプリ内でのローカルの使用のために 1 つまたは複数の Windows ランタイム インターフェイスを実装するだけです。You're not authoring a Windows Runtime class (runtime class); you just want to implement one or more Windows Runtime interfaces for local consumption within your app. この場合、winrt::implements から直接派生し、機能を実装します。You derive directly from winrt::implements in this case, and implement functions.
  • ランタイム クラスを作成しますYou are authoring a runtime class. アプリで使用するコンポーネントを作成している場合があります。You might be authoring a component to be consumed from an app. または、XAML ユーザー インターフェイス (UI) で使用する型を作成していることがあり、その場合は両方を実装して、同じのコンパイル ユニット内のランタイム クラスを使用しています。Or you might be authoring a type to be consumed from XAML user interface (UI), and in that case you're both implementing and consuming a runtime class within the same compilation unit. このような場合、ツールで winrt::implements から派生するクラスを生成することができます。In these cases, you let the tools generate classes for you that derive from winrt::implements.

どちらの場合も、C++/WinRT API を実装する型は、実装型と呼ばれます。In both cases, the type that implements your C++/WinRT APIs is called the implementation type.

重要

実装型の概念を投影型と区別することは重要です。It's important to distinguish the concept of an implementation type from that of a projected type. 投影型については、「C++/WinRT での API の使用」で説明します。The projected type is described in Consume APIs with C++/WinRT.

ランタイム クラスを作成していない場合If you're not authoring a runtime class

最も単純なシナリオは、ローカル使用のための Windows ランタイム インターフェイスを実装しているケースです。The simplest scenario is where you're implementing a Windows Runtime interface for local consumption. ランタイム クラスは必要ありません。通常の C++ のクラスだけです。You don't need a runtime class; just an ordinary C++ class. たとえば、CoreApplication に基づいてアプリを記述している場合があります。For example, you might be writing an app based around CoreApplication.

注意

C++/WinRT Visual Studio Extension (VSIX) と NuGet パッケージ (両者が連携してプロジェクト テンプレートとビルドをサポート) のインストールと使用については、Visual Studio での C++/WinRT のサポートに関する記事を参照してください。For info about installing and using the C++/WinRT Visual Studio Extension (VSIX) and the NuGet package (which together provide project template and build support), see Visual Studio support for C++/WinRT.

Visual Studio では、Visual C++ > Windows ユニバーサル > Core App (C++/WinRT) プロジェクト テンプレートで CoreApplication パターンを示しています。In Visual Studio, the Visual C++ > Windows Universal > Core App (C++/WinRT) project template illustrates the CoreApplication pattern. パターンは、Windows::ApplicationModel::Core::IFrameworkViewSource の実装を coreapplication::run に渡して開始します。The pattern begins with passing an implementation of Windows::ApplicationModel::Core::IFrameworkViewSource to CoreApplication::Run.

using namespace Windows::ApplicationModel::Core;
int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
    IFrameworkViewSource source = ...
    CoreApplication::Run(source);
}

CoreApplication はインターフェイスを使用してアプリの最初のビューを作成します。CoreApplication uses the interface to create the app's first view. 概念的には、IFrameworkViewSource は次のように見えます。Conceptually, IFrameworkViewSource looks like this.

struct IFrameworkViewSource : IInspectable
{
    IFrameworkView CreateView();
};

さらに、概念的には、CoreApplication::run の実装は次のように見えます。Again conceptually, the implementation of CoreApplication::Run does this.

void Run(IFrameworkViewSource viewSource) const
{
    IFrameworkView view = viewSource.CreateView();
    ...
}

したがって、開発者は、IFrameworkViewSource インターフェイスを実装します。So you, as the developer, implement the IFrameworkViewSource interface. C++/WinRT には、基本構造体テンプレート winrt::implements があり、COM スタイルのプログラミングを使用せずにインターフェイス (1 つまたは複数) を簡単に実装することができます。C++/WinRT has the base struct template winrt::implements to make it easy to implement an interface (or several) without resorting to COM-style programming. 実装から型を派生して、インターフェイスの機能を実装するだけです。You just derive your type from implements, and then implement the interface's functions. 以下にその方法を示します。Here's how.

// App.cpp
...
struct App : implements<App, IFrameworkViewSource>
{
    IFrameworkView CreateView()
    {
        return ...
    }
}
...

IFrameworkViewSource に対応済みです。That's taken care of IFrameworkViewSource. 次に、IFrameworkView インターフェイスを実装するオブジェクトを返します。The next step is to return an object that implements the IFrameworkView interface. アプリ上にそのインターフェイスを実装することも選択できます。You can choose to implement that interface on App, too. 次のコード例は、少なくともウィンドウを起動してデスクトップ上で実行する最小限のアプリを表します。This next code example represents a minimal app that will at least get a window up and running on the desktop.

// App.cpp
...
struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
    IFrameworkView CreateView()
    {
        return *this;
    }

    void Initialize(CoreApplicationView const &) {}

    void Load(hstring const&) {}

    void Run()
    {
        CoreWindow window = CoreWindow::GetForCurrentThread();
        window.Activate();

        CoreDispatcher dispatcher = window.Dispatcher();
        dispatcher.ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit);
    }

    void SetWindow(CoreWindow const & window)
    {
        // Prepare app visuals here
    }

    void Uninitialize() {}
};
...

アプリ IFrameworkViewSource であるため、Run に渡すことだけができます。Since your App type is an IFrameworkViewSource, you can just pass one to Run.

using namespace Windows::ApplicationModel::Core;
int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
    CoreApplication::Run(winrt::make<App>());
}

Windows ランタイム コンポーネントでランタイム クラスを作成する場合If you're authoring a runtime class in a Windows Runtime Component

型が、アプリケーションから使用する Windows ランタイム コンポーネントにパッケージ化されている場合は、ランタイム クラスである必要があります。If your type is packaged in a Windows Runtime Component for consumption from an application, then it needs to be a runtime class.

ヒント

インターフェイス定義言語 (IDL) ファイルを編集するときに、ビルドのパフォーマンスを最適化するために、また IDL からその生成されたソース コード ファイルへの論理的な通信手段のために、各ランタイム クラスを独自の IDL (.idl) ファイル内で宣言することをお勧めします。We recommend that you declare each runtime class in its own Interface Definition Language (IDL) (.idl) file, in order to optimize build performance when you edit an IDL file, and for logical correspondence of an IDL file to its generated source code files. Visual Studio では、生成されるすべての .winmd ファイルをルート名前空間と同じ名前の単一のファイルにマージします。Visual Studio merges all of the resulting .winmd files into a single file with the same name as the root namespace. その最終的な .winmd ファイルが、コンポーネントのユーザーが参照するファイルになります。That final .winmd file will be the one that the consumers of your component will reference.

次に例を示します。Here's an example.

// MyRuntimeClass.idl
namespace MyProject
{
    runtimeclass MyRuntimeClass
    {
        // Declaring a constructor (or constructors) in the IDL causes the runtime class to be
        // activatable from outside the compilation unit.
        MyRuntimeClass();
        String Name;
    }
}

この IDL では、Windows ランタイム (ランタイム) クラスを宣言します。This IDL declares a Windows Runtime (runtime) class. ランタイム クラスは、通常実行可能な境界を越えて、モダン COM インターフェイス経由でアクティブ化および使用可能な型です。A runtime class is a type that can be activated and consumed via modern COM interfaces, typically across executable boundaries. プロジェクトに IDL ファイルを追加し、ビルドすると、C++/WinRT ツールチェーン (midl.exe および cppwinrt.exe) が実装型を生成します。When you add an IDL file to your project, and build, the C++/WinRT toolchain (midl.exe and cppwinrt.exe) generate an implementation type for you. IDL ファイルのワークフローの動作の例については、「XAML コントロール: C++/WinRT プロパティへのバインド」をご覧ください。For an example of the IDL file workflow in action, see XAML controls; bind to a C++/WinRT property.

上記の IDL の例を使用すると、実装型は、\MyProject\MyProject\Generated Files\sources\MyRuntimeClass.hMyRuntimeClass.cpp という名前のソース コード ファイルの winrt::MyProject::implementation::MyRuntimeClass という名前の C++ 構造体スタブです。Using the example IDL above, the implementation type is a C++ struct stub named winrt::MyProject::implementation::MyRuntimeClass in source code files named \MyProject\MyProject\Generated Files\sources\MyRuntimeClass.h and MyRuntimeClass.cpp.

実装型は次のようになります。The implementation type looks like this.

// MyRuntimeClass.h
...
namespace winrt::MyProject::implementation
{
    struct MyRuntimeClass : MyRuntimeClassT<MyRuntimeClass>
    {
        MyRuntimeClass() = default;

        winrt::hstring Name();
        void Name(winrt::hstring const& value);
    };
}

// winrt::MyProject::factory_implementation::MyRuntimeClass is here, too.

使用されている F バインド ポリモーフィズム パターンに注意してください (MyRuntimeClass は、その基本である MyRuntimeClassT へのテンプレート引数としてそれ自体を使用します)。Note the F-bound polymorphism pattern being used (MyRuntimeClass uses itself as a template argument to its base, MyRuntimeClassT). これは、CRTP (curiously recurring template pattern (奇妙に再帰したテンプレート パターン)) とも呼ばれます。This is also called the curiously recurring template pattern (CRTP). 上方向に継承チェーンをたどると、MyRuntimeClass_base が見つかります。If you follow the inheritance chain upwards, you'll come across MyRuntimeClass_base.

template <typename D, typename... I>
struct MyRuntimeClass_base : implements<D, MyProject::IMyRuntimeClass, I...>

そのため、このシナリオでは、継承のルートの階層はもう一度 winrt::implements 基本構造体のテンプレートです。So, in this scenario, at the root of the inheritance hierarchy is the winrt::implements base struct template once again.

詳細、コード、および Windows ランタイム コンポーネントの API の作成に関するチュートリアルについては、「C++/WinRT でのイベントの作成」を参照してください。For more details, code, and a walkthrough of authoring APIs in a Windows Runtime component, see Author events in C++/WinRT.

XAML UI で参照されるランタイム クラスを作成する場合If you're authoring a runtime class to be referenced in your XAML UI

型が XAML UI によって参照される場合、XAML と同じプロジェクトになっていても、ランタイム クラスである必要があります。If your type is referenced by your XAML UI, then it needs to be a runtime class, even though it's in the same project as the XAML. 通常は実行可能な境界を越えてアクティブ化されますが、ランタイム クラスでは、代わりにそれを実装するコンパイル ユニット内で使用できます。Although they are typically activated across executable boundaries, a runtime class can instead be used within the compilation unit that implements it.

このシナリオでは、API を使用する および のどちらも作成します。In this scenario, you're both authoring and consuming the APIs. ランタイム クラスを実装するための手順は、Windows ランタイム コンポーネントと基本的に同じです。The procedure for implementing your runtime class is essentially the same as that for a Windows Runtime Component. このため、前のセクション —Windows ランタイム コンポーネントでランタイム クラスを作成する場合を参照してください。So, see the previous section—If you're authoring a runtime class in a Windows Runtime Component. これと唯一異なる詳細な点は、IDL から、C++/WinRT ツールチェーンが、実装型だけでなく投影型も生成することです。The only detail that differs is that, from the IDL, the C++/WinRT toolchain generates not only an implementation type but also a projected type. このシナリオでは "MyRuntimeClass" というだけではあいまいなことを理解することは重要です。これは、その名前を持つさまざまな種類の複数のエンティティがあるためです。It's important to appreciate that saying only "MyRuntimeClass" in this scenario may be ambiguous; there are several entities with that name, of different kinds.

  • MyRuntimeClass はランタイム クラスの名前です。MyRuntimeClass is the name of a runtime class. ただし、これは実際には IDL で宣言され、一部のプログラミング言語で実装されたアブストラクションです。But this is really an abstraction: declared in IDL, and implemented in some programming language.
  • MyRuntimeClass は、C++ 構造体 winrt::MyProject::implementation::MyRuntimeClass の名前です。これはランタイム クラスの C++/WinRT の実装です。MyRuntimeClass is the name of the C++ struct winrt::MyProject::implementation::MyRuntimeClass, which is the C++/WinRT implementation of the runtime class. すでに見たように、プロジェクトを別に実装および使用している場合、この構造体は実装しているプロジェクトにのみ存在します。As we've seen, if there are separate implementing and consuming projects, then this struct exists only in the implementing project. これは実装型、または実装です。This is the implementation type, or the implementation. この型は、(cppwinrt.exe ツールによって) ファイル \MyProject\MyProject\Generated Files\sources\MyRuntimeClass.hMyRuntimeClass.cpp で生成されます。This type is generated (by the cppwinrt.exe tool) in the files \MyProject\MyProject\Generated Files\sources\MyRuntimeClass.h and MyRuntimeClass.cpp.
  • MyRuntimeClass は C++ 構造体 winrt::MyProject::MyRuntimeClass の形式の投影型の名前です。MyRuntimeClass is the name of the projected type in the form of the C++ struct winrt::MyProject::MyRuntimeClass. プロジェクトを別に実装および使用している場合、この構造体は使用しているプロジェクトにのみ存在します。If there are separate implementing and consuming projects, then this struct exists only in the consuming project. これは投影型、または投影です。This is the projected type, or the projection. この型は、(cppwinrt.exe によって) ファイル \MyProject\MyProject\Generated Files\winrt\impl\MyProject.2.h で生成されます。This type is generated (by cppwinrt.exe) in the file \MyProject\MyProject\Generated Files\winrt\impl\MyProject.2.h.

投影型と実装型

このトピックに関連する投影型の一部を次に示します。Here are the parts of the projected type that are relevant to this topic.

// MyProject.2.h
...
namespace winrt::MyProject
{
    struct MyRuntimeClass : MyProject::IMyRuntimeClass
    {
        MyRuntimeClass(std::nullptr_t) noexcept {}
        MyRuntimeClass();
    };
}

ランタイム クラスで INotifyPropertyChanged インターフェイスを実装する例のチュートリアルについては、「XAML コントロール、C++/WinRT プロパティへのバインド」を参照してください。For an example walkthrough of implementing the INotifyPropertyChanged interface on a runtime class, see XAML controls; bind to a C++/WinRT property.

このシナリオでランタイム クラスを使用するための手順は、「C++/WinRT での API の使用」で説明しています。The procedure for consuming your runtime class in this scenario is described in Consume APIs with C++/WinRT.

ランタイム クラスのコンストラクターRuntime class constructors

上記で見た一覧から除くためのポイントを次に示します。Here are some points to take away from the listings we've seen above.

  • 各コンストラクターを IDL で宣言することで、コンストラクターは実装型と投影型の両方で生成されます。Each constructor you declare in your IDL causes a constructor to be generated on both your implementation type and on your projected type. IDL で宣言したコンストラクターは、別のコンパイル ユニットからランタイム クラスを使用するために使用されます。IDL-declared constructors are used to consume the runtime class from a different compilation unit.
  • コンストラクターを IDL で宣言してもしなくても、std::nullptr_t を受け取るコンストラクターのオーバーロードが投影型に対して生成されます。Whether you have IDL-declared constructor(s) or not, a constructor overload that takes std::nullptr_t is generated on your projected type. std::nullptr_t コンストラクターの呼び出しは、"同じ" コンパイル ユニットからのランタイム クラスの使用における "2 つのステップのうちの最初のステップ" です。Calling the std::nullptr_t constructor is the first of two steps in consuming the runtime class from the same compilation unit. 詳細とコード例については、「C++/WinRT での API の使用」を参照してください。For more details, and a code example, see Consume APIs with C++/WinRT.
  • 同じコンパイル ユニットからランタイム クラスを使用している場合、(MyRuntimeClass.h の) 実装型で非既定のコンストラクターを直接実装することもできます。If you're consuming the runtime class from the same compilation unit, then you can also implement non-default constructors directly on the implementation type (which, remember, is in MyRuntimeClass.h).

注意

ランタイム クラスが別のコンパイル ユニットから使用することが予測される場合は (これは一般的です)、IDL にコンストラクター (少なくとも既定のコンストラクター) を含めます。If you expect your runtime class to be consumed from a different compilation unit (which is common), then include constructor(s) in your IDL (at least a default constructor). これを行うことで、実装型とともにファクトリの実装も取得します。By doing that, you'll also get a factory implementation alongside your implementation type.

同じコンパイル ユニット内でのみランタイム クラスを作成および使用する場合は、IDL でコンストラクターを宣言しないでください。If you want to author and consume your runtime class only within the same compilation unit, then don't declare any constructor(s) in your IDL. ファクトリの実装は不要であり、生成されません。You don't need a factory implementation, and one won't be generated. 実装型の既定のコンストラクターが削除されますが、簡単に編集して、代わりに既定にすることができます。Your implementation type's default constructor will be deleted, but you can easily edit it and default it instead.

同じコンパイル ユニット内でのみランタイム クラスを作成および使用する場合は、コンストラクターのパラメーターが必要であり、実装型で直接必要なコンストラクターを作成します。If you want to author and consume your runtime class only within the same compilation unit, and you need constructor parameters, then author the constructor(s) that you need directly on your implementation type.

ランタイム クラスのメソッド、プロパティ、イベントRuntime class methods, properties, and events

ワークフローでは IDL を使ってランタイム クラスとそのメンバーを宣言すること、そしてツールによってプロトタイプとスタブの実装が自動的に生成されることを説明しました。We've seen that the workflow is to use IDL to declare your runtime class and its members, and then the tooling generates prototypes and stub implementations for you. ランタイム クラスのメンバーに対して自動的に生成されるそれらのプロトタイプについては、IDL で宣言されている型から別の型を渡すように編集することが "できます"。As for those autogenerated prototypes for the members of your runtime class, you can edit them so that they pass around different types from the types that you declare in your IDL. ただし、それができるのは、IDL で宣言されている型が、実装バージョンで宣言されている型に転送できる場合だけです。But you can do that only as long as the type that you declare in IDL can be forwarded to the type that you declare in the implemented version.

例をいくつか紹介します。Here are some examples.

  • パラメーターの型を緩和できます。You can relax parameter types. たとえば、IDL でメソッドが SomeClass を受け取る場合、実装ではそれを IInspectable に変更できます。For example, if in IDL your method takes a SomeClass, then you could choose to change that to IInspectable in your implementation. これができるのは、すべての SomeClassIInspectable に転送できるためです (もちろん、逆はできません)。This works because any SomeClass can be forwarded to IInspectable (the reverse, of course, wouldn't work).
  • コピー可能なパラメーターを参照渡しではなく値で受け取ることができます。You can accept a copyable parameter by value, instead of by reference. たとえば、SomeClass const&SomeClass const& に変更します。For example, change SomeClass const& to SomeClass const&. コルーチンに参照をキャプチャしないようにする必要があるときは、それが必要です (「パラメーターの引き渡し」をご覧ください)。That's necessary when you need to avoid capturing a reference into a coroutine (see Parameter-passing).
  • 戻り値を緩和できます。You can relax the return value. たとえば、voidwinrt::fire_and_forget に変更できます。For example, you can change void to winrt::fire_and_forget.

最後の 2 つは、非同期イベント ハンドラーを作成する場合に非常に便利です。The last two are very useful when you're writing an asynchronous event handler.

実装型とインターフェイスをインスタンス化して返すInstantiating and returning implementation types and interfaces

このセクションでは、IStringable および IClosable インターフェイスを実装する、MyType という名前の実装型を例として見ていきましょう。For this section, let's take as an example an implementation type named MyType, which implements the IStringable and IClosable interfaces.

winrt::implements から直接 MyType を派生できます (ランタイム クラスではありません)。You can derive MyType directly from winrt::implements (it's not a runtime class).

#include <winrt/Windows.Foundation.h>

using namespace winrt;
using namespace Windows::Foundation;

struct MyType : implements<MyType, IStringable, IClosable>
{
    winrt::hstring ToString(){ ... }
    void Close(){}
};

または IDL から生成することができます (これはランタイム クラスです)。Or you can generate it from IDL (it's a runtime class).

// MyType.idl
namespace MyProject
{
    runtimeclass MyType: Windows.Foundation.IStringable, Windows.Foundation.IClosable
    {
        MyType();
    }    
}

MyType から、プロジェクションの一部として使用するまたは返すことができる IStringable または IClosable オブジェクトへ移動するには、winrt::make 関数テンプレートを呼び出す必要があります。To go from MyType to an IStringable or IClosable object that you can use or return as part of your projection, you should call the winrt::make function template. make は実装型の既定のインターフェイスを返します。make returns the implementation type's default interface.

IStringable istringable = winrt::make<MyType>();

注意

ただし、XAML UI から型を参照している場合は、同じプロジェクト内に実装型と投影型の両方があります。However, if you're referencing your type from your XAML UI, then there will be both an implementation type and a projected type in the same project. その場合、make は投影型のインスタンスを返します。In that case, make returns an instance of the projected type. そのシナリオのコード例については、「XAML コントロール、C++/WinRT プロパティへのバインド」を参照してください。For a code example of that scenario, see XAML controls; bind to a C++/WinRT property.

IStringable インターフェイスのメンバーを呼び出すには (上記のコード例の場合) istringable のみ使用できます。We can only use istringable (in the code example above) to call the members of the IStringable interface. ただし C++/WinRT インターフェイス (投影されたインターフェイス) は winrt::Windows::Foundation::IUnknown から派生します。But a C++/WinRT interface (which is a projected interface) derives from winrt::Windows::Foundation::IUnknown. したがって、その上で IUnknown::as (または IUnknown::try_as) を呼び出して他の投影された型またはインターフェイスのクエリを実行し、これを使用するか返すことができます。So, you can call IUnknown::as (or IUnknown::try_as) on it to query for other projected types or interfaces, which you can also either use or return.

istringable.ToString();
IClosable iclosable = istringable.as<IClosable>();
iclosable.Close();

すべての実装のメンバーにアクセスし、後でインターフェイスを呼び出し元に返す必要がある場合、winrt::make_self 関数テンプレートを使用します。If you need to access all of the implementation's members, and then later return an interface to a caller, then use the winrt::make_self function template. make_self は、実装型をラッピングする winrt::com_ptr を返します。make_self returns a winrt::com_ptr wrapping the implementation type. (矢印演算子を使用して) そのインターフェイスのすべてのメンバーにアクセスすることができます。これをそのまま呼び出し元に返すか、またはそこで as を呼び出して結果として得られるインターフェイス オブジェクトを呼び出し元に返します。You can access the members of all of its interfaces (using the arrow operator), you can return it to a caller as-is, or you can call as on it and return the resulting interface object to a caller.

winrt::com_ptr<MyType> myimpl = winrt::make_self<MyType>();
myimpl->ToString();
myimpl->Close();
IClosable iclosable = myimpl.as<IClosable>();
iclosable.Close();

MyType クラスは、プロジェクションの一部ではなく、実装です。The MyType class is not part of the projection; it's the implementation. ただしこの方法では、仮想関数呼び出しのオーバーヘッドがなく、その実装メソッドを直接呼び出すことができます。But this way you can call its implementation methods directly, without the overhead of a virtual function call. 上記の例では、MyType::ToStringIStringable で投影されたメソッドと同じ署名を使用するにもかかわらず、アプリケーション バイナリ インターフェイス (ABI) を交差することなく非仮想メソッドを直接呼び出しています。In the example above, even though MyType::ToString uses the same signature as the projected method on IStringable, we're calling the non-virtual method directly, without crossing the application binary interface (ABI). Com_ptrMyType 構造体へのポインターを保持するだけであるため、myimpl 変数と矢印演算子を介して、MyType の他の内部の詳細にもアクセスできます。The com_ptr simply holds a pointer to the MyType struct, so you can also access any other internal details of MyType via the myimpl variable and the arrow operator.

インターフェイス オブジェクトがあり、それが実装上のインターフェイスであることがわかった場合は、winrt::get_self 関数テンプレートを使用して実装に戻すことができます。In the case where you have an interface object, and you happen to know that it's an interface on your implementation, then you can get back to the implementation using the winrt::get_self function template. もう一度、これは仮想関数の呼び出しを回避し、実装で直接取得することができる手法です。Again, it's a technique that avoids virtual function calls, and lets you get directly at the implementation.

注意

Windows SDK バージョン 10.0.17763.0 (Windows 10 Version 1809) 以降をインストールしていない場合は、winrt::get_self ではなく winrt::from_abi を呼び出す必要があります。If you haven't installed the Windows SDK version 10.0.17763.0 (Windows 10, version 1809), or later, then you need to call winrt::from_abi instead of winrt::get_self.

次に例を示します。Here's an example. 別の例が、BgLabelControl カスタム コントロール クラスの実装に関する記事にあります。There's another example in Implement the BgLabelControl custom control class.

void ImplFromIClosable(IClosable const& from)
{
    MyType* myimpl = winrt::get_self<MyType>(from);
    myimpl->ToString();
    myimpl->Close();
}

ただし元のインターフェイス オブジェクトのみ参照に保持されます。But only the original interface object holds on to a reference. れを保持する場合は、com_ptr::copy_from を呼び出すことができます。If you want to hold on to it, then you can call com_ptr::copy_from.

winrt::com_ptr<MyType> impl;
impl.copy_from(winrt::get_self<MyType>(from));
// com_ptr::copy_from ensures that AddRef is called.

実装型自体は、winrt::Windows::Foundation::IUnknown から派生したものではないため、as 関数はありません。The implementation type itself doesn't derive from winrt::Windows::Foundation::IUnknown, so it has no as function. その場合でも、1 つのインスタンスを作成し、そのインターフェイスのすべてのメンバーにアクセスできます。Even so, you can instantiate one, and access the members of all of its interfaces. ただしこれを行う場合、未処理の実装型のインスタンスを呼び出し元に返さないでください。But if you do this, then don't return the raw implementation type instance to the caller. 代わりに、前に示した手法のいずれかを使用して、投影されるインターフェイスまたは com_ptr を返します。Instead, use one of the techniques shown above and return a projected interface, or a com_ptr.

MyType myimpl;
myimpl.ToString();
myimpl.Close();
IClosable ic1 = myimpl.as<IClosable>(); // error

実装型のインスタンスがある場合は、対応する投影型を想定している関数にこれを渡す必要があり、その後実行できます。If you have an instance of your implementation type, and you need to pass it to a function that expects the corresponding projected type, then you can do so. 変換演算子は (cppwinrt.exe ツールによって実装型が生成されたという条件で) 実装型に存在し、これを可能にします。A conversion operator exists on your implementation type (provided that the implementation type was generated by the cppwinrt.exe tool) that makes this possible. 実装型の値は、対応する投影型の値を想定しているメソッドに直接渡すことができます。You can pass an implementation type value directly to a method that expects a value of the corresponding projected type. 実装型のメンバー関数から、対応する投影型の値を想定しているメソッドに *this を渡すことができます。From an implementation type member function, you can pass *this to a method that expects a value of the corresponding projected type.

// MyProject::MyType is the projected type; the implementation type would be MyProject::implementation::MyType.

void MyOtherType::DoWork(MyProject::MyType const&){ ... }

...

void FreeFunction(MyProject::MyOtherType const& ot)
{
    MyType myimpl;
    ot.DoWork(myimpl);
}

...

void MyType::MemberFunction(MyProject::MyOtherType const& ot)
{
    ot.DoWork(*this);
}

既定以外のコンストラクターを持つ型からの派生Deriving from a type that has a non-default constructor

ToggleButtonAutomationPeer::ToggleButtonAutomationPeer(ToggleButton) は既定以外のコンストラクターの例です。ToggleButtonAutomationPeer::ToggleButtonAutomationPeer(ToggleButton) is an example of a non-default constructor. 既定のコンストラクターがないので、ToggleButtonAutomationPeer を作成するには、オーナー に渡す必要があります。There's no default constructor so, to construct a ToggleButtonAutomationPeer, you need to pass an owner. したがって、ToggleButtonAutomationPeer から派生する場合、オーナー を受け取りベースに渡すコンストラクターを提供する必要があります。Consequently, if you derive from ToggleButtonAutomationPeer, then you need to provide a constructor that takes an owner and passes it to the base. 実際には次のようになります。Let's see what that looks like in practice.

// MySpecializedToggleButton.idl
namespace MyNamespace
{
    runtimeclass MySpecializedToggleButton :
        Windows.UI.Xaml.Controls.Primitives.ToggleButton
    {
        ...
    };
}
// MySpecializedToggleButtonAutomationPeer.idl
namespace MyNamespace
{
    runtimeclass MySpecializedToggleButtonAutomationPeer :
        Windows.UI.Xaml.Automation.Peers.ToggleButtonAutomationPeer
    {
        MySpecializedToggleButtonAutomationPeer(MySpecializedToggleButton owner);
    };
}

実装型の生成されるコンストラクターは次のようになります。The generated constructor for your implementation type looks like this.

// MySpecializedToggleButtonAutomationPeer.cpp
...
MySpecializedToggleButtonAutomationPeer::MySpecializedToggleButtonAutomationPeer
    (MyNamespace::MySpecializedToggleButton const& owner)
{
    ...
}
...

唯一のない部分は、基底クラスにコンストラクター パラメーターを渡す必要があることです。The only piece missing is that you need to pass that constructor parameter on to the base class. 上述の F バインド ポリモーフィズム パターンを覚えていますか。Remember the F-bound polymorphism pattern that we mentioned above? C++/WinRT で使われるそのパターンの詳細を理解したら、基底クラスと呼ばれるものが何かを理解することができます (または実装クラスのヘッダー ファイルだけを参照することができます)。Once you're familiar with the details of that pattern as used by C++/WinRT, you can figure out what your base class is called (or you can just look in your implementation class's header file). これは、このケースで基底クラス コンストラクターを呼び出す方法です。This is how to call the base class constructor in this case.

// MySpecializedToggleButtonAutomationPeer.cpp
...
MySpecializedToggleButtonAutomationPeer::MySpecializedToggleButtonAutomationPeer
    (MyNamespace::MySpecializedToggleButton const& owner) : 
    MySpecializedToggleButtonAutomationPeerT<MySpecializedToggleButtonAutomationPeer>(owner)
{
    ...
}
...

基底クラス コンストラクターは、ToggleButton を期待します。The base class constructor expects a ToggleButton. ToggleButton となるのは MySpecializedToggleButton ですAnd MySpecializedToggleButton is a ToggleButton.

(基底クラスにコンストラクター パラメーターを渡すために) 上記で説明した編集を行うまで、コンパイラは、コンストラクターにフラグを設定し、(この場合は) MySpecializedToggleButtonAutomationPeer_base<MySpecializedToggleButtonAutomationPeer> と呼ばれる型で利用可能な適切な既定のコンストラクターがないことを指摘します。Until you make the edit described above (to pass that constructor parameter on to the base class), the compiler will flag your constructor and point out that there's no appropriate default constructor available on a type called (in this case) MySpecializedToggleButtonAutomationPeer_base<MySpecializedToggleButtonAutomationPeer>. 実際には、実装型の基底クラスの基底クラスです。That's actually the base class of the bass class of your implementation type.

名前空間: 投影型、実装型、ファクトリNamespaces: projected types, implementation types, and factories

このトピックで前に説明したように、C++/WinRT のランタイム クラスは、複数の名前空間に複数の C++ クラスの形式で存在します。As you've seen previously in this topic, a C++/WinRT runtime class exists in the form of more than one C++ class in more than one namespace. したがって、名前 MyRuntimeClass の意味は、winrt::MyProject 名前空間内と winrt::MyProject::implementation 名前空間内では異なります。So, the name MyRuntimeClass has one meaning in the winrt::MyProject namespace, and a different meaning in the winrt::MyProject::implementation namespace. 現在のコンテキストがどちらの名前空間かを認識し、別の名前空間の名前が必要な場合は名前空間プレフィックスを使います。Be aware of which namespace you currently have in context, and then use namespace prefixes if you need a name from a different namespace. 問題になる名前空間についてさらに詳しく見てみましょう。Let's look more closely at the namespaces in question.

  • winrt::MyProjectwinrt::MyProject. この名前空間には投影型が含まれます。This namespace contains projected types. 投影型のオブジェクトはプロキシです。基本的に基になるオブジェクトへのスマート ポインターであり、基になるオブジェクトは自分のプロジェクト内で実装されている場合も、別のコンパイル単位内で実装されている場合もあります。An object of a projected type is a proxy; it's essentially a smart pointer to a backing object, where that backing object might be implemented here in your project, or it might be implemented in another compilation unit.
  • winrt::MyProject::implementationwinrt::MyProject::implementation. この名前空間には、実装型が含まれます。This namespace contains implementation types. 実装型のオブジェクトはポインターではありません。値であり、完全な C++ スタック オブジェクトです。An object of an implementation type is not a pointer; it's a value—a full C++ stack object. 実装型を直接構築しないでください。代わりに、winrt::make を呼び出し、テンプレート パラメーターとして実装型を渡します。Don't construct an implementation type directly; instead, call winrt::make, passing your implementation type as the template parameter. このトピックでは前に動作する winrt::make の例を示しました。別の例については、「XAML コントロール: C++/WinRT プロパティへのバインド」をご覧ください。We've shown examples of winrt::make in action previously in this topic, and there's another example in XAML controls; bind to a C++/WinRT property.
  • winrt::MyProject::factory_implementationwinrt::MyProject::factory_implementation. この名前空間にはファクトリが含まれます。This namespace contains factories. この名前空間のオブジェクトでは、 IActivationFactory がサポートされています。An object in this namespace supports IActivationFactory.

次の表では、異なるコンテキストで使う必要がある最小限の名前空間の修飾を示します。This table shows the minimum namespace qualification you need to use in different contexts.

コンテキスト内の名前空間The namespace that's in context 投影型を指定する場合To specify the projected type 実装型を指定する場合To specify the projected type
winrt::MyProjectwinrt::MyProject MyRuntimeClass implementation::MyRuntimeClass
winrt::MyProject::implementationwinrt::MyProject::implementation MyProject::MyRuntimeClass MyRuntimeClass

重要

実装から投影型を返す必要がある場合は、MyRuntimeClass myRuntimeClass; を記述することにより実装型をインスタンス化しないように注意してください。When you want to return a projected type from your implementation, be careful not to instantiate the implementation type by writing MyRuntimeClass myRuntimeClass;. その場合の適切な手法とコードは、このトピックのセクション「実装型とインターフェイスをインスタンス化して返す」で示してあります。The correct techniques and code for that scenario are shown previously in this topic in the section Instantiating and returning implementation types and interfaces.

そのシナリオでの MyRuntimeClass myRuntimeClass; に関する問題は、スタック上に winrt::MyProject::implementation::MyRuntimeClass オブジェクトが作成されることです。The problem with MyRuntimeClass myRuntimeClass; in that scenario is that it creates a winrt::MyProject::implementation::MyRuntimeClass object on the stack. その (実装型の) オブジェクトは、いくつかの点で投影型のように動作します。同じ方法でそのオブジェクトのメソッドを呼び出すことができ、投影型に変換することさえできます。That object (of implementation type) behaves like the projected type in some ways—you can invoke methods on it in the same way; and it even converts to a projected type. ただし、スコープが終了すると、C++ の通常のルールに従って、オブジェクトは破棄されます。But the object destructs, as per normal C++ rules, when the scope exits. そのため、そのオブジェクトへの投影型 (スマート ポインター) を返した場合、そのポインターは中ぶらりんになります。So, if you returned a projected type (a smart pointer) to that object, then that pointer is now dangling.

この種のメモリ破損のバグを診断するのは困難です。This memory corruption type of bug is difficult to diagnose. したがって、デバッグ ビルドの場合は、C++/WinRT アサーションでスタック検出機能を使ってこの誤りをキャッチすると役に立ちます。So, for debug builds, a C++/WinRT assertion helps you catch this mistake, using a stack-detector. ただし、コルーチンはヒープ上に割り当てられるので、誤りがコルーチン内にある場合、この誤りに関するヘルプは得られません。But coroutines are allocated on the heap, so you won't get help with this mistake if you make it inside a coroutine.

さまざまな C++/WinRT 機能での投影型と実装型の使用Using projected types and implementation types with various C++/WinRT features

以下では、型が必要な C++/WinRT のさまざまな機能と、必要な型の種類 (投影型、実装型、両方) を示します。Here are various places where a C++/WinRT features expects a type, and what kind of type it expects (projected type, implementation type, or both).

機能Feature 受け入れられる型Accepts 説明Notes
T (スマート ポインターを表す)T (representing a smart pointer) 投影Projected 誤った実装型の使用については、「名前空間: 投影型、実装型、ファクトリ」の注意をご覧ください。See the caution in Namespaces: projected types, implementation types, and factories about using the implementation type by mistake.
agile_ref<T> BothBoth 実装型を使う場合は、コンストラクターの引数を com_ptr<T> にする必要があります。If you use the implementation type, then the constructor argument must be com_ptr<T>.
com_ptr<T> 実装Implementation 投影型を使うと、次のエラーが発生します: 'Release' is not a member of 'T'Using the projected type generates the error: 'Release' is not a member of 'T'.
default_interface<T> BothBoth 実装型を使った場合、最初に実装されたインターフェイスが返されます。If you use the implementation type, then the first implemented interface is returned.
get_self<T> 実装Implementation 投影型を使うと、次のエラーが発生します: '_abi_TrustLevel': is not a member of 'T'Using the projected type generates the error: '_abi_TrustLevel': is not a member of 'T'.
guid_of<T>() BothBoth 既定のインターフェイスの GUID が返されます。Returns the GUID of the default interface.
IWinRTTemplateInterface<T>
投影Projected 実装型を使ってもコンパイルされますが、それは誤りです。「名前空間: 投影型、実装型、ファクトリ」の注意をご覧ください。Using the implementation type compiles, but it's a mistake—see the caution in Namespaces: projected types, implementation types, and factories.
make<T> 実装Implementation 投影型を使うと、次のエラーが発生します: 'implements_type': is not a member of any direct or indirect base class of 'T'Using the projected type generates the error: 'implements_type': is not a member of any direct or indirect base class of 'T'
make_agile(T const&amp;) BothBoth 実装型を使う場合は、引数を com_ptr<T> にする必要があります。If you use the implementation type, then the argument must be com_ptr<T>.
make_self<T> 実装Implementation 投影型を使うと、次のエラーが発生します: 'Release': is not a member of any direct or indirect base class of 'T'Using the projected type generates the error: 'Release': is not a member of any direct or indirect base class of 'T'
name_of<T> 投影Projected 実装型を使った場合、既定のインターフェイスの文字列化された GUID が返されます。If you use the implementation type, then you get the stringified GUID of the default interface.
weak_ref<T> BothBoth 実装型を使う場合は、コンストラクターの引数を com_ptr<T> にする必要があります。If you use the implementation type, then the constructor argument must be com_ptr<T>.

重要な APIImportant APIs