BSTR 覚え書き 2002.2.19 kanegon create 2002.2.23 kanegon update BSTR は見かけ上(Windowsヘッダ上)、LPOLESTR と等しい。 typedef WCHAR OLECHAR; // ※ typedef OLECHAR __RPC_FAR * LPOLESTR; typedef const OLECHAR __RPC_FAR * LPCOLESTR; ※ 16bit Platform では OLECHAR == char しかし、実際には先頭に文字列長さを保持しており、この2つを混同しないこと。 BSTR のポインタはメモリ上の先頭を指していないため、BSTR を要求する関数に LPOLESTR を渡してはならない。また、その関係で BSTR のメモリ管理は完全に別物 である。通常のメモリアロケート関数で処理しないこと。 ただし、LPOLESTR を要求する関数に BSTR を渡すことは問題ない。 LPOLESTR ↓ ┌──────────┬─┐ │ string │\0│ └──────────┴─┘ BSTR ↓ ┌───┬──────────┬─┐ │length│ string │\0│ └───┴──────────┴─┘ ※ lentth は終端 '\0' を含まない string の byte 数 BSTR の領域の取得/解放は SysAllocString()/SysFreeString() または その関連 API を使用して行なう。 BSTR SysAllocString(const OLECHAR* psz); BSTR SysAllocStringLen(const OLECHAR* psz, UINT len); BSTR SysAllocStringByteLen(LPCSTR psz, UINT len); INT SysReAllocString(BSTR* pbstr, const OLECHAR* psz); INT SysReAllocStringLen(BSTR* pbstr, const OLECHAR* psz, UINT len); void SysFreeString(BSTR bstr); UINT SysStringByteLen(BSTR bstr); UINT SysStringLen(BSTR bstr); ※ このあたり、パラメタおよび戻り値の型が MSDN とヘッダで軒並み異なって いる。仕様変更でもあったのか? SysReAllocString() の戻り値は何者? ここで、char* から BSTR の変換には以下を使用できるように思われる。 BSTR str = ::SysAllocStringByteLen(psz, strlen(psz) * sizeof(OLECHAR)); しかし、この関数は第一パラメタのバイナリ列を BSTR にそのままコピーするだけで ANSI-UNICODE 変換とかするわけでないのでこの目的には使えない。注意すること。 通常の VC++ のコーディングでは、このあたりの変換はマクロやラッパクラスを使っ て行なうが、API のみで実装する場合、以下のような感じになる(たぶん)。 [char* => BSTR] size = ::MultiByteToWideChar(CP_ACP, 0, psz, -1, NULL, 0) - 1; bstr = ::SysAllocStringByteLen(NULL, size * sizeof(OLECHAR)); ::MultiByteToWideChar(CP_ACP, 0, psz, -1, bstr, size); [BSTR => char*] size = ::WideCharToMultiByte(CP_ACP, 0, bstr, -1, NULL, 0, NULL, NULL) - 1; psz = new char[size + 1]; ::WideCharToMultiByte(CP_ACP, 0, bstr, -1, psz, size + 1, NULL, NULL); また、MSDN には情報が見つからなかったが、便利なユーティリティ関数も存在して いる。以下は _bstr_t クラスなどの内部処理で使われているものである。 BSTR bstr = _com_util::ConvertStringToBSTR(psz); // throw(_com_error) char* psz = _com_util::ConvertBSTRToString(bstr); // throw(_com_error) これを使うと変換が1行で記述でき、かなり便利である。 # もちろん _bstr_t の方がもっと便利である。 このユーティリティ関数で確保した領域の解放にはそれぞれ SysFreeString()、 delete を使用するみたい。 SysFreeString(bstr); delete[] psz; 同様に VC++ のヘッダを見ていくと、BSTR から BSTR の生成では、 BSTR str = ::SysAllocStringByteLen(reinterpret_cast(bstr), ::SysStringByteLen(bstr)); を使用している。 単純に SysAllocString() を使うのに比べると、効率悪そうだが、 SysAllocStringByteLen() の説明に If psz is Null, a string of the requested length is allocated, but not initialized. The string psz can contain embedded null characters, and does not need to end with a Null. Free the returned string later with SysFreeString. とあり、BSTR は途中に NULL 文字を含んだ文字列を扱うことができるため、おそらく その対処と思われる。 # SysAllocString() は終端 NULL の標準文字列しか扱えない。 このように途中に NULL 文字を含み、長さ情報を自前で保持する BSTR であるが、 終端の NULL も明確に保証されている。BSTR の領域を終端 NULL の LPOLESTR とし て使用するのは問題ない。 SysAllocStringByLen() の長さパラメタの説明には Number of bytes to copy from psz. A null character is placed afterwards, allocating a total of len+1 bytes. と記述がある。 さらに、元になる文字列がない場合の領域作成(0初期化なし)は bstr = ::SysAllocStringByteLen(NULL, len); である。このとき、len は生成する領域の byte 数を指定すること。 (終端の NULL はカウントしない)