バグ:COM用文字列型(BSTR)の扱い

Posted: 2012年11月18日

COM用文字列型(BSTR)の扱いでよく書いてしまうバグを紹介します。
ちなみに、COMとは、Component Object Modelの略であり、再利用可能なソフトウェア コンポーネントを作成するための仕様です。
よく関わるものは、msxmlでxmlを操作する際に使用します。(msxmlがcomでできているため)

プログラムプログラム:

 #include  <stdio.h>
 #include <stdlib.h>

 int main(int argc, char *argv[])
 {
    BSTR strSrc = ::SysAllocString(L"12345");
    CString strDest = (LPCTSTR)(_bstr_t)strSrc ;

    // 処理

    return 0;
 }

このプログラムにどのような文字列変換関連のバグがあると思いますか?

  • BSTRの領域解放忘れ

改善版プログラム改善版プログラム:

 #include  <stdio.h>
 #include <stdlib.h>

 int main(int argc, char *argv[])
 {
    BSTR strSrc = ::SysAllocString(L"12345");
    CString strDest = (LPCTSTR)(_bstr_t)strSrc ;

    // 処理

    ::SysFreeString(bstr2);

    return 0;
 }

解説解説:

BSTRは、SysAllocStringで領域を確保するため、解放の必要がある、
今回の例では、領域を確保しているため分かりやすいが
分かりづらい場合も存在する

    BSTR* strNodeName;
    IXMLDOMNode pConfigNode;

    // XML読み込み処理

    // ノード名の取得
    hr = pConfigNode->get_nodeName(&strNodeName);

    // 処理

    ::SysFreeString(bstr2);

この場合だと、get_nodenameで取得しているため、領域を確保している感がない。
(BSTRがポインターのように見えないせいもあると思う)
しかし、解放の必要があるため忘れるとメモリーリークとなる。
Visual studioなどでデバッグ実行すると通常は、メモリーリークの存在を教えてくれるが
COMに関わるメモリーリークは、教えてくれないため厄介なバグの元になる。

自動的に解放する型も存在しています。

  • CComBSTR (BSTRのクラス型 デストラクタで解放されるまたは、Emptyメソッド)
  • _bstr_t (BSTRのスマートポインター型(参照カウントを用いて解放のタイミングを見計らう))

BSTRで受け取った後
上記の型に変換後に解放したほうが、リークの心配は和らぎそう
もちろん、上記の型で受け取れる場合は、それで対応したほうがなお安心

CComBSTR注意点

CComBSTRは、文字列を代入するたびに解放しなければメモリーリークする可能性がある

    CComBSTR strNodeName;
    strNodeName = _T("12345");
    // strNodeName.Empty();  // 解放処理
    strNodeName = _T("67890");  // リーク発生

結構癖が有りそうなため個人的には_bstr_tを使用することをおすすめしています。

_bstr_tを用いた場合

_bstr_tを用いてxmlの読み込みプログラムは以下のようになります。

    BSTR* strNodeName;
    IXMLDOMNode pConfigNode;

    // XML読み込み処理

    // ノード名の取得
    hr = pConfigNode->get_nodeName(&strNodeName);

    _bstr_t strTempNodeName(strNodeName, false); // falseでstrNodeNameの領域を使用する

    // 処理

_bstr_tのコンストラクタの第二引数で領域をコピーするかを指定する。
true(コピーした場合)は、strNodeNameを解放する必要があるが、falseは、_bstr_tが領域を解放するため、何もしなくていい。

注意点

VARIANT型にBSTRを設定した場合または、関数から値を取得した結果 VARIANTがBSTRの場合も
解放の必要がある。
その場合も_bstr_tに変換後、使用することをお勧めします。

カテゴリー: バグ, プログラム | タグ: , , , , | コメント無し »

コメント