私のMFCアプリケーションには、CViewといくつかのフローティング非モーダルダイアログがあります。私は現在、ビューの無効化/再描画によってダイアログが再描画される理由を理解しようとしています。これは、ダイアログがビューと重なっていない場合でも発生します。
特定のダイアログの再描画を要求する人をデバッグ/追跡する方法を知っている人はいますか?ダイアログでWM_PAINTメッセージをインターセプトするのは遅すぎるようです。
助けてくれてありがとう!
幸運をお祈りしています、
ファビアン
私のMFCアプリケーションには、CViewといくつかのフローティング非モーダルダイアログがあります。私は現在、ビューの無効化/再描画によってダイアログが再描画される理由を理解しようとしています。これは、ダイアログがビューと重なっていない場合でも発生します。
特定のダイアログの再描画を要求する人をデバッグ/追跡する方法を知っている人はいますか?ダイアログでWM_PAINTメッセージをインターセプトするのは遅すぎるようです。
助けてくれてありがとう!
幸運をお祈りしています、
ファビアン
非モーダルダイアログはWS_POPUPウィンドウだと思いますよね?(つまり、アプリウィンドウ内だけでなく、画面上のどこにでも表示できるフローティングウィンドウです。)
フローティングウィンドウの場合、技術的にはフレームウィンドウの子ウィンドウではありませんが、ドキュメントでは、実際に所有者を使用する必要がある場所で親という用語を使用する傾向があるため、混乱しやすくなります。子ウィンドウは親のクライアント領域の外に表示できません。所有ウィンドウは表示できます。親ウィンドウが無効になると、子ウィンドウは無効になります。しかし、所有されているウィンドウはそうではありません。
WS_CHILDスタイルフラグを持つウィンドウのみが子ウィンドウになることができます。それ以外の場合、それらは所有ウィンドウです。
これらが所有されているウィンドウの場合、メインのアプリウィンドウから無効化を継承しないため、最初のパラメーターとしてNULLハンドルを使用して:: InvalidateRect()が呼び出される場所をコード内で探す必要があります。通常、これは初期化されていない変数が原因です。
これを呼び出すと::InvalidateRect(NULL, ...)
、すべてのウィンドウを無効にするようにWindowsに指示します。(実際には、すべてのウィンドウの親であるデスクトップウィンドウを無効にするようにWindowsに指示します)。MFCでは、すべてのウィンドウの基本クラスに、InvalidateRectのメソッドがあります。このメソッドは、ウィンドウAPIを呼び出しますが、現在のオブジェクトのウィンドウハンドルを使用します。一次近似として、そのウィンドウハンドルが正しく初期化されると想定できると思います。自分のコードで呼び出しを探すことから始める必要があります。
OnPaintを入手したら、無効化リクエストがどこから来たのかを知るには遅すぎます。したがって、このバグを見つけるには、コードを検査するかInvalidateRect()
、最初のパラメーターでNULLをインターセプトして探す必要があります。
これは一般的なケースでは難しい問題です。ウィンドウの全部または一部を無効にする方法がいくつかあり、ヘッダーファイルに多くのコードがあり、いくつかのことを自動的に実行するこの関数のバリアントを作成することで「役立ちます」。
InvalidateRect
(user32.dllにある)上部にブレークポイントを設定し、最初のパラメーターがnullであることを条件としてブレークポイントを設定しようとする場合があります。ただし、デバッガーの設定方法によっては、これを実行できない場合があります。
また、すべてのコードを強制的にコンパイルして、InvalidateRectへの呼び出しが制御する関数を介してリダイレクトされるようにすることもできます。
// in some header file that gets included early by all of your code
#define InvalidateRect my_InvalidateRect
// in one of your .cpp files.
BOOL WINAPI my_InvalidateRect(HWND hwnd, CONST RECT *prc, BOOL bErase)
{
#undef InvalidateRect
assert(hwnd != NULL);
InvalidateRect(hwnd, prc, bErase);
#define InvalidateRect my_InvalidateRect
};
それでも見つからない場合は、同じことをしInvalidateRgn
てRedrawWindow
これらの種類のバグは見つけるのが面倒です。私はあなたをうらやましくない。私自身のコードでは、InvalidateRectへの直接呼び出しを恒久的に禁止しています。これらは常にラッパー関数を通過する必要があるため、デバッグビルドでNULLウィンドウハンドルを常にチェックできます。ただし、繰り返しになりますが、私はMFCを使用しないため、この種のポリシーを適用する方が簡単です。
これを見てください:
http://msdn.microsoft.com/en-us/library/01c9aaty(VS.80).aspx
おそらくOnPaintをオーバーライドして、メッセージの送信元を見つけることができます...
編集:
フレームワークは、Windowsまたはアプリケーションがアプリケーションのウィンドウの一部を再描画するように要求すると、このメンバー関数を呼び出します。
したがって、OSが再描画を要求しているか、アプリケーション内から実行されます。
このフォーラムスレッドの最初の回答もご覧ください: http ://social.msdn.microsoft.com/Forums/en/vcgeneral/thread/3f53fce3-38dd-441b-b112-82eff4dafc9e
よくわからないMFCで何かをデバッグするのと同じ方法で行います。新しいプロジェクトで動作を再現することにより、問題を切り分けようとします。
したがって、新しいMFCアプリケーションを作成し、それにモードレスダイアログを1つ追加し、CViewでInvalidateを呼び出して、それがまだ発生するかどうかを確認します。
それでも発生する場合は、メインフレームがそのペイントメッセージを送信する必要があります。したがって、メインフレームのPreTranslateMessageでそれをキャプチャしてみることができます。
それが発生しない場合は、アプリケーションで行ったことが原因です。もしそうなら、あなたはそれがもう起こらない原因を見つけるまで、あなたのアプリケーションから複雑さを取り除くことによってあなたがしたことを解決しようとすることができます。
WM_PAINTメッセージがダイアログによっても受信される理由は、ダイアログがすでにKarimによって提案されているようにCViewの子ウィンドウであるためです。
おそらく何が起こるかというと、ビューの再描画を引き起こす無効化の後、WM_PAINT
メッセージがこのウィンドウに送信され、このウィンドウの標準のOnPaint()ハンドラー(CWndのメンバー)WM_PAINT
がその子ウィンドウ(ダイアログ)にメッセージを送信します。
WM_PAINTメッセージ自体は、UpdateWindow()
またはへの呼び出しに応答してWindowsによって送信されます。MSDNRedrawWindow()
を参照してください。
WM_PAINTメッセージはシステムによって生成されるため、アプリケーションから送信しないでください。
メッセージ処理メカニズムの背後にあるシーンに最も近いのは、CWndから派生したCViewクラスのWindowProcをオーバーライドすることです。これは、WM_PAINTおよびその他のメッセージが受信されるコールバックです。
乾杯ホルガー