COM サーバーの詳細説明

この節では,Fortran COM サーバーの詳細説明として以下の内容を説明しています。

DLL または EXE COM サーバーの選択

DLL (プロセス内) COM サーバーと EXE (プロセス外) COM サーバーのどちらを選択するかは,性能対耐久性のどちらを選ぶかです。

性能と耐久性の選択に加えて,以下の因子も考慮すべきです。

EXE サーバーの利点を得るために,DLL サーバーを代理サーバー (surrogate) にロードすることができます。これは,次の節で説明します。

DLL 代理サーバー

プロセス内 DLL サーバーは,代理サーバーの助けを借りて別のプロセスで実行することができます。代理サーバーは,別のプロセスとして実行し,DLL サーバーをロードし,DLL サーバーがローカル・サーバーとして動作できるようにするメカニズムの全てを提供します。Windows は,DLLHOST.EXE という名前の標準代理サーバーを提供しています。代理サーバーの主な利点は,故障の分離です。つまり,サーバーがクラッシュした場合,クライアントがクラッシュしない,またはその逆です。不利な点は性能です。DLL と対照的に,別のアプリケーションでメソッドを実行するための著しい性能オーバヘッドがあります。

DLL サーバーは,システム・レジストリのエントリを介して代理サーバーと関連されています。これは,DLL サーバーを AppID と関連付けることで行われます。AppID は GUID です。標準代理サーバーを使用する場合,DLL のクラスの CLSID を AppID として,または,新規に作成した GUID を使うことができます。新しい GUID を作成するには,...\Common\Tools ディレクトリの GUIDGEN.EXE を使用します。

  1. クラスの HKEY_CLASSES_ROOT\CLSID\{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx} キーの下に,CLSID の値で AppID エントリを追加します。AddingMachine クラスの例では,レジストリ・キーは HKEY_CLASSES_ROOT\CLSID\{904245FC-DD6D-11D3-9835-0000F875E193} で,AppID 値は {904245FC-DD6D-11D3-9835-0000F875E193} です。

  2. HKEY_CLASSES_ROOT\AppID キーの下に,AppID を使ってキーを追加します。AddingMachine クラスの例では,レジストリ・キーは,HKEY_CLASSES_ROOT\AppID\{904245FC-DD6D-11D3-9835-0000F875E193} です。キーの基本値にはクラス名を使用します。たとえば,"AddingMachine Class" です。値に空の文字列を与えて,"DllSurrogate" エントリを追加します。

代理サーバーを使用するためには,システムには Windows 2000, Windows XP,Windows NT 4.0 SP2 以降,Windows 98,Windows Me, または DCOM アップデートを行った Windows 95 が必要です。

クライアントは,プロセス内 DLL サーバーをロードするのではなく代理サーバーを使用するには,CLSCTX_INPROC_SERVER ではなく を要求しなければなりません。メソッド呼び出しが異なったプロセス間で行われるため,代理サーバーを使用するには,プロキシ/スタブが登録された DLL サーバーが必要になります。プロキシ/スタブについての詳細は,「マーシャリング,プロキシ,およびスタブ」を参照してください。

カスタム代理サーバーを書くこともできます。詳細は,『Platform SDK』オンライン・ドキュメントを参照してください。

ウィザードが生成するコードについて

ウィザードは,project-nametemplates という名前のプロジェクトのサブディレクトリのファイルからプロジェクト用のコードを生成します。project-name.hie ファイルには,ドキュメントされていないテキスト言語で書かれた COM サーバーの定義が含まれています。ウィザードが編集するので,project-name.hie ファイルを手動で編集する必要はありません。

project-nametemplates ディレクトリ中の他の多くのファイルは,プロジェクト用に生成したソース・ファイルのテンプレートです。これらのテンプレートには,生成したソースにそのままコピーできるソース・コードと定義した COM サーバーに固有のコードを生成するためのウィザードのガイドとなる埋め込み指示文が含まれています。指示文は,project-name.hie ファイルの情報を使用します。指示文はドキュメントされないもので変更されるべきものです。

新しい Fortran COM Server プロジェクトを作成すると,AppWizard は project-nametemplates ディレクトリを作成し,Visual Fortran COM Server Wizard テンプレート・ディレクトリ ...\Df98\Templates\COMServer からテンプレートをコピーします。

Fortran COM Server Wizard テンプレート・ディレクトリは,Visual Fortran のリリース毎に変更されるかもしれませんが,project-nametemplates ディレクトリ中のテンプレートは決して自動的には更新されません。たとえば,Visual Fortran バージョン 6.5 を使用して COM サーバーを作成し,Visual Fortran の次のリリース (たとえば,バージョン 6.5A) が更新されたテンプレートを含んでいる場合,作成した COM サーバーのテンプレートは自動的には新しい 6.5A テンプレートに更新されません。サーバーの定義を修正すると,プロジェクトはサーバーが作成された時の 6.5 テンプレートを引き続き使用します。これには,開発しテストしたプロジェクトに異なったコードを導入しないという利点があります。

しかしながら,プロジェクトで使用していたテンプレートを修正したくなる場合が 2 つあります。

テンプレートを修正する利点は,ウィザードが生成するコードをカスタマイズできることです。テンプレートを修正する不利点には,以下のものがあります。


COM ErrorInfo オブジェクトに対するサポートの追加

サーバーが HRESULT に返すものよりももっと完全なエラー情報を返すことができるように,COM は ErrorInfo オブジェクトをサポートしています。ErrorInfo オブジェクトは,エラーの文脈上の説明,エラーのソース,およびエラーが発生したインタフェースのインタフェース ID を返します。ErrorInfo オブジェクトはまた,エラーを取り扱うヘルプ情報を提供するヘルプ・ファイルのエントリへのポインタも含むことができます。

ErrorInfo オブジェクトをサポートするには,サーバーが ISupportErrorInfo インタフェースを実装しなければなりません。サーバーにこのインタフェースの基本実装を追加することができます。

ISupportErrorInfo は,1 メソッド InterfaceSupportsErrorInfo を含んでいます。基本実装は,クラスのインタフェースの全てが ErrorInfo オブジェクトをサポートする場合には十分な S_OK を返します。そうでなければ,インタフェース ID に渡されるものをテストし,ErrorInfo オブジェクトをサポートするインタフェースには S_OK を返し,サポートしないものには E_FAIL を返すようにメソッドを修正しなければなりません。

ErrorInfo オブジェクトをサポートするインタフェースが HRESULT にエラー状態を返す時,ICreateErrorInfo インタフェースを使って ErrorInfo オブジェクトを初期化しなければなりません。Fortran での ErrorInfo オブジェクトの使い方の例は,...\Df98\SAMPLES\ADVANCED\COM\ERRORINFO サンプルを参照してください。ErrorInfo オブジェクトの詳細は,『Platform SDK』オンライン・ドキュメントを参照してください。

スレッド化モデル

ウィザードは,DLL COM サーバーで作成するクラスに対して 2 つの COM スレッド化モデルをサポートしています。それは,アパートメントとシングルです。ウィザードは,アパートメント・スレッド化モデル (シングル・スレッド・アパートメント・モデル "STA" としても知られています) を基本設定として使用します。アパートメント・スレッド化モデルの基本規則は,以下のとおりです。

これは,クラスがオブジェクト間でグローバル・データを共有している場合,グローバル・データはスレッド同期プリミティブを使って同時に参照されることから保護されなければならないということを意味します。これは,クラスの 2 つのインスタンスが異なったスレッドで実行されるためです。しかしながら,オブジェクト毎のインスタンス・データ,つまり,クラス構造型の欄は,COM メカニズムによる同時参照から保護されています。オブジェクトが第 1 オブジェクトの再入呼び出しの引き金となる他のオブジェクトを呼び出す場合を除いて,これは事実です。

シングル・スレッド化モデルを使用するクラスは,オブジェクト毎のデータと同様に,クラスのグローバル・データへの同時参照を心配する必要がありません。クラスの全てのオブジェクトは,シングル・スレッドに作成され,クラスの 1 オブジェクトのみがいつでも実行されます。

ウィザードが生成した EXE COM サーバーは,シングル・スレッドです。全てのメソッド呼び出しは,サーバーのメッセージ・キューで順番に並べられます。そのため,EXE サーバーでは,オブジェクト毎のデータと同様に,クラスのグローバル・データへの同時参照を心配する必要がありません。

COM スレッド化モデルの詳細な説明は,本ドキュメントの目的を超えています。詳細は,『Platform SDK』オンライン・ドキュメントを参照してください。

マーシャリング,プロキシ,およびスタブ

COM は,マーシャリング,プロキシおよびスタブを使用することで,別のプロセスのオブジェクトの使用 (EXE サーバーまたは DLL 代理サーバーを使用する時),および上記のスレッド化モデルをサポートしてます。この節では,マーシャリング,プロキシおよびスタブの概要を説明します。詳細は,『Platform SDK』オンライン・ドキュメントを参照してください。

マーシャリングは,メソッド呼び出しに対するパラメタを読み取り,そして,他の実行コンテキスト (たとえば,スレッド,プロセス,またはマシン) への送信のためにそれらを準備するプロセスです。マーシャリングは,プロキシ で終了します。クライアントからの視点から見ると,プロキシはオブジェクト自身と同じインタフェースを持っています。プロキシの作業は,クライアントと同じ実行コンテキストにオブジェクトのようなオブジェクトを作成することです。

プロキシは,クライアント・コードがオブジェクトが実際に存在する場所を気にしなくていいようにします。プロキシは,メソッド・パラメタをマーシャリングし,それらをサーバーのオブジェクトに関連するスタブに送信します。スタブは,パラメタを非マーシャリングし,サーバーのメソッドを呼び出します。サーバーの視点から見ると,それが同じ実行コンテキストでクライアントから呼び出される時とこれは違いはありません。プロセス内 DLL サーバーではないサーバーは,常にプロキシ/スタブの組を要求します。プロセス内 DLL サーバーは,クライアントとオブジェクトが異なったアパートメントにある時にプロキシ/スタブの組を要求します。

プロキシ/スタブの組をサーバーに割り当てるのに次の 3 通りがあります。

タイプ・ライブラリー・
マーシャリング
メソッドとプロパティでオートメーション互換データ型のみを使用する場合,COM は自動的にサーバーに対するプロキシ/スタブとして Universal Marshaller を使用することができます。Universal Marshaller は,パラメタをマーシャリングする方法を決定するためにタイプ・ライブラリー中のサーバーの記述を使用します。これは,タイプ・ライブラリー・マーシャリングとして知られています。タイプ・ライブラリー・マーシャリングの使用は,サーバーをオートメーション互換データ型に制限すること以外は,ユーザー・プログラムに何の要求もしません。
MIDL ベース・
マーシャリング
プロジェクトは,タイプ・ライブラリーにサーバーの IDL 記述をコンパイルするために MIDL コンパイラを使用します。同時に,MIDL コンパイラはまた,サーバーに対してプロキシ/スタブ DLL をビルドするために必要な C ソース・コードも生成します。MIDL が生成したコードからプロキシ/スタブ DLL をビルドするには C コンパイラが必要です。プロキシ/スタブ DLL 自身は,プロセス内 DLL サーバーで,システムに登録される必要があります。サーバーが非オートメーション互換データ型を使用し,異なった実行コンテキストでクライアントがサーバーを使用する場合,MIDL ベース・マーシャリングを使用する必要があります。
カスタム・
マーシャリング
サーバーは,IMarshall インタフェースを実装することで独自のマーシャリングを実装することができます。このアプローチは,一般的にたくさんの作業を呼び出し,性能上の理由で行われます。

生成された「Do Not Edit」コードのマップ

この節では,ウィザードが生成した「Do Not Edit」ソース・ファイルに実装された COM サーバー機能の部品の概要を説明します。

ファイル名 説明
server.idl サーバーの IDL 記述を含んでいます。サーバーのタイプ・ライブラリーを生成するために,MIDL コンパイラでコンパイルされます。
servernameglobal.f90 サーバーに対するグローバル・データと関数を含んでいます。
dllmain.f90
(DLL サーバー)
全ての COM サーバー DLL が必要とするエクスポート関数を含んでいます。これには,DllMainDLLRegisterServerDLLUnregisterServerDllGetClassObject,および DllCanUnloadNow が含まれます。
server.def
(DLL サーバー)
リンカーに対してエクスポート関数を宣言します。
exemain.f90
(EXE サーバー)
EXE サーバーの主エントリ・ポイントを含んでいます。コマンド行引数も処理します。
serverhelper.f90 サーバーに対するヘルパ関数を含んでいます。
clsfactty.f90 サーバーが定義するクラスのインスタンスを作成するために使用される IClassFactory インタフェースの定義を含んでいます。
clsfact.f90 サーバーが定義するクラスのインスタンスを作成するために使用される IClassFactory インタフェースのメソッドを含んでいます。
classnameTY.f90 クラスの実装に使用されるパラメタと型の定義を含むモジュールを定義します。クラスの IUnknown メソッドの実装も含まれます。このファイルの別のインスタンスは,サーバーで定義される各クラスに対して生成されます。
interfacename.f90 インタフェースのメソッドの Fortran インタフェースを含むモジュールを定義します。クラスの VTBL から直接呼び出され,ユーザーが実装したメソッドを呼び出す Fortran ラッパ (wrapper) の実装も含まれています。このファイルの別のインスタンスは,クラスで定義される各インタフェースに対して生成されます。