C++/WinRT を使用した Windows ランタイム コンポーネント

このトピックでは、C++/WinRT を使用して Windows ランタイム コンポーネントを作成および使用する方法を示します。このコンポーネントは、任意の Windows ランタイム言語を使用してビルドされたユニバーサル Windows アプリから呼び出すことができます。

Windows ランタイム コンポーネントを C++/winrt で構築するのには、いくつかの理由があります。

  • 複雑な操作または負荷の高い操作で C++ のパフォーマンス上のメリットを得る。
  • 既に記述およびテストされている標準的な C++ コードを再利用する。
  • C# などで記述されたユニバーサル Windows プラットフォーム (UWP) アプリに Win32 機能を公開する。

一般に、C++/WinRT コンポーネントを作成する場合は、標準 C++ ライブラリからの型と組み込み型を使用できます。ただし、別の .winmd パッケージ内のコードとの間でデータをやり取りしているアプリケーション バイナリ インターフェイス (ABI) 境界は除きます。 ABI では Windows ランタイム型を使用します。 さらに、C++/WinRT コードでは、委任やイベントなどの型を使用することで、ご利用のコンポーネントから発生させて別の言語で処理できるイベントを実装してください。 C++/WinRT の詳細については、C++/WinRT に関するページを参照してください。

このトピックの残りの部分では、C++/WinRT で Windows ランタイム コンポーネントを作成する方法と、それをアプリケーションから使用する方法について説明します。

このトピックでビルドする Windows ランタイム コンポーネントには、温度計を表すランタイム クラスが含まれています。 また、このトピックでは、温度計のランタイム クラスを使用していて、温度を調整するための関数を呼び出すコア アプリについても示します。

Note

C++/WinRT Visual Studio Extension (VSIX) と NuGet パッケージ (両者が連携してプロジェクト テンプレートとビルドをサポート) のインストールと使用については、Visual Studio での C++/WinRT のサポートに関する記事を参照してください。

重要

C++/WinRT でランタイム クラスを使用および作成する方法についての理解をサポートするために重要な概念と用語については、「C++/WinRT での API の使用」と「C++/WinRT での作成者 API」を参照してください。

Windows ランタイム コンポーネント dll の名前付けのベスト プラクティス

重要

このセクションでは、Windows ランタイム コンポーネントをビルドする.dll ファイル (DLL) に使用することをお勧めする名前付け規則について説明します。 これは、Windows ランタイム コンポーネントからランタイム クラスを使用するときに C++/WinRT が従うアクティブ化シーケンスに関するすべてです。

クラス ファクトリをアクティブ化する場合、C++/WinRT は最初に RoGetActivationFactory の呼び出しを試みます。 失敗した場合、C++/WinRT は直接読み込む DLL を見つけようとします。 Windows ランタイムアクティブ化は、常に完全修飾クラス名に基づいています。 ロジックは、(その完全修飾クラス名から) クラス名を削除し、残っている完全な名前空間の名前の DLL を探します。 見つからない場合は、最も具体的なセグメント名を削除して、繰り返します。

たとえば、アクティブ化されるクラスに Contoso.Instruments.ThermometerWRC.Thermometer の完全修飾名があり、 RoGetActivationFactory が失敗した場合は、最初に Contoso.Instruments.ThermometerWRC.dllを探します。 それが見つからない場合は、 Contoso.Instruments.dllを探し、次に Contoso.dllを探します。

DLL が見つかった場合 (そのシーケンス内)、DLL の DllGetActivationFactory エントリ ポイントを使用して、(最初に試行した RoGetActivationFactory 関数を介して間接的にではなく) ファクトリを直接取得しようとします。 それでも、最終的な結果は呼び出し元と DLL に区別できません。

このプロセスは完全に自動化されており、登録やツールは必要ありません。 Windows ランタイム コンポーネントを作成する場合は、先ほど説明したプロセスで動作する DLL の名前付け規則を使用するだけで済みます。 また、Windows ランタイム コンポーネントを使用していて、正しい名前ではない場合は、説明に従って名前を変更できます。

Windows ランタイム コンポーネント (ThermometerWRC) を作成する

まず、Microsoft Visual Studio で、新しいプロジェクトを作成します。 Windows ランタイム コンポーネント (C++/WinRT) プロジェクトを作成し、それに ThermometerWRC ("温度計 Windows ランタイム コンポーネント") という名前を付けます。 [ソリューションとプロジェクトを同じディレクトリに配置する] がオフになっていることを確認します。 Windows SDK の最新の一般公開された (プレビュー以外の) バージョンを対象とします。 プロジェクトに ThermometerWRC という名前を付けると、このトピックの残りの手順が非常に簡単になります。

プロジェクトはまだビルドしないでください。

新しく作成されたプロジェクトには、 Class.idlという名前のファイルが含まれています。 ソリューション エクスプローラーで、そのファイル Thermometer.idl の名前を変更します (.idl ファイルの名前を変更すると、依存ファイル .h および .cpp も自動的に名前変更されます)。 Thermometer.idl の内容を、以下のリストで置き換えます。

// Thermometer.idl
namespace ThermometerWRC
{
    runtimeclass Thermometer
    {
        Thermometer();
        void AdjustTemperature(Single deltaFahrenheit);
    };
}

ファイルを保存します。 現時点ではプロジェクトは完成するまでビルドされませんが、ここでビルドすることは有用です。それによって、Thermometer ランタイム クラスを実装するためのソース コード ファイルが生成されるからです。 それでは、今すぐビルドしてみましょう (この段階で表示されることが予想されるビルド エラーは、Class.hClass.g.h が見つからないことに関係しています)。

ビルド プロセス中に、midl.exe ツールが実行されてコンポーネントの Windows ランタイム メタデータ ファイルが作成されます (これは \ThermometerWRC\Debug\ThermometerWRC\ThermometerWRC.winmd です)。 次に、 cppwinrt.exe ツールが ( -component オプションを使用して) 実行され、コンポーネントの作成をサポートするソース コード ファイルが生成されます。 これらのファイルには、IDL で宣言した Thermometer ランタイム クラスの実装を開始するためのスタブが含まれています。 これらのスタブは \ThermometerWRC\ThermometerWRC\Generated Files\sources\Thermometer.hThermometer.cpp です。

プロジェクト ノードを右クリックし、[エクスプローラーでフォルダーを開く] をクリックします。 これにより、エクスプローラーでプロジェクト フォルダーが開きます。 そこで、スタブ ファイル Thermometer.h および Thermometer.cpp をフォルダー \ThermometerWRC\ThermometerWRC\Generated Files\sources\ から、ご自分のプロジェクト ファイルを含むフォルダー \ThermometerWRC\ThermometerWRC\ にコピーし、コピー先のファイルを置き換えます。 ここで、Thermometer.hThermometer.cpp を開いてランタイム クラスを実装してみましょう。 Thermometer.h で、Thermometer の実装 (ファクトリ実装では "なく") に新しいプライベート メンバーを追加します。

// Thermometer.h
...
namespace winrt::ThermometerWRC::implementation
{
    struct Thermometer : ThermometerT<Thermometer>
    {
        ...

    private:
        float m_temperatureFahrenheit { 0.f };
    };
}
...

Thermometer.cpp で、次の一覧に示すように AdjustTemperature メソッドを実装します。

// Thermometer.cpp
...
namespace winrt::ThermometerWRC::implementation
{
    void Thermometer::AdjustTemperature(float deltaFahrenheit)
    {
        m_temperatureFahrenheit += deltaFahrenheit;
    }
}

Thermometer.hThermometer.cpp の先頭に static_assert がありますが、これを削除する必要があります。 これで、プロジェクトがビルドされます。

任意の警告によってビルドが妨げられる場合は、該当する警告を解決するか、またはプロジェクトのプロパティ C/C++>General>Treat Warnings As ErrorsNo (/WX-) に設定して、もう一度プロジェクトをビルドします。

コア アプリ (ThermometerCoreApp) を作成して Windows ランタイム コンポーネントをテストします。

ここで (ThermometerWRC ソリューション、または新しいソリューションのいずれかに) 新しいプロジェクトを作成します。 コア アプリ (C++/WinRT) プロジェクトを作成し、それに ThermometerCoreApp という名前を付けます。 2 つのプロジェクトが同じソリューションに属している場合は、ThermometerCoreApp をスタートアップ プロジェクトとして設定します。

Note

既に説明したように、Windows ランタイム コンポーネント (そのプロジェクト名は ThermometerWRC) の Windows ランタイム メタデータ ファイルは \ThermometerWRC\Debug\ThermometerWRC\ フォルダーに作成されます。 このパスの最初のセグメントは、ソリューション ファイルを含むフォルダーの名前です。次のセグメントは、Debug という名前のサブディレクトリです。最後のセグメントは、Windows ランタイム コンポーネントの名前を付けられたサブディレクトリです。 プロジェクトに ThermometerWRC という名前を指定しなかった場合、メタデータ ファイルは \<YourProjectName>\Debug\<YourProjectName>\ フォルダーに配置されます。

次に、コア アプリ プロジェクト (ThermometerCoreApp) で、参照を追加し、\ThermometerWRC\Debug\ThermometerWRC\ThermometerWRC.winmd を参照します (または 2 つのプロジェクトが同じソリューションに属している場合は、プロジェクト間の参照を追加します)。 [追加] をクリックしOKします。 次に、ThermometerCoreApp をビルドします。 万が一、ペイロード ファイル readme.txt が存在しないというエラーが表示された場合、Windows ランタイム コンポーネント プロジェクトからそのファイルを除外し、これを再ビルドしてから、ThermometerCoreApp を再ビルドします。

ビルド プロセス中に、 cppwinrt.exe ツールが実行され、参照されている .winmd ファイルが、コンポーネントの使用をサポートする投影型を含むソース コード ファイルに処理されます。 コンポーネントのランタイム クラス (ThermometerWRC.h という名前) の投影型のヘッダーは、フォルダー \ThermometerCoreApp\ThermometerCoreApp\Generated Files\winrt\ 内に生成されます。

そのヘッダーを App.cppに含めます。

// App.cpp
...
#include <winrt/ThermometerWRC.h>
...

また App.cpp で、次のコードを追加して、Thermometer オブジェクト (射影された型の既定のコンストラクターを使用) をインスタンス化し、温度計オブジェクトに対してメソッドを呼び出します。

struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
    ThermometerWRC::Thermometer m_thermometer;
    ...
    
    void OnPointerPressed(IInspectable const &, PointerEventArgs const & args)
    {
        m_thermometer.AdjustTemperature(1.f);
        ...
    }
    ...
};

ウィンドウをクリックするたびに、温度計のオブジェクトの温度が増加します。 コードをステップ実行して、アプリケーションから Windows ランタイム コンポーネントが実際に呼び出されていることを確認したい場合は、ブレークポイントを設定できます。

次のステップ

ご利用の C++/WinRT Windows ランタイム コンポーネントにさらに多くの機能または新しい Windows ランタイムの種類を追加するには、上に示したのと同じパターンに従うことができます。 まず、IDL を使用して、公開する機能を定義します。 次に、Visual Studio でプロジェクトをビルドして、スタブ実装を生成します。 次に、必要に応じて実装を完了します。 IDL で定義したメソッド、プロパティ、およびイベントは、Windows ランタイム コンポーネントを使用するアプリケーションから参照できます。 IDL の詳細については、「Microsoft インターフェイス定義言語 3.0 の概要」を参照してください。

Windows ランタイム コンポーネントにイベントを追加する方法の例については、「C++/WinRT でのイベントの作成」を参照してください。

トラブルシューティング

症状 修正
C++/WinRT アプリで、XAML を使用する C# Windows ランタイム コンポーネントを使用すると、"'MyNamespace_XamlTypeInfo' は 'winrt::MyNamespace' のメンバーではありません" という形式のエラーがコンパイラによって生成されます。"この MyNamespace" は、Windows ランタイム コンポーネントの名前空間の名前です。 使用する側の C++/WinRT アプリの pch.h で、#include <winrt/MyNamespace.MyNamespace_XamlTypeInfo.h> を追加します。必要に応じて "MyNamespace" を置き換えます。