デュアルインターフェース - MIDL

いちごパック > COM/ActiveXの解説 > デュアルインターフェース - MIDL

MIDLの目的

C++のclassとしてCOMのインターフェースを定義し、 インターフェースを扱うための手続きを実装するには手間がかかります。 Visual Studioでは、 COMのインターフェースとして必要なファイルをまとめて生成するための インターフェース定義言語MIDLを扱うツール(MIDLコンパイラ)が付属しています。 ここでは、MIDLについて説明します。

MIDLコンパイラ

MIDLコンパイラの名前はmidlで、ソースコードとして拡張子が.idlのファイルを実装して使います。 主にC++で使うことを想定していますが、それ以外の用途でも利用可能です。 midlの入力と出力は次のようになります。
タイプ内容用途
入力インターフェース定義ファイル(拡張子.idl)ソースコードとして作成
出力インターフェース(拡張子.h)インターフェースやクラス構造を保存したC/C++言語のヘッダファイル。ヘッダとして利用
出力タイプライブラリ(拡張子.tlb)インターフェースやクラス構造を保存したバイナリファイル。proxyとstubを提供することも可能
出力GUIDの実体のC言語ソース(拡張子_i.c)インターフェースやクラス構造で用いるGUIDの実体を保存したC言語ソースファイル。必要であればソースコードとして利用
出力proxyとstubのC言語ソース(拡張子_p.c)自動生成されたproxyとstubのC言語ソースコード。必要であればソースコードとして利用
MIDLはC++専用の独自インターフェースの生成、デュアルインターフェースの生成のいずれの場合でも利用できます。
出力されたファイルをすべて使う必要はありません。 例えばデュアルインターフェースを生成させる場合、 インターフェース(拡張子.h)とタイプライブラリのみを利用し、 生成されたC言語のコードを利用しないこともあります。

MIDLによるインターフェースの定義

MIDLは主にインターフェースを定義したソースコード、タイプライブラリを出力させるために使います。 インターフェースは次の文法で記述します。
[属性1,属性2,...]
interface インターフェース名 : 継承元インターフェース名
{
    C++に似たスタイルのメソッド定義
};
属性としては例えば次の属性を指定できます。 COMインターフェースには、少なくともobjectとuuidを含める必要があります。
属性内容
object必ず指定します。このinterfaceをCOMインターフェースとして定義します。object指定がない場合はDCE RPCと呼ばれる別のインターフェースとして扱われます。
uuidインターフェースのGUID(IID)を指定します。uuid(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX)の形で使います。
versionインターフェースのバージョンをversion(1.0)などの形で指定します。整数部を変えることは後方互換なし、小数部を増やすことは後方互換ありを意味します。
localstubを生成させないようにします。
pointer_defaultデフォルトのポインタ属性を指定します。pointer_default(ptr|ref|unique)のいずれかになります。
oleautomationCOMインターフェースがオートメーション互換であることを宣言します。
dualIDispatchとメソッド直接呼出しの双方に対応した、オートメーション互換のCOMインターフェースであることを宣言します。
通常、一度外部に公開したインターフェースに対して非互換のインターフェースを生成することはありませんので、 versionを含める必要はありません。 既存のインターフェースに対し非互換の機能を追加したい場合は、新しいインターフェースを定義し、 QueryInterface()経由で新しいインターフェースにアクセスできるようにすると後方互換を確保できます。 属性は、例えば次のように指定します。
[
    object,
    dual,
    uuid(964FC28B-E237-4C33-A5E6-5C384021145A),
    pointer_default(unique)
]
COMインターフェースでは、 IUnknown、IDispatch、またはそれらを継承したインターフェースを継承する必要があります。 IUnknownやIDispatchはMIDL用のヘッダファイルとして定義されており、 importによりこれらのファイルを取り込めます。
importは.idlソースコードを取り込むためのものです。
import ".idlファイル名";
例えば、Visual C++に最初から付属するIDispatchの.idlソースコードは、 次の2行で取り込めます。
import "unknwn.idl";
import "oaidl.idl";
COMインターフェースにおけるメソッドの定義はC++に似ていますが、 純粋仮想関数を表すvirtualや = 0は必要ありません。
デュアルインターフェースでは、引数の型として、 オートメーション - メソッド呼び出しの実装 で説明した、オートメーションで使う型(VARIANTで表現可能な型)を使う必要があります。 VARIANTで表現できない型を用いると、そのインターフェースはオートメーション互換ではなくなります。 例えば、LONGやDOUBLE、BSTRのみを使ったインターフェースはオートメーション互換ですが、 const char*などC++のポインタを使ったインターフェースはオートメーション互換ではありません。
例えば、BSTRの引数を1つとりHRESULTの戻り値を返すWrite()メソッドを備えた、 IIchigo4というデュアルインターフェースは、 次のように書くことができます。
interface IIchigo4 : IDispatch
{
    HRESULT Write( BSTR bstrWrite );
};

MIDLによるタイプライブラリの定義

.idlでインターフェースを定義すると、 C言語用のヘッダファイルとソースコードが生成されます。 C言語やC++言語でビルドするだけならこれらのファイルがあれば十分ですが、 スクリプト言語から呼び出すためにインターフェースの定義を参照したり、 システムのproxyとstubを介して他のプロセスからEXEサーバを呼び出すためには、 タイプライブラリと呼ばれるバイナリファイルを生成させる必要があります。
タイプライブラリを生成させるには、.idlファイルにlibraryを追加します。
[属性1,属性2,...]
library ライブラリ名
{
    interface ライブラリに取り込むインターフェース;

[属性1,属性2,...] coclass クラス名 { [追加属性1,追加属性2,...] interface クラスが提供するインターフェース名; }; };
libraryにinterfaceを含めると、 タイプライブラリを読み込んだ時にそのインターフェースを参照できるようになります。 また、libraryにcoclassを含めると、 タイプライブラリを読み込んだ時にそのcoclassを参照できるようになります。 いずれも複数回指定することも可能です。 タイプライブラリに必要なインターフェースやcoclassを含めると良いでしょう。
libraryの属性としては例えば次の属性が指定できます。versionとuuidを指定すると良いでしょう。
属性内容
uuidタイプライブラリのGUID(LIBID)を指定します。uuid(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX)の形で使います。
versionタイプライブラリのバージョンをversion(1.0)などの形で指定します。整数部を変えることは後方互換なし、小数部を増やすことは後方互換ありを意味します。
例えば、次のように指定できます。
[
    version(1.0),
    uuid(74e6d628-b3b0-4ea2-89b3-c85be7bb9e5a)
]
library内には、タイプライブラリに含めたいinterfaceやcoclassを列挙します。
coclassの属性としては、例えば次の属性が指定できます。uuid属性はcoclassでは必須になります。
属性内容
uuidタイプライブラリのGUID(CLSID)を指定します。uuid(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX)の形で使います。
hiddenこのcoclassをCOMインターフェースブラウザで非表示としてほしいことを指示します。coclass自体は有効です。
coclass内のinterfaceには、次の追加属性を指定できます。 defaultは、coclassが提供するインターフェースと、sourceインターフェースのそれぞれについて1つずつ指定できます。
属性内容
default複数インターフェースに対応したcoclassで、1つのインターフェースしか扱えないスクリプト言語などで取得すべきインターフェースを指定します。
sourceこのcoclassはIConnectionPointContainerに対応し、IConnectionPointContainerはここで指定されたインターフェースを受け入れます。
libraryにinterfaceを含める際、IUnknownやIDispatchといった継承元インターフェースは、タイプライブラリから参照できる必要があります。
システム提供のIUnknownやIDispatchといったインターフェースの定義は、 すべてまとめてstdole32.tlbというタイプライブラリに保存されています。 stdole32.tlbのように他のタイプライブラリを参照したい場合は、libraryの中でimportlibを利用します。 importlibで指定できるファイルは.tlbファイルか、または.tlbファイルを取り込んだ.exeファイルや.dllファイルになります。
    importlib("タイプライブラリのファイル名");
stdole32.tlbはIUnknownを継承するすべてのインターフェースで必要になりますので、 常にlibraryに取り込んでおくと良いでしょう。
    importlib("stdole32.tlb");
インターフェース定義とタイプライブラリ生成のコード例としてサンプルMIDLコードを用意しましたので、 完全なコードが必要な型はこちらをご参照ください。

関連ページ

  • COM/ActiveXの解説ページ 目次
  • 前の項目: C++とスクリプト言語の両立
  • 次の項目: タイプライブラリの登録とその利用
  • サンプルコード: サンプルMIDLコード