{"id":103162,"date":"2019-11-29T07:00:00","date_gmt":"2019-11-29T15:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103162"},"modified":"2019-11-28T16:31:05","modified_gmt":"2019-11-29T00:31:05","slug":"20191129-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191129-00\/?p=103162","title":{"rendered":"Using contexts to return to a COM apartment later"},"content":{"rendered":"<p>We&#8217;ve been looking at COM contexts <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191128-00\/?p=103157\"> lately<\/a>, and so far all of these COM contexts were custom contexts created for the purpose of being able to bulk-disconnect all objects in them. But there are also the COM contexts that COM creates automatically for you, and those are also interesting.<\/p>\n<p>Each apartment has a COM context object, and you can access it by calling the <code>Co\u00adGet\u00adObject\u00adContext<\/code> function. You can then use the <code>IContext\u00adCallback::<\/code><code>Context\u00adCallback<\/code> to get back to that context.<\/p>\n<p>In other words, you can capture the current context and return to it any time you like. This can be used if you want to get the effect of marshaling, but when the thing you want to marshal isn&#8217;t a COM object.<\/p>\n<pre>void StartSave(std::function&lt;void(bool)&gt; saveComplete)\r\n{\r\n  start_save().on_completed(\r\n    [saveComplete = std::move(saveComplete)](bool result)\r\n  {\r\n    saveComplete(result);\r\n  });\r\n}\r\n<\/pre>\n<p>This version of <code>Start\u00adSave<\/code> starts the <i>save<\/i> operation, and when the save is complete, it calls the <code>std::function<\/code> with the result. The callback could happen on any thread, but the <code>std::function<\/code> may have captured objects that have COM apartment affinity, like references to other COM apartment-affine objects.<\/p>\n<p>We can update the <code>Start\u00adSave<\/code> function so that the <code>saveComplete<\/code> is invoked in the same apartment that initiated the <code>Start\u00adSave<\/code> operation.<\/p>\n<pre><span style=\"color: blue;\">auto CaptureCurrentApartmentContext()\r\n{\r\n  winrt::com_ptr&lt;IContextCallback&gt; context;\r\n  check_hresult(CoGetObjectContext(IID_PPV_ARGS(context.put())));\r\n  return context;\r\n}<\/span>\r\n\r\nvoid StartSave(std::function&lt;void(bool)&gt; saveComplete)\r\n{\r\n  start_save().on_completed(\r\n    [saveComplete = std::move(saveComplete),\r\n     <span style=\"color: blue;\">context = CaptureCurrentApartmentContext()<\/span>](bool result)\r\n  {\r\n    <span style=\"color: blue;\">InvokeInContext(context.Get(), [&amp;]()\r\n    {<\/span>\r\n      saveComplete(result);\r\n    <span style=\"color: blue;\">});<\/span>\r\n  });\r\n}\r\n<\/pre>\n<p>This trick is useful if you have an object that was created on a UI thread and must be destructed on that same UI thread, but you also capture a strong reference to the object so it can be used by background threads. If the background thread is the one that releases the last strong reference, the object will be destructed on the background thread. To fix that, you can make the destructor run on the UI thread.<\/p>\n<pre>\/\/ Error checking elided for expository purposes.\r\nclass MyThing\r\n{\r\n  Microsoft::WRL::ComPtr&lt;IContextCallback&gt; m_context;\r\n  MyThing()\r\n  {\r\n    CoGetObjectContect(IID_PPV_ARGS(&amp;m_context));\r\n  }\r\n\r\n  ...\r\n  ULONG Release()\r\n  {\r\n      LONG refCount = InterlockedDecrement(&amp;m_refCount);\r\n      if (refCount == 0) {\r\n        \/\/ Normally, we would do a \"delete this\",\r\n        \/\/ but we will go through the ContextCallback to ensure\r\n        \/\/ that the deletion happens on the correct thread.\r\n        InvokeInContext(m_context.Get(), [this]()\r\n        {\r\n          delete this;\r\n        });\r\n      }\r\n      return refCount;\r\n  }\r\n};\r\n<\/pre>\n<p>More generally, you may have a C++ object that has UI thread affinity, but you want to kick off some background work, and when the background work is complete, it wants to switch back to the UI thread to finish the work. You can capture the <code>IContext\u00adCallback<\/code> on the UI thread, and then use the <code>IContext\u00adCallback<\/code> to get back to the UI thread when you&#8217;re ready.<\/p>\n<p>Another case where you would want to return to an earlier context is in the case of a coroutine. By default, in C#, <code>await<\/code> operations resume execution in the same context that performed the <code>await<\/code>. In C++, you can accomplish this by capturing the <code>IContext\u00adCallback<\/code> at the point of the <code>co_await<\/code> and then resume execution inside that same context. This is how C++\/WinRT makes <code>co_await<\/code> on <code>IAsync\u00adAction<\/code> and <code>IAsync\u00adOperation<\/code> objects resume execution in the same thread context.<\/p>\n<p>Capturing the current <code>IContext\u00adCallback<\/code> gives you a way to &#8220;go back home again&#8221;: You can use it to get back to the thread context at some future point.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You can go back home again.<\/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-103162","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You can go back home again.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103162","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=103162"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103162\/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=103162"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103162"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103162"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}