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 キャッシュという仕組みに依存しています。興味のある方は下記関連文書の記事をご覧ください。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Web/DB プログラミング徹底解説