{"id":2723,"date":"2013-11-06T07:00:00","date_gmt":"2013-11-06T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/11\/06\/couninitalize-will-ask-a-dll-if-it-is-okay-to-unload-now-but-the-answer-is-a-foregone-conclusion\/"},"modified":"2013-11-06T07:00:00","modified_gmt":"2013-11-06T07:00:00","slug":"couninitalize-will-ask-a-dll-if-it-is-okay-to-unload-now-but-the-answer-is-a-foregone-conclusion","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20131106-00\/?p=2723","title":{"rendered":"CoUninitalize will ask a DLL if it is okay to unload now, but the answer is a foregone conclusion"},"content":{"rendered":"<p>\nThe\n<code>Dll&shy;Can&shy;Unload&shy;Now<\/code> entry point\nis exported by COM in-proc servers.\nCOM host applications call\n<code>Co&shy;Free&shy;Unused&shy;Libraries<\/code> periodically\nto ask COM to do DLL housecleaning,\nand in response, COM asks each DLL if it is okay to be unloaded.\nIf so, then COM unloads the DLL.\n<\/p>\n<p>\nWhat is not well-known is that COM also does DLL housecleaning\nwhen you shut down the last apartment by calling\n<code>Co&shy;Uninitialize<\/code>.\nWhen that happens, COM will still go around asking each DLL\nwhether it&#8217;s okay to be unloaded,\nbut the question is merely a formality, because\n<i>regardless of your answer, COM will unload you anyway<\/i>.\n<\/p>\n<p>\nThe story here is that COM is being shut down for the process,\nso COM knows that when the last\n<code>Co&shy;Uninitialize<\/code> is finished,\nall COM objects will be destroyed.\nAfter all, if you don&#8217;t have COM, then you can&#8217;t have any COM objects.\n<\/p>\n<p>\nAs a courtesy, COM will ask you,\n&#8220;Is it okay to unload you?&#8221;\nin case you want to do some early cleanup.\nBut it will ignore your answer.\n<\/p>\n<p>\nThis means that you need to exercise caution if you call\n<code>Co&shy;Uninitialize<\/code> or\n<code>Co&shy;Free&shy;Unused&shy;Libraries<\/code> from\nyour COM in-proc server,\nbecause the call may end up freeing your code out from under you.\n<\/p>\n<p>\nFor example, one third-party crash I investigated boiled down to a\nCOM object whose destructor went like this:\n<\/p>\n<pre>\nMyComObject::~MyComObject()\n{\n .. blah blah blah ..\n \/\/ Let DllCanUnloadNow know that we have one\n \/\/ fewer active COM object\n _Module.Unlock();\n CoFreeUnusedLibraries();\n}\n<\/pre>\n<p>\nIt so happened that this was the last COM object created\nby the DLL,\nso the <code>_Module.Unlock()<\/code> call dropped\nthe DLL object count to zero.\nThe COM server then inexplicably called\n<code>Co&shy;Free&shy;Unused&shy;Libraries<\/code> (something\nthat is supposed to be called by the host, not a plug-in),\nand\n<code>Co&shy;Free&shy;Unused&shy;Libraries<\/code> did\nwhat it was told and asked each DLL,\n&#8220;Hey,\ndo you mind if I unload you now?&#8221;\nThe DLL&#8217;s\n<code>Dll&shy;Can&shy;Unload&shy;Now<\/code> function\nsaw that the active COM object count was zero,\nso it said,\n&#8220;Sure, go ahead.&#8221;\n<\/p>\n<p>\nI hope you see where this is going.\n<\/p>\n<p>\nCOM unloads your DLL because you said you were okay\nwith it.\nThe call to\n<code>Co&shy;Free&shy;Unused&shy;Libraries<\/code> eventually\nreturns,\nbut its return address is inside the <code>My&shy;Com&shy;Object<\/code>\ndestructor, which was unloaded because\n<i>you said it was okay to unload<\/i>.\n<\/p>\n<p>\nThe fix here is to remove the call to\n<code>Co&shy;Free&shy;Unused&shy;Libraries<\/code>.\nIt shouldn&#8217;t have been there in the first place.\n<\/p>\n<p>\nA more common error is creating a background thread\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/11\/05\/10463645.aspx\">\nwithout bumping the DLL reference count<\/a>.\nWhen the last COM apartment shuts down, COM will free\nyour DLL,\nthereby stranding your worker thread.\nYou need to use the\n<code>Free&shy;Library&shy;And&shy;Exit&shy;Thread<\/code>\ntrick to keep your DLL loaded until the background thread finishes.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Dll&shy;Can&shy;Unload&shy;Now entry point is exported by COM in-proc servers. COM host applications call Co&shy;Free&shy;Unused&shy;Libraries periodically to ask COM to do DLL housecleaning, and in response, COM asks each DLL if it is okay to be unloaded. If so, then COM unloads the DLL. What is not well-known is that COM also does DLL housecleaning [&hellip;]<\/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-2723","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The Dll&shy;Can&shy;Unload&shy;Now entry point is exported by COM in-proc servers. COM host applications call Co&shy;Free&shy;Unused&shy;Libraries periodically to ask COM to do DLL housecleaning, and in response, COM asks each DLL if it is okay to be unloaded. If so, then COM unloads the DLL. What is not well-known is that COM also does DLL housecleaning [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/2723","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=2723"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/2723\/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=2723"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=2723"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=2723"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}