この節では,Fortran COM サーバーの詳細説明として以下の内容を説明しています。
DLL (プロセス内) COM サーバーと EXE (プロセス外) COM サーバーのどちらを選択するかは,性能対耐久性のどちらを選ぶかです。
DLL サーバーは,EXE サーバー以上の性能での利点を提供します。DLL サーバーはクライアントのアドレス空間にロードされるので,メソッドの呼び出しでのオーバヘッドが少なくなります。クライアント・コードとサーバー・オブジェクトが同じ COM アパートメントにある場合,メソッド呼び出しは DLL ルーチン呼び出しと同じ効率です。
EXE サーバーは,DLL サーバー以上の耐久性での利点を提供します。サーバー・オブジェクトが別のアドレス空間にあるため,オブジェクトはクライアントのメモリー処理およびその逆の影響を受けません。サーバーがクラッシュした場合でも,クライアントは全てのメソッド呼び出しの結果を検証し,死んだ (dead) オブジェクトから復旧するためのステップを行う限り,クライアントはクラッシュしません。
性能と耐久性の選択に加えて,以下の因子も考慮すべきです。
EXE サーバーでは,オブジェクトはクライアントから分離したセキュリティ・コンテキストで実行できます。DLL サーバーでは,オブジェクトのメソッドのコードはクライアントのアクセス・トークンを使って実行されます。
EXE サーバーは,COM 分散オブジェクト・サポートを使用してリモート・マシン上で実行することができます。
EXE サーバーの利点を得るために,DLL サーバーを代理サーバー (surrogate) にロードすることができます。これは,次の節で説明します。
プロセス内 DLL サーバーは,代理サーバーの助けを借りて別のプロセスで実行することができます。代理サーバーは,別のプロセスとして実行し,DLL サーバーをロードし,DLL サーバーがローカル・サーバーとして動作できるようにするメカニズムの全てを提供します。Windows は,DLLHOST.EXE という名前の標準代理サーバーを提供しています。代理サーバーの主な利点は,故障の分離です。つまり,サーバーがクラッシュした場合,クライアントがクラッシュしない,またはその逆です。不利な点は性能です。DLL と対照的に,別のアプリケーションでメソッドを実行するための著しい性能オーバヘッドがあります。
DLL サーバーは,システム・レジストリのエントリを介して代理サーバーと関連されています。これは,DLL サーバーを AppID と関連付けることで行われます。AppID は GUID です。標準代理サーバーを使用する場合,DLL のクラスの CLSID を AppID として,または,新規に作成した GUID を使うことができます。新しい GUID を作成するには,...\Common\Tools ディレクトリの GUIDGEN.EXE を使用します。
クラスの 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} です。
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 つあります。
Visual Fortran の新しいリリースが,COM サーバー・プロジェクトで使用できる付加的な機能を含んでいる場合があります。新しい機能の幾つかは,新しいテンプレートに依存するかもしれません。これらの機能は,project-nametemplates ディレクトリのテンプレートを更新しない限り,既存のプロジェクトでは有効にはなりません。Fortran COM Server Wizard テンプレート・ディレクトリ ...\Df98\Templates\COMServer の全てのファイルを project-nametemplates ディレクトリにコピーし,同じ名前で全てのファイルを置き換えることによって,これを行います。サーバーの定義が修正され,プロジェクト・ソースが再作成される次回に,プロジェクトは新しいテンプレートを使用します。
ウィザードが生成したコードを修正したくなる場合があります。project-nametemplates ディレクトリのテンプレートを編集することができます。そのままコピーした編集コードは素直なものです。埋め込まれた指示文を修正はサポートされていませんし,修正したテンプレートを使用しようとした時にウィザードが失敗するかもしれません。埋め込まれた指示文は,アットマーク (@) 文字で始まります。その次の文字は,指示文の型を決定します。多くの指示文の最後もまた,アットマーク (@) 文字の後に指示文の型に合った終了文字で終わります。たとえば,@[ と @] は,指示文の型の 1 つの終了子です。
テンプレートを修正する利点は,ウィザードが生成するコードをカスタマイズできることです。テンプレートを修正する不利点には,以下のものがあります。
ウィザードが不正なコードを生成しないような,または失敗しないような方法でテンプレートを注意深く修正しなければなりません。
新しいテンプレートに自分の行った修正を再度適用することなしに,Visual Fortran の新しいリリースが提供するテンプレートで自分のプロジェクトのテンプレートを更新することはできません。
COM ErrorInfo オブジェクトに対するサポートの追加
サーバーが HRESULT に返すものよりももっと完全なエラー情報を返すことができるように,COM は ErrorInfo オブジェクトをサポートしています。ErrorInfo オブジェクトは,エラーの文脈上の説明,エラーのソース,およびエラーが発生したインタフェースのインタフェース ID を返します。ErrorInfo オブジェクトはまた,エラーを取り扱うヘルプ情報を提供するヘルプ・ファイルのエントリへのポインタも含むことができます。
ErrorInfo オブジェクトをサポートするには,サーバーが ISupportErrorInfo インタフェースを実装しなければなりません。サーバーにこのインタフェースの基本実装を追加することができます。
階層ペインで ErrorInfo オブジェクトをサポートしたいクラスを選択します。
クラス名を右マウス・ボタンでクリックして,ポップアップ・メニューを表示し,「Add Pre-defined Interface」を選択します。
「Select an Interface」ダイアログボックスで「ISupportErrorInfo」を選択し,「OK」をクリックします。
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 つのオブジェクト A と B が同じ STA スレッドで作成され,A がメソッド呼び出しを処理する場合,A が完了するまで他のクライアントは A と B のどちらも呼び出すことができません。
B が A とは異なったスレッドで作成される場合,A が処理されている間でもメソッド呼び出しを受け取ることができます。その逆もできます。
これは,クラスがオブジェクト間でグローバル・データを共有している場合,グローバル・データはスレッド同期プリミティブを使って同時に参照されることから保護されなければならないということを意味します。これは,クラスの 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」ソース・ファイルに実装された COM サーバー機能の部品の概要を説明します。
ファイル名 | 説明 |
---|---|
server.idl | サーバーの IDL 記述を含んでいます。サーバーのタイプ・ライブラリーを生成するために,MIDL コンパイラでコンパイルされます。 |
servernameglobal.f90 | サーバーに対するグローバル・データと関数を含んでいます。 |
dllmain.f90 (DLL サーバー) |
全ての COM サーバー DLL が必要とするエクスポート関数を含んでいます。これには,DllMain,DLLRegisterServer,DLLUnregisterServer,DllGetClassObject,および 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) の実装も含まれています。このファイルの別のインスタンスは,クラスで定義される各インタフェースに対して生成されます。 |