{"id":110354,"date":"2024-10-09T07:00:00","date_gmt":"2024-10-09T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110354"},"modified":"2024-10-09T19:22:49","modified_gmt":"2024-10-10T02:22:49","slug":"20241009-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20241009-00\/?p=110354","title":{"rendered":"If threads are created without a message queue, why can I post to them immediately upon creation?"},"content":{"rendered":"<p><a title=\"About messages and message queues\" href=\"https:\/\/learn.microsoft.com\/windows\/win32\/winmsg\/about-messages-and-message-queues\"> The documentation for Windows message queues<\/a> says that<\/p>\n<blockquote class=\"q\"><p>To avoid the overhead of creating a message queue for non-GUI threads, all threads are created initially without a message queue. The system creates a thread-specific message queue only when the thread makes its first call to one of the specific user functions.<\/p><\/blockquote>\n<p>A customer was unable to observe the documented behavior. According to their experiments, they found that they were able to post a message to a thread immediately upon its creation!<\/p>\n<pre>DWORD CALLBACK ThreadProc(void* parameter)\r\n{\r\n    \/\/ This succeeds (?)\r\n    BOOL success =\r\n        PostThreadMessage(GetCurrentThreadId(), WM_APP, 314, 159);\r\n\r\n    \/\/ So does this (!)\r\n    MSG msg;\r\n    success = PeekMessage(&amp;msg, nullptr, 0, 0, PM_REMOVE);\r\n\r\n    \/\/ And it's our message (!)\r\n    if (msg.message == WM_APP) {\r\n        OutputDebugStringW(L\"We received the message (?)\\r\\n\");\r\n    }\r\n\r\n    return 0;\r\n}\r\n<\/pre>\n<p>What&#8217;s going on here? We were able to post a message to the thread despite it never having created a message queue.<\/p>\n<p>What&#8217;s going on here is that <code>Post\u00adThread\u00adMessage<\/code> is itself a message queue creation function!<\/p>\n<p>So at the time you call <code>Post\u00adThread\u00adMessage<\/code>, the thread does not have a message queue. When you call it, the system says, &#8220;First, I need to create a message queue for the current thread if it doesn&#8217;t have one.&#8221; This is what creates the message queue. And then we post the thread message into that queue.<\/p>\n<p>If you want to see <code>Post\u00adThread\u00adMessage<\/code> fail due to the lack of a message queue in the destination thread, then use it to post a message into the message queue of some <i>other<\/i> thread.<\/p>\n<pre>static auto ready = CreateEvent(nullptr, TRUE, FALSE, nullptr);\r\nstatic auto exit = CreateEvent(nullptr, TRUE, FALSE, nullptr);\r\n\r\n\/\/ Start a thread and wait for it to start.\r\nDWORD id;\r\nauto thread = CreateThread(nullptr, 0, [](void*) {\r\n    SetEvent(ready);\r\n    return WaitForSingleObject(exit, INFINITE);\r\n}, nullptr, 0, &amp;id);\r\n\r\nWaitForSingleObject(ready, INFINITE);\r\n\r\n\/\/ Now that it has started, try to post it a message.\r\nauto success = PostThreadMessage(id, WM_APP, 314, 159);\r\n\r\n\/\/ Tell the thread to exit.\r\nSetEvent(exit);\r\n<\/pre>\n<p>In this case, the <code>Post\u00adThread\u00adMessage<\/code> fails because the thread has not yet created a queue.<\/p>\n<p>The customer&#8217;s experiment failed because it made the thread post a thread message to itself, and the act of posting a thread message creates the message queue. To observe the absence of a message queue, you have to do the post from another thread.<\/p>\n<p>So which functions create a message queue?<\/p>\n<p>The functions that are guaranteed to create a message queue are <code>Peek\u00adMessage<\/code>, <code>Get\u00adMessage<\/code>, and <code>Create\u00adWindow<\/code> (or other functions that create windows like <code>DialogBox<\/code>). There may be other functions that also create a message queue as a side effect (like <code>Post\u00adThread\u00adMessage<\/code>), but you shouldn&#8217;t rely on them.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Check who is doing the posting.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-110354","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Check who is doing the posting.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110354","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=110354"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110354\/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=110354"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110354"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110354"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}