BSTR とは?
COM プログラミングでよく出てくる、BSTR とは?
BSTR は COM で利用される一般的な文字列の形式です。
BSTR は基本的にワイドキャラクタ文字列として定義されていますが、その手前にデータ領域のバイト数を示すバイト数(4バイト)と、 終端に (00 00) という2バイトのデータが割り当てられます。
データ領域を示すバイト数には、終端の NULL (00 00) は含まれません。
従って、次のようなコードは誤りです。
BSTR bstr = L"Hello";
これでは変数 bstr は "Hello" というワイドキャラクタ文字列をポイントしていますが、 その文字列の前にデータバイト数を表すバイト数が割り当てられていませんので、正しい BSTR ではありません。
BSTR は COM のメモリアロケータで割り当てますので、割り当てには SysAllocString、解放には SysFreeString を用います。
BSTR bstr = SysAllocString( L"Hello" ); ... SysFreeString( bstr );
BSTR の割り当ての様子を見てみよう
それでは実際に BSTR が本当に上図のような形式で割り当てられるのか、確認してみましょう。
以下のようなテストプログラムを用意します。
#include <windows.h> #include <stdio.h> void main() { BSTR bstr = SysAllocString( L"Hello" ); if ( !bstr ) { printf( "SysAllocString failed.\n" ); return; } __asm int 3; SysFreeString( bstr ); return; }
このコードでは SysAllocString で bstr を割り当て、その後、int 3 でデバッグブレークします。
このように書くと、デバッガがアタッチされている環境ではデバッガに制御が渡されます。 このテクニックはデバッグの実験を行うときには便利ですので、ご存じでなかった人は覚えておくと良いと思います。
このコードを bstr1.cpp という名前で保存して、SDK のコマンドプロンプトから次のようにして、 テストプログラム bstr1.exe を作成してください。
> cl /c /Zi bstr1.cpp > link /DEBUG /RELEASE oleaut32.lib bstr1.obj
/Zi や /DEBUG, /RELEASE などのオプションを設定するのは、以下のデバッグを行うにあたり必要な情報を含むデバッグシンボルファイルを生成するためです。
さて、このプログラムで SysAllocString によって割り当てられる BSTR を確認しましょう。
cdb 上で上で作成した bstr1.exe を実行します。
C:\Debuggers>cdb C:\src\test\bstr1.exe
Microsoft (R) Windows Debugger Version 6.11.0001.404 X86
Copyright (c) Microsoft Corporation. All rights reserved.
CommandLine: C:\src\test\bstr1.exe
Symbol search path is: srv*C:\websymbols*http://msdl.microsoft.com/download/symbols
Executable search path is:
ModLoad: 00400000 0041d000 bstr1.exe
ModLoad: 76e10000 76f4c000 ntdll.dll
ModLoad: 76ab0000 76b84000 C:\Windows\system32\kernel32.dll
ModLoad: 75100000 7514a000 C:\Windows\system32\KERNELBASE.dll
ModLoad: 76d80000 76e0f000 C:\Windows\system32\OLEAUT32.dll
ModLoad: 76950000 76aac000 C:\Windows\system32\ole32.dll
ModLoad: 76b90000 76c3c000 C:\Windows\system32\msvcrt.dll
ModLoad: 76900000 7694e000 C:\Windows\system32\GDI32.dll
ModLoad: 75260000 75329000 C:\Windows\system32\USER32.dll
ModLoad: 76f50000 76f5a000 C:\Windows\system32\LPK.dll
ModLoad: 75330000 753cd000 C:\Windows\system32\USP10.dll
ModLoad: 753d0000 75471000 C:\Windows\system32\RPCRT4.dll
(ed0.80c): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=0012fb0c edx=76e564f4 esi=fffffffe edi=00000000
eip=76eae60e esp=0012fb28 ebp=0012fb54 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!LdrpDoDebuggerBreak+0x2c:
76eae60e cc int 3
g コマンドでプログラムを実行します。int 3 でブレークするはずです。
0:000> g
ModLoad: 75890000 758af000 C:\Windows\system32\IMM32.DLL
ModLoad: 76830000 768fc000 C:\Windows\system32\MSCTF.dll
(ed0.80c): Break instruction exception - code 80000003 (first chance)
eax=002583bc ebx=7ffdf000 ecx=00000002 edx=00000002 esi=00000000 edi=00000000
eip=00401027 esp=0012ff3c ebp=0012ff40 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
bstr1!main+0x27:
00401027 cc int 3
ここでブレークしました。dv コマンドでローカル変数を確認します。
0:000> dv
bstr = 0x002583bc "Hello"
確かに bstr には "Hello" という文字列がセットされているようです。
その前後のメモリの様子を確認するために、dc コマンドで bstr のアドレス 0x002583bc より 0x10 小さい場所から メモリをみてみましょう。
0:000> dc 0x002583bc - 0x10 002583ac 00000000 6d7424bf 1800d4da 0000000a .....$tm........ 002583bc 00650048 006c006c 0000006f abababab H.e.l.l.o....... 002583cc abababab 00000000 00000000 e977273b ............;'w. 002583dc 0000d4da 002500c4 00253fd8 feeefeee ......%..?%..... 002583ec feeefeee feeefeee feeefeee feeefeee ................ 002583fc feeefeee feeefeee feeefeee feeefeee ................ 0025840c feeefeee feeefeee feeefeee feeefeee ................ 0025841c feeefeee feeefeee feeefeee feeefeee ................
確かに Hello というワイドキャラクタの文字列の前には、4バイトで 0000000a が、後ろには 0000 が確認できますね。
以上で、確かに BSTR が予定通りの形式で割り当てられたことが確認できました。
尚、SysFreeString で BSTR を解放した後に、直ちにそれが解放されるかどうかという点については、 BSTR キャッシュという仕組みに依存しています。興味のある方は下記関連文書の記事をご覧ください。