{"id":6133,"date":"2006-09-19T20:37:00","date_gmt":"2006-09-19T20:37:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/vcblog\/2006\/09\/19\/mixing-deterministic-and-non-deterministic-cleanup\/"},"modified":"2019-02-18T18:54:42","modified_gmt":"2019-02-18T18:54:42","slug":"mixing-deterministic-and-non-deterministic-cleanup","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/mixing-deterministic-and-non-deterministic-cleanup\/","title":{"rendered":"Mixing deterministic and non-deterministic cleanup"},"content":{"rendered":"<p class=\"MsoNormal\"><font face=\"Arial\"><font size=\"2\">Hi, my name is Alan Chan.<span>&nbsp; <\/span>I&rsquo;m a software design engineer in Visual C++ libraries team.<span>&nbsp; <\/span>As the name suggests, my team owns some of the C\/C++ libraries such as ATL, MFC, CRT, STL, and OpenMP.<span>&nbsp; <\/span>Today, I&rsquo;m going to talk about one very interesting COM interop bug that I have worked on recently.<span>&nbsp; <\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\">\n<p><font face=\"Arial\" size=\"2\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><font face=\"Arial\"><font size=\"2\">Basic scenario: We have a mixed MFC and .NET application.<span>&nbsp; <\/span>When the user presses a button in the application, it pops up a MFC dialog which hosts a managed control.<span>&nbsp;&nbsp; <\/span>This managed control implements IQuickActivate and is activated through <\/font><\/font><a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/34942z60.aspx\"><font face=\"Arial\" size=\"2\">COleControlSite::QuickActivate()<\/font><\/a><font face=\"Arial\" size=\"2\">.<span>&nbsp; <\/span>This function creates all the appropriate sinks &#8211; in this case IAdviseSinkEx and IPropertyNotifySink &ndash; and passes them to the managed control through <\/font><a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/com\/html\/504cb272-da1c-4ffb-b4b1-fdf288901660.asp\"><font face=\"Arial\" size=\"2\">IQuickActivate::QuickActivate()<\/font><\/a><font face=\"Arial\"><font size=\"2\">.<span>&nbsp; <\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\">\n<p><font face=\"Arial\" size=\"2\"><\/font><\/p>\n<p>&nbsp;<\/p>\n<p class=\"MsoNormal\">\n<p><font face=\"Arial\" size=\"2\">W<\/font><\/p>\n<p><font face=\"Arial\"><font size=\"2\">hen the user dismisses the dialog, the destructor of COleControlSite is called.<span>&nbsp; <\/span>The destructor disconnects all the sink COM objects that were passed in through QuickActivate(), deactivates the managed control, and deletes the sink COM objects.<span>&nbsp;<\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Arial\"><font size=\"2\"><span><\/span><\/font><\/font>&nbsp;<\/p>\n<p class=\"MsoNormal\"><font face=\"Arial\"><font size=\"2\"><span><\/span><\/font><\/font><font face=\"Arial\"><font size=\"2\">This works on the whole.<span>&nbsp; <\/span>However, if the user opens and closes the dialog repeatedly, the application will AV and crash.<span>&nbsp; <\/span>If you read the title, you can probably guess what&rsquo;s happening!<span>&nbsp; <\/span>It turns out that the garbage collector (GC) is calling IUnknown::Release() on the IAdviseSinkEx and IPropertyNotifySink COM objects after they have been deleted.<\/font><\/font><\/p>\n<p class=\"MsoNormal\">&nbsp;<\/p>\n<p class=\"MsoNormal\"><font face=\"Arial\" size=\"2\">As most of you know already, whenever a COM object is passed from native code to managed code, the .Net framework creates <\/font><a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/8bwh56xe.aspx\"><font face=\"Arial\" size=\"2\">Runtime-Callable Wrappers (RCW)<\/font><\/a><font face=\"Arial\" size=\"2\"> automatically.<span>&nbsp; <\/span>The RCW wraps the object and controls lifetime.<span>&nbsp;&nbsp; <\/span>In this case, when the destructor of COleControlSite calls Unadvise() to disconnect the sinks, the managed control does, in fact, disconnect the sinks and drop all references to the RCWs of the sink objects.<span>&nbsp; <\/span>However, IUnknown::Release() is not called at this point.<span>&nbsp; <\/span>IUnknown::Release() is called in the RCW&rsquo;s finalizer function.<span>&nbsp; <\/span>Dropping all references to the RCWs only means that the RCWs are eligible for garbage collection.<span>&nbsp; <\/span>The GC can collect these RCWs at anytime.<span>&nbsp; <\/span>Garbage collection is determined by memory pressure.<span>&nbsp; <\/span>If these RCWs were garbage collected and finalized after the actual sink COM objects have been deleted, it will cause an AV.<\/font><\/p>\n<p class=\"MsoNormal\">&nbsp;<\/p>\n<p class=\"MsoNormal\"><font face=\"Arial\" size=\"2\">So how do we fix this?<span>&nbsp; <\/span>We need some way to clean up these RCWs deterministically.<span>&nbsp; <\/span>Sadly, RCW doesn&rsquo;t implement <\/font><a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/system.idisposable.aspx\"><font face=\"Arial\" size=\"2\">IDisposable interface<\/font><\/a><font face=\"Arial\" size=\"2\"> or else we could call <\/font><a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/system.idisposable.dispose.aspx\"><font face=\"Arial\" size=\"2\">IDisposable::Dispose()<\/font><\/a><font face=\"Arial\" size=\"2\"> for cleanup.<span>&nbsp; <\/span>Fortunately, the framework provides a function, <\/font><a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/system.runtime.interopservices.marshal.releasecomobject.aspx\"><font face=\"Arial\" size=\"2\">Marshal.ReleaseComObject()<\/font><\/a><font face=\"Arial\" size=\"2\">, for deterministic cleanup.<span>&nbsp; <\/span>Each RCW keeps a reference count of how many managed references are pointing to itself.<span>&nbsp; <\/span>(This is completely separated from the reference count in the COM object.)<span>&nbsp; <\/span>Every time you copy or QueryInterface() a managed COM reference, the RCW automatically increments its reference count.<span>&nbsp; <\/span>When ReleaseComObject() is called, the RCW decrements its reference count.<span>&nbsp; <\/span>As soon as the reference count reaches zero, the RCW will then call the finalizer function and release all the COM reference counts that it is holding deterministically.<\/font><\/p>\n<p class=\"MsoNormal\">&nbsp;<\/p>\n<p class=\"MsoNormal\"><font face=\"Arial\"><font size=\"2\">Therefore, similar to calling IUnknown::Release() in native code, we should always call ReleaseComObject() on the managed COM references before it goes out of scope.<span>&nbsp; <\/span>This should guarantee that IUnknown::Release() is called deterministically.<span>&nbsp; <\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Arial\"><font size=\"2\"><\/font><\/font>&nbsp;<\/p>\n<p class=\"MsoNormal\"><font face=\"Arial\"><font size=\"2\">I hope this will save you time when debugging COM interop bugs.<span>&nbsp; <\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\">\n<p><font face=\"Arial\" size=\"2\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><font face=\"Arial\" size=\"2\">Thanks,<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Arial\" size=\"2\">Alan Chan<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Arial\" size=\"2\">Visual C++ Libraries Team.<\/font><\/p>\n<p><span><br><\/span><\/p>\n<p class=\"MsoNormal\">\n<p><font face=\"Arial\" size=\"2\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\">\n<p><font face=\"Arial\" size=\"2\">&nbsp;<\/font><\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hi, my name is Alan Chan.&nbsp; I&rsquo;m a software design engineer in Visual C++ libraries team.&nbsp; As the name suggests, my team owns some of the C\/C++ libraries such as ATL, MFC, CRT, STL, and OpenMP.&nbsp; Today, I&rsquo;m going to talk about one very interesting COM interop bug that I have worked on recently.&nbsp; &nbsp; [&hellip;]<\/p>\n","protected":false},"author":289,"featured_media":35994,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-6133","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cplusplus"],"acf":[],"blog_post_summary":"<p>Hi, my name is Alan Chan.&nbsp; I&rsquo;m a software design engineer in Visual C++ libraries team.&nbsp; As the name suggests, my team owns some of the C\/C++ libraries such as ATL, MFC, CRT, STL, and OpenMP.&nbsp; Today, I&rsquo;m going to talk about one very interesting COM interop bug that I have worked on recently.&nbsp; &nbsp; [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/6133","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/users\/289"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=6133"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/6133\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media\/35994"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media?parent=6133"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=6133"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=6133"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}