COMの文字列にはBSTR
というデータ型が使われています。
BSTR
が定義されているWindows SDKのヘッダーを見てみましょう。
#ifndef _PREFAST_
typedef OLECHAR *BSTR;
#else
typedef _Null_terminated_ OLECHAR *BSTR;
#endif
OLECHAR
はもともとCOMの前身であるOLE (Object Linking and Embedding) で使われていた文字型の名残で、当時はANSI MBCSのchar
でしたが、現在はWCHAR
すなわちwchar_t
と等価です。Windows上ではsizeof(wchar_t) == 2
であり、UTF-16エンコーディングのUnicode文字を表現するのに使われます。
#if defined(_WIN32) && !defined(OLE2ANSI)
typedef WCHAR OLECHAR;
typedef __RPC_string OLECHAR *LPOLESTR;
typedef __RPC_string const OLECHAR *LPCOLESTR;
#define OLESTR(str) L##str
#else
typedef char OLECHAR;
typedef LPSTR LPOLESTR;
typedef LPCSTR LPCOLESTR;
#define OLESTR(str) str
#endif
「ふーん、じゃあBSTR
はwchar_t
へのポインタなんだね。てことは、BSTR
を要求するAPIにはwchar_t
へのポインタを渡してもいいんだ!」
ダメです。絶対にやめてください。
そもそもBSTRとは?
BSTRはCOMだけでなく旧VB/VBAでも使われている、長さ情報を持った自己記述的文字列型の一種です。Windows SDKの <oaidl.h> で定義されているVARIANT
型とも深い関わりがあります。
BSTRのデータ構造に関しては、以下の資料をよく読めばイメージできるはずです。
実際にコードで試してみます。
#define NOMINMAX
#include <Windows.h>
#include <tchar.h>
#include <cstdio>
#include <clocale>
#include <initializer_list>
int main() {
_tsetlocale(LC_ALL, _T(""));
for (auto str : { L"abc", L"hoge", L"Hello" }) {
BSTR bstr = ::SysAllocString(str);
const UINT sizeInBytes = *(reinterpret_cast<UINT32*>(bstr) - 1);
const UINT sysByteLen = ::SysStringByteLen(bstr);
const UINT sysStrLen = ::SysStringLen(bstr);
wprintf(L"BSTR: wsz = \"%ls\", size = %u[bytes], byte-len = %u, len = %u\n", bstr, sizeInBytes, sysByteLen, sysStrLen);
::SysFreeString(bstr);
bstr = nullptr;
}
struct { INT32 len; wchar_t str[128]; } dummyData = { 65535, L"abc" };
BSTR bstrIllegal = dummyData.str;
const UINT sysByteLen = ::SysStringByteLen(bstrIllegal);
const UINT sysStrLen = ::SysStringLen(bstrIllegal);
wprintf(L"Illegal BSTR: byte-len = %u, len = %u\n", sysByteLen, sysStrLen);
}
以上のように、BSTRはメモリの確保/解放や長さ取得に専用のAPIを利用します。
BSTRをC/C++で読み取る際はwchar_t
のゼロ終端文字列として扱うこともできますが、その逆は不可です。決してwchar_t
のゼロ終端文字列をBSTRとして扱ってはいけません。未定義の異常動作を引き起こす原因になります。
BSTRのラッパー
Visual C++ではATL::CComBSTR
や_bstr_t
といったラッパークラス型(スマートポインタ)が用意されており、ライフサイクル管理の煩雑なBSTRを比較的簡単に扱うことができます。
WIL (Windows Implementation Library) にもwil::unique_bstr
が用意されています。
ただし、BSTRラッパーの内部では結局システムAPIをコールするので、ヒープ管理にCRT (C Runtime) を利用するATL::CStringW
や標準C++のstd::wstring
と違い、速度やメモリ効率などのパフォーマンス面で不利です。
BSTRラッパーはBSTRを要求するCOMコンポーネントとの境界面 (API) にのみ使用し、普段は文字列の管理にATL::CStringW
やstd::wstring
を使うべきです。