teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

1

修正

2021/02/26 22:40

投稿

subka
subka

スコア8

answer CHANGED
@@ -1,6 +1,63 @@
1
- 原因が判明しました
1
+ 事象が発生するコードの検証中に原因が分かった為、こちらに記載しま
2
2
 
3
+ ###原因
3
- 諸々情報をまとめております
4
+ 他サイトから引用した以下様なコード原因でした。
5
+ Qiita 等でも記事にされているメジャーな使い方の様です。
6
+ ```C++
7
+ BSTR str = CComBSTR(L"ABC");
8
+ ```
9
+ ###原因詳細
4
- 一旦こちら質問へ回答見送って頂いて結構です。
10
+ 上述コード処理以下の通りとなります。
11
+ ①CComBSTR クラスのコンストラクタが呼び出され、L"ABC"のメモリが確保される。
12
+ ②str に L"ABC" の参照が渡される。
13
+ ③CComBSTR クラスのデストラクタが呼び出され、L"ABC"のメモリが解放される。
5
14
 
15
+ つきまして、str は見掛け上 L"ABC" が確保されたアドレスを参照していますが、
16
+ COM 内部処理的には L"ABC" のアドレスが解放された状態となります。
17
+
18
+ 質問本文では CComBSTR の呼び出しが一度のみのコードを記載しておりましたが、
19
+ 該当のコードの引数である var の値を設定する際に CComBSTR による値設定を行っていたことがそもそもの原因となります。
20
+
21
+ 以下サンプルの様に、上記の処理を複数回実行すると同様の事象が発生します。
22
+
23
+ ###サンプル
24
+ ```C++
25
+ int main() {
26
+ BSTR abc = SysAllocString(L"ABC");
27
+ SysFreeString(abc);
28
+ BSTR def = SysAllocString(L"DEF");
29
+ SysFreeString(def);
30
+ }
31
+ ```
32
+ 3行目(SysFreeString(abc);)の時点で abc は解放されていますが、
33
+ 4行目でも abc の参照先は変わらず、参照先には L"ABC" が残っています。
34
+
35
+ これは COM 内部のメモリ管理をしている機能に起因しています。
36
+ COM API が確保するメモリはスタックでもヒープでもなく、COM 管理下のメモリです。
37
+ COM が自管理下にあるメモリを参照しているか否かの判断は、実際に参照されているかではなく、
38
+ COM 内部のメモリ管理をする機能が参照しているか否かで判断されます。
39
+ SysFreeString は、この機能上での参照を解除します。
40
+
41
+ そして5行目で再度 L"DEF" を確保する際、確保する先は先程解放した L"ABC" が格納されているアドレスです。
42
+ つまり、この時点でまだ L"ABC" のアドレスを参照している abc の値も変わります。
43
+
44
+ ちなみに以下でも同様です。
45
+ ```C++
46
+ int main() {
47
+ BSTR abc = CComBSTR(L"ABC");
48
+ BSTR def = CComBSTR(L"DEF");
49
+ }
50
+ ```
51
+ ###その他
52
+ 同関数内のスコープの異なる位置でメモリ確保をしたり、質問本文の様にルーチンを分離して実行してみたり、BSTR と CComBSTR を混在させたり、などなど検証している内に事象が発生しないパターンが存在することもわかりました。
53
+
6
- まとめ情報は後程ここに記載します。
54
+ 検証が雑だっせいかもせんが、
55
+ 時により二度目のメモリ確保の際に、一度目に解放したメモリ以外を確保するパターンが確認できました。
56
+
57
+ 問題となっている事象の原因が既に分かっていたのと、条件がきれいにまとめられなくなりそうだったので細かい検証は見送りましたが、タイミングや環境如何では不都合なく動く可能性もある様です。
58
+ ###解決策
59
+ CComBSTR クラスのコンストラクタを利用した BSTR および CComBSTR の初期化をするべきでない。
60
+ 事前に CComBSTR クラスを用意しているのであれば CComBSTR::Copy を利用する。
61
+ そうでなければ SysAllocString を利用して、値の受け取り側がラッパークラスでなければ各要素で SysFreeString を明示する。
62
+ ###宜しければご教示下さい
63
+ 素人の検証・見解ですので、内容に相違がある場合はご指摘をお願いします。