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

head2.gif 2.4.1 Visual BasicによるCOMコンポーネントの実装
 Visual BasicでCOMコンポーネントを実装する場合には,次のようにする。

1) プロジェクトを新規作成する

 [ファイル]−[新規プロジェクト]を選択し,新規プロジェクトを作る。表示されるダイアログボックスでは,作成するアプリケーションの種類として“ActiveX DLL”を選択する(Fig.2-13)。

Fig.2-13 新規プロジェクトの作成

fig2-13.gif

2) プロジェクトのプロパティを設定する

 ActiveX DLLで新規プロジェクトを作成すると,Project1というプロジェクトが生成され,そのなかにClass1というクラスモジュールができる。これは,プロジェクトエクスプローラで確認することができる(Fig.2-14)。

Fig.2-14 プロジェクトエクスプローラ

fig2-14.gif

 COMコンポーネントを開発する場合には,プロジェクトのプロパティとクラスモジュールのプロパティを設定する必要がある。Visual BasicでCOMコンポーネントを開発する場合,プロジェクトがCOMサーバーに,クラスモジュールがCOMコンポーネントに,それぞれ対応する。デフォルトの状態ではクラスモジュールが1つしかないが,メニューから[プロジェクト]−[クラスモジュールの追加]を選択し,クラスモジュールを追加してゆくことで,1つのCOMサーバーに複数のCOMコンポーネントを実装することができる。

 ではまず,プロジェクトのプロパティの設定から説明する。

 [プロジェクト]メニューの[Project1のプロパティ]を選択すると,プロジェクトのプロパティダイアログボックスが開き,プロジェクトのプロパティを設定することができる。

 プロジェクトのプロパティダイアログボックスには,[全般][実行可能ファイルの作成][コンパイル][コンポーネント][デバッグ]という5つのページがあるが,設定しておくべきなのは,[全般]ページと[コンポーネント]ページの2つである。それ以外のページの設定は,デフォルトのままでかまわない。

○[全般]ページの設定

 [全般]ページでは,プロジェクト名や作成するActiveXコントロールの種類などを設定する(Fig.2-15

Fig.2-15 [全般]ページの設定

fig2-15.gif

 このページで重要なのは,次の5つである。

・プロジェクト名
 プロジェクトの名前を指定する。この名前は,作成したCOMコンポーネントのプログラムIDの一部になる。たとえば,プロジェクト名に“Test”と指定したとすると,プログラムIDのピリオドからまえが“Test”になり,“Test.〜”という名前のプログラムIDになる(〜の部分は,次に説明するクラスモジュールのクラス名になる)。
 ここで作成するCOMコンポーネントは,プログラムIDに“Samplecom.test”という名前を付けたいので,プロジェクト名は“Samplecom”とする。
・プロジェクトの説明
 スタンドアロンアプリケーションを構築する場合,プロジェクトの説明は単純なコメントでしかないが,COMコンポーネントを作るときには,プロジェクトの説明に入力した文字がタイプライブラリに書き込まれる。わかりやすくいえば,そのCOMコンポーネントを利用したいCOMクライアントを開発するため,Visual Basicで参照設定するとき,[参照設定]ダイアログボックスには説明の文字列として,ここで入力した値が表示される。プロジェクトの説明が記入されていない場合,[参照設定]ダイアログボックスにはプロジェクト名が表示される。
・対話型インタフェースの抑制
 [対話型インタフェースの抑制]にチェックを付けると,このプログラムでフォームを使ったり,MsgBox関数でメッセージボックスを画面上に表示したりすることができなくなる。COMでビジネスロジックを構築する場合には,そのCOMコンポーネントはサーバーで動作する。しかし,動作時にサーバーに誰もログインしていない状態は十分考えられるので,ユーザーインタフェースを表示すべきではない(たとえば,MsgBox関数でメッセージを表示した場合,誰がサーバー上で表示されたメッセージボックスの[OK]ボタンを押すのか考えてほしい)。そういった意味で,COMでビジネスロジックを構築する場合には,この項目にチェックを付けることが望ましい。
 なお,この項目にチェックを付けた場合,MsgBox関数の出力は画面ではなく,イベントログに出力されるようになる。
・ActiveXコントロールのアップグレード
 プロジェクトをロードしたときに,そのプロジェクトが使っているActiveXコントロールの上位バージョンがシステムにインストールされていた場合,新しいものに置き換えるかどうかを設定する。このオプションの設定が必要なのは,COMを使う側,すなわちCOMクライアント側であり,COMサーバー側には関係ない。通常,このオプションにはチェックを付けておく。
・スレッドモデル
 COMコンポーネントのスレッドモデルを設定する。スレッドモデルとは,マルチスレッド動作するときに,どのような方法で同期をとるのかという種別のことである。
 ActiveX DLLの場合には,「アパートメントスレッド」か「シングルスレッド」のいずれかしか選択できない。詳細は省くが,MTSやCOM+では,シングルスレッドのCOMは管理できないことになっている。よって,Visual BasicでCOMコンポーネントを構築する場合には,おのずとアパートメントスレッドを選択することになる。

○[コンポーネント]ページの設定

 [コンポーネント]ページでは,作成するCOMコンポーネントの互換性などを設定する(Fig.2-16)。

Fig.2-16 [コンポーネント]ページ

fig2-16.gif

・リモートサーバー
 作成するCOMコンポーネントを別のコンピュータから呼び出せるようにするかどうかを設定する。Visual Basic Enterprise Editionでのみ指定可能である。本連載では,COMコンポーネントの通信に,Visual Basicの機能ではなく,MTSおよびCOM+の機能を使うので,この項目にはチェックを付けない。
・バイナリ間の互換性
 バイナリ間の互換性のオプションは,Visual BasicでCOMコンポーネントを実装する場合,非常に重要である。特に,いったん実装したCOMコンポーネントをあとから拡張するのであれば,設定したオプションによっては期待どおりに動作しなくなることがある。
 バイナリ間の互換性では,「互換なし」「プロジェクト互換」「バイナリ互換」のうち,いずれかを選択できる。

(a) 互換なし
 新しく作成したCOMコンポーネントは,古いバージョンと互換性がないことを示す。たとえば,実装したメソッドやプロパティの一部を削除したり,メソッドの引数を変更したりした場合には,このオプションを選択する。
 すると,コンパイル後のCOMコンポーネントには,新しいクラスIDとインタフェースIDが割り振られる。先に説明したように,COMコンポーネントは,クラスIDとインタフェースIDによって,どのようなCOMコンポーネントであるのかが区別される。よって,互換なしのオプションでCOMコンポーネントをコンパイルすると,古いバージョンのCOMコンポーネントと新しくコンパイルしたCOMコンポーネントは別物として扱われる。
 基本的に,互換なしに設定するのは,何らかの理由で同じプロジェクトファイルからまったく別のCOMコンポーネントを開発したいときだけである。
 ところで,COMコンポーネントはプログラムIDによっても識別される。Visual BasicでCOMコンポーネントを実装する場合,プログラムIDを構成するのはプロジェクト名である。よって,互換なしでコンパイルする場合には,COMコンポーネントを区別するために,プロジェクト名も変更しておいたほうがよい。
(b) プロジェクト互換
 プロジェクトファイルに含まれるCOMコンポーネントが互換性を保つようになる。具体的には,プログラムを修正した場合,旧バージョンと互換性がある部分は古いバージョンのままのクラスIDやインタフェースIDが用いられ,新規に追加したメソッドやプロパティ,引数が変更されたメソッドなど互換性のない部分は新しいクラスIDやインタフェースIDが割り当てられる。一般的にVisual BasicでCOMコンポーネントを開発する場合には,プロジェクト互換を選択する。
(c) バイナリ互換
 指定されたCOMサーバーの実行ファイル(dllファイル,ocxファイル,exeファイル)との互換性を調べてコンパイルする。互換性がない場合には,警告のダイアログメッセージが表示され,プログラムを修正するか,もしくは新しいクラスIDやインタフェースIDを割り当てるのかの選択を求められる。
 このオプションは,一般にCOMサーバーをクライアントなどに配布したのち,それをバージョンアップするときに用いられる。

 少々難しいと思われるかもしれないが,通常はプロジェクト互換を選択する(これはデフォルト値である)。
 問題となるのは,すでにCOMクライアントで使われているCOMコンポーネントをアップグレードしたい場合である。
 COMコンポーネントのメソッドやプロパティ,メソッドの引数がまったく変わらないのであれば,問題ない。しかし,あるメソッドを削除したり,メソッドの引数の数を変更したりした場合には,アップグレードしたCOMコンポーネントをCOMクライアントから呼び出したときに問題が生じる。
 たとえば,あるメソッドを削除した場合,メソッドが削除されたことを知らずにCOMクライアントから呼び出せば,その呼び出しは失敗するだろう(失敗するだけならばよいのだが,メソッドを削除した場合には,内部構成上,別のメソッドが呼ばれてしまうか,さもなくば一般保護例外が発生する)。
 そのような場合には,互換なしにするか,バイナリ互換にして新しいコンポーネントとして作成する必要がある。

3) クラスモジュールのプロパティを設定する

 プロジェクトのプロパティを設定したならば,次のクラスモジュールのプロパティを設定する(Fig.2-17)。

Fig.2-17 クラスモジュールのプロパティ

fig2-17.gif

 クラスモジュールには6つの設定があるが,設定しておきたいのは「(オブジェクト名)」である。
 COMコンポーネントのプログラムIDは,“プロジェクト名.クラスモジュールのオブジェクト名”となる。たとえば,プログラムIDとして,“プロジェクト名.comtest”という値を付けたいのであれば,クラスモジュールのオブジェクト名を“comtest”に設定する。

4) プロパティとメソッドを追加する

 次にクラスモジュールにメソッドおよびプロパティを実装して,COMコンポーネントを構築してゆく。

 といっても,さほど難しいことはない。通常のVisual Basicのプログラムと同様に,メソッドであればSubプロシージャもしくはFunctionプロシージャを,プロパティであればその変数のPublic宣言を,クラスモジュールのコードウィンドウに追加してゆくだけである。

 プロシージャの追加には,メニューから[ツール]−[プロシージャの追加]を選択してもよいし,そのままコードウィンドウに書き加えてもかまわない(Fig.2-18)。

Fig.2-18 プロシージャの追加

fig2-18.gif

 メソッドを追加する場合,種類は[Subプロシージャ]もしくは[Functionプロシージャ]とし,適用範囲は[Publicプロシージャ]とする。

 結局のところは,List 2-2に示したプログラムをそのまま入力すればよいことになる(Fig.2-19)。

Fig.2-19 COMコンポーネントの実装

fig2-19.gif

 このように,COMコンポーネントのメソッドやプロパティを構築してゆくのは簡単だが,プロパティに関しては,単純にPublic宣言するだけでは好ましくないことがある。たとえば,読み取り専用のプロパティを作りたい場合や,値の代入時に不正な値が代入されてしまうことを防ぐためにエラーチェックしたいような場合など,COMクライアントから直接変数に値を代入されては困るケースが考えられる。

 そのような場合には,変数をPublic宣言ではなくPrivate宣言にし,COMクライアントからアクセスできないようにしておき,値を設定するための関数,値を取得するための関数を,それぞれ作ることで対処する。

 たとえば,List 2-2Fig.2-19)では変数ValuePublic宣言しているが,ここでは代わりに値を設定するための関数および値を取得するための関数で実装してみることにする。

 まず,変数Valueの名前をほかの名前にし(ここでは仮にinValという名前にする),Public宣言ではなくPrivate宣言にしてプログラムを書き直す。変数の名前を変更するのは,あとでValueというプロパティを(Functionプロシージャとして)作るからである。名前が重複すると紛らわしいというだけの処置であり,それ以外の意味はない。できあがったプログラムは,List 2-3のようになる。

 次にValueプロパティを実装する。Visual Basicでは,メニューから[ツール]−[プロシージャの追加]を選択し,[種類]で[Propertyプロシージャ]を選ぶと,プロパティ用のプロシージャを作成することができる。ここでは,そのようにしてValueプロシージャを追加してみる(Fig.2-20)。

Fig.2-20 プロパティ用のプロシージャの追加

fig2-20.gif

 Fig.2-20のようにして,Valueという名前でPropertyプロシージャを追加すると,次の2つのプロシージャが生成される(Fig.2-21)。

   Public Property Get Value() As Variant
   Public Property Let Value(ByVal vNewValue As Variant)
Fig.2-21 作成された2つのプロシージャ

fig2-21.gif

 Get Valueというのが,値が参照されたときに呼び出されるプロシージャで,Let Valueというのが,値が設定(代入)されたときに呼び出されるプロシージャである。あるプロパティを読み取り専用にしたければ,Let Valueのほうのプロシージャを削除してしまえばよい(もちろん,あまり一般的ではないが,Get Valueのほうを削除すれば,書き込みのみで読み取り不可にすることもできる)。
 ここでは,「変数inValの内容を返す」という実装をGet Valueプロシージャに,「変数inValの値を設定する」という実装をLet Valueプロシージャに,それぞれ加える。そのようなプログラムは,List 2-4のようになる。

 なお,List 2-4の7行目と12行目を見るとわかるが,Get ValueプロシージャとLet Valueプロシージャの扱う型は,Propertyプロシージャを追加(Fig.2-20Fig.2-21参照)するときのデフォルトの型であるVariant型ではなく,Long型に変更してある(Variant型のままでもかまわないが,扱っているinVal変数はLong型なので,それに合わせたほうが都合がよい)。

 このように,プロパティを実装する場合には,その変数をPublic宣言してしまう方法とPropertyプロシージャを使う方法の2通りがある。しかし,一般的には,Propertyプロシージャを使うほうが好まれる。なぜなら,変数を公開するということは,プログラムを部品化するというオブジェクト指向的な考えに反するためである。

 また,変数を公開してしまうと,COMクライアントによって不正な値を代入される可能性も考えられる。たとえば,ある変数Aは1から4までの値しかとってはいけないのに,COMクライアントが5という値を代入してきた場合はどうなるだろうか。変数を公開した場合にはそれを防ぐことはできないが,Propertyプロシージャであれば,代入するときにIf文などで判断し,不正な値を代入しないようにすることができる(COMコンポーネントからエラーを返すには,実行時エラーを発生するためのErrオブジェクトのRaiseメソッドを使う。この方法については,「2.4.3 COMのエラー処理」で解説する)。


MTSやCOM+で管理されることを想定したCOMコンポーネントを作成するときには,変数をPublic宣言する方法は用いられない。なぜなら,MTSやCOM+は実体化したCOMオブジェクトを効率よく破棄する機構を備えており,COMクライアントから見ると,COMオブジェクトが意図しないときに破棄され,保持している変数の内容が失われてしまうことがあるためである。その点についての詳細は,Chapter 4で解説する。

 ところで,クラスモジュールには開発者が任意のメソッドやプロパティを追加できるわけだが,初期化の処理と終了時の処理を実行する2つの特別なプロシージャがある。初期化の処理を実行するプロシージャの名前は“Class_Initialize”,終了時の処理を実行するプロシージャの名前は“Class_Terminate”である。Class_InitializeプロシージャはCOMオブジェクトが実体化されたときに呼び出されるプロシージャであり,このプロシージャにはCOMオブジェクトの初期化処理を実装することができる。たとえば,List 2-4でいうと,値を保持する変数inValは初期化されていないので,COMクライアントがValueプロパティに何か値を設定しない限り値は不定である。もしCOMオブジェクトが実体化されたときにinVal変数を0に初期化したいのであれば,Class_InitializeプロシージャをList 2-5のように用意すればよい。

 ここではClass_Initializeプロシージャの使い方しか示さないが,COMオブジェクトが実行時に別のCOMオブジェクトを使う場合や,メモリを確保する場合には,それらのオブジェクトやメモリを破棄する処理をClass_Terminateプロシージャとして実装することになるだろう。


MTSおよびCOM+で管理されるCOMコンポーネントを開発するときには,一般にClass_InitializeプロシージャとClass_Terminateプロシージャは使われない。なぜなら,MTSおよびCOM+は,COMオブジェクトが破棄されてもそれを本当には破棄せず活かしたままの状態にしておき,ほかのCOMクライアントが呼び出されたときに再利用するので,Class_InitializeプロシージャとClass_Terminateプロシージャが呼び出されるタイミングが掴めないからである。この詳細は,Chapter 4で解説する。

5) dllファイルを作成する

 COMコンポーネントの実装が終わったら,メニューから[ファイル]−[Project1の作成...](ただし,Fig.2-15でプロジェクト名をSamplecomと変更しているため,実際のメニュー項目は[Samplecomの作成...]になる)を選び,dllファイルを実装する。実装したdllファイルが,開発したCOMサーバーということになる。
 Visual Basicでdllファイルを作成すると,レジストリにそのCOMサーバーの情報が格納され,利用可能な状態になる。

prevpg.gif Chapter 2 11/15 nextpg.gif