イン プロセスの Windows ランタイム コンポーネントの作り方

以前にアプリ ブローカーの作り方の記事(その1その2)を公開しました。今回は、アプリ ブローカーのような ランタイム ブローカーではなく、イン プロセス向けの Windows Runtime コンポーネントの作成方法を説明します。基になる題材は、Windows 8.1 サンプル パックにも含まれている

追記:
アプリ ブローカーの説明をしたセッション動画が、公開されています。
de:code Track1 Device  デスクトップ アプリケーションとWindows ストア アプリの連携
このMVA コースでサンプル コードも公開しています。

Windows Runtime in-process component authoring with proxy/stub generation sample になります。

このサンプルは、クライアント(C++、C#、JavaScript) - ProxyStub - Windows ランタイム コンポーネント(サーバー) という構造になっています。このサンプルの作り方は以下のようになります。

  1. 空のソリューション(ProxyStubsForWinRTComponents)を作成します。

  2. サーバーを ProxyStubsForWinRTComponents_server というプロジェクトで作成します。
    [追加]-[新規プロジェクト]-[C#]-[ストア アプリ]-[Windows アプリ]-[Windows ランタイム コンポーネント] プロジェクトを作成します。
    サンプルと同じように、BreadeServer.cs と OpenServer.cs を追加します(名前空間は、プロジェクトに合わせて修正しておきます)。
    (私の場合は、IBread、IApplication、IOven インターフェースの guid属性の値を新しく定義しています)
    InProcessServer
    プロジェクトのプロパティを使って、[ビルドイベント]-[ビルド後に実行するコマンド]に次のコマンドを登録します。
    call "$(DevEnvDir)..\..\VC\vcvarsall.bat" x86
    winmdidl /outdir:"$(ProjectDir)\" "$(TargetPath)"
    これでプロジェクトをビルドすると、ProxyStubsForWinRTComponents_server.idlという IDL ファイルが作成されるようになります。

  3. プロキシ スタブ を ProxyStubsForWinRTComponentsPS として追加します。
    [追加]-[新しいプロジェクト]-[Visual C++]-[ストア アプリ]-[Windows アプリ]-[DLL]プロジェクトを作成します。
    プロジェクトに含まれるファイルを削除します(dllmain.cpp, pch.cpp, pch.h,ProxyStubsForWinRTComponentsPS.cpp, ProxyStubsForWinRTComponentsPS.h,targetver.h)。
    プロジェクトに、ProxyStubsForWinRTComponents_server.idl を追加します。
    (ProxyStubsForWinRTComponents_server フォルダーの下から参照します)
    プロジェクトにモジュール定義ファイル(OvenServerPS.def)を追加して、次の内容を記述します。
    EXPORTS
    DllCanUnloadNow         PRIVATE
    DllGetClassObject       PRIVATE
    今度はプロジェクト プロパティの設定を次のようにします。
    ・[構成プロパティ]-[C/C++]-[全般]-[追加の #using ディレクトリ]
    $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories); を追加
    ・[構成プロパティ]-[C/C++]-[プリプロセッサ]-[プリプロセッサの定義]
    WIN32 を追加
    ・[構成プロパティ]-[C/C++]-[プリコンパイル済みヘッダー] を 「使用しない」に変更する。
    ・[構成プロパティ]-[リンカー]-[全般]-[インポート ライブラリの無視] を「はい」に変更する。
    ・[構成プロパティ]-[リンカー]-[入力]-[追加の依存ファイル]
    rpcrt4.lib;runtimeobject.lib; を追加する。
    ・[構成プロパティ]-[リンカー]-[システム]-[サブシステム]を
    Windows(/SUBSYSTEM:WINDOWS) に変更する。
    この変更は、実施しなくても問題ありません。
    ・ここまでの設定ができたら、ビルドを行います。
    ・ビルド後に、MIDLによって作成されたファイルをプロジェクトに追加します。
    dlldata.c
    ProxyStubsForWinRTComponents_server_i.c
    ProxyStubsForWinRTComponents_server_p.c (このファイルが、ProxyStubsForWinRTComponents_server_h.h をインクルードしています)
    ・これでビルドすれば、プロキシ スタブ DLL が作成されるようになります。
    この作業のポイントは、IDL を MIDL コンパイラーで処理した結果の出力ファイルをプロジェクトに追加することになります。

    ここまでの作業で、Windows ランタイム コンポーネント と プロキシ スタブ DLL が出来上がったことになります。

今度は、クライアントの作成になりますので、ソリューションに ProxyStubsForWinRTComponents_client_cpp プロジェクトを追加します。
・[追加]-[新しいプロジェクト]-[Visual C++]-[ストア アプリ]-[Windows アプリ]-[DLL]
・参照設定を行います。参照設定は、[追加]-[参照]、もしくは[プロジェクト プロパティ]-[共通プロパティ]-[参照] で行います。
ProxyStubsForWinRTComponents_server
ProxyStubsForWinRTComponentsPS
ReferenceManager

・pch.h に次のコードを追加します。

 // サンプル用に追加
#include <wrl\event.h>
#include <wrl\implements.h>
#include <wrl\wrappers\corewrappers.h>
#include "Common\LayoutAwarePage.h"
#include "Common\SuspensionManager.h"

このコードを追加してから、SDK サンプルに含まれる各種のコードを追加します。
・Commonフォルダーに次のファイルを追加します。
layoutawarepage.h、layoutawarepage.cpp、StandardStyles.xaml、suspensionmanager.h、suspensionmanager.cpp
・Sample-Utils に SampleTemplateStyles.xaml を追加します。
・Constants.h を追加します。
・MainPage.xaml、MainPage.xaml.h、MainPage.xaml.cpp を サンプルのコードに合わせます。
・CustomException.xaml、CustomException.xaml.h、CustomException.xaml.cpp を追加します。
・CustomExceptionWRL.xaml、CustomExceptionWRL.xaml.h、CustomExceptionWRL.xaml.cpp を追加します。
・OvenClient.xaml、OvenClient.xaml.h、OvenClient.xaml.cpp を追加します。
・OvenClientWRL.xaml、OvenClientWRL.xaml.h、OvenClientWRL.xaml.cpp を追加します。
追加したファイルに含まれる名前空間などは、プロジェクトに合わせて変更しておきます。
・[プロジェクト プロパティ]-[C/C++]-[全般]-[追加のインクルード ディレクティブ]へ
「..\ProxyStubsForWinRTComponentsPS」 を追加します。この作業によって、プロキシ スタブ のヘッダー ファイル(ProxyStubsForWinRTComponents_server_h.h)の解決が行われます。
・Package.appxmanifest をコンテキスト メニューから[ファイルを開くアプリケーションを選択]-[XMLエディタ]で編集モードにして、次のコードを追加します。

 <Extensions>
  <Extension Category="windows.activatableClass.proxyStub">
    <ProxyStub ClassId="A1852DD8-A6AA-4821-B7DB-B2B43312C554">
    <Path>ProxyStubsForWinRTComponentsPS.dll</Path>
    <Interface Name="IOven" InterfaceId="9E26981C-7CF6-4248-AD88-35AEABDE59D4" />
    <Interface Name="IBread" InterfaceId="397DC367-ADEF-4883-831F-CAC34360B0BE" />
    <Interface Name="IAppliance" InterfaceId="A1852DD8-A6AA-4821-B7DB-B2B43312C554" />
    <Interface Name="__FITypedEventHandler_2_ProxyStubsForWinRTComponents__zserver__COven_ProxyStubsForWinRTComponents__zserver__CBread" InterfaceId="e8156862-85c6-5785-8a87-94cf4dfde545" />
    </ProxyStub>
  </Extension>
</Extensions>

このコードの注意点は、インターフェースの名前や guid になります。
ProxyStub: IApplication に設定した guid 属性です。
IOven:IOven に設定した guid 属性です。
IBread:IBread に設定した guid 属性です。
IApplication:IApplication に設定した guid 属性です。
最後のインターフェースは、Oven クラス のイベントになります。このインターフェース名と guid は、midl コンパイラーの出力である ProxyStubsForWinRTComponents_server_h.h か dlldata.c から見つけます。

これでビルドすれば、完成です。サンプルのシナリオは、4種類あります。
・Using Custom Components
・Using Custom Components (Implemented using WRL)
・Handling Windows Runtime Exceptions
・Handling Windows Runtime Exceptions (Implemented using WRL)

Using Custom Components (Implemented using WRL) を試した場合に、プロキシ スタブを読み込んで動作します。それ以外のシナリオでは、プロキシ スタブが使用されることはありません。このプロキシ スタブ を使ったインプロセス コンポーネント を使用するクライアント プロジェクトのポイントは、

  • Windows ランタイム コンポーネントへの参照設定
  • プロキシ スタブ コンポーネントとへの参照設定
  • Package.appxmanifest の windows.activatableClass.proxyStub 定義を追加

にあります。もちろん、インプロセス コンポーネントなので、アプリ ブローカーと違ってクライアント プロセスに読み込まれるようになります。

最後に、C# プロジェクトを作成する場合も基本的な作業は C++ プロジェクトと同じになります。が、異なるのは プロキシ スタブ プロジェクトへのプロジェクト 参照を GUI で行えないので、プロジェクト ファイルを編集して追加する必要があることです。後は、実行すれば理解できますが、プロセスがマネージド なので、プロキシ スタブが読み込まれないで Windows ランタイム コンポーネントが読み込まれることです。これは、クライアントとコンポーネントの双方が C# で作成しているからです。

このような仕組みを紹介したのは、アプリ ブローカーを使わずに既存の COM コンポーネントなどを使用したい場合があると考えたからです。もちろん、Windows ストア アプリのプロセス無いに読み込まれるので、セキュリティ問題で実行できない COM コンポーネントもありますが、このようにプロキシ スタブを使用することで拡張が可能であるということを知っていることは重要なことだと私は考えています。

ProxyStubsForWinRTComponents.zip