{"id":112500,"date":"2026-07-02T07:00:00","date_gmt":"2026-07-02T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112500"},"modified":"2026-07-02T09:12:51","modified_gmt":"2026-07-02T16:12:51","slug":"20260702-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260702-00\/?p=112500","title":{"rendered":"The case of the thread executing from an unloaded third-party DLL"},"content":{"rendered":"<p>The Explorer team was investigating a crash that was occuring at a relatively high rate and found that it took the form of a thread executing from an unloaded third-party DLL.<\/p>\n<pre>0:173&gt; k\r\nRetAddr               Call Site\r\n00000000`557c5820     &lt;Unloaded_LibUtils_CloudNs_3.dll&gt;+0x265fe\r\n00000000`00000008     &lt;Unloaded_LibUtils_CloudNs_3.dll&gt;+0x2b5820\r\n00000000`0000000e     0x8\r\n00000000`00000008     0xe\r\n00000000`557c8c18     0x8\r\nffffffff`fffffffe     &lt;Unloaded_LibUtils_CloudNs_3.dll&gt;+0x2b8c18\r\n00000000`00000000     0xffffffff`fffffffe\r\n<\/pre>\n<p>There isn&#8217;t much on the stack at all.<\/p>\n<pre>0:173&gt; dps @rsp\r\n00000000`1248f920  00000000`557c5820 &lt;Unloaded_LibUtils_CloudNs_3.dll&gt;+0x2b5820\r\n00000000`1248f928  00000000`00000008\r\n00000000`1248f930  00000000`0000000e\r\n00000000`1248f938  00000000`00000008\r\n00000000`1248f940  00000000`557c8c18 &lt;Unloaded_LibUtils_CloudNs_3.dll&gt;+0x2b8c18\r\n00000000`1248f948  ffffffff`fffffffe\r\n00000000`1248f950  00000000`00000000\r\n00000000`1248f958  00000000`00000000\r\n00000000`1248f960  00000000`00000000\r\n00000000`1248f968  00000000`00000000\r\n00000000`1248f970  00000000`00000000\r\n00000000`1248f978  00000000`00000000\r\n00000000`1248f980  00000000`00000000\r\n00000000`1248f988  00007ff9`a2117344 kernel32!BaseThreadInitThunk+0x14\r\n00000000`1248f990  00000000`00000000\r\n00000000`1248f998  00000000`00000000\r\n<\/pre>\n<p>This is just a worker thread the operates entirely inside <tt>LibDB.CloudNs.3.dll<\/tt>. It doesn&#8217;t have a very deep stack, so I suspect that it&#8217;s idle and is waiting for work to do.<\/p>\n<p>For these types of investigations, there usually isn&#8217;t much to see directly in the crashing thread. That thread is the victim. You have to do additional research to figure out who unloaded the DLL prematurely.<\/p>\n<p>Some snooping around found another stack that involves this unloaded DLL:<\/p>\n<pre>0:159&gt; k\r\nRetAddr               Call Site\r\n00007ff9`9fdbbea0     ntdll!ZwWaitForMultipleObjects+0x14\r\n00007ff9`9fdbbd9e     KERNELBASE!WaitForMultipleObjectsEx+0xf0\r\n00000000`554d65fe     KERNELBASE!WaitForMultipleObjects+0xe\r\n00000000`55765820     &lt;Unloaded_LibDB_CloudNs_3.dll&gt;+0x965fe\r\n00000000`00000003     &lt;Unloaded_LibUtils_JsonNs_3.dll&gt;+0x255820\r\n00000000`00000004     0x3\r\n00000000`00000008     0x4\r\n00000000`55768c18     0x8\r\nffffffff`fffffffe     &lt;Unloaded_LibUtils_CloudNs_3.dll&gt;+0x258c18\r\n00000000`00000000     0xffffffff`fffffffe\r\n<\/pre>\n<p>The most recently unloaded DLLs are<\/p>\n<pre>00007ff9`6d7c0000 00007ff9`6d80a000   FabrikamContextMenu.dll\r\n00007ff9`115e0000 00007ff9`1172f000   LitWareSync.dll\r\n00007ff9`643d0000 00007ff9`64681000   CcNamespace.dll\r\n00000000`55440000 00000000`5550b000   LibDB_CloudNs_3.dll\r\n00000000`55860000 00000000`55998000   LibNet_CloudNs_3.dll\r\n00000000`557f0000 00000000`5585b000   LibJson_CloudNs_3.dll\r\n00000000`55510000 00000000`557e7000   LibUtils_CloudNs_3.dll\r\n00000000`561a0000 00000000`56238000   MSVCP100.dll\r\n00000000`56240000 00000000`56312000   MSVCR100.dll\r\n00007ff9`85130000 00007ff9`85167000   EhStorShell.dll\r\n00007ff9`3cac0000 00007ff9`3cb61000   wpdshext.dll\r\n00007ff9`78a00000 00007ff9`78a26000   EhStorAPI.dll\r\n00007ff9`686f0000 00007ff9`68754000   PlayToDevice.dll\r\n00007ff9`67110000 00007ff9`6718d000   provsvc.dll\r\n<\/pre>\n<p>So the <tt>LibDB.CloudNs.3.dll<\/tt> that got unloaded is just part of an entire ecosystem of <tt>Lib*.CloudNs.3.dll<\/tt> dynamic libraries that all got unloaded together.<\/p>\n<p>The ringleader of this operation appears to be <tt>CcNamespace.dll<\/tt>, which looks like the Contoso namespace extension that adds a &#8220;Contoso&#8221; node under <span style=\"text-decoration: line-through;\"><i>My Computer<\/i><\/span> <i>This PC<\/i> that gives you a view into all your Contoso things stored in the Contoso cloud service. All the other DLLs are helpers that the main <tt>CcNamespace.dll<\/tt> uses to accomplish its tasks.<\/p>\n<p>The main <tt>CcNamespace.dll<\/tt> was loaded by Explorer as a shell extension, and its <code>Dll\u00adCan\u00adUnload\u00adNow<\/code> function was returning <code>S_OK<\/code> when there were no active references to objects in <tt>CcNamespace.dll<\/tt>. Unfortunately, when it said &#8220;Sure, it&#8217;s safe to unload me&#8221;, that linchpin DLL unloaded all its minions, unaware that one of the minions (the utility library) had spun up some worker threads.<\/p>\n<p>You might think that the fix is to update the utility library&#8217;s <code>Dll\u00adCan\u00adUnload\u00adNow<\/code> to return <code>S_FALSE<\/code> if there are still busy background threads.\u00b9 But that doesn&#8217;t work because the utility library is probably not a COM DLL in the first place. It&#8217;s just a traditional DLL that <tt>CcNamespace.dll<\/tt> uses, and it is <tt>CcNamespace.dll<\/tt> that is the COM DLL.<\/p>\n<p>The <code>Dll\u00adCan\u00adUnload\u00adNow<\/code> in <tt>CcNamespace.dll<\/tt> could warn <tt>LibUtils.CloudNs.3.dll<\/tt> that it should start winding down, but you&#8217;re basically in a tricky spot because the <code>DLL_<wbr \/>PROCESS_<wbr \/>ATTACH<\/code> cannot wait for the worker thread to exit.<\/p>\n<p>I think the way to go is for the worker thread to increment the DLL reference count when it starts its worker thread, and to use <code>Free\u00adLibrary\u00adAnd\u00adExit\u00adThread<\/code> to exit the worker thread. Alternatively, it could make its worker thread a threadpool thread and use <code>Free\u00adLibrary\u00adWhen\u00adCallback\u00adReturns<\/code> to request that the system decrement the DLL reference count when it finishes.<\/p>\n<p>This is probably something the utility library should have done anyway. I suspect that the worker thread is not something that clients of the utility library are even aware of. It is just an implementation detail of the utility library, created without the knowledge of the main DLL.<\/p>\n<p>Fortunately, the application compatibility team has a copy of Contoso Cloud in their library, so even though we couldn&#8217;t reproduce the crash, we were still able to confirm that <tt>CcNamespace.dll<\/tt> is indeed the shell extension DLL whose unloading triggers the unloading of all the dependent DLLs.<\/p>\n<p>We were about to contact Contoso with our conclusions and suggestions for improvement, but we discovered that it would be pointless because Contoso discontinued that namespace extension years ago. They replaced it with a different way of integrating their cloud content into Windows; the only people using the namespace extension are those who still using an old version, either because they don&#8217;t want to pay for the upgrade, or because they are actively avoiding the upgrade because they like the old way.<\/p>\n<p>Those customers are using a product that has gone out of support. Contoso doesn&#8217;t care about those old customers any more. Windows will have to fix it without Contoso&#8217;s help.<\/p>\n<p>The Explorer team added an application compatibility flag for the Contoso Cloud namespace extension to say &#8220;When you load this shell extension, do a <code>Get\u00adModule\u00adHandle\u00adEx<\/code> with the <code>GET_<wbr \/>MODULE_<wbr \/>HANDLE_<wbr \/>EX_<wbr \/>FLAG_<wbr \/>PIN<\/code> flag so the DLL never unloads.&#8221; That way, even if the DLL says &#8220;Sure, go ahead and unload me, it&#8217;s totally safe, trust me,&#8221; and COM does a <code>FreeLibrary<\/code>, the DLL doesn&#8217;t actually unload.<\/p>\n<p>\u00b9 Even if you manage to get return <code>Dll\u00adCan\u00adUnload\u00adNow<\/code> to return <code>S_FALSE<\/code>, it doesn&#8217;t help if COM is being uninitialized. In that case, <a title=\"CoUninitalize will ask a DLL if it is okay to unload now, but the answer is a foregone conclusion\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20131106-00\/?p=2723\"> <code>CoUninitalize<\/code> will ask a DLL if it is okay to unload now, but the answer is a foregone conclusion<\/a>: If COM is shutting down, COM is going to unload all the DLLs that it loaded. It asks you if you are okay with it, not because it cares what your answer is, but to give you a chance to do cleanup outside of <code>DllMain<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Oops, I didn&#8217;t realize that I was still doing that.<\/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-112500","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Oops, I didn&#8217;t realize that I was still doing that.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112500","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=112500"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112500\/revisions"}],"predecessor-version":[{"id":112502,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112500\/revisions\/112502"}],"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=112500"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112500"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112500"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}