本記事では、WindowsのCOMライブラリを扱いやすくしてくれるcom_ptr_tクラスを紹介したいと思います。
com_ptr_tは、IUnknownインターフェースのQueryInterface、AddRef、Releaseメンバ関数周りを隠蔽してくれるテンプレートクラスで、いわゆるスマートポインタの一種です。
_com_ptr_tは、単体でもそれなりに便利ですが、これがVisual C++の色々な機能と結びついてさらに便利になっています。
例えばMSXML3を使ってXMLファイルを読み込むコードが、_com_ptr_tを使ってどのように簡単になるのかを見てみましょう。
#import "msxml3.dll" named_guids
int main(int argc, char* argv[])
{
// COMライブラリ初期化
::CoInitialize(NULL);
try {
MSXML2::IXMLDOMDocument2Ptr pDoc;
pDoc.CreateInstance(MSXML2::CLSID_DOMDocument);
pDoc->async = false;
if (pDoc->load(L"test.xml")) {
// Root要素名を表示する
printf("RootNodeName: %s\n",static_cast<const char*>(pDoc->documentElement->nodeName));
// Root要素の子要素一覧
MSXML2::IXMLDOMNodeListPtr pList = pDoc->documentElement->childNodes;
for (long i = 0, n = pList->length; i < n; ++i) {
MSXML2::IXMLDOMElementPtr pElement = pList->Getitem(i);
if (pElement) {
// 子ノードが要素の場合のみ処理する
printf("ChildNodeName: %s\n",static_cast<const char*>(pElement->nodeName));
}
}
}
}catch (_com_error& e) {
// 例外ハンドラ
printf("Error: %s\n", e.ErrorMessage());
}
// COMライブラリ終了
::CoUninitialize();
return 0;
}
面倒なQueryInterface、AddRef、Releaseが綺麗に消えていることが分かると思います。VBScriptに近いお手軽さです。
ところで、上記コードには、_com_ptr_tという文字列は存在しないということに注意して下さい。
_com_ptr_tが使われているのは、上記#importによって自動生成されるmsxml3.tlhというヘッダファイルの中です。ここに、
typedef _com_ptr_t<IXMLDOMDocument2, &__uuidof(IXMLDOMDocument2)> IXMLDOMDocument2Ptr; typedef _com_ptr_t<IXMLDOMElement, &__uuidof(IXMLDOMElement)> IXMLDOMDocument2Ptr;
というような感じの定義があるのです。
何か不都合があると、_com_error例外を投げてくれるのもありがたいところです。例外処理がシンプルになります。
一度COM周りのコードを書いた人なら分かると思いますが、_com_ptr_tを使わずに上記のコードを書いてみると、相当大変です。_com_ptr_t万歳。
一応最後に書いておきますが、_com_ptr_tは、MSXML3に限らず、他のCOMインターフェースに対しても使えます。例えば、CreateStreamOnHGlobalで取得したIStreamをIStreamPtrに代入して・・・ということもできますし、DirectX9周りで活用することもできます。色々と試してみて下さい。
但し、_com_ptr_tは、Visual C++の独自機能を相当使っているはずです。移植性が必要な場合は、使わない方が良いでしょう。