Windows ランタイム コンポーネントでイベントを生成するRaising events in Windows Runtime components

注意

/Winrt Windows ランタイムコンポーネントでイベントを発生させる方法については、「 /winrt でのイベントのC++作成」を参照してください。 C++To learn how to raise events in a C++/WinRT Windows Runtime Component, see Author events in C++/WinRT.

Windows ランタイム コンポーネントを使って、ユーザー定義のデリゲート型のイベントをバック グラウンド スレッド (ワーカー スレッド) で発生させ、このイベントを JavaScript で受け取る場合、以下のいずれかの方法でイベントを実装し、発生させることができます。If your Windows Runtime component raises an event of a user-defined delegate type on a background thread (worker thread) and you want JavaScript to be able to receive the event, you can implement and/or raise it in one of these ways:

  • (オプション 1) Windows.UI.Core.CoreDispatcher でイベントを生成し、JavaScript のスレッド コンテキストにマーシャリングします。(Option 1) Raise the event through the Windows.UI.Core.CoreDispatcher to marshal the event to the JavaScript thread context. 通常はこれが最適な方法ですが、シナリオによっては最速のパフォーマンスを実現できない場合があります。Although typically this is the best option, in some scenarios it might not provide the fastest performance.
  • (オプション 2) Windows.Foundation.EventHandler<Object> を使用します。ただし、型情報が失われます (ただし、イベントの型情報が失われます)。(Option 2) Use Windows.Foundation.EventHandler<Object> but lose type information (but lose the event type information). オプション 1 を実行できない場合、または十分なパフォーマンスが得られない場合、型情報が失われても問題がなければ、これは 2 番目に良い方法です。If Option 1 is not feasible or its performance is not adequate, then this is a good second choice if loss of type information is acceptable.
  • (オプション 3) コンポーネントに対し、独自のプロキシとスタブを作成します。(Option 3) Create your own proxy and stub for the component. このオプションは実装が最も難しいですが、型情報も保持され、要求が厳しいシナリオの場合に、オプション 1 よりパフォーマンスが高くなる可能性があります。This option is the most difficult to implement, but it preserves type information and might provide better performance compared to Option 1 in demanding scenarios.

これらのオプションをいずれも使用せずに、バックグラウンド スレッドでイベントを生成すると、JavaScript クライアントはイベントを受け取りません。If you just raise an event on a background thread without using one of these options, a JavaScript client will not receive the event.

背景Background

すべての Windows ランタイム コンポーネントとアプリは、どの言語を使用して作成しても、基本的に COM オブジェクトです。All Windows Runtime components and apps are fundamentally COM objects, no matter what language you use to create them. Windows API では、ほとんどのコンポーネントはアジャイル COM オブジェクトで、バックグラウンド スレッドと UI スレッドで同じようにオブジェクトと通信できます。In the Windows API, most of the components are agile COM objects that can communicate equally well with objects on the background thread and on the UI thread. COM オブジェクトをアジャイルにできない場合は、UI スレッドとバックグラウンド スレッドの境界を越えて他の COM オブジェクトと通信できるように、プロキシおよびスタブと呼ばれるヘルパー オブジェクトが必要になります。If a COM object can’t be made agile, then it requires helper objects known as proxies and stubs to communicate with other COM objects across the UI thread-background thread boundary. (COM の用語では、これをスレッド アパートメント間の通信と呼びます。)(In COM terms, this is known as communication between thread apartments.)

Windows API のほとんどのオブジェクトは、アジャイルであるか、プロキシとスタブが組み込まれています。Most of the objects in the Windows API are either agile or have proxies and stubs built in. ただし、Windows.Foundation.TypedEventHandler<TSender, TResult> などのジェネリックな型は、型引数を指定するまでは完全な型ではないため、プロキシとスタブを作成できません。However, proxies and stubs can’t be created for generic types such as Windows.Foundation.TypedEventHandler<TSender, TResult> because they are not complete types until you provide the type argument. プロキシまたはスタブがないために問題が発生するのは、JavaScript クライアントのみですが、コンポーネントを C++ や .NET 言語からだけでなく JavaScript からも使用したい場合は、次の 3 つのオプションのいずれかを使用する必要があります。It's only with JavaScript clients that the lack of proxies or stubs becomes an issue, but if you want your component to be usable from JavaScript as well as from C++ or a .NET language, then you must use one of the following three options.

(オプション 1) CoreDispatcher でイベントを生成する(Option 1) Raise the event through the CoreDispatcher

Windows.UI.Core.CoreDispatcher を使用するとユーザー定義のデリゲート型のイベントを送信でき、JavaScript でそのイベントを受け取ることができます。You can send events of any user-defined delegate type by using the Windows.UI.Core.CoreDispatcher, and JavaScript will be able to receive them. どのオプションを使用すればよいかわからない場合は、最初にこのオプションを試してください。If you are unsure which option to use, try this one first. イベントの発生からイベントの処理までの待ち時間が問題になる場合は、他のオプションを試してください。If latency between the event firing and the event handling becomes an issue, then try one of the other options.

次の例は、CoreDispatcher を使用して厳密に型指定されされたイベントを生成する方法を示します。The following example shows how to use the CoreDispatcher to raise a strongly-typed event. 型引数は Toast で、Object ではないことに注意してください。Notice that the type argument is Toast, not 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> を使用するが、型情報が失われる(Option 2) Use EventHandler<Object> but lose type information

バック グラウンド スレッドからイベントを送信するもう 1 つの方法は、Windows.Foundation.EventHandler<Object> をイベントの型として使用することです。Another way to send an event from a background thread is to use Windows.Foundation.EventHandler<Object> as the type of the event. Windows により、ジェネリック型が具体的にインスタンス化され、プロキシとスタブが提供されます。Windows provides this concrete instantiation of the generic type and provides a proxy and stub for it. 欠点は、イベント引数の型情報と送信者が失われることです。The downside is that the type information of your event args and sender is lost. C++ および .NET クライアントは、イベントを受け取ったときにキャストする型の情報をドキュメントから得る必要があります。C++ and .NET clients must know through documentation what type to cast back to when the event is received. JavaScript クライアントでは、元の型情報は必要ありません。JavaScript clients don’t need the original type information. メタデータの名前に基づいて、引数のプロパティを見つけます。They find the arg properties, based on their names in the metadata.

次の例は、C# で Windows.Foundation.EventHandler<Object> を使用する方法を示します。This example shows how to use Windows.Foundation.EventHandler<Object> in C#:

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 側では、次のようにこのイベントを利用します。You consume this event on the JavaScript side like this:

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

(オプション 3) 独自のプロキシとスタブを作成する(Option 3) Create your own proxy and stub

型情報を完全に保持するユーザー定義のイベント型で十分なパフォーマンスを得るには、独自のプロキシとスタブのオブジェクトを作成し、アプリ パッケージに埋め込む必要があります。For potential performance gains on user-defined event types that have fully-preserved type information, you have to create your own proxy and stub objects and embed them in your app package. 通常、このオプションを使用しなければならないのはまれで、他の 2 つのオプションをどちらも使用できない場合のみです。Typically, you have to use this option only in rare situations where neither of the other two options are adequate. また、このオプションで他の 2 つのオプションよりも高いパフォーマンスが実現されるという保証はありません。Also, there is no guarantee that this option will provide better performance than the other two options. 実際のパフォーマンスは、さまざまな要因によって決まります。Actual performance depends on many factors. アプリケーションでの実際のパフォーマンスを測定し、イベントが実際にボトルネックになっているかどうかを判別するには、Visual Studio プロファイラーまたは他のプロファイリング ツールを使用します。Use the Visual Studio profiler or other profiling tools to measure actual performance in your application and determine whether the event is in fact a bottleneck.

ここからは、C# を使用して基本的な Windows ランタイム コンポーネントを作成した後、C++ を使用してプロキシおよびスタブの DLL を作成する方法について説明します。これにより、非同期操作でコンポーネントにより生成された Windows.Foundation.TypedEventHandler<TSender, TResult> イベントを、JavaScript で利用できるようになります。The rest of this article shows how to use C# to create a basic Windows Runtime component, and then use C++ to create a DLL for the proxy and stub that will enable JavaScript to consume a Windows.Foundation.TypedEventHandler<TSender, TResult> event that's raised by the component in an async operation. (C++ または Visual Basic を使用してコンポーネントを作成することもできます。(You can also use C++ or Visual Basic to create the component. プロキシとスタブの作成に関連する手順は同じです。このチュートリアルは、Windows ランタイム インプロセス コンポーネントを作成するサンプル (C++/CX) に基づいて、その目的を説明します。The steps that are related to creating the proxies and stubs are the same.) This walkthrough is based on Creating a Windows Runtime in-process component sample (C++/CX) and helps explain its purposes.

このチュートリアルの内容は次のとおりです。This walkthrough has these parts:

  • ここでは、2 つの基本的な Windows ランタイム クラスを作成します。Here you will create two basic Windows Runtime classes. 1 つのクラスでは、Windows.Foundation.TypedEventHandler<TSender, TResult> 型のイベントを公開し、もう 1 つのクラスは、TValue の引数として JavaScript に返される型です。One class exposes an event of type Windows.Foundation.TypedEventHandler<TSender, TResult> and the other class is the type that's returned to JavaScript as the argument for TValue. 後の手順を完了するまで、これらのクラスは JavaScript と通信できません。These classes can't communicate with JavaScript until you complete the later steps.
  • このアプリは、メイン クラス オブジェクトをアクティブ化し、メソッドを呼び出して、Windows ランタイム コンポーネントで生成されたイベントを処理します。This app activates the main class object, calls a method, and handles an event that's raised by the Windows Runtime component.
  • これらはプロキシおよびスタブのクラスを生成するツールで必要です。These are required by the tools that generate the proxy and stub classes.
  • その後、IDL ファイルを使用して、プロキシおよびスタブの C ソース コードを生成します。You then use the IDL file to generate the C source code for the proxy and stub.
  • プロキシ/スタブ オブジェクトを登録すると、COM ランタイムがこれらを認識し、アプリ プロジェクトでプロキシ/スタブ DLL を参照できるようになります。Register the proxy-stub objects so that the COM runtime can find them, and reference the proxy-stub DLL in the app project.

Windows ランタイム コンポーネントを作成するにはTo create the Windows Runtime component

Visual Studio のメニュー バーから [ファイル]、[新しいプロジェクト] の順にクリックします。In Visual Studio, on the menu bar, choose File > New Project. [新しいプロジェクト] ダイアログ ボックスで、 [JavaScript]、[ユニバーサル Windows] の順に展開し、 [空のアプリケーション] をクリックします。In the New Project dialog box, expand JavaScript > Universal Windows and then select Blank App. プロジェクトに、「ToasterApplication」という名前を付け、 [OK] ボタンをクリックします。Name the project ToasterApplication and then choose the OK button.

ソリューションに C# Windows ランタイム コンポーネントを追加します。ソリューション エクスプ ローラーで、ソリューションのショートカット メニューを開き、 [追加]、[新しいプロジェクト] の順にクリックします。Add a C# Windows Runtime component to the solution: In Solution Explorer, open the shortcut menu for the solution and then choose Add > New Project. Visual C#>Microsoft Storeを展開し、Windows ランタイムコンポーネント を選択します。Expand Visual C# > Microsoft Store and then select Windows Runtime Component. プロジェクトに、「ToasterComponent」という名前を付け、 [OK] ボタンをクリックします。Name the project ToasterComponent and then choose the OK button. ToasterComponent は、後の手順で作成するコンポーネントのルート名前空間になります。ToasterComponent will be the root namespace for the components you will create in later steps.

ソリューション エクスプローラーでソリューションのショートカット メニューを開き、 [プロパティ] をクリックします。In Solution Explorer, open the shortcut menu for the solution and then choose Properties. [プロパティ ページ] ダイアログ ボックスの左側のウィンドウで、 [構成プロパティ] を選択して、ダイアログ ボックスの上部の [構成][デバッグ] に、 [プラットフォーム] を [x86]、[x64]、または [ARM] に設定します。In the Property Pages dialog box, select Configuration Properties in the left pane, and then at the top of the dialog box, set Configuration to Debug and Platform to x86, x64, or ARM. [OK] ボタンをクリックします。Choose the OK button.

重要 Platform = すべての CPU は、後でソリューションに追加するネイティブコード Win32 DLL に対して無効であるため、機能しません。Important Platform = Any CPU won’t work because it's not valid for the native-code Win32 DLL that you'll add to the solution later.

ソリューション エクスプ ローラーで、「class1.cs」を「ToasterComponent.cs」という名前に変更して、プロジェクトの名前と一致するようにします。In Solution Explorer, rename class1.cs to ToasterComponent.cs so that it matches the name of the project. Visual Studio により、新しいファイル名と一致するように、ファイル内のクラス名が自動的に変更されます。Visual Studio automatically renames the class in the file to match the new file name.

.cs ファイルで、Windows.Foundation 名前空間の using ディレクティブを追加して、TypedEventHandler をスコープに取り込みます。In the .cs file, add a using directive for the Windows.Foundation namespace to bring TypedEventHandler into scope.

プロキシとスタブが必要な場合、コンポーネントではインターフェイスを使用して、パブリック メンバーを公開する必要があります。When you require proxies and stubs, your component must use interfaces to expose its public members. ToasterComponent.cs では、トースター用に 1 つ、トースターが生成するトースト用にもう 1 つインターフェイスを定義します。In ToasterComponent.cs, define an interface for the toaster, and another one for the Toast that the toaster produces.

 C#この手順は省略できます。Note In C# you can skip this step. 代わりに、クラスを作成した後に、ショートカット メニューを開き、 [リファクター]、[インターフェイスの抽出] の順にクリックします。Instead, first create a class, and then open its shortcut menu and choose Refactor > Extract Interface. 生成されたコードで、インターフェイスのパブリック アクセシビリティを手動で指定します。In the code that's generated, manually give the interfaces public accessibility.

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

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

IToast インターフェイスには、トーストの型を取得して書き込むことができる文字列があります。The IToast interface has a string that can be retrieved to describe the type of toast. IToaster インターフェイスには、トーストを作成するメソッドと、トーストが作成されたことを示すイベントがあります。The IToaster interface has a method to make toast, and an event to indicate that the toast is made. このイベントでは、トーストの特定の部分 (つまり型) が返されるため型指定されたイベントと呼ばれます。Because this event returns the particular piece (that is, type) of toast, it's known as a typed event.

次に、これらのインターフェイスを実装したクラスをパブリックにしてシールする必要があります。これにより、後でプログラミングする JavaScript アプリからアクセスできるようになります。Next, we need classes that implement these interfaces, and are public and sealed so that they are accessible from the JavaScript app that you'll program later.

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

上記のコードでは、トーストを作成し、スレッド プールの作業項目を起動して、通知を生成します。In the preceding code, we create the toast and then spin up a thread-pool work item to fire the notification. IDE では、非同期呼び出しに対して await キーワードを適用することを推奨している場合がありますが、メソッドで操作結果に依存する処理は行わないため、ここでは必要ありません。Although the IDE might suggest that you apply the await keyword to the async call, it isn’t necessary in this case because the method doesn’t do any work that depends on the results of the operation.

前のコードの非同期呼び出しでは、ThreadPool. RunAsync だけを使用して、バックグラウンドスレッドでイベントを起動する簡単な方法を示す ますNote The async call in the preceding code uses ThreadPool.RunAsync solely to demonstrate a simple way to fire the event on a background thread. この特定のメソッドは、次の例で示すように記述することもできます.NET のタスク スケジューラは、async/await 呼び出しを自動的にマーシャリングして UI スレッドに返すため、正常に動作します。You could write this particular method as shown in the following example, and it would work fine because the .NET Task scheduler automatically marshals async/await calls back to the UI thread.  

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

ここでプロジェクトをビルドすると、クリーンにビルドされます。If you build the project now, it should build cleanly.

JavaScript アプリをプログラミングするにはTo program the JavaScript app

次に、JavaScript アプリにボタンを追加し、先ほど定義したクラスを使用してトーストを作成できるようにします。Now we can add a button to the JavaScript app to cause it to use the class we just defined to make toast. この作業を行う前に、先ほど作成した ToasterComponent プロジェクトへの参照を追加する必要があります。Before we do this, we must add a reference to the ToasterComponent project we just created. ソリューション エクスプローラーで、ToasterApplication プロジェクトのショートカット メニューを開き、 [追加] > [参照] を選択して、 [新しい参照の追加] を選択します。In Solution Explorer, open the shortcut menu for the ToasterApplication project, choose Add > References, and then choose the Add New Reference button. [新しい参照の追加] ダイアログ ボックスの、左側のウィンドウの [ソリューション] の下で、コンポーネント プロジェクトを選択してから、中央のウィンドウで [ToasterComponent] を選択します。In the Add Reference dialog box, in the left pane under Solution, select the component project, and then in the middle pane, select ToasterComponent. [OK] ボタンをクリックします。Choose the OK button.

ソリューション エクスプローラーで、ToasterApplication プロジェクトのショートカット メニューを開き、 [スタートアップ プロジェクトに設定] をクリックします。In Solution Explorer, open the shortcut menu for the ToasterApplication project and then choose Set as Startup Project.

default.js ファイルの末尾に、コンポーネントを呼び出し、コンポーネントに呼び戻される関数を含む名前空間を追加します。At the end of the default.js file, add a namespace to contain the functions to call the component and be called back by it. 名前空間には、トーストを作成する関数と、トースト完了のイベントを処理する関数の 2 つの関数を含めます。The namespace will have two functions, one to make toast and one to handle the toast-complete event. makeToast を実装すると、Toaster オブジェクトが作成され、イベント ハンドラーが登録されて、トーストが作成されます。The implementation of makeToast creates a Toaster object, registers the event handler, and makes the toast. 次に示すように、この時点では、イベント ハンドラーで何かをするわけではありません。So far, the event handler doesn’t do much, as shown here:

    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 関数をボタンにフックする必要があります。The makeToast function must be hooked up to a button. default.html を更新して、ボタンと、トーストを作成した結果を出力するためのスペースを追加します。Update default.html to include a button and some space to output the result of making toast:

    <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 を使用しない場合、この時点でアプリをローカル コンピューターで実行してボタンをクリックすると、トーストを作成することができます。If we weren’t using a TypedEventHandler, we would now be able to run the app on the local machine and click the button to make toast. でも、このアプリでは何も起こりません。But in our app, nothing happens. 理由を確認するには、ToastCompletedEvent を生成するマネージ コードをデバッグしてみましょう。To find out why, let’s debug the managed code that fires the ToastCompletedEvent. プロジェクトを停止し、メニュー バーで、 [デバッグ] > [ToasterApplication のプロパティ] を選択します。Stop the project, and then on the menu bar, choose Debug > Toaster Application properties. [デバッガーの種類][マネージのみ] に変更します。Change Debugger Type to Managed Only. もう一度メニュー バーで、 [デバッグ] > [例外] を選択して、 [Common Language Runtime Exceptions] (共通言語ランタイムの例外) を選択します。Again on the menu bar, choose Debug > Exceptions, and then select Common Language Runtime Exceptions.

ここでアプリを実行し、トースト作成ボタンをクリックします。Now run the app and click the make-toast button. デバッガーは、無効なキャスト例外をキャッチします。The debugger catches an invalid cast exception. メッセージからはわかりませんが、この例外は、インターフェイスのプロキシが存在しないために発生します。Although it’s not obvious from its message, this exception is occurring because proxies are missing for that interface.

プロキシが存在しない

コンポーネントのプロキシとスタブを作成するには、まず、インターフェイスに一意の ID または GUID を追加します。The first step in creating a proxy and stub for a component is to add a unique ID or GUID to the interfaces. ただし、使用する GUID の形式は、コーディングに C#、Visual Basic、または他の .NET 言語を使用する場合と、C++ を使用する場合で異なります。However, the GUID format to use differs depending on whether you're coding in C#, Visual Basic, or another .NET language, or in C++.

コンポーネントのインターフェイスの GUID を生成するには (C# および他の .NET 言語)To generate GUIDs for the component's interfaces (C# and other .NET languages)

メニュー バーで、[ツール] > [GUID の作成] を選択します。On the menu bar, choose Tools > Create GUID. ダイアログ ボックスで、5. のIn the dialog box, select 5. [Guid ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx...xxxx ")]。[Guid("xxxxxxxx-xxxx...xxxx")]. [新規 GUID] ボタンを選択し、次に [コピー] を選択します。Choose the New GUID button and then choose the Copy button.

GUID ジェネレーター ツール

インターフェイスの定義に戻り、次の例のように、IToaster インターフェイスの直前に、新しい GUID を貼り付けます。Go back to the interface definition, and then paste the new GUID just before the IToaster interface, as shown in the following example. (この例の GUID は使用しないでください。(Don't use the GUID in the example. 一意のインターフェイスごとに固有の GUID が必要です。)Every unique interface should have its own GUID.)

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

System.Runtime.InteropServices 名前空間の using ディレクティブを追加します。Add a using directive for the System.Runtime.InteropServices namespace.

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

コンポーネントのインターフェイスの GUID を生成するには (C++)To generate GUIDs for the component's interfaces (C++)

メニュー バーで、[ツール] > [GUID の作成] を選択します。On the menu bar, choose Tools > Create GUID. ダイアログ ボックスで、3. のIn the dialog box, select 3. [static const struct GUID = {...}] を選択します。static const struct GUID = {...}. [新規 GUID] ボタンを選択し、次に [コピー] を選択します。Choose the New GUID button and then choose the Copy button.

IToaster インターフェイスの定義の直前に GUID を貼り付けます。Paste the GUID just before the IToaster interface definition. 貼り付けた後、GUID は次の例のようになります。After you paste, the GUID should resemble the following example. (この例の GUID は使用しないでください。(Don't use the GUID in the example. 一意のインターフェイスごとに固有の GUID が必要です。)Every unique interface should have its own 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 をスコープに取り込みます。Add a using directive for Windows.Foundation.Metadata to bring GuidAttribute into scope.

ここで、const GUID を手動で GuidAttribute に変換すると、次の例のようにフォーマットされます。Now manually convert the const GUID to a GuidAttribute so that it's formatted as shown in the following example. 中かっこが角かっこと丸かっこに置き換えられ、末尾のセミコロンが削除されていることがわかります。Notice that the curly braces are replaced with brackets and parentheses, and the trailing semicolon is removed.

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

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

これで、インターフェイスに一意の ID が付与されたため、winmdidl コマンド ライン ツールに .winmd ファイルを入力して、IDL ファイルを作成してから、MIDL コマンド ライン ツールにその IDL ファイルを入力して、プロキシおよびスタブの C ソース コードを生成できます。Now that the interfaces have unique IDs, we can create an IDL file by feeding the .winmd file into the winmdidl command-line tool, and then generate the C source code for the proxy and stub by feeding that IDL file into the MIDL command-line tool. 次の手順に示すようにビルド後のイベントを作成すると、Visual Studio でこれが実行されます。Visual Studio do this for us if we create post-build events as shown in the following steps.

プロキシおよびスタブのソース コードを生成するにはTo generate the proxy and stub source code

カスタム ビルド後のイベントを追加するには、ソリューション エクスプローラーで、ToasterComponent プロジェクトのショートカット メニューを開き、[プロパティ] をクリックします。To add a custom post-build event, in Solution Explorer, open the shortcut menu for the ToasterComponent project and then choose Properties. プロパティ ページの左側のウィンドウで、[ビルド イベント] を選択し、[ビルド後の編集] ボタンをクリックします。In the left pane of the property pages, select Build Events, and then choose the Edit Post-build button. 次のコマンドを、ビルド後のコマンド ラインに追加します。Add the following commands to the post-build command line. (先にバッチ ファイルを呼び出して、winmdidl ツールを認識できるように環境変数を設定する必要があります。)(The batch file must be called first to set the environment variables to find the winmdidl tool.)

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 に変更します。Important  For an ARM or x64 project configuration, change the MIDL /env parameter to x64 or arm32.

.winmd ファイルが変更されるたびに、IDL ファイルを再生成するには、 [ビルド後のコマンドラインの実行条件][ビルドがプロジェクト出力を更新したとき] に変更します。To make sure the IDL file is regenerated every time the .winmd file is changed, change Run the post-build event to When the build updates the project output. ビルドイベントのプロパティページは次のようになります。 ビルドイベントThe Build Events property page should resemble this: build events

ソリューションをリビルドし、IDL を生成してコンパイルします。Rebuild the solution to generate and compile the IDL.

ToasterComponent プロジェクト ディレクトリ内で ToasterComponent.h、ToasterComponent_i.c、ToasterComponent_p.c、および dlldata.c を探すことで、MIDL がソリューションを正しくコンパイルしたことを確認できます。You can verify that MIDL correctly compiled the solution by looking for ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c, and dlldata.c in the ToasterComponent project directory.

プロキシおよびスタブのコードをコンパイルして DLL を生成するにはTo compile the proxy and stub code into a DLL

これで、必要なファイルがそろったので、ファイルをコンパイルして、C++ ファイルの DLL を生成できます。Now that you have the required files, you can compile them to produce a DLL, which is a C++ file. できる限り作業を簡単に行うために、プロキシのビルドをサポートする新しいプロジェクトを追加します。To make this as easy as possible, add a new project to support building the proxies. ToasterApplication ソリューションのショートカット メニューを開き、 [追加]、[新しいプロジェクト] の順にクリックします。Open the shortcut menu for the ToasterApplication solution and then choose Add > New Project. [新しいプロジェクト] ダイアログボックスの左ペインで、[ Visual C++>windows>ユニバーサルウィンドウ] を展開し、中央のウィンドウで [DLL (UWP アプリ)] を選択します。In the left pane of the New Project dialog box, expand Visual C++ > Windows > Univeral Windows, and then in the middle pane, select DLL (UWP apps). (これは C++ Windows ランタイム コンポーネント プロジェクトではないことに注意してください)。プロジェクトの名前に「Proxies」を指定して、 [OK] を選択します。(Notice that this is NOT a C++ Windows Runtime Component project.) Name the project Proxies and then choose the OK button. これらのファイルは、C# クラスで変更が発生したときに、ビルド後のイベントで更新されます。These files will be updated by the post-build events when something changes in the C# class.

Proxies プロジェクトは既定で、ヘッダーの .h ファイルと C++ の .cpp ファイルを生成します。By default, the Proxies project generates header .h files and C++ .cpp files. DLL は MIDL から生成されたファイルでビルドされるため、.h ファイルと .cpp ファイルは必要ありません。Because the DLL is built from the files produced from MIDL, the .h and .cpp files are not required. ソリューション エクスプローラーでこれらのショートカット メニューを開き、 [削除] をクリックして、メッセージが表示されたら削除を確定します。In Solution Explorer, open the shortcut menu for them, choose Remove, and then confirm the deletion.

この時点ではプロジェクトは空なので、MIDL で生成されたファイルを再び追加することができます。Now that the project is empty, you can add back the MIDL-generated files. Proxies プロジェクトのショートカット メニューを開き、 [追加] > [既存の項目] の順に選択します。Open the shortcut menu for the Proxies project, and then choose Add > Existing Item. ダイアログ ボックスで ToasterComponent のプロジェクト ディレクトリに移動し、ToasterComponent.h、ToasterComponent_i.c、ToasterComponent_p.c、および dlldata.c ファイルを選択します。In the dialog box, navigate to the ToasterComponent project directory and select these files: ToasterComponent.h, ToasterComponent_i.c, ToasterComponent_p.c, and dlldata.c files. [追加] ボタンをクリックします。Choose the Add button.

Proxies プロジェクトで .def ファイルを作成し、dlldata.c に記述されている DLL エクスポートを定義します。In the Proxies project, create a .def file to define the DLL exports described in dlldata.c. プロジェクトのショートカット メニューを開き、 [追加]、[新しい項目] の順にクリックします。Open the shortcut menu for the project, and then choose Add > New Item. ダイアログ ボックスの左側のウィンドウで [コード] を選択し、中央のウィンドウで [モジュール定義ファイル] を選択します。In the left pane of the dialog box, select Code and then in the middle pane, select Module-Definition File. ファイルに「proxies.def」という名前を付け、 [追加] ボタンをクリックします。Name the file proxies.def and then choose the Add button. この .def ファイルを開き、dlldata.c で定義されている EXPORTS を含むように変更します。Open this .def file and modify it to include the EXPORTS that are defined in dlldata.c:

EXPORTS
    DllCanUnloadNow         PRIVATE
    DllGetClassObject       PRIVATE

ここでプロジェクトをビルドすると、失敗します。If you build the project now, it will fail. このプロジェクトを正しくコンパイルするには、プロジェクトをコンパイルする方法およびプロジェクトがリンクされる方法を変更する必要があります。To correctly compile this project, you have to change how the project is compiled and linked. ソリューション エクスプローラーで、Proxies プロジェクト ノードのショートカット メニューを開き、 [プロパティ] を選択します。In Solution Explorer, open the shortcut menu for the Proxies project and then choose Properties. プロパティ ページを次のように変更します。Change the property pages as follows.

左側のウィンドウで [C/C++] > [プリプロセッサ] の順に選択します。右側のウィンドウで [プリプロセッサの定義] を選択して下矢印ボタンを選択し、 [編集] を選択します。In the left pane, select C/C++ > Preprocessor, and then in the right pane, select Preprocessor Definitions, choose the down-arrow button, and then select Edit. ボックスに次の定義を追加します。Add these definitions in the box:

WIN32;_WINDOWS

[C/C++] の [プリコンパイル済みヘッダー] の下で、 [プリコンパイル済みヘッダー][プリコンパイル済みヘッダーを使用しない] に変更し、 [適用] を選択します。Under C/C++ > Precompiled Headers, change Precompiled Header to Not Using Precompiled Headers, and then choose the Apply button.

[リンカー] > [全般] の下で、 [インポート ライブラリの無視][はい] に変更し、 [適用] を選択します。Under Linker > General, change Ignore Import Library to Yes, and then choose the Apply button.

[リンカー] の [入力] の下で、 [追加の依存ファイル] をクリックして下矢印ボタンをクリックし、 [編集] をクリックします。Under Linker > Input, select Additional Dependencies, choose the down-arrow button, and then select Edit. ボックスに次のテキストを追加します。Add this text in the box:

rpcrt4.lib;runtimeobject.lib

これらの lib を list 行に直接貼り付けないでください。Do not paste these libs directly into the list row. [編集] ボックスを使用して、Visual Studio の MSBuild で正しい追加の依存ファイルが維持されるようにします。Use the Edit box to ensure that MSBuild in Visual Studio will maintain the correct additional dependencies.

これらの変更を行ったら、 [プロパティ ページ] ダイアログ ボックスの [OK] ボタンをクリックします。When you have made these changes, choose the OK button in the Property Pages dialog box.

次に、ToasterComponent プロジェクトへの依存関係を設定します。Next, take a dependency on the ToasterComponent project. この設定により、常に Toaster がビルドされてからプロキシ プロジェクトがビルドされます。This ensures that the Toaster will build before the proxy project builds. Toaster プロジェクトでプロキシをビルドするファイルを生成するため、この設定が必要となります。This is required because the Toaster project is responsible for generating the files to build the proxy.

Proxies プロジェクトのショートカット メニューを開き、[プロジェクトの依存関係] をクリックします。Open the shortcut menu for the Proxies project and then choose Project Dependencies. Proxies プロジェクトが ToasterComponent プロジェクトに依存していることを示すようにチェック ボックスをオンにして、Visual Studio が正しい順序でビルドされるようにします。Select the check boxes to indicate that the Proxies project depends on the ToasterComponent project, to ensure that Visual Studio builds them in the correct order.

Visual Studio のメニュー バーで [ビルド]、[ソリューションのリビルド] を順にクリックして、ソリューションが正しくビルドされることを確認します。Verify that the solution builds correctly by choosing Build > Rebuild Solution on the Visual Studio menu bar.

プロキシおよびスタブを登録するにはTo register the proxy and stub

ToasterApplication プロジェクトで、package.appxmanifest のショートカット メニューを開き、 [プログラムから開く] をクリックします。In the ToasterApplication project, open the shortcut menu for package.appxmanifest and then choose Open With. [プログラムから開く] ダイアログ ボックスで、 [XML テキスト エディター] を選択し、 [OK] を選択します。In the Open With dialog box, select XML Text Editor and then choose the OK button. プロキシの GUID に基づいて windows.activatableClass.proxyStub 拡張機能を登録する複数の XML を貼り付けます。We're going to paste in some XML that provides a windows.activatableClass.proxyStub extension registration and which are based on the GUIDs in the proxy. .appxmanifest ファイルで使用する GUID を調べるには、ToasterComponent_i.c を開きます。To find the GUIDs to use in the .appxmanifest file, open ToasterComponent_i.c. 次の例のようなエントリを見つけます。Find entries that resemble the ones in the following example. また、IToast、IToaster、および 3 つ目のインターフェイス (Toaster と Toast の 2 つのパラメーターを持つ型指定されたイベント ハンドラー) の定義も確認します。Also notice the definitions for IToast, IToaster, and a third interface—a typed event handler that has two parameters: a Toaster and Toast. これは Toaster クラスで定義されているイベントと一致します。This matches the event that's defined in the Toaster class. IToast および IToaster の GUID が、C# ファイル内のインターフェイスで定義されている GUID と一致することを確認します。Notice that the GUIDs for IToast and IToaster match the GUIDs that are defined on the interfaces in the C# file. 型指定されたイベント ハンドラーのインターフェイスは自動生成されるため、このインターフェイスの GUID も自動生成されます。Because the typed event handler interface is autogenerated, the GUID for this interface is also autogenerated.

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 に貼り付けて、再フォーマットします。Now we copy the GUIDs, paste them in package.appxmanifest in a node that we add and name Extensions, and then reformat them. マニフェスト エントリは次の例のようになりますが、ここでも自分の GUID を使用してください。The manifest entry resembles the following example—but again, remember to use your own GUIDs. XML の ClassId の GUID が ITypedEventHandler2 と同じであることに注目します。Notice that the ClassId GUID in the XML is the same as ITypedEventHandler2. これは、その GUID が ToasterComponent_i.c の最初に記載されているためです。This is because that GUID is the first one that's listed in ToasterComponent_i.c. この GUID は、大文字と小文字が区別されません。The GUIDs here are case-insensitive. IToast および IToaster の GUID を手動で再フォーマットする代わりに、インターフェイスの定義に戻ると、正しい形式である GuidAttribute 値を取得できます。Instead of manually reformatting the GUIDs for IToast and IToaster, you can go back into the interface definitions and get the GuidAttribute value, which has the correct format. C++ では、コメントに正しい形式の GUID があります。In C++, there is a correctly-formatted GUID in the comment. いずれの場合も、ClassId とイベント ハンドラーの両方に使用される GUID は手動で再フォーマットする必要があります。In any case, you must manually reformat the GUID that's used for both the ClassId and the event handler.

      <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 ノードなどのピアとして貼り付けます。Paste the Extensions XML node as a direct child of the Package node, and a peer of, for example, the Resources node.

先に進む前に、次のことを確認してください。Before moving on, it’s important to ensure that:

  • ProxyStub ClassId は、ToasterComponent_i. c ファイルの最初の GUID に設定されます。The ProxyStub ClassId is set to the first GUID in the ToasterComponent_i.c file. ClassId には、このファイルに定義されている最初の GUID を使用します。Use the first GUID that's defined in this file for the classId. (これは、ITypedEventHandler2 の GUID と同じである可能性があります。)(This might be the same as the GUID for ITypedEventHandler2.)
  • Path は、プロキシ バイナリのパッケージの相対パスである。The Path is the package relative path of the proxy binary. (このチュートリアルでは、proxies.dll は ToasterApplication.winmd と同じフォルダーにあります。)(In this walkthrough, proxies.dll is in the same folder as ToasterApplication.winmd.)
  • GUID が正しい形式である。The GUIDs are in the correct format. (間違えやすいです。)(This is easy to get wrong.)
  • マニフェスト内のインターフェイス Id は、ToasterComponent_i. c ファイルの Iid がと一致します。The interface IDs in the manifest match the IIDs in ToasterComponent_i.c file.
  • インターフェイス名は、マニフェスト内で一意である。The interface names are unique in the manifest. これらの名前はシステムでは使用されないため、値を選ぶことができます。Because these are not used by the system, you can choose the values. 定義済みのインターフェイスに一致するわかりやすいインターフェイス名にすることをお勧めします。It is a good practice to choose interface names that clearly match interfaces that you have defined. 生成されたインターフェイスの場合、名前は生成されたインターフェイスを示すものになります。For generated interfaces, the names should be indicative of the generated interfaces. ToasterComponent_の i. c ファイルを使用すると、インターフェイス名を生成するのに役立ちます。You can use the ToasterComponent_i.c file to help you generate interface names.

ここでソリューションを実行しようとすると、proxies.dll がペイロードの一部ではないことを示すエラーが発生します。If you try to run the solution now, you will get an error that proxies.dll is not part of the payload. ToasterApplication プロジェクトの [参照設定] フォルダーのショートカット メニューを開き、 [参照の追加] をクリックします。Open the shortcut menu for the References folder in the ToasterApplication project and then choose Add Reference. Proxies プロジェクトの横のチェック ボックスをオンにします。Select the check box next to the Proxies project. また、ToasterComponent の横のチェック ボックスもオンにしてください。Also, make sure that the check box next to ToasterComponent is also selected. [OK] ボタンをクリックします。Choose the OK button.

これでプロジェクトがビルドされます。The project should now build. プロジェクトを実行し、トーストを作成できることを確認します。Run the project and verify that you can make toast.