IsDialogMessageが処理する前にメッセージを傍受する
本文の状態
日本語全文を表示中
詳細モードで約4分の本文を読めます。
同じ出来事の情報源
この情報源を基点に整理
Andrej Karpathy 厳選
IsDialogMessageで処理される前にメッセージを事前処理する方法についての技術記事です。
Source Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
前回は、ユーザーがダイアログの閉じるボタン(または同等の非クライアント領域ジェスチャー)をクリックしたことを認識する方法を見ました。システムが提供するダイアログを閉じるもう一つの方法は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デイリーブリーフで今日の重要ニュースをまとめ読み