読者です 読者をやめる 読者になる 読者になる

syghの新フラグメント置き場

プログラミングTipsやコード断片の保管場所です。お絵描きもときどき載せます。

WinSDKおよびATLのCOMユーティリティ

C++ Visual Studio プログラミングTips

(これは2010-11-23に書いた故OCNブログの記事を移植したものです)

MSXMLなどのCOMタイプ ライブラリを#importしたときなど、COMを扱うときによく見かけるのが、Visual C++ CRTヘルパーの _bstr_t クラスと _variant_t クラス(comutil.h)です。それぞれ、COMのBSTR文字列のラッパーと、VARIANT型のラッパーとなっています。
ただしC++プログラマーとしてはこれらがグローバル名前空間で定義されてやがることに殺意を覚えます。シンボル名がアンダースコアで始まっているのは、処理系依存のデータ型であることを誇示しているのか……
あとアンダースコアなしのエイリアス bstr_t と variant_t がtypedefじゃなく#defineなのも情けないです。MSの#define好きは異常*1

似たようなラッパーはATL(atlcomcli.h)にも用意されていて、ATL::CComBSTRとATL::CComVariantが該当するのですが、ついでにATL::CComSafeArray(atlsafe.h)なんかもあります。MFC専用だとCOleVariantというのもあります。

なお、_variant_tは、MFCライブラリでもたまに使われているのを見かけます(CMFCPropertyGridProperty::SetValue()/GetValue()とか)。

正直VC++ネイティブで文字列を扱う場合、他の環境への移植性を考慮しないで良いのであれば、迷わずCStringA/CStringW/CStringを使うのがベストです*2。そもそもテンプレート機能まである静的型付け言語C++で何が悲しくてVariantなんぞ使わないといけないのかとも思いますが、COMデュアル インターフェイスの境界ではたとえ非効率でもBSTRやVARIANT、SAFEARRAYを使わざるを得ません。で、COMのBSTRは::SysAllocString()/::SysFreeString()、SAFEARRAYは::SafeArrayCreate()/::SafeArrayDestroy()でヒープ管理されるので、こういうRAIIラッパーがないと発狂するでしょう。ただ、ラッパークラスはCRTアロケータを使っていない時点で速度が出ない(とくに文字列の結合が毎回再割り当てを伴うために遅いらしい)し、下手な使い方をするとメモリーリークの原因になるので、COMのRAIIラッパーを使用する場合はCOM境界のみに限定するべきです。ちなみにATL::CComBSTRはアタッチ・デタッチがやや特殊で、使い方を誤ると簡単にメモリーリークするらしいので、極力_bstr_tを使ったほうが無難らしいです。

*1:Win32 APIでも、enumを使うべき所なのにやたら#define使っているのがかなりうっとうしいです。あと悪名高いmin/maxマクロも後世に語り継がれる大ポカでしょう。

*2:C++標準のstd::string/wstringより最適化されていて高機能です。Windowsの文字列リソースを直接読み込めるLoadString()メソッドも用意されています。