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+プログラミングモデルでは、遅延バインディングされたイベント、および、パブリッシャー/サブスクライバとイベントシステムの間のメソッド呼び出しがサポートされる。

.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はまだ様々なテクノロジーで重要なソフトウェアの基盤として生き延びている。例えばマルチメディアAPIとして広く普及しているDirectXのAPIはCOMをベースにしている。2015年にWindows 10とともにリリースされたDirectX 12 (Direct3D 12) も、依然としてCOMベースであり、C++を第1級言語としている。2018年時点では、マイクロソフトはCOMのサポートやCOMそのものをやめてしまう計画を持っていない。
COMはまた、コンパイル時点でAPIの情報を必要としないスクリプトからCOMオブジェクトを呼び出すためのインタフェース（動的ダックタイピング）を提供するため、Microsoft OfficeやInternet Explorerのようなネイティブアプリケーションのスクリプト制御のための技術として理想的でもある。COMにおけるあらゆる要素を一意に識別するために開発されたGUIDシステムはマイクロソフトによるUUIDの実装であり、COM以外でもユニークなIDが必要とされる場合に広く利用されている。
トランザクションやコンポーネントのキューといったようなCOM+が提供する複数のサービスはエンタープライズな.NETアプリケーションでもまだ重要である。
制約はあるものの、.NETはCOMに対して相互運用性のサポートがある。.NETはランタイム呼び出し可能ラッパー (RCW) を実装することでCOMオブジェクトを利用できる。COMクライアントはCOM呼び出し可能ラッパー (CCW) によって、特定の制約に従った.NETオブジェクトを利用できる。COMと.NETの両方の側からは相手側のオブジェクトがネイティブなオブジェクトに見える。
.NET Remotingでは、オブジェクトがプロセスやマシンの境界を越えてリファレンスや値を透過的にマーシャリングできるようにして、COMが持つ数多くのリモート実行の欠点を解決する。

Windowsランタイム

Windows 8およびWindows RTにて、新たなアプリケーションの開発・実行基盤としてWindowsランタイム (WinRT) が導入された。WinRTはCOMを拡張したネイティブ技術であるが、Windowsメタデータ (WinMD) および言語プロジェクション (language projection) と呼ばれる技術により、.NET言語やJavaScriptなどからも透過的に利用可能である。

技術的詳細

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コンポーネントに関する情報の記述手段として、MIDL（マイクロソフトインタフェース定義言語、IDLも参照）やタイプライブラリがある。MIDLはテキストファイルで主として開発時に用いられる。タイプライブラリは型に関する言語非依存の情報を提供するバイナリファイルであり、開発時だけでなく実行時に参照されることも想定されている。
COMコンポーネントの開発では、普通IDLで型を定義することから始める。IDLファイルではオブジェクト指向的なクラスやインタフェース、構造体、列挙体などのユーザ定義型をプログラミング言語に依存せず記述できる。このIDLではC/C++に似たキーワードと、それに追加してインタフェースを定義する「interface」、コクラスを定義する「coclass」、それらの集合を現す「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のコクラス定義を考える。
interface ISomeInterface : IUnknown
{
    HRESULT DoSomething();
};

coclass SomeClass
{
    [default] interface ISomeInterface;
};
上記のコード断片は、ISomeInterfaceというインターフェイスを実装する、SomeClassという名前のCOMクラスを宣言している。
これは概念的には次のようなC++のクラスを定義することに等しい。
struct ISomeInterface : public IUnknown{
    virtual HRESULT DoSomething() = 0;};class SomeClass : public ISomeInterface{
    ...};
ISomeInterfaceはC++の純粋仮想基底クラスに相当するものである。
COMのインターフェイスおよびコクラスを含むIDLファイルはタイプライブラリファイルにコンパイルされる。タイプライブラリはCOMクライアントのコンパイル時あるいは実行時に解釈され、オブジェクトがどのインターフェイスをサポートするかを決定し、オブジェクトのインターフェイスメソッドを呼び出すために利用される。
C++では、Windows API関数のひとつであるCoCreateInstance()を使用してCOMオブジェクトをインスタンス化することができる。引数にはクラスID (CLSID) およびインターフェイスID (IID) を指定する。
ISomeInterface* pSomeInterface = NULL;HRESULT hr = CoCreateInstance(
    CLSID_SomeClass,
    NULL,
    CLSCTX_INPROC_SERVER,
    IID_ISomeInterface,
    reinterpret_cast<void**>(&pSomeInterface));
この例では、ISomeInterfaceインターフェイスを実装するSomeClassオブジェクトへのポインタを取得するためにCOMサブシステムが使われる。これは概念的に次のようなC++のコードと等しい。
ISomeInterface* pSomeInterface = new SomeClass();
そしてコクラスはCOMの世界ではオブジェクト指向のクラスである。コクラスの主要機能は、(1) バイナリの性質と、(2) 結果的にプログラミング言語に非依存ということである。

レジストリ

Windowsの場合、COMのクラス、インターフェイス、タイプライブラリはWindowsレジストリにあるGUIDのリストであり、クラスは"HKEY_CLASSES_ROOT\CLSID"に、インターフェイスは"HKEY_CLASSES_ROOT\interface"にある。COMライブラリは各COMオブジェクトが正しいローカルライブラリを特定するためやリモートサービスのネットワーク上の位置を特定するためにレジストリを使用する。DLLサーバー型のCOMコンポーネントはregsvr32というコマンドラインプログラムを使用してレジストリ登録および登録解除を行なうが、EXEサーバー型のCOMコンポーネントは自身に/RegServerあるいは/UnRegServerスイッチを付けて起動し、レジストリ登録および登録解除を行なう。
システムレジストリを使用する性質上、レジストリ登録されたCOMコンポーネントはコンピュータあるいはネットワーク上のすべてのCOMクライアントから共有される。COMコンポーネントをバージョンアップする際にインターフェイスを変更・追加する場合、旧バージョンをレジストリ登録解除したうえで新バージョンをレジストリ登録し直すか、異なる名前およびGUIDを持つインターフェイスを改めて定義した別のコンポーネントをレジストリ登録する必要がある。前者はバージョンアップの際にCOMクライアントの更新は不要だが、破壊的な仕様変更を避けるなど後方互換性に配慮した設計が求められる。後者は新しいバージョンに対応するCOMクライアントを再作成する必要があるが、複数のバージョンを共存させることができ、バージョン間の互換性を考慮する必要はない。たとえばDirect3Dのバージョン7/8/9/10/11/12は、すべて異なるインターフェイスを持つ独立したCOMコンポーネントであり、同一コンピュータ上に共存できる。典型的にいって、COMにおける最良のアプローチは、機能を拡張するためには新しいインターフェイスを定義することである。なおCOMコンポーネントのマイナーバージョンアップの際は、通例既存のインターフェイスを継承して新たなインターフェイスを定義する手法が採られる。たとえばDirect3D 10.1あるいは11.1/11.2/11.3における各インターフェイスは、Direct3D 10.0あるいは11.0のインターフェイスからそれぞれ派生した新しいインターフェイスを定義する方法で機能拡張が行なわれている。
Windows XP以降では、「分離アプリケーションとSide-by-Sideアセンブリ」と呼ばれる仕組みを使い、レジストリではなくマニフェストファイルを使用してコンポーネント依存関係を解決することも可能である。この仕組みを用いることで、COMコンポーネントをシステム全体で共有せず、アプリケーションごとにプライベートアセンブリとして配布・運用することも可能となる。

リフレクション

COMの初期の時代、オブジェクトがどのような機能を提供するのかということをクライアントから確認するためには、インスタンスを実際に生成して（IUnknownインタフェースの）QueryInterfaceメソッドを呼び出してみるしかなかった。
この検証方法は、特定の業務のために適切なコンポーネントを選択したり、オブジェクトが提供するメソッドの使用方法を開発者が理解できるようにしたりといったところで、多くのアプリケーションにとって不便であるということになった。
結果的にコンポーネントを完全に確認できるCOMタイプライブラリが導入された。タイプライブラリは、コンポーネントのCLSID、コンポーネント実装のインタフェースのIID、これらのインタフェースの各メソッドの解説といった情報を含んでいる。タイプライブラリは一般にVisual BasicやVisual StudioのようなRAD環境でクライアントアプリケーションの開発者を支援するために利用されている。

プログラミング

COMは「言語にとらわれない」(language-agnostic) あるいは「言語非依存」(language-independent) のバイナリ標準であり、タイプライブラリすなわちバイナリで定義されたデータ型とインターフェイスを解釈する機能を実装できさえすれば、いかなるプログラミング言語でも開発できる。
ランタイムライブラリ（厳密に言えばプログラマ）は、COMの環境に立ち入って、インスタンス化してCOMオブジェクトの参照をカウントし、バージョン情報からオブジェクトを問い合わせて、新しいバージョンのオブジェクトを利用してコーディングし、新しいバージョンが利用可能でない場合は古いバージョンでも動作するようにフォールトレトラントをコーディングするといった責任がある。

アプリケーションとネットワークの透過性

プロセス内から、またはコンピューター内のプロセスの境界を越えて、あるいはDCOMテクノロジを利用してネットワークを超えて、COMオブジェクトをインスタンス化して参照できる。
プロセスの外やリモートにあるオブジェクトはメソッドコールや戻り値をやりとりするためにマーシャリングを利用できる。
マーシャリングでは、オブジェクトや、オブジェクトを利用しているコードは見えない。