IsDialogMessageが処理する前にメッセージを傍受する
IsDialogMessageで処理される前にメッセージを事前処理する方法についての技術記事です。
キーポイント
WindowsダイアログボックスでのESCキー処理のカスタマイズ手法を解説
IsDialogMessage関数の前にメッセージをインターセプトする実装方法
DLGC_WANTALLKEYSやDLGC_WANTMESSAGEを考慮したESCキー処理の実装
ダイアログ手続きがEndDialogを使用する場合の制限に言及
影響分析・編集コメントを表示
影響分析
この記事はWindowsプログラミングにおけるダイアログメッセージ処理の高度な実装技術を解説しており、特定の開発者コミュニティには有用な情報を提供している。ただし、AI/テクノロジーニュース全体から見ると、限定的な技術トピックであり、広範な影響はない。
編集コメント
Windowsネイティブアプリケーション開発者向けの専門的な技術記事であり、AI業界全体のトレンドとは直接関係が薄い内容です。
前回は、ユーザーがダイアログの閉じるボタン(または同等の非クライアント領域ジェスチャー)をクリックしたことを認識する方法を見ました。システムが提供するダイアログを閉じるもう一つの方法はESCキーの押下であり、フローチャートから、これはIsDialogMessage関数によって処理されることが分かりました。
ダイアログボックスにIDCANCELボタンがない場合、IsDialogMessageはESCキーを無視します。したがって、ESCキーを処理したい場合は、IsDialogMessageが処理する前に傍受する必要があります。
さて、システムが提供するダイアログ終了の追加経路が将来追加される可能性を懸念するかもしれません。(もしかすると、新しいタッチジェスチャーが発明されるかもしれません。)しかし、将来のシステム提供の終了メカニズムがどの経路を取るか予測できないため、コード化すべき対象がありません。できることは、既知の経路をカバーし、将来の終了メカニズムがいずれかの既存経路に従うことを望むことだけです。
フローチャートから、ESCキー経路はIsDialogMessage関数を経由することが分かりました。したがって、IsDialogMessageが処理する前にESCキーを傍受すればよいのです。
これを行う一つの方法は、IsDialogMessageを呼び出す前にESCキーを認識することです。
最初の試みは次のようになるかもしれません:
while (⟦ダイアログがまだアクティブ⟧ && GetMessage(&msg, NULL, 0, 0, 0)) { if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE) { ⟦カスタムESCキー処理を実行⟧ } else if (!IsDialogMessage(hdlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
しかし、これはDLGC_WANTALLKEYSを宣言するコントロールを尊重しません。DLGC_WANTALLKEYSは、コントロールがすべてのキーボードメッセージを処理したいことを示します。ESCキーもその一つです。DLGC_WANTMESSAGEも同様に、コントロールがこの特定のメッセージを処理したいことを示します。
bool IsDialogESC(HDLG hdlg, MSG const* msg) { if (msg->message != WM_KEYDOWN || msg->wParam != VK_ESCAPE) { return false; } if (msg->hwnd != hdlg && !IsChild(hdlg, msg->hwnd)) { return false; } auto code = SendMessage(msg->hwnd, WM_GETDLGCODE, msg->wParam, (LPARAM)msg); if (code & (DLGC_WANTALLKEYS | DLGC_WANTMESSAGE)) { return false; } return true; }
これがダイアログ終了の可能性のあるESCキーであるためには、メッセージはVK_ESCAPE仮想キーコードを持つWM_KEYDOWNメッセージでなければなりません。また、メッセージのターゲットウィンドウはダイアログ自体か、その子孫でなければなりません。最後に、ターゲットウィンドウがこのメッセージを処理したいと主張してはなりません。
できるだけ早く早期リターンできるように、テストの順序を慎重に決めました。ESCキーのチェックはメッセージを調べるだけでできます。ターゲットウィンドウが適切かどうかのチェックは少し手間がかかりますが、それほど悪くはありません。ターゲットウィンドウがメッセージを処理したいかどうかのチェックが最もコストのかかるテストなので、最後に行います。
このヘルパー関数をカスタムメッセージループに組み込むことができます。
while (⟦ダイアログがまだアクティブ⟧ && GetMessage(&msg, NULL, 0, 0, 0)) { if (IsDialogESC(hdlg, &msg)) { ⟦カスタムESCキー処理を実行⟧ } else if (!IsDialogMessage(hdlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
これは素晴らしいように聞こえますが、この変更を行えない場合もあるかもしれません。例えば、ダイアログボックスのダイアログプロシージャがEndDialogを呼び出してダイアログを閉じる場合、メッセージループは終了し、制御はダイアログを表示した関数に戻ります。その関数は、ダイアログが閉じられた理由を知る必要があるかもしれません。特に、ESCキーが押されたかどうかを知る必要があるかもしれません。
この問題については次回検討します。
原文を表示
Last time, we looked at recognizing that the user clicked the Close button (or equivalent nonclient gestures) on a dialog. The other system-provided pathway to dismissing a dialog is pressing ESC, and we saw in our flow diagram that this is done by the IsDialogMessage
If your dialog box doesn’t have an IDCANCEL
Now, you might be concerned that additional pathways for system-provided dialog dismissal may be added later. (Who knows, maybe a new touch gesture will be invented.) But you can’t predict what pathway that future system-provide dismissal will take, so you have nothing to code to. All you can do is cover the pathways that you know and hope that any future dismissal mechanisms will follow one of them.
We saw from our diagram that the ESC pathway consists of the IsDialogMessage
One way to do this is to recognize the ESC key before calling IsDialogMessage
Your first try might go like this:
while (⟦ dialog still active ⟧ && GetMessage(&msg, NULL, 0, 0, 0)) { if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE) { ⟦ do custom ESC key handling ⟧ } else if (!IsDialogMessage(hdlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
However, this fails to honor controls that declare DLGC_WANTALLKEYS
DLGC_WANTMESSAGE
bool IsDialogESC(HDLG hdlg, MSG const* msg) { if (msg->message != WM_KEYDOWN || msg->wParam != VK_ESCAPE) { return false; } if (msg->hwnd != hdlg && !IsChild(hdlg, msg->hwnd)) { return false; } auto code = SendMessage(msg->hwnd, WM_GETDLGCODE, msg->wParam, (LPARAM)msg); if (code & (DLGC_WANTALLKEYS | DLGC_WANTMESSAGE)) { return false; } return true; }
In order for this to be a potential dialog-dismissing ESC, the message must be a WM_KEYDOWN
I carefully ordered the tests so that we can early-out as quickly as possible. Checking for the ESC key can be done by inspecting the message. Checking that the target window is acceptable is a little more work, but not too bad. Checking whether the target window wants to handle the message is the most expensive test, so we do that last.
Now we can incorporate this helper function into our custom message loop.
while (⟦ dialog still active ⟧ && GetMessage(&msg, NULL, 0, 0, 0)) { if (IsDialogESC(hdlg, &msg)) { ⟦ do custom ESC key handling ⟧ } else if (!IsDialogMessage(hdlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
This all sounds great, but you might not be able to make this change. For example, maybe the dialog box’s dialog procedure uses EndDialog()
We’ll study this problem next time.
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み