{"id":2918,"date":"2024-01-17T11:29:45","date_gmt":"2024-01-17T16:29:45","guid":{"rendered":"https:\/\/badecho.com\/?p=2918"},"modified":"2024-01-17T11:29:49","modified_gmt":"2024-01-17T16:29:49","slug":"message-queue-messages","status":"publish","type":"post","link":"https:\/\/badecho.com\/index.php\/2024\/01\/17\/message-queue-messages\/","title":{"rendered":"Managed Interception of Message Queue Messages Using Win32 Hooks"},"content":{"rendered":"\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2024\/01\/MessageQueueMessages.png\" alt=\"Managed Interception of Message Queue Messages Using Win32 Hooks\" class=\"wp-image-2921\" width=\"856\" height=\"448\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2024\/01\/MessageQueueMessages.png 856w, https:\/\/badecho.com\/wp-content\/uploads\/2024\/01\/MessageQueueMessages-300x157.png 300w, https:\/\/badecho.com\/wp-content\/uploads\/2024\/01\/MessageQueueMessages-768x402.png 768w, https:\/\/badecho.com\/wp-content\/uploads\/2024\/01\/MessageQueueMessages-480x251.png 480w\" sizes=\"(max-width: 856px) 100vw, 856px\" \/><\/figure><\/div>\n\n\n\n<p>Hello and welcome to what will be the final article in a series that looks at the interception of window messages with managed code. The first two articles dealt with intercepting messages bound for a window procedure (for both <a href=\"https:\/\/badecho.com\/index.php\/2023\/12\/29\/local-window-messages\/\" target=\"_blank\" rel=\"noreferrer noopener\">local<\/a> and <a href=\"https:\/\/badecho.com\/index.php\/2024\/01\/13\/external-window-messages\/\" target=\"_blank\" rel=\"noreferrer noopener\">external<\/a> windows); this final one will deal with grabbing messages within a <a href=\"https:\/\/badecho.com\/index.php\/2023\/12\/29\/local-window-messages\/#message-loops\" target=\"_blank\" rel=\"noreferrer noopener\">message loop<\/a>.<\/p>\n\n\n\n<p>The message queue hook procedure differs from the one used in the previous articles, so we&#8217;ll need to provide another implementation of <code>IMessageSource&lt;THookProc&gt;<\/code>, with the enclosing type being <code>GetMessageHookProc<\/code>.<\/p>\n\n\n\n<p>Message queue hooks are somewhat similar to window procedure hooks; however, there are some crucial differences, including support for mutable message details. <\/p>\n\n\n\n<p>We already added support in the <a href=\"https:\/\/badecho.com\/index.php\/2024\/01\/13\/external-window-messages\/\" target=\"_blank\" rel=\"noreferrer noopener\">previous article<\/a> for <code>WH_GETMESSAGE<\/code> in our Native DLL, so this will probably (and hopefully) be a shorter write (for me) and read (for you).<\/p>\n\n\n\n<h2>The Hook Procedure for GetMessage<\/h2>\n\n\n\n<p>In the two previous articles, we used the <code>WindowHookProc<\/code> callback delegate, whose signature is compatible with what the <code>WH_CALLWNDPROC<\/code> and <code>WH_CALLWNDPROCRET<\/code> hook types expect.<\/p>\n\n\n\n<p>While the <code>WH_GETMESSAGE<\/code> hook procedure has a signature that looks identical to <code>WH_CALLWNDPROC<\/code>\/<code>WH_CALLWNDPROCRET<\/code>,  there is one big difference: the message details (pointed to by <code>lParam<\/code>) are mutable.<\/p>\n\n\n\n<p>I don&#8217;t know about you, but changing the details of messages about to be pumped through another process&#8217;s message queue sounds like a great way to inject a ton of chaos to me. So of course, we must add support for this!<\/p>\n\n\n\n<h6>GetMessageHookProc.cs<\/h6>\n\n\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/\/ &lt;summary&gt;\n\/\/\/ Represents a callback that receives messages about to be returned from a message queue.\n\/\/\/ &lt;\/summary&gt;\n\/\/\/ &lt;param name=&quot;hWnd&quot;&gt;A handle to the window.&lt;\/param&gt;\n\/\/\/ &lt;param name=&quot;msg&quot;&gt;The message.&lt;\/param&gt;\n\/\/\/ &lt;param name=&quot;wParam&quot;&gt;Additional message-specific information.&lt;\/param&gt;\n\/\/\/ &lt;param name=&quot;lParam&quot;&gt;Additional message-specific information.&lt;\/param&gt;\n\/\/\/ &lt;returns&gt;\n\/\/\/ The result of the message processing, which of course depends on the message being processed.\n\/\/\/ &lt;\/returns&gt;\npublic delegate HookResult GetMessageHookProc(IntPtr hWnd, \n                                              ref uint msg, \n                                              ref IntPtr wParam, \n                                              ref IntPtr lParam);\n<\/pre>\n\n\n<p>You might take a look at the delegate above and say, &#8220;Boy, that&#8217;s a lot ref parameters.&#8221; You would be correct friend!<\/p>\n\n\n\n<p>All these parameters can be changed by a hook procedure installed in the hook chain. Let us make this possible in .NET-land by creating a new implementation of <code>IMessageSource&lt;THookProc&gt;<\/code> specifically for <code>WH_GETMESSAGE<\/code> hooks.<\/p>\n\n\n\n<h2>Creating a Message Queue Message Source<\/h2>\n\n\n\n<p>If you read the <a href=\"https:\/\/badecho.com\/index.php\/2023\/12\/29\/local-window-messages\/\" target=\"_blank\" rel=\"noreferrer noopener\">first article<\/a>, you will recall the <a href=\"https:\/\/badecho.com\/index.php\/2023\/12\/29\/local-window-messages\/#window-wrapper\" target=\"_blank\" rel=\"noreferrer noopener\"><code>WindowWrapper<\/code> type<\/a> and implementation of <code>IMessageSource&lt;WindowHookProc&gt;<\/code>, and it is from this type that all our subsequent window hooking types are derived.<\/p>\n\n\n\n<p>We will be taking a similar approach with messages queues; however, this class will be sealed and self-contained, given the singular nature of the hook itself. <\/p>\n\n\n\n<p>Additionally, there is no need to wrap any sort of unmanaged handle, be it an HWND or whatnot, with message queues; all we need is a thread ID.<\/p>\n\n\n\n<p>So it&#8217;ll look a little bit different.<\/p>\n\n\n\n<h6>MessageQueueMessageSource.cs<\/h6>\n\n\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/\/ &lt;summary&gt;\n\/\/\/ Provides a publisher of messages being read from a message queue.\n\/\/\/ &lt;\/summary&gt;\npublic sealed class MessageQueueMessageSource : IMessageSource&lt;GetMessageHookProc&gt;, IDisposable\n{\n    private readonly CachedWeakList&lt;GetMessageHookProc&gt; _hooks = new();\n    private readonly MessageOnlyExecutor _hookExecutor = new();\n    private readonly int _threadId;\n\n    private bool _disposed;\n    private bool _queueHooked;\n\n    \/\/\/ &lt;summary&gt;\n    \/\/\/ Initializes a new instance of the &lt;see cref=&quot;MessageQueueMessageSource&quot;\/&gt; class.\n    \/\/\/ &lt;\/summary&gt;\n    \/\/\/ &lt;param name=&quot;threadId&quot;&gt;\n    \/\/\/ The identifier for the thread whose message pump we're hooking.\n    \/\/\/ &lt;\/param&gt;\n    public MessageQueueMessageSource(int threadId)\n    {\n        _threadId = threadId;\n    }\n\n    \/\/\/ &lt;inheritdoc\/&gt;\n    public void AddHook(GetMessageHookProc hook)\n    {\n        Require.NotNull(hook, nameof(hook));\n\n        _hooks.Add(hook);\n\n        InitializeHook();\n    }\n\n    \/\/\/ &lt;inheritdoc\/&gt;\n    public void RemoveHook(GetMessageHookProc hook)\n    {\n        Require.NotNull(hook, nameof(hook));\n\n        _hooks.Remove(hook);\n    }\n\n    \/\/\/ &lt;inheritdoc\/&gt;\n    public void Dispose()\n    {\n        if (_disposed)\n            return;\n\n        if (_queueHooked)\n        {\n            _queueHooked = !Hooks.RemoveHook(HookType.GetMessage, _threadId);\n\n            if (_queueHooked)\n                Logger.Warning(Strings.UnhookMessageQueueFailed.InvariantFormat(_threadId));\n        }\n\n        _hookExecutor.Dispose();\n\n        _disposed = true;\n    }\n\n    private async void InitializeHook()\n    {\n        if (_hookExecutor.Window != null)\n            return;\n         \n        await _hookExecutor.RunAsync();\n            \n        if (_hookExecutor.Window == null)\n            throw new InvalidOperationException(Strings.MessageQueueForHookFailed);\n\n        _hookExecutor.Window.AddHook(GetMessageProcedure);\n\n        _queueHooked = Hooks.AddHook(HookType.GetMessage,\n                                     _threadId,\n                                     _hookExecutor.Window.Handle);\n    }\n\n    private HookResult GetMessageProcedure(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)\n    {\n        uint localMsg = msg;\n        IntPtr localWParam = wParam;\n        IntPtr localLParam = lParam;\n\n        foreach (GetMessageHookProc hook in _hooks)\n        {\n            var result = hook(hWnd, ref localMsg, ref localWParam, ref localLParam);\n\n            if (result.Handled)\n                break;\n        }\n        \n        if (localMsg != msg || localWParam != wParam || localLParam != lParam)\n            Hooks.ChangeMessageDetails(localMsg, localWParam, localLParam);\n\n        \/\/ We always mark it as handled, we don't want further processing by any supporting\n        \/\/ infrastructure.\n        return new HookResult(IntPtr.Zero, true);\n    }\n}\n<\/pre>\n\n\n<p>The above code will relay all messages about to be returned from a <code>GetMessage<\/code> function call to our code and allow us to make any changes we wish to the message&#8217;s details. <\/p>\n\n\n\n<p>You&#8217;ll notice that this class also uses a <code>MessageOnlyExecutor<\/code> to receive the inbound hook messages from our native DLL, much like <code>GlobalWindowWrapper<\/code> did. <a href=\"https:\/\/badecho.com\/index.php\/2024\/01\/13\/external-window-messages\/#executors\" target=\"_blank\" rel=\"noreferrer noopener\">Refer to the second article in this series<\/a> for more information on the role of this class.<\/p>\n\n\n\n<p>And they said you couldn&#8217;t use Win32 hooks in .NET! Pfft.<\/p>\n\n\n\n<h2>Other Hook Types and Going Global<\/h2>\n\n\n\n<p>We can easily add support for all the other various hook types by following a similar approach as demonstrated above. <\/p>\n\n\n\n<p>If we want to create global, system-wide hooks (very dangerous and fun), we&#8217;d need to have some additional P\/Invoke functions that would do the same stuff as <code>AddHook<\/code> but without specifying the thread ID.<\/p>\n\n\n\n<p>Some hook types either only support global installations or are only helpful when used in a global context. So I&#8217;ll eventually add this capability to the Bad Echo codebase, and you should be able to add it to yours easily; no article on the matter needs to be written.<\/p>\n\n\n\n<h2>Messages: Intercepted!<\/h2>\n\n\n\n<p>This concludes the three-part series on intercepting window messages from .NET code. Hooks are an interesting Windows programming topic, so I hope you found the information shared as enjoyable as I did.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"alignright size-full\"><img loading=\"lazy\" width=\"112\" height=\"112\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2020\/09\/omnigasm112-e1601023634957.png\" alt=\"Yay, we're done.\" class=\"wp-image-179\"\/><\/figure><\/div>\n\n\n\n<p>The majority of the code mentioned in these articles can be found on the <a href=\"https:\/\/github.com\/BadEcho\/core\" target=\"_blank\" rel=\"noreferrer noopener\">Bad Echo core technologies source repository<\/a>.<\/p>\n\n\n\n<p>Have fun, and don&#8217;t do anything you&#8217;re not supposed to with this code! This was probably the shortest article I&#8217;ve ever written; I should do it more often.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hello and welcome to what will be the final article in a series that looks at the interception of window [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[10],"tags":[41,42,85,84],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v14.9 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\r\n<title>Managed Interception of Message Queue Messages Using Hooks<\/title>\r\n<meta name=\"description\" content=\"Shows how, with managed code, we can intercept messages being retrieved from a thread&#039;s message queue using Win32 hooks.\" \/>\r\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\r\n<link rel=\"canonical\" href=\"https:\/\/badecho.com\/index.php\/2024\/01\/17\/message-queue-messages\/\" \/>\r\n<meta property=\"og:locale\" content=\"en_US\" \/>\r\n<meta property=\"og:type\" content=\"article\" \/>\r\n<meta property=\"og:title\" content=\"Managed Interception of Message Queue Messages Using Hooks\" \/>\r\n<meta property=\"og:description\" content=\"Shows how, with managed code, we can intercept messages being retrieved from a thread&#039;s message queue using Win32 hooks.\" \/>\r\n<meta property=\"og:url\" content=\"https:\/\/badecho.com\/index.php\/2024\/01\/17\/message-queue-messages\/\" \/>\r\n<meta property=\"og:site_name\" content=\"omni&#039;s hackpad\" \/>\r\n<meta property=\"article:published_time\" content=\"2024-01-17T16:29:45+00:00\" \/>\r\n<meta property=\"article:modified_time\" content=\"2024-01-17T16:29:49+00:00\" \/>\r\n<meta property=\"og:image\" content=\"https:\/\/badecho.com\/wp-content\/uploads\/2024\/01\/MessageQueueMessages.png\" \/>\r\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\r\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebSite\",\"@id\":\"https:\/\/badecho.com\/#website\",\"url\":\"https:\/\/badecho.com\/\",\"name\":\"omni&#039;s hackpad\",\"description\":\"Game Code Disassembly. Omnified Modification. Madness.\",\"publisher\":{\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":\"https:\/\/badecho.com\/?s={search_term_string}\",\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/badecho.com\/index.php\/2024\/01\/17\/message-queue-messages\/#primaryimage\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/badecho.com\/wp-content\/uploads\/2024\/01\/MessageQueueMessages.png\",\"width\":856,\"height\":448,\"caption\":\"Managed Interception of Message Queue Messages Using Win32 Hooks\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/badecho.com\/index.php\/2024\/01\/17\/message-queue-messages\/#webpage\",\"url\":\"https:\/\/badecho.com\/index.php\/2024\/01\/17\/message-queue-messages\/\",\"name\":\"Managed Interception of Message Queue Messages Using Hooks\",\"isPartOf\":{\"@id\":\"https:\/\/badecho.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2024\/01\/17\/message-queue-messages\/#primaryimage\"},\"datePublished\":\"2024-01-17T16:29:45+00:00\",\"dateModified\":\"2024-01-17T16:29:49+00:00\",\"description\":\"Shows how, with managed code, we can intercept messages being retrieved from a thread's message queue using Win32 hooks.\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/badecho.com\/index.php\/2024\/01\/17\/message-queue-messages\/\"]}]},{\"@type\":\"Article\",\"@id\":\"https:\/\/badecho.com\/index.php\/2024\/01\/17\/message-queue-messages\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2024\/01\/17\/message-queue-messages\/#webpage\"},\"author\":{\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\"},\"headline\":\"Managed Interception of Message Queue Messages Using Win32 Hooks\",\"datePublished\":\"2024-01-17T16:29:45+00:00\",\"dateModified\":\"2024-01-17T16:29:49+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2024\/01\/17\/message-queue-messages\/#webpage\"},\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\"},\"image\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2024\/01\/17\/message-queue-messages\/#primaryimage\"},\"keywords\":\".NET,C#,C++,Win32\",\"articleSection\":\"General Dev\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/badecho.com\/index.php\/2024\/01\/17\/message-queue-messages\/#respond\"]}]},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\",\"name\":\"Matt Weber\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/badecho.com\/#personlogo\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/7e345ac2708b3a41c7bd70a4a0440d41?s=96&d=mm&r=g\",\"caption\":\"Matt Weber\"},\"logo\":{\"@id\":\"https:\/\/badecho.com\/#personlogo\"}}]}<\/script>\r\n<!-- \/ Yoast SEO plugin. -->","_links":{"self":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts\/2918"}],"collection":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/comments?post=2918"}],"version-history":[{"count":10,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts\/2918\/revisions"}],"predecessor-version":[{"id":2945,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts\/2918\/revisions\/2945"}],"wp:attachment":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/media?parent=2918"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/categories?post=2918"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/tags?post=2918"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}