Chapter 9.
COMに使われる諸技術(1)



この章では第5章で登場したIIDについて(およそ9年ぶりに)解説します。

IIDは様々なヘッダファイルに似たような内容の定義がありますが、(おそらく)最も使用されている定義はGuiddef.hの以下の定義です。
Guiddef.hの一部
typedef GUID IID;
上記の定義よりIIDGUIDの別名であると言えます。そのため、この章ではGUIDについて解説します。



GUID

GUIDとはGlobally Unique IDentifierの略で日本語ではグローバル一意識別子と呼ばれています。これはMicrosoftが独自に設計したものではなく、Distributed Computing Environment(DCE)によって定められたUniversally Unique Idenifier(UUID)が元になっています。つまり、GUIDとはUUIDのMicrosoftによる実装であると言えます。そして、UUID(≒GUID)は、インターネットドメインのように管理団体が管理することなしに、ネットワーク上で必ず一意であることが保障される128ビットの数値として設計されています。なお、UUIDは現時点ではRFC 4122として公開されているので興味があればそちらも参照してみてください。

ここで重要なことは、COMインターフェースはIIDによって識別されるため、異なるコンポーネントに対して同じIIDを指定してQueryInterface関数を呼び出せば、必ず同じインターフェースが返されることが保障されることです。(当然、そのIIDに対応するインターフェースを実装している場合だけですが)

Windowsの世界では、このGUIDIID以外にも様々なものの識別子として使用しています。以下に代表的なものを挙げます。
APPID COMにおけるアプリケーション(EXE)の識別子
CATID コンポーネントカテゴリの識別子
CLSID コンポーネント自身の識別子
FMTID ドキュメントプロパティ(Chapter 19. - OLE on .NET Framework)の識別子
LIBID タイプライブラリの識別子
SID NTセキュリティにおけるSIDとは関係なく、IServiceProviderインターフェースが使用するサービス(これもNTサービスとは関係がない)に対する識別子
それぞれの詳細な内容については執筆が続けば解説する時が来るかもしれません。上の表には挙げていませんが、UUIDもRPCで使用されています。

GUIDは128ビットの構造体であると述べましたが、実際の定義は以下のようになっています。
typedef struct _GUID {
    unsigned long  Data1;
    unsigned short Data2;
    unsigned short Data3;
    unsigned char  Data4[ 8 ];
} GUID;
第5章のinterface.hのコメント部分にある {F86DB061-E8ED-413b-93B0-F4BBC0E23ECD} という文字列はGUIDを人間が読めるように文字列化したもので、波括弧を除いた部分のフォーマットはRFCでも定義されています。なお、Data1、Data2およびData3はビッグエンディアンでフォーマットされているため、以下のように1バイトずつ出力すると上と同じ結果は得られません。
for (size_t i = 0; i < sizeof(IID_IChap5_1); i++) {
    printf("%02x", ((const BYTE*)&IID_IChap5_1)[i]);
}
GUIDと文字列の相互変換はWindows APIが用意されており基本的にそれらを使用することになります。
文字列への変換 StringFromCLSID
StringFromGUID2
StringFromIID
文字列からの変換 CLSIDFromString
IIDFromString
なお、今回はこれらのAPIの解説はしません。



GUIDの生成

任意のGUIDをプログラムの実行中に生成するにはCoCreateGuidを使用します。ただし、一般的なプログラムで実行中にGUIDを生成することはほとんどなくソースコードに埋め込んで使用するのが大半であると思います。ソースコードに埋め込むためのGUIDをテキスト形式で生成するのに使用するツールがGuidGenです。以下にその画面を示します。

Visual Studio 6.0
guidgen.exe (Visual Studio 6.0)

Visual Studio 2010
guidgen.exe (Visual Studio 2010)

新しいVisual Studioではフォーマットの種類が増えていることと日本語で表示されていること以外に機能的な違いはないと思います。私としてはフォーマットにMIDL形式がないのが不満なところですが・・・。それぞれのフォーマットの種類について解説します。

1. IMPLEMENT_OLECREATE(...)
言わずと知れたMFCのIMPLEMENT_OLECREATEマクロの形式でGUIDを生成します。ここでは特に解説しません。
// {EEB35EFC-8629-432a-9A4C-6652008355E9}
IMPLEMENT_OLECREATE(<<class>>, <<external_name>>, 
0xeeb35efc, 0x8629, 0x432a, 0x9a, 0x4c, 0x66, 0x52, 0x0, 0x83, 0x55, 0xe9);

2. DEFINE_GUID(...)
主にC/C++のヘッダファイルで使用する形式です。INITGUID定義の有無(#defineしているかどうか)によって、定数宣言となるかextern宣言となるかが分かれる形式です。ほとんど使うことはないと思います。
// {EEB35EFC-8629-432a-9A4C-6652008355E9}
DEFINE_GUID(<<name>>, 
0xeeb35efc, 0x8629, 0x432a, 0x9a, 0x4c, 0x66, 0x52, 0x0, 0x83, 0x55, 0xe9);

3. static const struct GUID = { ... }
C/C++の定数の形式で生成します。第5章で使用した形式です。
// {EEB35EFC-8629-432a-9A4C-6652008355E9}
static const GUID <<name>> = 
{ 0xeeb35efc, 0x8629, 0x432a, { 0x9a, 0x4c, 0x66, 0x52, 0x0, 0x83, 0x55, 0xe9 } };

4. レジストリ形式 ( {xxxxxxx-xxxx ... xxxx })
APPID、CATID、CLSID、IIDおよびLIBIDがレジストリに記録される時の形式です。レジストリに登録するべき情報はVC++が自動的に構成するため、この形式のGUIDを直接使用することはまずないと思います。が、MIDLの形式が欲しい場合は削除する部分が一番少ないので、この形式を選択することになります。
{EEB35EFC-8629-432a-9A4C-6652008355E9}

5. [Guid("xxxxxxx-xxxx ... xxxx")]
C#におけるGuidAttribute属性の形式です。C#でCOM相互運用するアプリケーションを作成しない限り使用することはないと思います。
[Guid("EEB35EFC-8629-432a-9A4C-6652008355E9")]

6. <Guid("xxxxxxx-xxxx ... xxxx")>
VB.NETにおけるGuidAttribute属性の形式です。VB.NETで...(以下略)
<Guid("EEB35EFC-8629-432a-9A4C-6652008355E9")>



UUIDについて

プログラムで使用する点においてはGUIDとまったく差はありませんが、APIおよびツールについては利用する局面がやや異なるため解説しておきます。

任意のUUIDをプログラムの実行中に生成するにはUuidCreateを使用します。また、UUIDバージョン1に従ったMACアドレスと時間からUUIDを生成するUuidCreateSequentialもあります。さらに、すべてのビットが0のUUIDを生成する誰得APIのUuidCreateNilもあります。UuidCreateが使用するUUIDのバージョンは4以降は確実ですが4か5かは調べていません。UUIDのバージョンについては前述のRFCに記載されています。

UuidCreateSequentialは、生成される値が推測可能であるため、分散コンピューティングにおいてはセキュリティ的な脆弱性となるため推奨されていません。が、この連番のUUIDはCOMを使用するローカルな大規模(Office製品のように巨大なオートメーションサーバであるような)システムを構築する際に、多量のGUIDを管理しなければならない局面において効果を発揮します。連番のGUIDが有利であるのはレジストリの構成が楽になるからですが、COMで使用するレジストリについては後の章で解説します。また、ローカルシステムでは、連番を推測することによる攻撃の対象となりにくい(システムが推測される値を使用するように修正されない限りは推測が意味を成さない)のも利点となります。

GUIDを生成するのと同様にUUIDを生成するツールがUUIDGENです。コマンドラインツールであり、実行すると1つのUUIDを標準出力に出力して終了します。以下にその画面を示します。
uuidgen.exe (Visual Studio 2010)
オプションとして以下の画面のような起動引数を取ります。
uuidgen.exe -h (Visual Studio 2010)
ここでは各オプションについては説明しません。連番のUUIDを多量に生成するには以下の画面のように-xと-nを組み合わせて使用します。
uuidgen.exe -x (Visual Studio 2010)
モザイクを掛けているところはMACアドレスになっています。自身で実行してPCのMACアドレスと同値になっていることを確認してみると良いかと思います。なお、古いVisual StudioのUUIDGENでは-xオプションを使用することはできません。

UUIDの生成にはLinuxのuuidgenを代わりに使用することもできます。この場合は、-tオプションを使用することにより連番を生成できます。以下の画面はCent OS 6.2のuuidgenの実行結果です。
uuidgen -t (Cent OS 6.2)
連番の間隔があまり狭くありませんが、コマンドの入力に時間がかかっているからでしょうか?UUIDのアルゴリズム的にはこの間の数値を使用しても問題とはならないはずです。

連番を多量に生成できるということがUUIDGENの最大の利点です。また、バッチファイルで完全に一意な名前のファイルやフォルダを生成するという使い方もあります。その場合は当然連番である必要はありません。



以上でGUIDの解説としたいと思います。この章で重要なことは冒頭からCOMインターフェースの解説を書いている部分までです。それ以外はすべて余談です。忘れていましたが、MSDNのGUIDを翻訳したページを7年ぐらい前に作っていたので、そちらも合わせて読むと良いかもしれません。

元々はHRESULTと合わせて解説する予定でしたが、執筆するといらないことを書きすぎた予想外に大きなサイズとなってしまったので、GUIDのみで1つの章としました。


前へ
COM研究室へ
総合トップへ