Component Object Model
単一のクラスファクトリオブジェクトは複数のクラスのオブジェクトを生成するかもしれない。すなわち、異なるクラスIDを持つ2つのオブジェクトが同じクラスファクトリオブジェクトによって生成されるかもしれない。しかしながら、これはCOMシステムにとっては曖昧なものではない。
別のオブジェクトにオブジェクト生成の責任を委ねることにより、抽象度が非常に高くなり、開発者に高い柔軟性をもたらす。例えば、シングルトンやその他のオブジェクト生成パターンの実装が容易になる。またファクトリオブジェクトはCOMオブジェクトのメモリ割り当てからアプリケーションの呼び出しを守る。
クライアントアプリケーションがクラスファクトリオブジェクトを入手できるようにする必要性から、COMサーバーはそれらを適切に公開しなければならない。クラスファクトリはサーバーコードの特質上、別の方法で公開される。DLL サーバーはDllGetClassObject()というグローバル関数をエクスポートしなければならない。EXEサーバーは実行時にCoRegisterClassObject()というWindows API関数でクラスファクトリを登録しなければならない。
下記はクラスファクトリを利用したオブジェクト生成のシーケンスの一般的な概略である。
オブジェクトのクラスファクトリを、Windows API関数CoGetClassObject()で取得する。
CoGetClassObject()を呼び出すためには作成するオブジェクトのクラスIDが提供されていなければならない。C++によるコード例を以下に示す。
上記のコードはCLSID_SomeObjectというクラスIDによって識別されたCOMオブジェクトのクラスファクトリが必要であることを示している。このクラスファクトリオブジェクトはIClassFactoryインタフェースで返される。
上記はオブジェクトをインスタンス化するクラスファクトリの非常に基本的な使用例である。上位レベルのコンストラクタも利用可能であり、その一部は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) あるいは「言語非依存」(language-independent) のバイナリ標準であり、タイプライブラリすなわちバイナリで定義されたデータ型とインターフェイスを解釈する機能を実装できさえすれば、いかなるプログラミング言語でも開発できる。
ランタイムライブラリ(厳密に言えばプログラマ)は、COMの環境に立ち入って、インスタンス化してCOMオブジェクトの参照をカウントし、バージョン情報からオブジェクトを問い合わせて、新しいバージョンのオブジェクトを利用してコーディングし、新しいバージョンが利用可能でない場合は古いバージョンでも動作するようにフォールトトレラントをコーディングするといった責任がある。
プロセス内から、またはコンピュータ内のプロセスの境界を越えて、あるいはDCOMテクノロジを利用してネットワークを超えて、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スレッド、どちらからでもマーシャリングなしに直接呼び出せる。