Component Object Model
Component Object Model(COM、コンポーネント・オブジェクト・モデル)とは、マイクロソフト が提唱するソフトウェアの再利用を目的とした技術のことである。ソフトウェア間での通信に用いられる。
COMを使用して開発されたソフトウェアをCOMコンポーネントと呼ぶ。COMコンポーネントは、特定の開発言語に依存せず、C言語やJavaなど、様々な言語により開発を行うことができる。COMという用語はソフトウェア開発の世界ではOLE、OLEオートメーション、ActiveX、COM+、DCOMをカバーする包括的な用語としてよく使われる。COMコンポーネントは、他ソフトウェアと通信するためのインタフェースを有している。アプリケーションソフトウェアは、公開されているインタフェースを介してCOMコンポーネントと通信をし、それらを組み合わせることでサービスを提供する。言語による割り付けの違いは、参照カウンタを利用してオブジェクトの生成と破棄を彼ら自身の責任とすることにより解決する。オブジェクトの異なるインタフェース間のキャストはQueryInterface
関数で行う。メソッド呼び出しをデリゲート(委譲)する形でサブオブジェクトの集合(アグリゲーションと呼ぶ)を生成する方法がCOM内における最適な継承方法である。
COMは主としてMicrosoft Windows上で使用されるが、UNIXやMac OSでも使用することができる。COMの前身はOLEである。COMは.NET Frameworkに置き換えられてゆくものと考えられており、またWindows Communication Foundation (WCF) を通じてWebサービスをサポートする。WCFがXMLベースのSOAPメッセージを利用するのに対し、ネットワークで接続されたDCOMはバイナリの独自仕様フォーマットを利用する。COMはまたソフトウェアコンポーネントシステムとしてCORBAやJava Beansと競合関係にある。
目次
COMの歴史
- 1991年、COMの前身であるOLEが、OLE 1としてWindows 3.1とともに公開され、1992年にはOLE 2が公開された。
- 1994年、OCXもしくはOLEコントロールがVBXコントロールの後継として紹介される。それと同時に、OLEは、もはや単なる頭文字ではなく、コンポーネント技術を表す用語となった。
- 1996年初頭、マイクロソフトは、OLEのうちでインターネットと関連のあるいくつかの技術をActiveXとして名称変更した。やがて、OLEとして公開されていた技術がActiveXに統合され始める。
- 1997年、マイクロソフトは再びコンポーネントを使用するこれらの技術の変名を行い、Component Object Modelとした。
関連技術
COMはWindowsで主要なソフトウェア開発プラットフォームであり、数多くの技術の開発に影響を与えた。
COM+
エンタープライズレベルのオペレーティングシステム (OS) の代替としてWindowsのポジションを確立するためだけでなく、分散トランザクションをサポートし、メモリとプロセッサ(スレッド)の管理の改善するため、マイクロソフトはWindows NT 4.0 Service Pack 4でMicrosoft Transaction Server (MTS) を導入した。
Windows 2000でのCOMの重要な拡張は(MTSによる外部ツールの組み合わせとは対照的に)OSへ統合することであり、これをCOM+と改名した。この時点でマイクロソフトは個別の要素としてDCOMを重視していなかった。COM+の追加レイヤでトランザクショナルCOMコンポーネントを直接的に取り扱った。COM+コンポーネントはコンポーネントサービスアプリケーションインタフェースを通じて追加された。
COM+は「コンポーネントファーム (component farms)」で動作できる利点があった。コードが正しい場合、コンポーネントはメモリからアンロードすることなく初期化ルーチンを新たに呼び出すことにより再利用できた。以前はDCOMだけで可能だったコンポーネントの分散化(他のマシンからの呼び出し)も可能となった。
またCOM+では、COM+イベントという、サブスクライバ/パブリッシャー型のイベントメカニズムを導入し、アプリケーション間非同期メッセージングプロトコルであるMSMQを利用するための新たな方法として、Queued Componentsというコンポーネントを介するモデルを提供した。これにより、COM+プログラミングモデルでは、遅延バインディングされたイベント、及び、パブリッシャー/サブスクライバとイベントシステムの間のメソッド呼び出しがサポートされる。
DCOM
.NET
COMプラットフォームは.NET Frameworkに大幅に取って代わられ、マイクロソフトは.NETに注力する戦略に集中している。COMは複雑で高性能なVisual BasicやASPで実装されたフロントエンドのコードと接続するためによく利用されていた。
好感されている.NETに対してCOMはある程度批判されている。.NETがWindows FormsとWeb Formの両方に対してジャストインタイムコンパイル方式と共にVisual Basicに似たラピッドデベロップメントツールを提供するため、バックエンドコードはC#、Visual Basic.NET、C++/CLIを含むあらゆる.NET言語で実装できる。
それでもCOMはまだ様々なテクノロジーで重要なソフトウェアの基盤として生き延びている。例えば広く普及しているDirectXのAPIはCOMをベースにしている。これを記述している時点では、マイクロソフトはCOMのサポートやCOMそのものをやめてしまう計画を持っていない。COMはまた、コンパイル時点でAPIの情報を必要としないスクリプトからCOMオブジェクトを呼び出すためのインタフェース(動的ダックタイピング)を提供するため、Microsoft OfficeやInternet Explorerのようなアプリケーションのスクリプト制御のための技術として理想的でもある。COMのために開発されたGUIDシステムはユニークなIDが必要とされる場合に広く利用されている。
トランザクションやコンポーネントのキューといったようなCOM+が提供する複数のサービスはエンタープライズな.NETアプリケーションでもまだ重要である。
制約はあるものの、.NETはCOMに対して下位互換性のサポートがある。.NETはランタイム呼び出し可能ラッパー (RCW) を実装することでCOMオブジェクトを利用できる。COMオブジェクトはCOM呼び出し可能ラッパー (CCW) によって、特定の制約に従った.NETオブジェクトを利用できる。COMと.NETの両方の側からは相手側のオブジェクトがネイティブなオブジェクトに見える。
.NET Remotingでは、オブジェクトがプロセスやマシンの境界を越えてリファレンスや値を透過的にマーシャリングできるようにして、COMが持つ数多くのリモート実行の欠点を解決する。
技術的詳細
COMコンポーネントにはクラスID (CLSID) が割り当てられ、COMコンポーネント同士はクラスIDによって区別される。CLSIDはGUIDで構成されている。各COMコンポーネントは1つ以上のインタフェースを公開することで機能を提供している。インタフェース同士はインタフェースID (IID) で区別される。IIDもGUIDで構成されている。
COMインタフェースはプログラミング言語とCOMとを結び付けている。COMコンポーネントへのアクセスは全てインタフェースを通さなければならない。これによって、プロセスやコンピュータを跨いでCOMコンポーネントへアクセスすることができるようになっている(コンピュータを跨いでのアクセスにはDCOMが必要)。
インタフェース
全てのCOMコンポーネントはIUnknown
と呼ばれるインタフェースを継承する必要がある。また、全てのCOMインタフェースはIUnknown
から派生している。IUnknown
は、AddRef
/ Release
/ QueryInterface
という3つのメソッドを持っている。AddRef
とRelease
はインタフェースの生存期間を管理するための参照カウントを実装するメソッドである。そしてQueryInterface
は、IIDを指定してコンポーネントが実装している他のインタフェースを取得するメソッドである。これはC++のdynamic_cast
やJavaやC#の型変換演算子に相当する。
COMコンポーネントのインタフェースは、反射性、対称性、推移性を備えている必要がある。反射性とは、あるインタフェースから、QueryInterface
をそのインタフェース自身を表すIIDを与えて呼び出すと、返ってくるインタフェースは元と同じものでなければならないということである。対称性とは、インタフェースAからQueryInterface
でインタフェースBが取得できる場合、インタフェースBからインタフェースAが取得できなければならないということである。推移性とは、対称性と似ているが、インタフェースBがインタフェースAから取得でき、インタフェースCがインタフェースBから取得できる場合、インタフェースCはインタフェースAから取得できなければならないということである。
インタフェースには、インタフェースを実装した関数へのポインタをその宣言時の順に並べた仮想関数テーブルへのポインタが含まれている。この関数へのポインタを並べた構造は、OLE 1.0のときからOLEシステムとの通信に使われていた。
COMはコンポーネント間の通信のためにほかにも多くの標準インタフェースを定めている。たとえばデータストリームを管理するIStream
が挙げられる。これはファイルストリームのコンポーネントがファイルを読み書きする際に使うことが考えられる。IStream
のRead
メソッドとWrite
メソッドはストリームに対して読み取りと書き込みを行うことが想定される。他の例として、IOleObject
は、呼び出した側がコンポーネントの境界を決められるメソッドを持っており、また「開く」、「保存」などの操作を行うことができる。
コクラス
COMではクラスのことをコクラス (coclass) と呼ぶ。コクラスは、COMにおける(オブジェクト指向プログラミングのような)クラスを定義する言語非依存の手段である。
1つのコクラスは、1つ以上のインタフェースの具体的な実装を提供する。COMに対応しているプログラミング言語であれば、C++、Visual Basicなどどんな言語でも実装を行うことができる。
COMは、Windows開発の世界に、実装からインタフェースを切り分けるという概念を意識させることをもたらした。これは、今日プログラマがシステム構築に影響を与えることになった。この基本的な概念の延長に、1つのインタフェースに対して複数の実装を用意するという考えが挙がってきた。これは、実行時にアプリケーションがいくつもの実装から選ぶことができるということである。
インタフェース定義言語とタイプライブラリ
タイプライブラリはCOMのメタデータを保持している。しかしそれらはまずMIDL(マイクロソフトインタフェース定義言語、IDLも参照)を使って記述される。
COMコンポーネントの開発では、普通IDLで型を定義することから始める。IDLファイルではオブジェクト指向的なクラスやインタフェース、構造体、列挙体などのユーザ定義型をプログラミング言語に依存せず記述できる。このIDLではC/C++に似たキーワードと、それに追加してインタフェース定義に用いる「interface」とクラスの集合を現す「library」という2つのキーワードを使用する。また各宣言の前に角括弧[]で括って属性を指定でき、インタフェースにGUID (IID)を指定したり配列引数とその長さを示す引数との関係を指示したりできる。
IDLファイルはMIDLコンパイラで様々なプログラミング言語に向けてコンパイルされる。C/C++用にはインタフェースなどが宣言されたヘッダファイルとGUIDを定義したCのソースファイル、またCOMのメソッド呼び出しをRPC用に変換する「プロキシ」とそれを元に戻す「スタブ」のソースファイルも生成される。
MIDLはIDLファイルからタイプライブラリ(.TLBファイル)も作る。タイプライブラリに含まれているバイナリのメタデータはコンパイラや実行環境(Visual BasicやDelphi、.NETのCLRなど)で利用される。その結果タイプライブラリファイルで定義されたコクラスをその言語や環境に持ち込んで使用できるようになる。
オブジェクトフレームワークとしてのCOM
COMの基本原則はそれがオブジェクト指向の哲学に基づいているということである。オブジェクト指向開発と実装を実現するためのプラットフォームである。
COMはランタイムフレームワークであるため、タイプは明示的に識別可能でありランタイム時に指定可能である必要がある。これを実現するためにGUIDが使われる。それぞれのCOMのタイプはランタイム時(コンパイル時)に自分の識別用GUIDを指名する。
COMのタイプについての情報がコンパイル時とランタイム時の両方でアクセスできるようにする必要性から、COMはタイプライブラリを提供する。COMがオブジェクトの相互作用のための動的なフレームワークとしてその能力を発揮するのはタイプライブラリが効果的に利用されているからである。
以下の例にあるIDLのコクラス定義を考える。
coclass MyObject
{
[default] interface IMyObject;
[default, source] dispinterface _IMyObjectEvents;
};
上記のコードの断片は、IMyObjectというインタフェースを実装しなければならず、_IMyObjectEventsというイベントインタフェースを(実装ではなく)サポートしなければならない、MyObjectというCOMのクラスを宣言している。
イベントインタフェースの部分はさておき、これは概念的には次のようなC++のクラスを定義することに等しい。
class CSomeObject : public ISomeInterface
{
...
...
...
};
ISomeInterfaceはC++の純粋基底クラスに相当するところである。
COMクラスのMyObjectを振り返ってみよう。このコクラスの定義がIDLで形式化されてタイプライブラリがコンパイルされると、個別のプログラミング言語のコンパイラはこのタイプライブラリを読み込んで正しく解釈し、そして(特定のコンパイラで)何らかのコードを生成して、最終的にはMyObjectコクラスであるCOMと考えられるバイナリの実行コードが生成される。
COMコクラスの実装がビルドされシステムで利用可能になると、次にどのようにしてインスタンス化されるのかという疑問が起きる。C++のような言語で、このコクラスを利用したいコクラスから、(IID_IMyObjectというIIDで指定した)インタフェースと同じように、このコクラスのCLSIDを指定するCoCreateInstance() APIを利用できる。CoCreateInstance()の呼び出しは以下の通り。
CoCreateInstance(
CLSID_MyObject,
NULL,
CLSCTX_INPROC_SERVER,
IID_IMyObject,
(void**)&m_pIMyObject
);
これは概念的に次のようなC++のコードと等しい。
ISomeInterface* pISomeInterface = new CSomeObject();
前者はIMyObjectインタフェースを実装するオブジェクトへのポインタを取得したいというCOMサブシステムを表しており、このインタフェースのCLSID_MyObjectの特定の実装を要求している。後者はISomeInterfaceインタフェースを実装するC++のクラスのインタフェースを生成したいということを表しており、C++のクラスとしてCSomeObjectを使用している。
そしてコクラスはCOMの世界ではオブジェクト指向のクラスである。コクラスの主要機能は、(1) バイナリの性質と、(2) 結果的にプログラミング言語に非依存ということである。
レジストリ
Windowsの場合、COMのクラス、インタフェース、タイプライブラリはWindows レジストリにあるGUIDのリストであり、クラスはHKEY_CLASSES_ROOT\CLSIDに、インタフェースはHKEY_CLASSES_ROOT\interfaceにある。COMライブラリは各COMオブジェクトが正しいローカルライブラリを特定するためやリモートサービスのネットワーク上の位置を特定するためにレジストリを使用する。
参照カウンタ
最も基本的なCOMのインタフェースであるIUnknown(全てのCOMインタフェースはここから派生する)は、QueryInterfaceによる機能の確認と、AddRef()及びRelease()を入れることによるオブジェクトの寿命の管理という2つの主要な概念をサポートする。参照カウンタと機能の確認は(オブジェクトの各インタフェースに対してではなく)オブジェクトに対して適用される。従って注意深く実装する必要がある。
インタフェースへのアクセスを要求したクライアントが存在している限り個々のオブジェクトが生存していることを保障し、逆にオブジェクトを使用していたすべてのコードが終了してそのオブジェクトが不要になった時にオブジェクトを適切に処分するため、インタフェースの参照カウンタというテクニックがCOMでは要求される。COMオブジェクトは参照カウンタが0に達したときに自分のメモリを自分で解放する責任がある。
これを実装するため、COMオブジェクトは一般的に参照カウンタのための整数値を持つ。オブジェクトのインタフェースからAddRef()が呼ばれるとこの整数値が加算される。Release()が呼ばれるとこの整数値は減算される。AddRef()とRelease()はCOMオブジェクトのクライアントがそのオブジェクトの寿命に関与できる唯一の手段である。内部の整数値はCOMオブジェクトのプライベートなメンバーであり、直接的にはアクセスできない。
AddRef()の目的は、COMオブジェクトへの新しい参照が生じて、その参照が正当である限り生存し続ける必要があるということを、そのCOMオブジェクトに対して示すことである。Release()の目的は、クライアント(またはクライアントコードの一部)がもうオブジェクトを必要としなくなり、もし参照カウンタが0に達した場合にはオブジェクトが自らを破棄するであろうということを示すことである。
一部の言語(例えばVisual Basic)では自動参照カウンタが提供されるため、COMオブジェクトの開発者はソースコード中で内部参照カウンタを明示的に保持する必要がない。C言語でCOMを利用する場合、明示的な参照カウンタの操作が必要である。C++では、自分自身でそれを管理することもできるし、参照カウンタを全部管理してくれるスマートポインタを利用することも選択できる。
下記はCOMオブジェクトで適切な参照カウンタの制御を簡単にするためのAddRef()とRelease()を呼び出す際の一般的なガイドラインである。
- (戻り値またはoutパラメーターで)インタフェースの参照を返す関数(オブジェクトメソッドとグローバル関数のいずれも)は、それをリターンする前に、背後にあるオブジェクトの参照カウンタを加算しておくべきである。従って関数やメソッドの内部で、AddRef()は(リターンする)インタフェースの参照に対して呼び出される。IUnknownインタフェースのQueryInterface()メソッドはこの実例である。従って、リターンされたインタフェースのリファレンスは既に加算されており、リターンされたインタフェースの参照のAddRefを再度呼びだす必要がないということを開発者は理解していなければならない。
- インタフェースのポインタが上書きされるかスコープから外れる前にインタフェースの参照からRelease()を呼び出さなければならない。
- インタフェースの参照ポインタからコピーを作る場合、そのポインタでAddRef()を呼び出すべきである。結局この場合は、背後にあるオブジェクトのもう1つの参照を実際に作成している。
- インタフェースを参照するだけで内部リソースを割り当てる必要があることからインタフェース毎に参照をカウントするようにオブジェクトが実装されているかもしれないため、参照した特定のインタフェースに対してAddref()とRelease()を呼び出さなければならない。
- これらの関数の追加の呼び出しはケーブルを超えてリモートオブジェクトに送信されない。プロキシはリモートオブジェクトの参照を1つだけ保持し、ローカルの参照カウンタを管理する。
COMの開発を容易にして促進するため、マイクロソフトはC++の開発者のためにActive Template Library (ATL) を導入した。ATLは高レベルのCOM開発パラダイムを提供する。これはまたスマートポインタオブジェクトを提供することによってCOMクライアントのアプリケーション開発者が参照カウンタを直接管理しなくてもよいようにする。
その他にはMFC、VBScript、Visual Basic、ECMAScript (JavaScript)、Delphiなどのライブラリや言語がCOMに対応している。
インストール
COMはクラスファクトリを利用してCOMオブジェクトのインストール(つまり生成)プロセスを標準化する。COMオブジェクトを作成するため、2つの関連した項目が存在していなければならない。
- クラスID
- クラスファクトリ
各COMクラスまたはコクラスはユニークなクラスID (GUID) で関連付けられていなければならない。またクラスファクトリとも関連付けられていなければならない(レジストリを利用して行う)。クラスファクトリ自身はCOMオブジェクトである。これはIClassFactoryまたはIClassFactory2インタフェースを持つオブジェクトでなければならない(後者はライセンス管理をサポートしているインタフェースである)。これらのオブジェクトは他のオブジェクトを生成する責任がある。
クラスファクトリオブジェクトは一般的にはCOMオブジェクト自身と同じ実行ファイル内(つまりサーバーコード内)に含まれている。ターゲットオブジェクトを作成するようにクラスファクトリを呼び出すとき、このターゲットオブジェクトのクラスIDが提供されていなければならない。このようにしてクラスファクトリはどのクラスのオブジェクトをインスタンス化するのかを把握する。
単一のクラスファクトリオブジェクトは複数のクラスのオブジェクトを生成するかもしれない。すなわち、異なるクラスIDを持つ2つのオブジェクトが同じクラスファクトリオブジェクトによって生成されるかもしれない。しかしながら、これはCOMシステムにとっては曖昧なものではない。
別のオブジェクトにオブジェクト生成の責任を委ねることにより、抽象度が非常に高くなり、開発者に高い柔軟性をもたらす。例えば、シングルトンやその他のオブジェクト生成パターンの実装が容易になる。またファクトリオブジェクトはCOMオブジェクトのメモリ割り当てからアプリケーションの呼び出しを守る。
クライアントアプリケーションがクラスファクトリオブジェクトを入手できるようにする必要性から、COMサーバーはそれらを適切に公開しなければならない。クラスファクトリはサーバーコードの特質上、別の方法で公開される。DLL サーバーはDllGetClassObject()というグローバル関数をエクスポートしなければならない。EXE サーバーは実行時にCoRegisterClassObject()というWindows API関数でクラスファクトリを登録しなければならない。
下記はクラスファクトリを利用したオブジェクト生成のシーケンスの一般的な概略である。
- オブジェクトのクラスファクトリをCoGetClassObject()というAPI(Windowsの標準API)で取得する。
CoGetClassObject()を呼び出すためには作成するオブジェクトのクラスIDが提供されていなければならない。C++によるコード例を以下に示す。上記のコードはCLSID_SomeObjectというクラスIDによって識別されたCOMオブジェクトのクラスファクトリが必要であることを示している。このクラスファクトリオブジェクトはIClassFactoryインタフェースで返される。IClassFactory* pIClassFactory = NULL; CoGetClassObject ( CLSID_SomeObject, CLSCTX_ALL, NULL, IID_IClassFactory, (LPVOID*)&pIClassFactory );
- 返却されたクラスファクトリオブジェクトを使って元々意図していたCOMオブジェクトのインタフェースを生成するように要求する。C++によるコード例を以下に示す。
上記のコードは、クラスファクトリオブジェクトのCreateInstance()メソッドを使用して、IID_ISomeObjectというGUIDで識別されるインタフェースを公開しているオブジェクトを生成することを示している。このオブジェクトのISomeObjectインタフェースへのポインタが返される。クラスファクトリオブジェクトはそれ自身がCOMオブジェクトであることから、もう必要なければリリースする必要があるということにも注意してほしい(要するにこれのRelease()メソッドを呼び出さなければならない)。
ISomeObject* pISomeObject = NULL; if (pIClassFactory) { pIClassFactory->CreateInstance ( NULL, IID_ISomeObject, (LPVOID*)&pISomeObject ); pIClassFactory->Release(); pIClassFactory = NULL; }
上記はオブジェクトをインスタンス化するクラスファクトリの非常に基本的な使用例である。上位レベルのコンストラクタも利用可能であり、その一部はWindows APIを直接利用しないものもある。
例えば、アプリケーションはオブジェクトのクラスファクトリを取得せずにCOMオブジェクトを直接生成するためにCoCreateInstance() APIを利用できる。しかしながら、CoCreateInstance() APIはオブジェクトのクラスファクトリを取得するためにCoGetClassObject() APIを内部で呼び出しており、そしてCOMオブジェクトを生成するためにクラスファクトリのCreateInstance()メソッドを使用している。
CreateObject()というオブジェクトをインスタンス化するためのグローバル関数の他に、C++ではnewを利用できる。C++のコンストラクタはIClassFactory::CreateInstance()メソッドを呼び出して(CoGetClassObject() APIで)目的のオブジェクトのクラスファクトリオブジェクトを取得することを隠蔽する。
PowerBuilderのPowerScriptのように上位レベルのオブジェクトを生成するコンストラクタを提供する言語もある。しかしながらCoGetClassObject()とIClassFactoryインタフェースを使った非常に基本的なオブジェクト生成手法もまだ利用できる。
リフレクション
COMの初期の時代、オブジェクトがどのような機能を提供するのかということをクライアントから確認するためには、インスタンスを実際に生成して(IUnknownインタフェースの)QueryInterfaceメソッドを呼び出してみるしかなかった。
この検証方法は、特定の業務のために適切なコンポーネントを選択したり、オブジェクトが提供するメソッドの使用方法を開発者が理解できるようにしたりといったところで、多くのアプリケーションにとって不便であるということになった。
結果的にコンポーネントを完全に確認できるCOMタイプライブラリが導入された。タイプライブラリは、コンポーネントのCLSID、コンポーネント実装のインタフェースのIID、これらのインタフェースの各メソッドの解説といった情報を含んでいる。タイプライブラリはVisual BasicやVisual StudioのようなRAD環境でクライアントアプリケーションの開発者をアシストするために一般に利用されている。
プログラミング
COMはバイナリ標準(言語からは認知できない (language agnostic) とも言う)であり、バイナリで定義されたデータ型とインタフェースを解釈して実装する機能を使ってどんなプログラミング言語でも開発できる。
ランタイムライブラリ(厳密に言えばプログラマ)は、COMの環境に立ち入って、インスタンス化してCOMオブジェクトの参照をカウントし、バージョン情報からオブジェクトを問い合わせて、新しいバージョンのオブジェクトを利用してコーディングし、新しいバージョンが利用可能でない場合は古いバージョンでも動作するようにフォールトレトラントをコーディングするといった責任がある。
アプリケーションとネットワークの透過性
プロセス内から、またはコンピューター内のプロセスの境界を越えて、あるいはDCOMテクノロジを利用してネットワークを超えて、COMオブジェクトをインスタンス化して参照できる。
プロセスの外やリモートにあるオブジェクトはメソッドコールや戻り値をやりとりするためにマーシャリングを利用できる。
マーシャリングでは、オブジェクトや、オブジェクトを利用しているコードは見えない。
COMのスレッド
COMでは、アパートメントモデルとして知られているコンセプトによってスレッドの問題を解決している。ここで言うアパートとは、単一のスレッドまたはスレッドのグループの中にある実行コンテキストが1つまたは複数のCOMオブジェクトに関連づけられていることを指している。
アパートメントでは以下のガイドラインが関連するスレッドとオブジェクトのために示される。
- 1つのCOMオブジェクトは1つのアパートメントだけに関連づけられる。オブジェクトが実行時に生成された時点で関連づけられる。オブジェクトの初期化後は一生そのアパートメントに属する。
- COMスレッド(つまりCOMオブジェクトを生成したか、COMのメソッドコールが行われたスレッド)もまた1つのアパートメントに関連づけられる。COMオブジェクトと同様、スレッドとアパートメントの関連づけは初期化時に決定される。各COMのスレッドもまた終了するまで指定されたアパートメントに属し続ける。
- メソッドを呼び出すスレッドとオブジェクトが同じアパートメントに属す場合、COMの介入無しに直接呼び出される。
- メソッドを呼び出すスレッドとオブジェクトが異なるアパートメントに属す場合、そのメソッド呼び出しははマーシャリングを利用して行われる。これにはプロキシとスタブが利用される。
COMの世界では、シングルスレッドアパートメント (STA)、マルチスレッドアパートメント (MTA)、中立アパートメント (NA) の3つのアパートメントモデルがある。各アパートメントは、オブジェクトの内部状態が複数のスレッドを超えて同期されるかどうかという1つのメカニズムを表している。
シングルスレッドアパートメントモデルは非常に一般的に利用されているモデルである。ここではCOMオブジェクトはデスクトップアプリケーションのユーザーインタフェースに似た立場にある。STAモデルでは、単一のスレッドがオブジェクトのメソッドを動かすことに専念している。つまり常に単一のスレッドでオブジェクトのメソッドが実行される。このようなアパートメントでは、アパートメントの外にあるスレッドからのメソッドコールはマーシャリングされ、(標準のWindowsのメッセージキューを利用して)システムによって自動的にキューに入れられる。これによりその呼び出しが完了してから各オブジェクトのメソッド呼び出しが実行されるようになり、レースコンディションや同期性の欠如といった心配がない。プロセスの中で最初に作られたSTAは特にメインSTAと呼ばれ、マルチスレッドを全く考慮していないCOMオブジェクトはメインSTAだけで動作させられる。
COMオブジェクトのメソッドの同期を自分で取りたい場合、メソッドを呼び出したスレッドと同一のスレッドでメソッドを処理するようにできる。これをマルチプルスレッドアパートメントと呼ぶ。ただし、STAスレッドからのMTAオブジェクトのメソッド呼び出しはマーシャリングされる。プロセスは複数のCOMオブジェクトで構成でき、その一部をSTAにしてそれ以外はMTAを利用するというようにできる。
COM+で導入されたスレッド中立アパートメントは、オブジェクトのメソッド呼び出しを管理する必要がない場合に用いられる。唯一の条件はオブジェクトの全てのメソッドがリエントラント(再入可能)でなければならないということである。中立アパートメントに属すオブジェクトのメソッドは、STAスレッド・MTAスレッド、どちらからでもマーシャリングなしに直接呼び出せる。
関連項目
参考文献
外部リンク
- COM テクノロジ
- COM (Component Object Model) - MSDN Library テンプレート:En icon