{"id":111557,"date":"2025-09-04T07:00:00","date_gmt":"2025-09-04T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111557"},"modified":"2025-09-03T08:02:59","modified_gmt":"2025-09-03T15:02:59","slug":"20250904-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250904-00\/?p=111557","title":{"rendered":"How can I write a C++\/WinRT <CODE>IAsyncOperation&lt;T&gt;<\/CODE> where <CODE>T<\/CODE> is not a Windows Runtime type?, part 2"},"content":{"rendered":"<p>Last time, <a title=\"How can I write a C++\/WinRT IAsyncOperation&lt;T&gt; where T is not a Windows Runtime type?, part 1\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250903-00\/?p=111546\"> we smuggled an arbitrary C++ value inside an <code>IInspectable<\/code><\/a> but noted the lack of safety. So let&#8217;s add some safety.<\/p>\n<p>To ensure that the object is in-process, you can declare the interface as &#8220;local&#8221;, meaning that the interface is for use only within a single process, and the MIDL compiler will not generate proxies for it. Even easier is just defining the interface manually, without involving the MIDL compiler at all.<\/p>\n<pre>struct\r\nDECLSPEC_UUID(\"\u27e6some guid\u27e7\")\r\nIValueAsInspectable : ::IUnknown\r\n{\r\n};\r\n<\/pre>\n<p>The trick here is that manually defining the interface lets us put C++ stuff inside it.<\/p>\n<pre>template&lt;typename T&gt; struct ValueAsInspectable;\r\n\r\nstruct\r\nDECLSPEC_UUID(\"\u27e6some guid\u27e7\")\r\nIValueAsInspectable : ::IUnknown\r\n{\r\n    std::type_info const* type;\r\n\r\n    template&lt;typename T&gt;\r\n    T&amp; get_value()\r\n    {\r\n        if (*type != typeid(T)) {\r\n            throw std::bad_cast();\r\n        }\r\n        return static_cast&lt;ValueAsInspectable&lt;T&gt;*&gt;(this)\r\n            -&gt;value;\r\n    }\r\n};\r\n\r\ntemplate&lt;typename T&gt;\r\nstruct ValueAsInspectable :\r\n    winrt::implements&lt;ValueAsInspectable&lt;T&gt;,\r\n    winrt::Windows::Foundation::IInspectable,\r\n    IValueAsInspectable&gt;\r\n{\r\n    T value;\r\n\r\n    template&lt;typename...Args&gt;\r\n    ValueAsInspectable(Args&amp;&amp;... args) :\r\n        value{ std::forward&lt;Args&gt;(args)... }\r\n    {\r\n        this-&gt;&gt;type = std::addressof(typeid(T));\r\n    }\r\n};\r\n<\/pre>\n<p>The <code>IValueAsInspectable<\/code> has a COM interface ID, so it can be queried for. But once you get it, you can call the C++ method <code>get_value&lt;T&gt;()<\/code>. That method first validates that you are using the matching <code>T<\/code>, throwing a <code>bad_cast<\/code> exception if not. If you pass that test, then it returns a reference to the value hiding inside.<\/p>\n<p>And for convenience, we can add these helpers. The first two we have seen already:<\/p>\n<pre>template&lt;typename T, typename...Args&gt;\r\nwinrt::Windows::Foundation::IInspectable\r\n    MakeValueAsInspectable(Args&amp;&amp;... args)\r\n{\r\n    return winrt::make&lt;ValueAsInspectable&lt;T&gt;&gt;(\r\n        std::forward&lt;Args&gt;(args)...);\r\n}\r\n\r\ntemplate&lt;typename T&gt;\r\nwinrt::Windows::Foundation::IInspectable\r\n    MakeValueAsInspectable(T&amp;&amp; arg)\r\n{\r\n    return winrt::make&lt;ValueAsInspectable&lt;\r\n        std::remove_reference_t&lt;T&gt;&gt;&gt;(\r\n        std::forward&lt;T&gt;(arg));\r\n}\r\n<\/pre>\n<p>Next are functions for extracting the value from an object that we believe to be a <code>Value\u00adAs\u00adInspectable&lt;T&gt;<\/code>.<\/p>\n<pre>template&lt;typename T&gt;\r\nT&amp; ValueRefFromInspectable(\r\n    winrt::Windows::Foundation::IInspectable const&amp; arg)\r\n{\r\n    return arg.as&lt;IValueAsInspectable&gt;()-&gt;\r\n        get_value&lt;T&gt;();\r\n}\r\n\r\ntemplate&lt;typename T&gt;\r\nT CopyValueFromInspectable(\r\n    winrt::Windows::Foundation::IInspectable const&amp; arg)\r\n{\r\n    return ValueRefFromInspectable&lt;T&gt;(arg);\r\n}\r\n\r\ntemplate&lt;typename T&gt;\r\nT MoveValueFromInspectable(\r\n    winrt::Windows::Foundation::IInspectable &amp;&amp; arg)\r\n{\r\n    return std::move(ValueRefFromInspectable&lt;T&gt;(arg));\r\n}\r\n<\/pre>\n<p>The basic function is <code>Value\u00adRef\u00adFrom\u00adInspectable&lt;T&gt;<\/code> which produces an lvalue reference from an inspectable that we assume represents a <code>Value\u00adAs\u00adInspectable&lt;T&gt;<\/code>. (If we&#8217;re wrong, it throws a <code>bad_cast<\/code> exception.) Building on that are two functions which either copy or move the value out of the <code>Value\u00adAs\u00adInspectable&lt;T&gt;<\/code>.<\/p>\n<p>I gave the function that returns an lvalue name that emphasizes that you get a reference from the inspectable, hoping to prevent people from getting a reference to an expiring inspectable:<\/p>\n<pre>\/\/ Code in italics is wrong\r\n\/\/ Don't do this.\r\n<i>auto&amp; value = ValueRefFromInspectablt&lt;Widget&gt;(\r\n    co_await DoSomethingAsync());<\/i>\r\n<\/pre>\n<p>Note also that if you modify the value reference or move the value out of the inpectable, this affects the underlying object, which means that other people that have a reference to the same inspectable will see the object change state.<\/p>\n<pre>void MutateTheWidget(winrt::Windows::Foundation::IInspectable obj)\r\n{\r\n    auto&amp; widget = ValueRefFromInspectable&lt;Widget&gt;(obj);\r\n    \u27e6 do something that modifies the widget \u27e7\r\n}\r\n\r\nwinrt::fire_and_forget Sample()\r\n{\r\n    auto obj = co_await DoSomethingAsync();\r\n    MutateTheWidget(obj);\r\n    auto&amp; widget = ValueRefFromInspectable&lt;Widget&gt;(obj);\r\n    \/\/ this code sees that the widget has been mutated\r\n}\r\n<\/pre>\n<p>But really, if you need a coroutine that produces a non-Windows Runtime type, then use a non-Windows Runtime coroutine library. I&#8217;m personally partial to <code>wil::task<\/code> (and its COM-aware buddy <code>wil::com_task<\/code>), seeing as I wrote it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Safer smuggling.<\/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-111557","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Safer smuggling.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111557","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=111557"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111557\/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=111557"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111557"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111557"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}