{"id":112348,"date":"2026-05-22T07:00:00","date_gmt":"2026-05-22T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112348"},"modified":"2026-05-22T15:13:07","modified_gmt":"2026-05-22T22:13:07","slug":"20260522-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260522-00\/?p=112348","title":{"rendered":"Why do you say that a COM STA thread must pump messages if I see sample code creating STA threads and not pumping messages?"},"content":{"rendered":"<p>One of the rules for COM single-threaded apartments (STA) is that the thread in that apartment must pump messages. But we also see code that initializes COM in single-threaded mode but which never pumps messages. Consider <a href=\"https:\/\/github.com\/microsoft\/Windows-classic-samples\/blob\/adf93f04125e0b5bd84ad0a9a073f7fea993a360\/Samples\/MSXML\/DynamDom\/cpp\/dynamDOM.cpp#L271C1-L280C2\"> this function<\/a> from the <a href=\"https:\/\/github.com\/microsoft\/Windows-classic-samples\/blob\/adf93f04125e0b5bd84ad0a9a073f7fea993a360\/Samples\/MSXML\/DynamDom\/README.md\"> XML DOM object dynamic creation sample<\/a>:<\/p>\n<pre>int __cdecl wmain()\r\n{\r\n    HRESULT hr = CoInitialize(NULL);\r\n    if (SUCCEEDED(hr))\r\n    {\r\n        dynamDOM();\r\n        CoUninitialize();\r\n    }\r\n    return 0;\r\n}\r\n<\/pre>\n<p>The <code>Co\u00adInitialize<\/code> function initializes COM in single-threaded apartment mode, and then the program does some work, and then it uninitializes COM, and it <i>never pumps messages<\/i>. What gives? Shouldn&#8217;t there be a message loop?<\/p>\n<p>The rule about single-threaded apartments is that they must pump messages <i>when idle<\/i>. If they are busy doing something, then clearly they can&#8217;t pump messages because they are busy doing something!\u00b9<\/p>\n<p>If your thread initializes COM as a single-threaded apartment, and then does a bunch of work, and then uninitializes COM, then that&#8217;s great. Your thread was never idle, so it never got a chance to pump messages. (Though if your thread made COM calls out to other threads, COM will pump messages while waiting for the reply, so it did pump messages while the thread was idle.)<\/p>\n<p>Failing to pump messages when idle means that when another thread wants to communicate with your thread, it never gets a response. Now, if your thread is busy, then it&#8217;s fine that the other thread doesn&#8217;t get a response from you\u2014you&#8217;re busy with something else after all. But if you are in a single-threaded COM apartment and you have finished with whatever you&#8217;re doing, you need to pump messages to see if there&#8217;s any work that COM wants you to do, or you need to uninitialize COM.<\/p>\n<p>Now, you might say, &#8220;Look, my thread doesn&#8217;t create any windows, and it doesn&#8217;t do any cross-thread COM stuff, so who cares that it&#8217;s not pumping messages? It&#8217;s not like anybody is ever going to ask this thread to do anything, and since it created no windows, nobody could send it anything.&#8221;<\/p>\n<p>Aha, but you see, your thread <i>did<\/i> create a window. When you initialize a thread as a single-threaded apartment, <u>COM<\/u> creates a window. It creates this window so that it can receive inbound requests for the thread to do something. If you don&#8217;t pump messages, then you have a thread blocked not pumping messages, which will jam up window broadcasts.<\/p>\n<p>\u00b9 An intentionally obtuse interpretation of the rule that &#8220;an STA thread must pump messages&#8221; would be that your thread can&#8217;t do anything except call <code>GetMessage<\/code> and <code>DispatchMessage<\/code>! Because any other line of code would not be &#8220;pumping messages&#8221;.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You need to pump messages when idle, but maybe you are never idle.<\/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-112348","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You need to pump messages when idle, but maybe you are never idle.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112348","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=112348"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112348\/revisions"}],"predecessor-version":[{"id":112349,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112348\/revisions\/112349"}],"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=112348"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112348"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112348"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}