たまにはCOMしましょうかね…(遠い目)。
IDLファイル書きました。こんなの。
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid( 9874C279-FE45-43a1-9ED6-ED667E5BA089 )
]
interface IHoge : IUnknown
{
HRESULT Hoge();
}
こいつをMIDLでコンパイると、(元のファイル名がHoge.idlだとして)以下の4つのファイルができます。
- Hoge_h.h
- Hoge_i.c
- Hoge_p.c
- dlldata.c
Hoge_h.hは、このインターフェイスの定義をC++風に書き直したものです。
こいつを、このインターフェイスを使うクライアントに提供します。
この中には、IID_IHogeの宣言も含まれています。
Hoge_i.cは、GUIDの定義ファイルです。
今回の例の場合、この中にIID_IHogeの定義が含まれます。
Hoge_p.cとdlldata.cは、このインターフェイスのプロキシとスタブを生成するためのソースファイルです。
ここでは割愛。
さて、これでコンポーネント側はできたとして(実装は小人さんが手伝ってくれました)、次はこれを利用するアプリ側を作りましょう。
ここで問題です。
アプリ側ではCoCreateInstanceを使うために、CLSID_HogeとIID_IHogeの定義が必要です。
しかし、CLSID_Hogeなんてものはどこにも定義してありません。
それから、ユーザに渡したHoge_h.hには、IID_IHogeの宣言しか入っていません。
さぁどうしましょう?
MicrosoftがWindows SDKで提供しているインターフェイスは、そのCLSIDやIIDの定義がスタティックライブラリに入っているものがあります。
しかし、COMは本来、インポートライブラリ不要のアーキテクチャです。
ヘッダファイルにでも入れて提供してあげれば済むことなので、このためにHoge.libなんて作りたくありません。
IIDの定義は、Hoge_i.cに入っています。
このファイルをユーザーに提供すればいいんでしょうか?
できれば、ユーザーに渡すのはヘッダファイルだけにしたいところです。
それとも、実体の定義を伴うヘッダファイルなんて邪道でしょうかね?
CLSIDはどこで定義しましょう?
IDLファイル中でも、こんな風にすればできます。
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid( 9874C279-FE45-43a1-9ED6-ED667E5BA089 )
]
interface IHoge : IUnknown
{
HRESULT Hoge();
}
[ uuid( 56BFECD7-0642-4594-9CA8-6C1B77DE0C95 ) ]
coclass Hoge
{
[ default ] interface IHoge;
}
これは、IHogeを実装するクラスHogeの定義です。
こう定義してやると、Hoge_h.hにCLSID_Hogeの宣言が、Hoge_i.cに定義が、それぞれ生成されます。
しかし、同時に、Hoge_h.hにHogeクラスの先行宣言が加わります。
このHogeクラスとは何者でしょうか?
これはCOMクラスですから、そんなクラスを宣言されたところで、new Hoge; でインスタンスを生成することはできません。
先行宣言によって可能になるのは、その型のポインタ型を定義することくらいですが、メソッドの宣言が含まれないため、そのポインタを利用してメソッドを呼び出すことができません。
第一、C++でCOMを扱うときに利用するのはインターフェイスポインタであって、CLSID以外でクラスの存在を意識することはないのです。クラスポインタなんて必要ありません。
まったく役立たずです。
IDLファイルにcoclassだけでなくタイプライブラリの定義も加えてやれば、VBや.NETからの呼び出しが可能になり、そこではnew Hoge;でのインスタンス生成が可能になります。
しかし、そのためにHoge_h.hでHogeクラスが宣言されている必要はゼロです。VBや.NETは、C++のヘッダファイルを必要としないのですから。
というわけで、
- ユーザにHoge_i.cは提供したくない
- IDLでcoclassは定義したくない
ということになりました。
IDLでcoclassを定義しない以上、CLSIDの定義をMIDLに頼ることはできません。
ならば、自分でヘッダファイルでも作って、そこに書いてやるしかないでしょう。
せっかくファイルを作るのならば、IIDの定義もそっちでやってしまいましょう。
特定のマクロが定義されているときは定義に、そうでないときは宣言になるようにスイッチしてあげればいいでしょう。
事実、Windows SDKに含まれるDEFINE_GUIDはそうなっていますし、guidgen.exeはほとんどコピペで使えるDEFINE_GUID形式の表記を生成してくれますから、これを使えばいいでしょう。
ふーむ…これで最適解になりましたかね?
MIDLが若干気に食わないものの、こうするのが定石、なのかな?