この特集のトップページへ
Chapter 2:Component Object Model

head2.gif 2.3.2 オブジェクトを実体化するときの処理
 NewキーワードやCreateObject関数を使ってCOMオブジェクトを実体化する場合,その実体化は,COMライブラリによって処理される。COMライブラリはWindowsに含まれるサービスで,システムに登録されたCOMサーバーを呼び出してCOMオブジェクトの実体化などの処理を担う部分である。COMライブラリは深い部分では極めて高度な操作をしているので,ここではその処理の概要だけを説明する。

attenticon.gif COM+の場合には,以下に説明するものと若干処理内容が異なる。COM+におけるCOMオブジェクトの実体化処理については,Chapter 4で改めて解説する。

 COMライブラリのなかには,SCM(Service Control Manager)というサービスがあり,COMオブジェクトの実体化要求は,ここに届けられる。実体化要求には,「どのCOMオブジェクトを実体化し,どのインタフェースを取得するのか」という情報が含まれる。

 COMコンポーネントには,COMコンポーネントを識別するためのクラスID(CLSID)という識別番号が割り当てられていて,その番号でどのCOMコンポーネントなのかを区別する。クラスIDは128ビットの数値列で,COMコンポーネントの開発時にランダムに設定される。このランダム値は,ネットワークカードのMACアドレス(ネットワークカードごとにネットワーク機器メーカーが割り当てた12バイトの数値。市場に出回っている各ネットワークカードで,この値が重複することはない)や構築時刻などから構成されるもので,発生するごとに必ず違った値となる。そのため,COMコンポーネントの種類はクラスIDによって一意に決定される。

 また,COMコンポーネント(およびそれを実体化したCOMオブジェクト)に備わっているそれぞれのインタフェースには,インタフェースID(IID)と呼ばれる識別番号が割り当てられている。この識別番号もクラスIDと同じく128ビットのランダム値であり,インタフェースを一意に決定するものである。

 Newキーワードでオブジェクトを実体化するときには,Visual Basicの参照設定によって取り込まれたクラスIDがSCMに渡される。

 CreateObject関数でオブジェクトを実体化するときには,参照設定によって得たクラスIDは使われない。代わりに,CreateObject関数の引数で指定された名前が使われる。たとえば,ADOコンポーネントのConnectionオブジェクトを実体化する場合には,次のように,引数“ADODB.Connection”を伴って呼び出すことになっている。

   Dim objADOConnection As ADODB.Connection
   Set objADOConnection = CreateObject("ADODB.Connection")

 この引数“ADODB.Connection”もCOMコンポーネントを識別するための名前である。この名前のことをプログラムID(ProgID)と呼ぶ。

 残念ながら,オブジェクトを実体化するときには,SCMに対してプログラムIDではなくクラスIDを与えなければならないことになっている。そのため,CreateObject関数でオブジェクトを実体化する場合,Visual Basicは事前にプログラムIDをクラスIDへと変換する。プログラムIDからクラスIDに変換する処理は,COMライブラリに含まれていて,そのルーチンを呼び出すとプログラムIDからクラスIDへと変換される。

 COMライブラリに含まれるプログラムIDの変換ルーチンは,レジストリHKEY_CLASSES_ROOTの配下に存在するプログラムIDと一致するレジストリキーを見つけることによって,プログラムIDをクラスIDへと変換している。

 たとえば,HKEY_CLASSES_ROOT\ADODB.Connectionに,Fig.2-9のような記述があったとする。このとき,ADODB.ConnectionというプログラムIDに対応するクラスIDは{00000514-0000-0010-8000-00AA006D2EA4}であるとわかる。当然ながら,CreateObject関数で実体化する場合,引数に指定されたプログラムIDがレジストリHKEY_CLASSES_ROOTの直下に存在しなければ,その操作は失敗することになる。

Fig.2-9 プログラムID

fig2-09.gif


プログラムIDは,ランダム値ではなくCOMコンポーネントの開発者が任意の文字列として設定するため,クラスIDと違い,COMコンポーネントごとに固有の値ではないことがある。すなわち,別のCOMコンポーネントに同じプログラムIDが割り当てられる可能性があるということだ。たとえば,AというコンピュータにはXという機能をもつ“foo.bar”というプログラムIDのCOMコンポーネントがインストールされていて,BというコンピュータにはYというXとは別の機能をもつ“foo.bar”というプログラムIDのCOMコンポーネントがインストールされている,という可能性もある。この場合,Xという機能を想定したCOMクライアントは,Aというコンピュータでは正しく動作するが,Bというコンピュータでは正しく動作しなくなる。そういった事態を避けたければ,オブジェクトの実体化にCreateObject関数ではなく,Newキーワードを使えばよい。NewキーワードはプログラムIDではなくクラスIDでCOMコンポーネントを識別するため,機能が違うCOMコンポーネントを誤って使ってしまうことはなくなる。ただし,これはNewキーワードを使ったほうが優れているということではない。たとえば,古いバージョンのCOMコンポーネントと新しいバージョンのCOMコンポーネントでは,クラスIDが違うこともある。その場合,同じプログラムIDを割り当てている場合,CreateObject関数を利用すると古いバージョンのCOMコンポーネントでも新しいバージョンのCOMコンポーネントでも動作するが,Newキーワードを利用すると,参照設定したバージョンのCOMコンポーネントでしか動作しない。

 さて,SCMは,クラスIDを受け取ると,そのCOMコンポーネントがどのCOMサーバー(dllファイル,ocxファイル,exeファイルなどの実行ファイル)によって提供されるのかを調べる。このときSCMは,レジストリHKEY_CLASSES_ROOT\CLSIDの配下を調べる。ここには,クラスIDをキーとした項目がずらりと並んでおり,どの実行ファイルで提供されるのか,COMサーバーのプログラム名が記載されている(Fig.2-10)。

Fig.2-10 HKEY_CLASSES_ROOT\CLSID

fig2-10.gif

 実体化したいCOMオブジェクトを提供するCOMコンポーネントのクラスIDがレジストリのHKEY_CLASSES_ROOT\CLSIDの直下に登録されていれば,そのCOMサーバーをメモリに読み込む。そして,COMサーバーに対してCOMオブジェクトの実体化を依頼し,COMオブジェクトが実体化される。

 COMオブジェクトが実体化されたら,そのCOMオブジェクトで提供されているインタフェースのうち,指定されたものがSCMを通じてCOMクライアントに戻される。

 Visual Basicの場合,どのインタフェースが戻されるのかは,変数の宣言による。たとえば,次のように変数をADODB.Connectionとして宣言した場合には,オブジェクトが備えているADODB.Connectionというインタフェースが戻される(実際には,このような「名前」で判断されるのではなく,参照設定によってADODB.Connectionという名前に結び付けられたインタフェースIDの値と一致するものが返されるのだが,詳細は割愛する)。

   Dim objADOConnection As ADODB.Connection

 一方,次のようにObject型として宣言した場合,もしくは,何も宣言しなかった場合(これは,Dim obj As Variantと宣言するのと等価である)には,Dispatchという特別なインタフェースが戻される。

   Dim obj As Object

 Dispatchインタフェースは,簡単にいうと,すべてのオブジェクトを汎用的に扱うインタフェースであり,COMオブジェクトのメソッドを呼び出したりプロパティを参照したりするたびに,そのCOMオブジェクトが本当にそのメソッドやプロパティを備えているかどうかを調査する。つまり,それだけ速度が遅くなる。よって,パフォーマンスを重視するのであれば,COMオブジェクトを格納する変数は変数の宣言を省いたりObject型と宣言したりせず,参照設定に基づいたCOMオブジェクト本来の型として宣言すべきである。

 以上がCOMオブジェクトを使うまでの一連の処理である。これらをまとめて図示するとFig.2-11のようになる。

Fig.2-11 COMオブジェクトを使うまでの流れ

fig2-11.gif

 少々厄介であるが,重要な点は,COMの各種情報はレジストリに保存されているという点にある。これからVisual BasicでCOMを作る方法を説明するわけだが,作ったCOMを使うためには,レジストリにその情報を登録する必要があるという点に注意したい。単純にファイルをコピーしただけでは,そのCOMを利用することはできない。

prevpg.gif Chapter 2 8/15 nextpg.gif