{"id":35753,"date":"2005-04-28T09:00:00","date_gmt":"2005-04-28T09:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2005\/04\/28\/rescuing-thread-messages-from-modal-loops-via-message-filters\/"},"modified":"2023-08-04T14:25:24","modified_gmt":"2023-08-04T21:25:24","slug":"20050428-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050428-00\/?p=35753","title":{"rendered":"Rescuing thread messages from modal loops via message filters"},"content":{"rendered":"<p>As we have seen recently, thread messages are eaten by modal loops because they have nowhere to go when dispatched. However, there is a way to see them before they vanish, provided the modal loop is cooperative.<\/p>\n<p>The WH_MSGFILTER message hook allows you to receive messages passed to <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/api\/winuser\/nf-winuser-callmsgfilterw\"> the <code>CallMsgFilter<\/code> function<\/a>. Fortunately, all the modal loops in the window manager use <code>CallMsgFilter<\/code> to allow the thread to capture thread messages before they are lost. Therefore, this gives you a way to snoop on messages as they travel through modal loops.<\/p>\n<p>Let&#8217;s add a message filter to the program we wrote last time to see how messages pass through a message filter. Note that <strong>this is the wrong way to solve the problem<\/strong>. <a title=\"Watching thread messages disappear\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050427-10\/?p=35763\"> The correct solution was illustrated last time<\/a>. I&#8217;m doing it the wrong way to illustrate message filters since they are not well-understood. (For example, a valid reason for a message filter would to prevent the menu loop from seeing certain input.)<\/p>\n<p>Start with the program from last the before we changed the <code>PostThreadMessage<\/code> to a <code>PostMessage<\/code>, then make the following changes:<\/p>\n<pre><span style=\"border: solid 1px currentcolor; border-bottom: none;\">HHOOK g_hhkMSGF;                                                      <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                                                     <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">LRESULT CALLBACK MsgFilterProc(int code, WPARAM wParam, LPARAM lParam)<\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">{                                                                     <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\"> MSG* pmsg = (MSG*)lParam;                                            <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\"> if (code &gt;= 0 &amp;&amp; IsThreadMessage(pmsg)) return TRUE;                 <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\"> return CallNextHookEx(g_hhkMSGF, code, wParam, lParam);              <\/span>\r\n<span style=\"border: solid 1px currentcolor; border-top: none;\">}                                                                     <\/span>\r\n\r\nBOOL\r\nOnCreate(HWND hwnd, LPCREATESTRUCT lpcs)\r\n{\r\n <span style=\"border: solid 1px currentcolor; border-bottom: none;\">g_hhkMSGF = SetWindowsHookEx(WH_MSGFILTER, MsgFilterProc,<\/span>\r\n <span style=\"border: 1px currentcolor; border-style: none solid;\">   NULL, GetCurrentThreadId());                          <\/span>\r\n <span style=\"border: solid 1px currentcolor; border-top: none;\">if (!g_hhkMSGF) return FALSE;                            <\/span>\r\n DWORD dwThread;\r\n HANDLE hThread = CreateThread(NULL, 0, ThreadProc,\r\n       UintToPtr(GetCurrentThreadId()), 0, &amp;dwThread);\r\n ...\r\n<\/pre>\n<p>Here, we installed a message filter hook on our thread so that we can seem messages as they pass through modal loops. The <code>code<\/code> parameter tells us what type of modal loop retrieved the message; we ignore it here since we want to do our filtering for all modal loops.<\/p>\n<p>Run this program and observe that the beeps are no longer lost because our message filter is getting a chance to see them and react to them.<\/p>\n<p>The message filter trick relies on all modal loops sending the messages they retrieve through a message filter before dispatching them. If you are writing code that is going into a library, and you have a modal loop, then you too should call the message filter before dispatching messages you&#8217;ve retrieved, in case the program using your library wants to do something with the message.<\/p>\n<pre>MSG msg;\r\nwhile (GetMessage(&amp;msg, NULL, 0, 0)) {\r\n <span style=\"border: solid 1px currentcolor;\">if (!CallMsgFilter(&amp;msg, MSGF_MYLIBRARY)) {<\/span>\r\n  TranslateMessage(&amp;msg);\r\n  DispatchMessage(&amp;msg);\r\n <span style=\"border: solid 1px currentcolor;\">}                                          <\/span>\r\n}\r\n<\/pre>\n<p>The value <code>MSGF_MYLIBRARY<\/code> is an arbitrary positive value you can choose and document in your library&#8217;s header file. You can see examples of this in the <code>commctrl.h<\/code> header file:<\/p>\n<pre>#define MSGF_COMMCTRL_BEGINDRAG     0x4200\r\n#define MSGF_COMMCTRL_SIZEHEADER    0x4201\r\n#define MSGF_COMMCTRL_DRAGSELECT    0x4202\r\n#define MSGF_COMMCTRL_TOOLBARCUST   0x4203\r\n<\/pre>\n<p>These are the message filters called by the modal loops in the shell common controls library.<\/p>\n<p>One question you might ask is, &#8220;Why use a message filter hook instead of a <code>GetMessage<\/code> hook?&#8221;<\/p>\n<p>Message filter hooks are less expensive than <code>GetMessage<\/code> hooks because they are called only upon request, as opposed to a <code>GetMessage<\/code> hook which is called for every retrieved message. Message filter hooks also tell you <strong>which<\/strong> modal loop is doing the filtering, in case you want to adjust your behavior accordingly.<\/p>\n<p>The downside of message filter hooks is that all modal loops need to remember to call <code>CallMsgFilter<\/code> as part of their dispatch loop.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The <CODE>WH_MSGFILTER<\/CODE> hook lets you snoop on messages as they travel through modal loops.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-35753","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The <CODE>WH_MSGFILTER<\/CODE> hook lets you snoop on messages as they travel through modal loops.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/35753","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=35753"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/35753\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=35753"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=35753"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=35753"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}