{"id":112786,"date":"2026-10-21T07:00:00","date_gmt":"2026-10-21T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112786"},"modified":"2026-10-21T11:22:59","modified_gmt":"2026-10-21T18:22:59","slug":"20261021-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20261021-00\/?p=112786\/","title":{"rendered":"Why didn&#8217;t we use <CODE>Ro&shy;Register&shy;For&shy;Apartment&shy;Shutdown<\/CODE> to clean up the unmarshalable delegate?"},"content":{"rendered":"<p>In my attempt to <a title=\"Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260803-00\/?p=112582\"> create a fake agile wrapper that is technically agile but is not useful outside its home apartment<\/a>, <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260803-00\/?p=112582&amp;commentid=144559#comment-144559\"> Luca Bacci suggested, among other things<\/a>, using the <code>Ro\u00adRegister\u00adFor\u00adApartment\u00adShutdown<\/code> function.<\/p>\n<p>The <code>Ro\u00adRegister\u00adFor\u00adApartment\u00adShutdown<\/code> function is nice, but it doesn&#8217;t suit our needs.<\/p>\n<p>One problem is that the object registered for apartment shutdown must be agile. (If you pass a non-agile object, the call fails.) That means that our wrapper must be agile. If we put the non-marshalable non-agile object inside our wrapper, we now have an agile object (the wrapper) that holds a non-agile object (the original object) as a member, which creates the risk that the wrapper will run down in the wrong apartment. The normal way of fixing that is to use <code>Ro\u00adGet\u00adAgile\u00adReference<\/code> to create an agile wrapper around the non-agile object, and store that agile wrapper in our container object.<\/p>\n<p>This puts us back where we started: We are trying to implement that agile wrapper!<\/p>\n<p>Even if we got past that problem, we have another problem: At apartment shutdown, COM calls the wrapper object&#8217;s <code>UnInitialize<\/code> method, and there&#8217;s a warning on the using the <code>Ro\u00adUnregister\u00adFor\u00adApartment\u00adShutdown<\/code> function:<\/p>\n<blockquote class=\"q\"><p><b>Warning<\/b>: Don&#8217;t call the <code>Ro\u00adUnregister\u00adFor\u00adApartment\u00adShutdown<\/code> function from the <code>OnUninitialize<\/code> callback.<\/p><\/blockquote>\n<p>But wait, what if the destructor of the object we are wrapping wants to call <code>Ro\u00adUnregister\u00adFor\u00adApartment\u00adShutdown<\/code>? It might happen indirectly because it has its own reference to another object, that in turn tries to call <code>Ro\u00adUnregister\u00adFor\u00adApartment\u00adShutdown<\/code>.<\/p>\n<p>For example, it might hold another fake agile wrapper whose <code>UnInitialize<\/code> method has already been called! (We can work around that in our agile wrapper by nulling out the non-agile pointer so we don&#8217;t double-release, but maybe there&#8217;s some other object that uses <code>Ro\u00adUnregister\u00adFor\u00adApartment\u00adShutdown<\/code> that is less resilient to this scenario.)<\/p>\n<p>Another requirement of <code>Ro\u00adUnregister\u00adFor\u00adApartment\u00adShutdown<\/code> is that it must be called from the same apartment that registered. This doesn&#8217;t quite put us back where we started, but it makes things more complicated because the whole point of the exercise is to find a way to release the object without having to try to switch to the apartment. I guess what we could do is use <code>IContext\u00adCallback<\/code> to try to switch back to the apartment to release the original object and unregister the apartment shutdown callback. If the apartment switch fails, then we assume it&#8217;s because the apartment has already run down, so we assume that the <code>IApartment\u00adShutdown::<wbr \/>On\u00adUninitialize<\/code> method was called, and it released the object for us. (Of course, that still creates the problem that we are cleaning up inside the <code>On\u00adUninitialize<\/code> callback, which meas that nobody else can unregister their apartment shutdown callback.)<\/p>\n<p>The last note on this is that the implementation is written on the assumption that there are not a lot of apartment shutdown callbacks. Registration is <var>O<\/var>(1), but unregistration is <var>O<\/var>(<var>n<\/var>) because it does a linear search through all of the apartment&#8217;s registered shutdown callbacks to find the one being unregistered. By comparison, the Global Interface Table is designed on the assumption that it will have to manage a lot of objects, so it can look up its cookies in <var>O<\/var>(1) time. The proposed alternate design would register and unregister a lot of apartment shutdown callbacks, so it&#8217;s pushing the apartment shutdown callback list into a usage pattern it wasn&#8217;t optimized for.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Too many requirements, some of which bring us full circle.<\/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-112786","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Too many requirements, some of which bring us full circle.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112786","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=112786"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112786\/revisions"}],"predecessor-version":[{"id":112787,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112786\/revisions\/112787"}],"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=112786"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112786"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112786"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}