WPF アドインの概要

.NET Framework には、開発者がアドイン機能拡張をサポートするアプリケーションの作成に使用できるアドイン モデルが用意されています。 このアドイン モデルを使用することで、アプリケーション機能に統合され、アプリケーション機能を拡張するアドインを作成できます。 一部のシナリオでは、アドインによって提供されるユーザー インターフェイスをアプリケーションで表示する必要があります。このトピックでは、WPF が .NET Framework アドイン モデルを強化してこうしたシナリオを実現するしくみ、背後にあるアーキテクチャ、その利点、および制限事項について説明します。

必須コンポーネント

.NET Framework アドイン モデルを十分に理解していることが必要です。 詳細については、「アドインおよび拡張機能」を参照してください。

アドインの概要

新しい機能を追加するためにアプリケーションを再コンパイルして再配置する手間を省くことを目的として、アプリケーションは機能拡張メカニズムを実装し、開発者 (ファーストパーティ、サードパーティのいずれも) が元のアプリケーションに統合される別のアプリケーションを作成できるようにしています。 こうした拡張機能をサポートする方法としては、アドイン ("アドオン"、"プラグイン" などとも呼ばれる) の使用が最も一般的です。 アドインによる機能拡張を公開する実際のアプリケーションとして、次のようなものが挙げられます。

  • Internet Explorer のアドオン。

  • Windows Media Player のプラグイン。

  • Visual Studio のアドイン。

たとえば、Windows Media Player のアドイン モデルにおいて、サードパーティの開発者は、Windows Media Player の機能をさまざまな形で拡張する "プラグイン" を実装できます。たとえば、Windows Media Player でネイティブにサポートされていないメディア形式 (DVD、MP3 など) のデコーダーおよびエンコーダー、オーディオ効果、スキンなどを作成できます。 各アドイン モデルは、アプリケーション固有の機能を公開するためにビルドされますが、どのアドイン モデルにも共通するエンティティや動作がいくつかあります。

典型的な機能拡張ソリューションの主な 3 つのエンティティは、コントラクトアドイン、およびホスト アプリケーションです。 コントラクトは、次の 2 つの方法で、アドインとホスト アプリケーションとの統合方法を定義します。

  • アドインは、ホスト アプリケーションによって実装される機能と統合されます。

  • ホスト アプリケーションで、アドインを統合するための機能を公開します。

アドインを使用するには、ホスト アプリケーションが実行時にアドインを検索して読み込む必要があります。 したがって、アドインをサポートするアプリケーションには次の機能が必要です。

  • 探索: ホスト アプリケーションによってサポートされるコントラクトに準拠しているアドインを検索します。

  • アクティブ化: アドインを読み込んで実行し、アドインとの通信を確立します。

  • 分離:アプリケーション ドメインまたはアプリケーション プロセスを使用することで他から切り離すための境界を確立し、アドインの使用に伴うセキュリティおよび実行に関する潜在的な問題からアプリケーションを保護します。

  • 通信: アドインとホスト アプリケーションが分離境界を越えて通信し、メソッドを呼び出したりデータを渡したりできるようにします。

  • 有効期間管理: アプリケーション ドメインとアプリケーション プロセスの読み込みおよびアンロードを、クリーンで予測可能な方法で行います (「アプリケーション ドメイン」を参照)。

  • バージョン管理:ホスト アプリケーションとアドインのどちらかに、新バージョンが作成された後も引き続き通信できるようにします。

このことからわかるように、堅牢なアドイン モデルの開発は簡単なことではありません。 そのため、.NET Framework にはアドイン モデルのビルド用のインフラストラクチャが用意されています。

注意

アドインの詳細については、「アドインおよび拡張機能」を参照してください。

.NET Framework アドイン モデルの概要

.NET Framework アドイン モデルは、System.AddIn 名前空間にあり、アドイン機能拡張の開発を単純化するために設計された型のセットが用意されています。 .NET Framework アドイン モデルの基本単位は "コントラクト" です。コントラクトでは、ホスト アプリケーションとアドイン間の通信方法が定義されています。 コントラクトのホスト アプリケーション固有のビューを使用して、コントラクトはホスト アプリケーションに公開されます。 同様に、コントラクトのアドイン固有のビューがアドインに公開されます。 アダプターは、ホスト アプリケーションとアドインが、コントラクトの対応するビュー間で通信するために使用されます。 コントラクト、ビュー、およびアダプターはセグメントと見なされ、関連セグメントのセットがパイプラインを構成します。 .NET Framework アドイン モデルでは、パイプラインを基盤として、それを基に探索、アクティブ化、セキュリティ分離、実行分離 (アプリケーションのドメインとプロセスを両方使用)、通信、有効期間管理、およびバージョン管理がサポートされます。

このサポート全体を使用して、開発者はホスト アプリケーションの機能を統合するアドインをビルドできます。 ただし、一部のシナリオでは、アドインが提供するユーザー インターフェイスをホスト アプリケーションで表示する必要があります。.NET Framework の各プレゼンテーション テクノロジにはユーザー インターフェイスを実装するための独自のモデルがあるため、.NET Framework アドイン モデルでは特定のプレゼンテーション テクノロジはサポートされていません。 代わりに、WPF では、.NET Framework アドイン モデルが、アドイン用の UI サポートで拡張されます。

WPF アドイン

WPF を .NET Framework アドイン モデルと併用することで、ホスト アプリケーションでアドインのユーザー インターフェイスを表示する必要があるさまざまなシナリオに対応できます。特に、WPF によるこれらのシナリオの対応には、次の 2 つのプログラミング モデルが使用されます。

  1. アドインが UI を返す。 アドインでは、コントラクトの定義に従って、メソッド呼び出しを介してホスト アプリケーションに UI が返されます。 このシナリオは、次の場合に使用されます。

    • アドインで返される UI の外観が、動的に生成されるレポートなど、実行時にしか存在しないデータまたは条件に依存している。

    • アドインで提供されるサービスのUI が、そのアドインを使用できるホスト アプリケーションの UI と異なっている。

    • アドインは主にホスト アプリケーション向けのサービスを実行し、UI を使用してホスト アプリケーションにステータスをレポートする。

  2. アドインが UI である。 アドインが、コントラクトの定義に従う UI です。 このシナリオは、次の場合に使用されます。

    • アドインは表示以外のサービス (広告など) を提供しない。

    • アドインで提供されるサービスの UI が、そのアドインを使用できるすべてのホスト アプリケーションで共通である (電卓、カラー ピッカーなど)。

これらのシナリオでは、UI オブジェクトをホスト アプリケーション ドメインとアドイン アプリケーション ドメインとの間で受け渡しできる必要があります。 .NET Framework アドイン モデルは、アプリケーション ドメイン間の通信においてリモート処理に依存しているため、それらの間で受け渡しされるオブジェクトはリモート処理可能である必要があります。

リモート処理可能なオブジェクトとは、次の 1 つ以上に該当するクラスのインスタンスです。

注意

リモート処理可能な .NET Framework オブジェクトの作成の詳細については、「オブジェクトをリモート処理可能にする」を参照してください。

WPF UI 型は、リモート処理可能ではありません。 この問題を解決するため、WPF では .NET Framework アドイン モデルが拡張され、アドインによって作成される WPF UI をホスト アプリケーションで表示できるようになります。 これは WPF によって、INativeHandleContract インターフェイス、および FrameworkElementAdapters クラスによって実装される 2 つの静的メソッド ContractToViewAdapterViewToContractAdapter という、2 つの方法でサポートされています。 大まかに、これらの型とメソッドは次のように使用されます。

  1. WPF では、アドインによって提供されるユーザー インターフェイスは、図形、コントロール、ユーザー コントロール、レイアウト パネル、ページなど、FrameworkElement から直接または間接的に派生したクラスである必要があります。

  2. UI がアドインとホスト アプリケーションとの間で受け渡しされることをコントラクトで宣言する場合、必ず (FrameworkElement ではなく) INativeHandleContract として宣言する必要があります。INativeHandleContract はアドイン UI のリモート処理可能な表現で、分離境界を越えて受け渡しできます。

  3. アドインのアプリケーション ドメインから渡される前に、FrameworkElement は、ViewToContractAdapter の呼び出しによって、INativeHandleContract としてパッケージ化されます。

  4. ホスト アプリケーションのアプリケーション ドメインに渡された後、INativeHandleContract は、ContractToViewAdapter の呼び出しによって、FrameworkElement として再パッケージ化する必要があります。

INativeHandleContractContractToViewAdapter、および ViewToContractAdapter の使用方法は、個々のシナリオによって異なります。 以降のセクションでは、各プログラミング モデルについて詳しく説明していきます。

ユーザー インターフェイスを返すアドイン

アドインが UI をホスト アプリケーションに返すには、次のことが必要です。

  1. ホスト アプリケーション、アドイン、およびパイプラインが、.NET Framework の「アドインおよび拡張機能」の説明に従って作成されている必要があります。

  2. コントラクトで IContract が実装されている必要があります。また、UI を返すためには、戻り値が INativeHandleContract 型であるメソッドがコントラクトで宣言されている必要があります。

  3. アドインとホスト アプリケーションとの間で受け渡される UI は、直接または間接的に、FrameworkElement から派生している必要があります。

  4. アドインによって返される UI は、分離境界を越える前に、FrameworkElement から INativeHandleContract に変換される必要があります。

  5. 返される UI は、分離境界を越えた後で、INativeHandleContract から FrameworkElement に変換される必要があります。

  6. ホスト アプリケーションでは、返された FrameworkElement を表示します。

UI を返すアドインを実装する方法の例については、「UI を返すアドインを作成する」を参照してください。

ユーザー インターフェイスであるアドイン

アドインが UI である場合、次のことが必要です。

  1. ホスト アプリケーション、アドイン、およびパイプラインが、.NET Framework の「アドインおよび拡張機能」の説明に従って作成されている必要があります。

  2. アドインのコントラクト インターフェイスで、INativeHandleContract が実装されている必要があります。

  3. ホスト アプリケーションに渡されるアドインが、直接または間接的に、FrameworkElement から派生している必要があります。

  4. アドインは、分離境界を越える前に FrameworkElement から INativeHandleContract に変換される必要があります。

  5. アドインは、分離境界を越えた後で INativeHandleContract から FrameworkElement に変換される必要があります。

  6. ホスト アプリケーションでは、返された FrameworkElement を表示します。

UI であるアドインを実装する方法の例については、「UI であるアドインを作成する」を参照してください。

複数の UI を返すアドイン

アドインで提供される複数のユーザー インターフェイスをホスト アプリケーションで表示することはよくあります。 たとえば、UI であるアドインが、ホスト アプリケーションにステータス情報も UI として提供するとします。 このようなアドインは、ユーザー インターフェイスを返すアドインのモデルとユーザー インターフェイスであるアドインのモデルの両方の手法を組み合わせることで実装できます。

アドインと XAML ブラウザー アプリケーション

ここまでの例では、ホスト アプリケーションはスタンドアロン アプリケーションとしてインストールされています。 XAML ブラウザー アプリケーション (XBAP) はアドインをホストすることもできますが、そのためには次に示すビルドと実装の要件を満たす必要があります。

  • パイプライン (フォルダーとアセンブリ) とアドイン アセンブリを、XBAP と同じフォルダーにある、クライアント コンピューターの ClickOnce アプリケーション キャッシュにダウンロードするよう、XBAP アプリケーション マニフェストを特別に構成する必要があります。

  • アドインを探索して読み込む XBAP コードで、XBAP の ClickOnce アプリケーション キャッシュを、パイプラインとアドインの場所として使用する必要があります。

  • XBAP では、アドインが起点サイトにある圧縮しないファイルを参照する場合、アドインを特別なセキュリティ コンテキストの下で読み込む必要があります。XBAP によってホストされる場合、アドインが参照できるのは、ホスト アプリケーションの起点サイトにある圧縮しないファイルのみです。

これらのタスクについて、次のサブセクションで詳しく説明します。

ClickOnce 配置のためのパイプラインとアドインの構成

XBAP では、ClickOnce 配置キャッシュの安全なフォルダーにダウンロードされ、そこから実行されます。 XBAP でアドインをホストするには、パイプラインとアドインのアセンブリも同じ安全なフォルダーにダウンロードする必要があります。 このためには、パイプラインとアドインのどちらのアセンブリもダウンロード対象に含まれるよう、アプリケーション マニフェストを構成する必要があります。 これは、Visual Studio で実行することが最も簡単ですが、Visual Studio でパイプラインをアセンブリとして検出するには、パイプラインとアドインのアセンブリが、ホスト XBAP プロジェクトのルート フォルダーに存在する必要があります。

したがって、まず、パイプライン アセンブリとアドイン アセンブリの各プロジェクトのビルド出力を設定し、パイプラインとアドインのアセンブリを XBAP プロジェクトのルートにビルドします。 次の表は、ホストの XBAP プロジェクトと同じソリューションとルートのフォルダーに格納される、パイプライン アセンブリ プロジェクトとアドイン アセンブリ プロジェクトのビルド出力パスを示します。

表 1:XBAP でホストされるパイプライン アセンブリのビルド出力パス

パイプライン アセンブリ プロジェクト ビルド出力パス
コントラクト ..\HostXBAP\Contracts\
アドイン ビュー ..\HostXBAP\AddInViews\
アドイン側のアダプター ..\HostXBAP\AddInSideAdapters\
ホスト側のアダプター ..\HostXBAP\HostSideAdapters\
アドイン ..\HostXBAP\AddIns\WPFAddIn1

次に、パイプライン アセンブリとアドイン アセンブリを XBAP コンテンツ ファイルとして Visual Studio に指定します。手順は次のとおりです。

  1. ソリューション エクスプローラーで各パイプライン フォルダーを右クリックし、 [プロジェクトに含める] を選択して、パイプラインとアドインのアセンブリをプロジェクトに含めます。

  2. [プロパティ] ウィンドウで、パイプライン アセンブリとアドイン アセンブリそれぞれについて、 [ビルド アクション][コンテンツ] に設定します。

最後に、パイプラインとアドインのどちらのアセンブリ ファイルもダウンロード対象に含まれるよう、アプリケーション マニフェストを構成します。 ファイルは、XBAP アプリケーションが占有する ClickOnce キャッシュ内のフォルダーのルートにあるフォルダー内に存在している必要があります。 この構成は、次の手順に従って、Visual Studio で行うことができます。

  1. XBAP プロジェクトを右クリックして、 [プロパティ][発行] の順にクリックし、 [アプリケーション ファイル] ボタンをクリックします。

  2. [アプリケーション ファイル] ダイアログ ボックスで、各パイプラインとアドインの DLL の [発行の状況][含める (自動)] に設定し、各パイプラインとアドインの DLL の [ダウンロード グループ][(必須)] に設定します。

アプリケーション ベースからのパイプラインとアドインの使用

パイプラインとアドインは ClickOnce 配置用に構成されると、XBAP と同じ ClickOnce キャッシュ フォルダーにダウンロードされます。 このパイプラインとアドインを XBAP から使用するには、XBAP コードでそれらをアプリケーション ベースから取得する必要があります。 .NET Framework アドイン モデルのさまざまな型やメンバーでは、パイプラインおよびアドインを使用できるよう、このシナリオ用に特別なサポートが提供されています。 まず、パスは ApplicationBase 列挙値で識別されます。 この値を適切なアドイン メンバーのオーバーロードと併用することで、次のようなパイプラインを使用できます。

ホストの起点サイトへのアクセス

アドインが起点サイトのファイルを参照できるように、アドインはホスト アプリケーションと等価なセキュリティ分離を使用して読み込む必要があります。 このセキュリティ レベルは AddInSecurityLevel.Host 列挙値によって示され、アドインがアクティブ化されると Activate メソッドに渡されます。

WPF アドイン アーキテクチャ

これまで見てきたように、最上位のレベルでは、WPF を使用することによって、.NET Framework アドインで、(FrameworkElement から直接または間接的に派生した) ユーザー インターフェイス を、INativeHandleContractViewToContractAdapter、および ContractToViewAdapter を使用して実装できます。 その結果、ホスト アプリケーションに FrameworkElement が返され、ホスト アプリケーションの UI で表示されます。

簡単な UI アドイン シナリオでは、開発者に必要な詳細はこれだけです。 より複雑なシナリオ、特にレイアウト、リソース、データ バインディングなど、追加の WPF サービスの活用を想定したシナリオでは、その利点と制約を把握するために UI をサポートする .NET Framework アドイン モデルが、WPF で拡張されるしくみについて詳しい知識が必要です。

基本的に、WPF ではアドインからホスト アプリケーションに UI は渡されません。その代わり、WPF では UI の Win32 ウィンドウ ハンドルが WPF 相互運用性を使用して渡されます。 つまり、アドインの UI がホスト アプリケーションに渡されると、次の処理が行われます。

  • アドイン側では、WPF によって、ホスト アプリケーションによって表示される UI のウィンドウ ハンドルが取得されます。 このウィンドウ ハンドルは、HwndSource から派生し、INativeHandleContract を実装する内部 WPF クラスによってカプセル化されます。 このクラスのインスタンスは、ViewToContractAdapter によって返され、アドインのアプリケーション ドメインからホスト アプリケーションのアプリケーション ドメインにマーシャリングされます。

  • ホスト アプリケーション側では、HwndSource は、HwndHost から派生し、INativeHandleContract を消費する内部 WPF クラスとして、WPF によって再パッケージ化されます。 このクラスのインスタンスは、ContractToViewAdapter によってホスト アプリケーションに返されます。

HwndHost は、ウィンドウ ハンドルによって識別されるユーザー インターフェイスを WPF ユーザー インターフェイスで表示するために存在します。 詳細については、「WPF と Win32 の相互運用性」を参照してください。

まとめると、INativeHandleContractViewToContractAdapter、および ContractToViewAdapter は、WPF UI のウィンドウ ハンドルを、アドインからホスト アプリケーションに渡すために存在します。渡されると、HwndHost によってカプセル化され、ホスト アプリケーションの UI を表示します。

注意

ホスト アプリケーションは HwndHost を取得するので、ホスト アプリケーションでは ContractToViewAdapter から返されるオブジェクトを、アドインによって実装された型 (UserControl など) に変換できません。

その特質上、HwndHost には、ホスト アプリケーションの使用に影響する制約があります。 ただし、WPF では、HwndHost がアドイン シナリオ用の複数の機能で拡張されます。 その利点と制約について、以下に説明します。

WPF アドインの利点

WPF アドインのユーザー インターフェイスは、HwndHost から派生する内部クラスを使用してホスト アプリケーションで表示されるため、そのようなユーザー インターフェイスは、レイアウト、レンダリング、データ バインディング、スタイル、テンプレート、リソースなどの WPF UI サービスに関して、HwndHost の機能の制約を受けます。 ただし、WPF では、その内部 HwndHost サブクラスが、次のような追加機能で強化されます。

  • ホスト アプリケーションの UI とアドインの UI との間を Tab キーで移動できます。 "アドインが UI である" プログラミング モデルでは、アドインが完全に信頼されているか部分的に信頼されているかに関係なく、アドイン側のアダプターが QueryContract をオーバーライドして、Tab キーによる移動処理を有効にする必要があります。

  • ホスト アプリケーションのユーザー インターフェイスで表示されるアドインのユーザー インターフェイスのアクセシビリティ要件を順守します。

  • アプリケーション ドメインが複数あるシナリオで WPF アプリケーションが安全に実行されます。

  • アドインがセキュリティ分離を使用して実行されている場合 (部分的に信頼されているセキュリティ サンドボックス)、アドイン UI ウィンドウ ハンドルへの不正アクセスを回避します。 ViewToContractAdapter を呼び出すことでこのセキュリティが保証されます。

    • "アドインが IU を返す" プログラミング モデルで、アドイン UI のウィンドウ ハンドルを分離境界を越えて渡す唯一の方法は、ViewToContractAdapter を呼び出すことです。

    • "アドインが UI である" プログラミング モデルでは、アドイン側のアダプターで QueryContract をオーバーライドして ViewToContractAdapter を呼び出すこと (前の例に従って) が、アドイン側のアダプターの QueryContract 実装をホスト側のアダプターから呼び出すことに相当する処理として必要です。

  • アプリケーション ドメインの実行を何重にも保護します。 アプリケーション ドメインの制約に起因して、アドイン アプリケーション ドメインでスローされた未処理の例外は、分離境界が存在していても、アプリケーション全体のクラッシュにつながります。 ただし、WPF と .NET Framework アドイン モデルでは、この問題を回避して、アプリケーションの安定性を向上させる簡単な手段が提供されます。 UI を表示する WPF アドインでは、ホスト アプリケーションが WPF アプリケーションである場合に、アプリケーション ドメインが実行されているスレッドの Dispatcher が作成されます。 WPF アドインの DispatcherUnhandledException イベントを処理することで、アプリケーション ドメインで発生した未処理の例外をすべて検出できます。 CurrentDispatcher プロパティから Dispatcher を取得できます。

WPF アドインの制約

WPF には、HwndSourceHwndHost、ウィンドウ ハンドルによって提供される既定の動作が強化される利点もありますが、ホスト アプリケーションで表示されるアドイン ユーザー インターフェイスに対する制約もあります。

  • ホスト アプリケーションで表示されるアドイン ユーザー インターフェイスでは、ホスト アプリケーションのクリッピング動作が考慮されません。

  • 相互運用性シナリオの空域の概念もアドインに適用されます (「技術領域の概要」を参照)。

  • リソースの継承、データ バインディング、コマンド実行など、ホスト アプリケーションの UI サービスは、アドイン ユーザー インターフェイスで自動的には使用可能になりません。 これらのサービスをアドインに提供するには、パイプラインを更新する必要があります。

  • アドイン UI に回転、拡大縮小、傾斜などの変換は適用できません (「変換の概要」を参照)。

  • System.Drawing 名前空間の描画操作によってレンダリングされるアドイン ユーザー インターフェイス内のコンテンツに、アルファ ブレンド効果を含めることができます。 ただし、それを含むアドイン UI とホスト アプリケーション UI のいずれも、100% 不透明である必要があります。言い換えると、どちらについても Opacity プロパティが 1 に設定されている必要があります。

  • アドイン UI が含まれるホスト アプリケーションのウィンドウの AllowsTransparency プロパティが true の場合、アドインは非表示になります。 このことは、アドイン UI が 100% 不透明 (Opacity プロパティの値が 1) の場合にも該当します。

  • アドイン UI は、同じトップレベル ウィンドウ内の他の WPF 要素より前面に表示する必要があります。

  • アドインの UI には、VisualBrush を使用してレンダリングできる部分はありません。 その代わり、アドインは生成された UI のスナップショットを取り、コントラクトで定義されているメソッドを使用してホスト アプリケーションに渡すことができるビットマップを作成できます。

  • メディア ファイルをアドイン UI 内の MediaElement で再生することはできません。

  • ホスト アプリケーションがアドイン UI 用に生成されたマウス イベントを受け取ったり、発生させたりすることはなく、ホスト アプリケーション UI の IsMouseOver プロパティの値は false に設定されます。

  • アドイン UI 内のコントロール間を、フォーカスが移動しても、GotFocus イベントや LostFocus イベントをホスト アプリケーションで受け取ったり、発生したりすることはありません。

  • アドイン UI を含むホスト アプリケーション部分は、印刷時には白く出力されます。

  • アドイン UI によって作成されたすべてのディスパッチャー (Dispatcher を参照) は、ホスト アプリケーションが実行を継続する場合、オーナー アドインがアンロードされる前に手動でシャットダウンする必要があります。 コントラクトは、アドインがアンロードされる前にホスト アプリケーションがアドインにシグナルを送信するためのメソッドを実装でき、それによりアドイン UI がそのディスパッチャーをシャットダウンできます。

  • アドイン UI が InkCanvas である場合、または InkCanvas を含んでいる場合、そのアドインをアンロードすることはできません。

パフォーマンスの最適化

既定では、複数のアプリケーション ドメインが使用されていると、各アプリケーションで必要とされているさまざまな .NET Framework アセンブリがすべてそのアプリケーションのドメインに読み込まれます。 その結果、新しいアプリケーション ドメインを作成してその中でアプリケーションを開始するために必要な時間がパフォーマンスに影響します。 ただし、.NET Framework には、アセンブリが既に読み込まれている場合に、それをアプリケーション ドメイン間で共有するようアプリケーションに指示することで開始時間を短縮する手段が用意されています。 これを実行するには、LoaderOptimizationAttribute 属性を使用します。これを、エントリ ポイント メソッド (Main) に適用する必要があります。 この場合、アプリケーション定義を実装するコードのみを使用する必要があります (「アプリケーション管理の概要」を参照)。

関連項目