https://devblogs.microsoft.com/oldnewthing/20260226-00/?p=112090 Skip to main content [RE1Mu3b] Microsoft Dev Blogs Dev Blogs Dev Blogs * Home * Developer + Microsoft for Developers + Visual Studio + Visual Studio Code + Develop from the cloud + All things Azure + Xcode + DevOps + Windows Developer + ISE Developer + Azure SDK + Command Line + Aspire * Technology + DirectX + Semantic Kernel * Languages + C++ + C# + F# + TypeScript + PowerShell Team + Python + Java + Java Blog in Chinese + Go * .NET + All .NET posts + .NET Aspire + .NET MAUI + AI + ASP.NET Core + Blazor + Entity Framework + NuGet + Servicing + .NET Blog in Chinese * Platform Development + #ifdef Windows + Microsoft Foundry + Azure Government + Azure VM Runtime Team + Bing Dev Center + Microsoft Edge Dev + Microsoft Azure + Microsoft 365 Developer + Microsoft Entra Identity Developer + Old New Thing + Power Platform * Data Development + Azure Cosmos DB + Azure Data Studio + Azure SQL + OData + Revolutions R + Unified Data Model (IDEAs) + Microsoft Entra PowerShell * More [ ] Search Search * No results Cancel * Dev Blogs * The Old New Thing * Intercepting messages inside IsDialogMessage, installing the message filter February 26th, 2026 intriguinglike2 reactions Intercepting messages inside IsDialogMessage, installing the message filter Raymond Chen Raymond Chen Show more Last time, we saw that one way to intercept the ESC in the standard dialog message loop is to use your own dialog message loop. However, you might not be able to do this, say, because the dialog procedure uses EndDialog(), and the dialog exit code is not retrievable from a custom message loop. The IsDialogMessage includes an extensibility point that lets you hook into the message processing. You can register a message filter hook and listen for MSGF_DIALOGBOX. Before processing a message, the IsDialogMessage function does a CallMsgFilter with the message that it is about to process and the filter code MSGF_DIALOGBOX. If the filter result is nonzero (indicating that one of the hooks wanted to block default processing), then the IsDialogMessage returns without doing anything. This lets us grab the ESC from IsDialogMessage before it turns into an IDCANCEL. Here's our first attempt. (There will be more than one.) HWND hdlgHook; #define DM_ESCPRESSED (WM_USER + 100) LRESULT CALLBACK DialogEscHookProc(int nCode, WPARAM wParam, LPARAM lParam) { if (code == MSGF_DIALOGBOX) { auto msg = (MSG*)lParam; if (IsDialogESC(hdlgHook, msg)) { return SendMessage(hdlg, DM_ESCPRESSED, 0, lParam); } } return CallNextHookEx(nullptr, nCode, wParam, lParam); } Our hook procedure first checks that it's being called by IsDialogMessage. if so, and the message is a press of the ESC key destined for our dialog box (or a control on that dialog box), then send the dialog box a DM_ESCPRESSED message to ask it what it thinks. The dialog procedure can return TRUE to block default processing or FALSE to allow default processing to continue. Here is the handler in the dialog procedure itself: INT_PTR CALLBACK DialogProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: hdlgHook = hdlg; [ other dialog initialization as before [?] [ ending with "return (whatever)" [?] case DM_ESCPRESSED: if ([ we want to process the ESC key ourselves [?]) { [ do custom ESC key processing [?] SetWindowLongPtr(hdlg, DWLP_MSGRESULT, TRUE); return TRUE; } break; [ handle other messages [?] } return FALSE; } When the dialog initializes, remember its handle as the dialog for which the DialogEscHookProc is operating. When the dialog is informed that the ESC key was pressed, we decide whether we want to process the ESC key ourselves. If so, then we do that custom processing and set up to return TRUE from the window procedure. For dialog procedures, this is done by setting the message result to the desired window procedure result and then returning TRUE to block default dialog box message processing and instead return the value we set (which is TRUE) from the window procedure. Finally, we install the message hook before we create the dialog box and remove it when the dialog box dismisses. auto hook = SetWindowsHookEx(WM_MSGFILTER, DialogEscHookProc, nullptr, GetCurrentThreadId()); auto result = DialogBox(hinst, MAKEINTRESOURCE(IDD_WHATEVER), hwndOwner, DialogProc); UnhookWindowsHookEx(hook); This is the basic idea, but we see that there are a few problems. One is that we are communicating the dialog box handle through a global variable. This means that we can't have multiple threads using this hook at the same time. Fortunately, that can be fixed by changing the variable to be thread_local, although this does drag in the cost of thread-local variables. But even if we do that, we have a problem if two copies of this dialog box are shown by the same thread. For example, one of the controls in the dialog might launch another copy of this dialog, but with different parameters. For example, a "View certificate" dialog might have a button called "View parent certificate". We'll take up these issues (and others) next time. 2 * * * * * * 1 0 * Facebook Share on Facebook * Share on X * LinkedIn Share on Linkedin Category Old New Thing Topics Code Share * * * Author Raymond Chen Raymond Chen Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information. 1 comment Join the discussion. Leave a commentCancel reply Sign in [ ] [Reply] [Cancel] Code of Conduct Sort by : Newest Newest Popular Oldest * [png] Joshua Hudson February 26, 2026 0 Collapse this comment Copy link Getting rid of the global variable 1) Know the class names of all affected dialog boxes 2) check window class in the hook Log in to Vote or Reply Read next February 27, 2026 Intercepting messages inside IsDialogMessage, fine-tuning the message filter Raymond Chen Raymond Chen March 2, 2026 What sort of horrible things happen if my dialog has a non-button with the control ID of IDCANCEL? Raymond Chen Raymond Chen Stay informed Get notified when new posts are published. Email * [ ] Country/Region * [Select... ] I would like to receive the The Old New Thing Newsletter. Privacy Statement. Subscribe Follow this blog youtube Are you sure you wish to delete this comment? OK Cancel Sign in Theme Insert/edit link Close Enter the destination URL URL [ ] Link Text [ ] [ ] Open link in a new tab Or link to existing content Search [ ] No search term specified. Showing recent items. Search or use up and down arrow keys to select an item. Cancel [Add Link] Code Block x Paste your code snippet [ ] Ok Cancel What's new * Surface Pro * Surface Laptop * Surface Laptop Studio 2 * Copilot for organizations * Copilot for personal use * AI in Windows * Explore Microsoft products * Windows 11 apps Microsoft Store * Account profile * Download Center * Microsoft Store support * Returns * Order tracking * Certified Refurbished * Microsoft Store Promise * Flexible Payments Education * Microsoft in education * Devices for education * Microsoft Teams for Education * Microsoft 365 Education * How to buy for your school * Educator training and development * Deals for students and parents * AI for education Business * Microsoft AI * Microsoft Security * Dynamics 365 * Microsoft 365 * Microsoft Power Platform * Microsoft Teams * Microsoft 365 Copilot * Small Business Developer & IT * Azure * Microsoft Developer * Microsoft Learn * Support for AI marketplace apps * Microsoft Tech Community * Microsoft Marketplace * Marketplace Rewards * Visual Studio Company * Careers * About Microsoft * Company news * Privacy at Microsoft * Investors * Diversity and inclusion * Accessibility * Sustainability Your Privacy Choices Your Privacy Choices Consumer Health Privacy * Sitemap * Contact Microsoft * Privacy * Manage cookies * Terms of use * Trademarks * Safety & eco * Recycling * About our ads * (c) Microsoft 2026