Windows ランタイム コンポーネントでイベントを生成する

注意

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

Windows ランタイム コンポーネントで、ユーザー定義のデリゲート型のイベントをバック グラウンド スレッド (ワーカー スレッド) に生成し、このイベントを JavaScript で受け取れるようにする必要がある場合は、以下のいずれかの方法でイベントの実装や生成を行うことができます。

  • (オプション 1) Windows.UI.Core.CoreDispatcher でイベントを生成し、JavaScript のスレッド コンテキストにマーシャリングします。 通常はこれが最適な方法ですが、シナリオによっては最速のパフォーマンスを実現できない場合があります。
  • (オプション 2) Windows.Foundation.EventHandler<オブジェクト> を使用します (ただし、イベントの型に関する情報が失われます)。 オプション 1 を実行できない場合、または十分なパフォーマンスが得られない場合、型情報が失われても問題がなければ、これが次善の策です。 C# Windows ランタイム コンポーネントを作成している場合、Windows.Foundation.EventHandler<オブジェクト> 型は使用できません代わりに、その型が System.EventHandler に投影されるため、それを代わりに使用する必要があります。
  • (オプション 3) コンポーネントに対し、独自のプロキシとスタブを作成します。 このオプションは実装が最も難しいですが、型情報も保持され、要求が厳しいシナリオの場合に、オプション 1 よりパフォーマンスが高くなる可能性があります。

これらのオプションをいずれも使用せずに、バックグラウンド スレッドでイベントを生成すると、JavaScript クライアントはイベントを受け取りません。

背景

すべての Windows ランタイム コンポーネントとアプリは、どの言語を使用して作成しても、基本的に COM オブジェクトです。 Windows API では、ほとんどのコンポーネントはアジャイル COM オブジェクトで、バックグラウンド スレッドと UI スレッドで同じようにオブジェクトと通信できます。 COM オブジェクトをアジャイルにできない場合は、UI スレッドとバックグラウンド スレッドの境界を越えて他の COM オブジェクトと通信できるように、プロキシおよびスタブと呼ばれるヘルパー オブジェクトが必要になります。 (COM の用語では、これをスレッド アパートメント間の通信と呼びます。)

Windows API のほとんどのオブジェクトは、アジャイルであるか、プロキシとスタブが組み込まれています。 ただし、Windows.Foundation.TypedEventHandler<TSender, TResult> などのジェネリックな型は、型引数を指定するまでは完全な型ではないため、プロキシとスタブを作成できません。 プロキシまたはスタブがないために問題が発生するのは、JavaScript クライアントのみですが、コンポーネントを C++ や .NET 言語からだけでなく JavaScript からも使用したい場合は、次の 3 つのオプションのいずれかを使用する必要があります。

(オプション 1) CoreDispatcher でイベントを生成する

Windows.UI.Core.CoreDispatcher を使用するとユーザー定義のデリゲート型のイベントを送信でき、JavaScript でそのイベントを受け取ることができます。 どのオプションを使用すればよいかわからない場合は、最初にこのオプションを試してください。 イベントの発生からイベントの処理までの待ち時間が問題になる場合は、他のオプションを試してください。

次の例は、CoreDispatcher を使用して厳密に型指定されされたイベントを生成する方法を示します。 型引数は Toast で、Object ではないことに注意してください。

public event EventHandler<Toast> ToastCompletedEvent;
private void OnToastCompleted(Toast args)
{
    var completedEvent = ToastCompletedEvent;
    if (completedEvent != null)
    {
        completedEvent(this, args);
    }
}

public void MakeToastWithDispatcher(string message)
{
    Toast toast = new Toast(message);
    // Assume you have a CoreDispatcher at class scope.
    // Initialize it here, then use it from the background thread.
    var window = Windows.UI.Core.CoreWindow.GetForCurrentThread();
    m_dispatcher = window.Dispatcher;

    Task.Run( () =>
    {
        if (ToastCompletedEvent != null)
        {
            m_dispatcher.RunAsync(CoreDispatcherPriority.Normal,
            new DispatchedHandler(() =>
            {
                this.OnToastCompleted(toast);
            })); // end m_dispatcher.RunAsync
         }
     }); // end Task.Run
}

(オプション 2) EventHandler<Object> を使用するが、型情報が失われる

注意

C# Windows ランタイム コンポーネントを作成している場合、Windows.Foundation.EventHandler<オブジェクト> 型は使用できません代わりに、その型が System.EventHandler に投影されるため、それを代わりに使用する必要があります。

バック グラウンド スレッドからイベントを送信するもう 1 つの方法は、Windows.Foundation.EventHandler<Object> をイベントの型として使用することです。 Windows により、ジェネリック型が具体的にインスタンス化され、プロキシとスタブが提供されます。 欠点は、イベント引数の型情報と送信者が失われることです。 C++ および .NET クライアントは、イベントを受け取ったときにキャストする型の情報をドキュメントから得る必要があります。 JavaScript クライアントでは、元の型情報は必要ありません。 メタデータの名前に基づいて、引数のプロパティを見つけます。

次の例は、C# で Windows.Foundation.EventHandler<Object> を使用する方法を示します。

public sealed Class1
{
// Declare the event
public event EventHandler<Object> ToastCompletedEvent;

    // Raise the event
    public async void MakeToast(string message)
    {
        Toast toast = new Toast(message);
        // Fire the event from a background thread to allow this thread to continue
        Task.Run(() =>
        {
            if (ToastCompletedEvent != null)
            {
                OnToastCompleted(toast);
            }
        });
    }

    private void OnToastCompleted(Toast args)
    {
        var completedEvent = ToastCompletedEvent;
        if (completedEvent != null)
        {
            completedEvent(this, args);
        }
    }
}

JavaScript 側では、次のようにこのイベントを利用します。

toastCompletedEventHandler: function (event) {
   var toastType = event.toast.toastType;
   document.getElementById("toasterOutput").innerHTML = "<p>Made " + toastType + " toast</p>";
}

(オプション 3) 独自のプロキシとスタブを作成する

型情報を完全に保持するユーザー定義のイベント型で十分なパフォーマンスを得るには、独自のプロキシとスタブのオブジェクトを作成し、アプリ パッケージに埋め込む必要があります。 通常、このオプションを使用しなければならないのはまれで、他の 2 つのオプションをどちらも使用できない場合のみです。 また、このオプションで他の 2 つのオプションよりも高いパフォーマンスが実現されるという保証はありません。 実際のパフォーマンスは、さまざまな要因によって決まります。 アプリケーションでの実際のパフォーマンスを測定し、イベントが実際にボトルネックになっているかどうかを判別するには、Visual Studio プロファイラーまたは他のプロファイリング ツールを使用します。

ここからは、C# を使用して基本的な Windows ランタイム コンポーネントを作成した後、C++ を使用してプロキシおよびスタブの DLL を作成する方法について説明します。これにより、非同期操作でコンポーネントにより生成された Windows.Foundation.TypedEventHandler<TSender, TResult> イベントを、JavaScript で利用できるようになります。 (C++ または Visual Basic を使用してコンポーネントを作成することもできます。プロキシとスタブの作成に関連する手順は同じです)。このチュートリアルは、Windows ランタイムインプロセス コンポーネント サンプル (C++/CX) の作成に基づいており、その目的を説明するのに役立ちます。

このチュートリアルは、次のパートで構成されています。

  • ここでは、2 つの基本的な Windows ランタイム クラスを作成します。 1 つのクラスでは、Windows.Foundation.TypedEventHandler<TSender, TResult> 型のイベントを公開し、もう 1 つのクラスは、TValue の引数として JavaScript に返される型です。 後の手順を完了するまで、これらのクラスは JavaScript と通信できません。
  • このアプリは、メイン クラス オブジェクトをアクティブ化し、メソッドを呼び出して、Windows ランタイム コンポーネントで生成されたイベントを処理します。
  • これらはプロキシおよびスタブのクラスを生成するツールで必要です。
  • その後、IDL ファイルを使用して、プロキシおよびスタブの C ソース コードを生成します。
  • プロキシ/スタブ オブジェクトを登録すると、COM ランタイムがこれらを認識し、アプリ プロジェクトでプロキシ/スタブ DLL を参照できるようになります。

Windows ランタイム コンポーネントを作成するには

Visual Studio のメニュー バーで、[ファイル] > [新しいプロジェクト] を選択します。 [新しいプロジェクト] ダイアログ ボックスで、[JavaScript] > [ユニバーサル Windows] の順に展開し、[空のアプリ] を選択します。 プロジェクトに、「ToasterApplication」という名前を付け、 [OK] ボタンをクリックします。

ソリューションに、C# の Windows ランタイム コンポーネントを追加します。ソリューション エクスプローラーで、ソリューションのショートカット メニューを開き、[追加] > [新しいプロジェクト] を選択します。 [Visual C#] > [Microsoft Store] の順に展開し、[Windows ランタイム コンポーネント] を選択します。 プロジェクトに、「ToasterComponent」という名前を付け、 [OK] ボタンをクリックします。 ToasterComponent は、後の手順で作成するコンポーネントのルート名前空間になります。

ソリューション エクスプローラーでソリューションのショートカット メニューを開き、[プロパティ] をクリックします。 [プロパティ ページ] ダイアログ ボックスの左側のウィンドウで、[構成プロパティ] を選択して、ダイアログ ボックスの上部の [構成][デバッグ] に、[プラットフォーム] を [x86]、[x64]、または [ARM] に設定します。 [OK] を選択します。

重要 [プラットフォーム] を [Any CPU] に設定すると、後でソリューションに追加するネイティブ コード Win32 DLL で無効になるため、機能しません。

ソリューション エクスプ ローラーで、「class1.cs」を「ToasterComponent.cs」という名前に変更して、プロジェクトの名前と一致するようにします。 Visual Studio により、新しいファイル名と一致するように、ファイル内のクラス名が自動的に変更されます。

.cs ファイルで、Windows.Foundation 名前空間の using ディレクティブを追加して、TypedEventHandler をスコープに取り込みます。

プロキシとスタブが必要な場合、コンポーネントではインターフェイスを使用して、パブリック メンバーを公開する必要があります。 ToasterComponent.cs では、トースター用に 1 つ、トースターが生成するトースト用にもう 1 つインターフェイスを定義します。

C# ではこの手順を省略できます。 代わりに、最初に 1 つのクラスを作成し、次にそのショートカット メニューを開き、[リファクター] > [インターフェイスの抽出] を選択します。 生成されたコードで、インターフェイスのパブリック アクセシビリティを手動で指定します。

	public interface IToaster
        {
            void MakeToast(String message);
            event TypedEventHandler<Toaster, Toast> ToastCompletedEvent;

        }
        public interface IToast
        {
            String ToastType { get; }
        }

IToast インターフェイスには、トーストの型を取得して書き込むことができる文字列があります。 IToaster インターフェイスには、トーストを作成するメソッドと、トーストが作成されたことを示すイベントがあります。 このイベントでは、トーストの特定の部分 (つまり型) が返されるため型指定されたイベントと呼ばれます。

次に、これらのインターフェイスを実装したクラスをパブリックにしてシールする必要があります。これにより、後でプログラミングする JavaScript アプリからアクセスできるようになります。

	public sealed class Toast : IToast
        {
            private string _toastType;

            public string ToastType
            {
                get
                {
                    return _toastType;
                }
            }
            internal Toast(String toastType)
            {
                _toastType = toastType;
            }

        }
        public sealed class Toaster : IToaster
        {
            public event TypedEventHandler<Toaster, Toast> ToastCompletedEvent;

            private void OnToastCompleted(Toast args)
            {
                var completedEvent = ToastCompletedEvent;
                if (completedEvent != null)
                {
                    completedEvent(this, args);
                }
            }

            public void MakeToast(string message)
            {
                Toast toast = new Toast(message);
                // Fire the event from a thread-pool thread to enable this thread to continue
                Windows.System.Threading.ThreadPool.RunAsync(
                (IAsyncAction action) =>
                {
                    if (ToastCompletedEvent != null)
                    {
                        OnToastCompleted(toast);
                    }
                });
           }
        }

上記のコードでは、トーストを作成し、スレッド プールの作業項目を起動して、通知を生成します。 IDE では、非同期呼び出しに対して await キーワードを適用することを推奨している場合がありますが、メソッドで操作結果に依存する処理は行わないため、ここでは必要ありません。

上記のコードの非同期呼び出しでは、ThreadPool.RunAsync のみを使用して、バックグラウンド スレッドでイベントを発生させる簡単な方法を示します。 この特定のメソッドは、次の例で示すように記述することもできます。.NET のタスク スケジューラは、async/await 呼び出しを自動的にマーシャリングして UI スレッドに返すため、正常に動作します。  

	public async void MakeToast(string message)
    {
        Toast toast = new Toast(message)
        await Task.Delay(new Random().Next(1000));
        OnToastCompleted(toast);
    }

ここでプロジェクトをビルドすると、クリーンにビルドされます。

JavaScript アプリをプログラミングするには

次に、JavaScript アプリにボタンを追加し、先ほど定義したクラスを使用してトーストを作成できるようにします。 この作業を行う前に、先ほど作成した ToasterComponent プロジェクトへの参照を追加する必要があります。 ソリューション エクスプローラーで、ToasterApplication プロジェクトのショートカット メニューを開き、[追加] > [参照] を選択して、[新しい参照の追加] を選択します。 [新しい参照の追加] ダイアログ ボックスの、左側のウィンドウの [ソリューション] の下で、コンポーネント プロジェクトを選択してから、中央のウィンドウで [ToasterComponent] を選択します。 [OK] を選択します。

ソリューション エクスプローラーで、ToasterApplication プロジェクトのショートカット メニューを開き、[スタートアップ プロジェクトに設定] をクリックします。

default.js ファイルの末尾に、コンポーネントを呼び出し、コンポーネントに呼び戻される関数を含む名前空間を追加します。 名前空間には、トーストを作成する関数と、トースト完了のイベントを処理する関数の 2 つの関数を含めます。 makeToast を実装すると、Toaster オブジェクトが作成され、イベント ハンドラーが登録されて、トーストが作成されます。 次に示すように、この時点では、イベント ハンドラーで何かをするわけではありません。

	WinJS.Namespace.define("ToasterApplication"), {
       makeToast: function () {

          var toaster = new ToasterComponent.Toaster();
          //toaster.addEventListener("ontoastcompletedevent", ToasterApplication.toastCompletedEventHandler);
          toaster.ontoastcompletedevent = ToasterApplication.toastCompletedEventHandler;
          toaster.makeToast("Peanut Butter");
       },

       toastCompletedEventHandler: function(event) {
           // The sender of the event (the delegate's first type parameter)
           // is mapped to event.target. The second argument of the delegate
           // is contained in event, which means in this case event is a
           // Toast class, with a toastType string.
           var toastType = event.toastType;

           document.getElementById('toastOutput').innerHTML = "<p>Made " + toastType + " toast</p>";
        },
    });

makeToast 関数をボタンにフックする必要があります。 default.html を更新して、ボタンと、トーストを作成した結果を出力するためのスペースを追加します。

    <body>
        <h1>Click the button to make toast</h1>
        <button onclick="ToasterApplication.makeToast()">Make Toast!</button>
        <div id="toasterOutput">
            <p>No Toast Yet...</p>
        </div>
    </body>

TypedEventHandler を使用しない場合、この時点でアプリをローカル コンピューターで実行してボタンをクリックすると、トーストを作成することができます。 でも、このアプリでは何も起こりません。 理由を確認するには、ToastCompletedEvent を生成するマネージ コードをデバッグしてみましょう。 プロジェクトを停止し、メニュー バーで、[デバッグ] > [ToasterApplication のプロパティ] を選択します。 [デバッガーの種類][マネージのみ] に変更します。 もう一度メニュー バーで、[デバッグ] > [例外] を選択して、[Common Language Runtime Exceptions] (共通言語ランタイムの例外) を選択します。

ここでアプリを実行し、トースト作成ボタンをクリックします。 デバッガーは、無効なキャスト例外をキャッチします。 メッセージからはわかりませんが、この例外は、インターフェイスのプロキシが存在しないために発生します。

プロキシが存在しない

コンポーネントのプロキシとスタブを作成するには、まず、インターフェイスに一意の ID または GUID を追加します。 ただし、使用する GUID の形式は、コーディングに C#、Visual Basic、または他の .NET 言語を使用する場合と、C++ を使用する場合で異なります。

コンポーネントのインターフェイスの GUID を生成するには (C# および他の .NET 言語)

メニュー バーで、[ツール] > [GUID の作成] を選択します。 ダイアログ ボックスで、5. の [Guid("xxxxxxxx-xxxx...xxxx")] を選択します。 [新規 GUID] ボタンを選択し、次に [コピー] を選択します。

GUID ジェネレーター ツール

インターフェイスの定義に戻り、次の例のように、IToaster インターフェイスの直前に、新しい GUID を貼り付けます。 (この例では GUID を使用しないでください。すべての一意のインターフェイスには、独自の GUID が必要です)。

[Guid("FC198F74-A808-4E2A-9255-264746965B9F")]
        public interface IToaster...

System.Runtime.InteropServices 名前空間の using ディレクティブを追加します。

IToast インターフェイスについても、これらの手順を繰り返します。

コンポーネントのインターフェイスの GUID を生成するには (C++)

メニュー バーで、[ツール] > [GUID の作成] を選択します。 ダイアログ ボックスで、3. の [static const struct GUID = {...}] を選択します。 [新規 GUID] ボタンを選択し、次に [コピー] を選択します。

IToaster インターフェイスの定義の直前に GUID を貼り付けます。 貼り付けた後、GUID は次の例のようになります。 (この例では GUID を使用しないでください。すべての一意のインターフェイスには、独自の GUID が必要です)。

// {F8D30778-9EAF-409C-BCCD-C8B24442B09B}
    static const GUID <<name>> = { 0xf8d30778, 0x9eaf, 0x409c, { 0xbc, 0xcd, 0xc8, 0xb2, 0x44, 0x42, 0xb0, 0x9b } };

Windows.Foundation.Metadata の using ディレクティブを追加して、GuidAttribute をスコープに取り込みます。

ここで、const GUID を手動で GuidAttribute に変換すると、次の例のようにフォーマットされます。 中かっこが角かっこと丸かっこに置き換えられ、末尾のセミコロンが削除されていることがわかります。

// {E976784C-AADE-4EA4-A4C0-B0C2FD1307C3}
    [GuidAttribute(0xe976784c, 0xaade, 0x4ea4, 0xa4, 0xc0, 0xb0, 0xc2, 0xfd, 0x13, 0x7, 0xc3)]
    public interface IToaster
    {...

IToast インターフェイスについても、これらの手順を繰り返します。

これで、インターフェイスに一意の ID が付与されたため、winmdidl コマンド ライン ツールに .winmd ファイルを入力して、IDL ファイルを作成してから、MIDL コマンド ライン ツールにその IDL ファイルを入力して、プロキシおよびスタブの C ソース コードを生成できます。 次の手順に示すようにビルド後のイベントを作成すると、Visual Studio でこれが実行されます。

プロキシおよびスタブのソース コードを生成するには

カスタム ビルド後のイベントを追加するには、ソリューション エクスプローラーで、ToasterComponent プロジェクトのショートカット メニューを開き、[プロパティ] をクリックします。 プロパティ ページの左側のウィンドウで、[ビルド イベント] を選択し、[ビルド後の編集] ボタンをクリックします。 次のコマンドを、ビルド後のコマンド ラインに追加します。 (先にバッチ ファイルを呼び出して、winmdidl ツールを認識できるように環境変数を設定する必要があります。)

call "$(DevEnvDir)..\..\vc\vcvarsall.bat" $(PlatformName)
winmdidl /outdir:output "$(TargetPath)"
midl /metadata_dir "%WindowsSdkDir%References\CommonConfiguration\Neutral" /iid "$(ProjectDir)$(TargetName)_i.c" /env win32 /h "$(ProjectDir)$(TargetName).h" /winmd "Output\$(TargetName).winmd" /W1 /char signed /nologo /winrt /dlldata "$(ProjectDir)dlldata.c" /proxy "$(ProjectDir)$(TargetName)_p.c" "Output\$(TargetName).idl"

重要 ARM または x64 プロジェクト構成の場合、MIDL の /env パラメーターを x64 または arm32 に変更します。

.winmd ファイルが変更されるたびに IDL ファイルが再生成されるようにするには、[ ビルド後のイベントを実行する ] を [ ビルドによってプロジェクト出力が更新されたとき ] に変更します。[ビルド イベント] プロパティ ページは次のようになります。 ビルド イベント

ソリューションをリビルドし、IDL を生成してコンパイルします。

ToasterComponent プロジェクト ディレクトリ内で ToasterComponent.h、ToasterComponent_i.c、ToasterComponent_p.c、および dlldata.c を探すことで、MIDL がソリューションを正しくコンパイルしたことを確認できます。

プロキシおよびスタブのコードをコンパイルして DLL を生成するには

これで、必要なファイルがそろったので、ファイルをコンパイルして、C++ ファイルの DLL を生成できます。 できる限り作業を簡単に行うために、プロキシのビルドをサポートする新しいプロジェクトを追加します。 ToasterApplication ソリューションのショートカット メニューを開き、[追加] > [新しいプロジェクト] を選択します。 [新しいプロジェクト] ダイアログ ボックスの左側のペインで、[Visual C++] > [Windows] > [ユニバーサル Windows] の順に展開し、中央のペインで [DLL (UWP apps)](DLL (UWP アプリ)) を選択します (これは C++ Windows ランタイム コンポーネント プロジェクトではないことに注意してください)。プロジェクトの名前に「Proxies」を指定して、[OK] を選択します。 これらのファイルは、C# クラスで変更が発生したときに、ビルド後のイベントで更新されます。

Proxies プロジェクトは既定で、ヘッダーの .h ファイルと C++ の .cpp ファイルを生成します。 DLL は MIDL から生成されたファイルでビルドされるため、.h ファイルと .cpp ファイルは必要ありません。 ソリューション エクスプローラーでこれらのショートカット メニューを開き、[削除] をクリックして、メッセージが表示されたら削除を確定します。

この時点ではプロジェクトは空なので、MIDL で生成されたファイルを再び追加することができます。 Proxies プロジェクトのショートカット メニューを開き、[追加] > [既存の項目] を選択します。ダイアログ ボックスで ToasterComponent のプロジェクト ディレクトリに移動し、ToasterComponent.h、ToasterComponent_i.c、ToasterComponent_p.c、および dlldata.c ファイルを選択します。 [追加] ボタンを選びます。

Proxies プロジェクトで .def ファイルを作成し、dlldata.c に記述されている DLL エクスポートを定義します。 プロジェクトのショートカット メニューを開き、[追加] > [新しい項目] を選択します。 ダイアログ ボックスの左側のウィンドウで [コード] を選択し、中央のウィンドウで [モジュール定義ファイル] を選択します。 ファイルに「proxies.def」という名前を付け、[追加] ボタンをクリックします。 この .def ファイルを開き、dlldata.c で定義されている EXPORTS を含むように変更します。

EXPORTS
    DllCanUnloadNow         PRIVATE
    DllGetClassObject       PRIVATE

ここでプロジェクトをビルドすると、失敗します。 このプロジェクトを正しくコンパイルするには、プロジェクトをコンパイルする方法およびプロジェクトがリンクされる方法を変更する必要があります。 ソリューション エクスプローラーで、Proxies プロジェクト ノードのショートカット メニューを開き、[プロパティ] を選択します。 プロパティ ページを次のように変更します。

左側のペインで [C/C++] > [プリプロセッサ] を選択します。右側のペインで [プリプロセッサの定義] を選択して下矢印ボタンを選択し、[編集] を選択します。 ボックスに次の定義を追加します。

WIN32;_WINDOWS

[C/C++] > [プリコンパイル済みヘッダー] の下で、[プリコンパイル済みヘッダー][プリコンパイル済みヘッダーを使用しない] に変更し、[適用] ボタンを選択します。

[リンカー] > [全般] の下で、[インポート ライブラリの無視][はい] に変更し、[適用] ボタンを選択します。

[リンカー] > [入力] の下で、[追加の依存ファイル] を選択して下矢印ボタンを選択し、[編集] を選択します。 ボックスに次のテキストを追加します。

rpcrt4.lib;runtimeobject.lib

これらの lib を list 行に直接貼り付けないでください。 [編集] ボックスを使用して、Visual Studio の MSBuild で正しい追加の依存ファイルが維持されるようにします。

これらの変更を行ったら、[プロパティ ページ] ダイアログ ボックスの [OK] ボタンをクリックします。

次に、ToasterComponent プロジェクトへの依存関係を設定します。 この設定により、常に Toaster がビルドされてからプロキシ プロジェクトがビルドされます。 Toaster プロジェクトでプロキシをビルドするファイルを生成するため、この設定が必要となります。

Proxies プロジェクトのショートカット メニューを開き、[プロジェクトの依存関係] をクリックします。 Proxies プロジェクトが ToasterComponent プロジェクトに依存していることを示すようにチェック ボックスをオンにして、Visual Studio が正しい順序でビルドされるようにします。

Visual Studio のメニュー バーで [ビルド] > [ソリューションのリビルド] を選択して、ソリューションが正しくビルドされることを確認します。

プロキシおよびスタブを登録するには

ToasterApplication プロジェクトで、package.appxmanifest のショートカット メニューを開き、[プログラムから開く] をクリックします。 [プログラムから開く] ダイアログ ボックスで、[XML テキスト エディター] を選択し、[OK] を選択します。 プロキシの GUID に基づいて windows.activatableClass.proxyStub 拡張機能を登録する複数の XML を貼り付けます。 .appxmanifest ファイルで使用する GUID を調べるには、ToasterComponent_i.c を開きます。 次の例のようなエントリを見つけます。 また、IToast、IToaster、および 3 つ目のインターフェイス (Toaster と Toast の 2 つのパラメーターを持つ型指定されたイベント ハンドラー) の定義も確認します。 これは Toaster クラスで定義されているイベントと一致します。 IToast および IToaster の GUID が、C# ファイル内のインターフェイスで定義されている GUID と一致することを確認します。 型指定されたイベント ハンドラーのインターフェイスは自動生成されるため、このインターフェイスの GUID も自動生成されます。

MIDL_DEFINE_GUID(IID, IID___FITypedEventHandler_2_ToasterComponent__CToaster_ToasterComponent__CToast,0x1ecafeff,0x1ee1,0x504a,0x9a,0xf5,0xa6,0x8c,0x6f,0xb2,0xb4,0x7d);

MIDL_DEFINE_GUID(IID, IID___x_ToasterComponent_CIToast,0xF8D30778,0x9EAF,0x409C,0xBC,0xCD,0xC8,0xB2,0x44,0x42,0xB0,0x9B);

MIDL_DEFINE_GUID(IID, IID___x_ToasterComponent_CIToaster,0xE976784C,0xAADE,0x4EA4,0xA4,0xC0,0xB0,0xC2,0xFD,0x13,0x07,0xC3);

ここで GUID をコピーし、追加して Extensions という名前をつけたノードにある package.appxmanifest に貼り付けて、再フォーマットします。 マニフェスト エントリは次の例のようになりますが、ここでも自分の GUID を使用してください。 XML の ClassId の GUID が ITypedEventHandler2 と同じであることに注目します。 これは、その GUID が ToasterComponent_i.c の最初に記載されているためです。 この GUID は、大文字と小文字が区別されません。 IToast および IToaster の GUID を手動で再フォーマットする代わりに、インターフェイスの定義に戻ると、正しい形式である GuidAttribute 値を取得できます。 C++ では、コメントに正しい形式の GUID があります。 いずれの場合も、ClassId とイベント ハンドラーの両方に使用される GUID は手動で再フォーマットする必要があります。

	  <Extensions> <!--Use your own GUIDs!!!-->
        <Extension Category="windows.activatableClass.proxyStub">
          <ProxyStub ClassId="1ecafeff-1ee1-504a-9af5-a68c6fb2b47d">
            <Path>Proxies.dll</Path>
            <Interface Name="IToast" InterfaceId="F8D30778-9EAF-409C-BCCD-C8B24442B09B"/>
            <Interface Name="IToaster"  InterfaceId="E976784C-AADE-4EA4-A4C0-B0C2FD1307C3"/>  
            <Interface Name="ITypedEventHandler_2_ToasterComponent__CToaster_ToasterComponent__CToast" InterfaceId="1ecafeff-1ee1-504a-9af5-a68c6fb2b47d"/>
          </ProxyStub>      
        </Extension>
      </Extensions>

Extensions XML ノードを Package ノードの直接の子として、かつ Resources ノードなどのピアとして貼り付けます。

先に進む前に、次のことを確認してください。

  • ProxyStub ClassId が、ToasterComponent_i.c ファイル内にある最初の GUID に設定されています。 ClassId には、このファイルに定義されている最初の GUID を使用します。 (これは、ITypedEventHandler2 の GUID と同じである可能性があります。)
  • Path は、プロキシ バイナリのパッケージの相対パスである。 (このチュートリアルでは、proxies.dll は ToasterApplication.winmd と同じフォルダーにあります。)
  • GUID が正しい形式である。 (間違えやすいです。)
  • マニフェスト内にあるインターフェイスの IDは、ToasterComponent_i.c ファイル内の IID と一致しています。
  • インターフェイス名は、マニフェスト内で一意である。 これらの名前はシステムでは使用されないため、値を選ぶことができます。 定義済みのインターフェイスに一致するわかりやすいインターフェイス名にすることをお勧めします。 生成されたインターフェイスの場合、名前は生成されたインターフェイスを示すものになります。 インターフェイス名の生成を容易にするために、ToasterComponent_i.c ファイルを使用することができます。

ここでソリューションを実行しようとすると、proxies.dll がペイロードの一部ではないことを示すエラーが発生します。 ToasterApplication プロジェクトの [参照設定] フォルダーのショートカット メニューを開き、[参照の追加] をクリックします。 Proxies プロジェクトの横のチェック ボックスをオンにします。 また、ToasterComponent の横のチェック ボックスもオンにしてください。 [OK] を選択します。

これでプロジェクトがビルドされます。 プロジェクトを実行し、トーストを作成できることを確認します。