2009年6月4日木曜日

CWindowImplBase へんてこりんな設計

今日は、以下の部分をデバッグしていて、devenv と os レベルでハングしまくりで死にそうになった。前回も、ひっかかったのだが再現性がなくて放置したところだ。


const _ATL_MSG* pOldMsg = pThis->m_pCurrentMsg;

pThis->m_pCurrentMsg = &msg;

// pass to the message map to process

LRESULT lRes;

BOOL bRet = pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam, lRes, 0);

// restore saved value for the current message

ATLASSERT(pThis->m_pCurrentMsg == &msg);


 よく見れば、変な実装である。イベント実行中に他のイベントが発生していないかをチェックしているという事であろうか?自分のクラスのインスタンスのメンバ変数にストックしておいて、値が変わっていない事を表明している。確かに・・・しかし、回りくどい。何故こんな事をしなければならないのか?それは、GetCurrentMessage() なる怪しげな関数があるからだ。なんというか、この設計は生理的に受け付け難いものを感じてしまう。しかし、このassertのおかげで、解決が速くなったので感謝すべきか?
 結局のところ、WM_PAINT によるメッセージ中に COM アウトゴーイング・インターフェイス=イベントを発生したハンドラから、再描画を促すコードが発行されているのが原因のようだった。しょうがないから、WM_PAINT を実行する部分をフラグによりロックアウトして対処した。擬似コードは以下。


HRESULT CFoo::OnPaint(HDC hDC) {
if( m_bPaintLockout ) return S_OK;
m_bPaintLockout = TRUE;
...
m_bPaintLockout = FALSE;
return S_OK;
}


追記:どう考えても変だ。Windowsはイベント・ドリブン型で、メッセージは基本シングル・プロセスで処理され、メッセージ・キューに溜まるだけのはずである。マルチスレッドにイベントを処理していなければ、このような事態に陥るはずがない。IOleInPlaceSiteWindowressImpl 絡みで、m_bWindowOnly = TRUE を設定しているだが、CWindowImplBaseT あたりの動きが不穏すぎて、よくわからない。ATLの設計はCRTPで、しかも変てこりんなので(CExeModuleとCServiceModuleの設計はひどかった)デバッグは勘弁してほしいところだ。
追記:こんだけ、くそみそに書いてて、結局、null アクセスしてただけだったよ…トホホ…。情けねぇ。

0 件のコメント: