拙作のウィンドウクラスに細工をしたらハマったというお話です。
きっかけはプロパティリスト(SetProp等のあれ)が遅い、と小耳に挟んだことでした。大したウィンドウを作るわけでもありませんが、更新頻度が異様に高いアプリは作ることがあるので、別の方法を試してみようとしたのです。
プロパティリストにはthisポインタを入れておき、メンバ関数でメッセージを処理するというお馴染みの使い方ですが、代わりといってもWTLみたいな頭おかしい実装は技量が及ばないので、静的std::mapにしました。
# 旧BorlandのVCLもWTLと同じ方法だったようですね
これを使って、PnPメッセージを処理するためダミーウィンドウを別スレッド上に実装した、ハードウェアを操作するクラスを書きました。
動作の確認としてコンソールアプリでグローバルに定義し、mainを抜けると...手元ではウィンドウクラスのオブジェクトよりstd::mapオブジェクトが先に破棄され、ウィンドウプロシージャ内でアクセス違反が発生してしまいました。
グローバルのオブジェクトは生成・破棄の順が規格上は未定義で、例えば何かのクラスに従属させるような制御方法もないとのこと。ちなみにマイクロソフトの場合、コンパイル時の出現順に生成し、破棄はその反対順だそうな。
「グローバル」というより静的な(staticに限らず)オブジェクトの寿命をコンパイラが区別できないという話らしく、静的メンバにしても意味はありません。
手っ取り早い回避法は、
ちゃんとやれば自然と3.になりますが、2.が無難なところでしょうか。デストラクタからも後始末関数を呼んでおけば(始末済みチェックの上で)、3.では明示的に呼ばずに済みます。
きっかけはプロパティリスト(SetProp等のあれ)が遅い、と小耳に挟んだことでした。大したウィンドウを作るわけでもありませんが、更新頻度が異様に高いアプリは作ることがあるので、別の方法を試してみようとしたのです。
プロパティリストにはthisポインタを入れておき、メンバ関数でメッセージを処理するというお馴染みの使い方ですが、代わりといってもWTLみたいな頭おかしい実装は技量が及ばないので、静的std::mapにしました。
# 旧BorlandのVCLもWTLと同じ方法だったようですね
これを使って、PnPメッセージを処理するためダミーウィンドウを別スレッド上に実装した、ハードウェアを操作するクラスを書きました。
動作の確認としてコンソールアプリでグローバルに定義し、mainを抜けると...手元ではウィンドウクラスのオブジェクトよりstd::mapオブジェクトが先に破棄され、ウィンドウプロシージャ内でアクセス違反が発生してしまいました。
グローバルのオブジェクトは生成・破棄の順が規格上は未定義で、例えば何かのクラスに従属させるような制御方法もないとのこと。ちなみにマイクロソフトの場合、コンパイル時の出現順に生成し、破棄はその反対順だそうな。
「グローバル」というより静的な(staticに限らず)オブジェクトの寿命をコンパイラが区別できないという話らしく、静的メンバにしても意味はありません。
手っ取り早い回避法は、
といったところでしょうか。
- プロパティリストに戻す
- 後始末関数で明示的にDestroyWindowできるようにする
- グローバルで定義せず、他の関数からはポインタ経由とする
ちゃんとやれば自然と3.になりますが、2.が無難なところでしょうか。デストラクタからも後始末関数を呼んでおけば(始末済みチェックの上で)、3.では明示的に呼ばずに済みます。
コメントする