{"id":104230,"date":"2020-09-17T07:00:00","date_gmt":"2020-09-17T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104230"},"modified":"2020-09-17T08:44:47","modified_gmt":"2020-09-17T15:44:47","slug":"20200917-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200917-00\/?p=104230","title":{"rendered":"The C++\/WinRT &#8220;capture&#8221; function helps you interoperate with the COM ABI world"},"content":{"rendered":"<p>C++\/WinRT is a freestanding library that is <a title=\"What does the \/ALTERNATENAME linker switch do?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200731-00\/?p=104024\"> not dependent upon any Windows operating system header files<\/a>. You are welcome to stay completely in the C++\/WinRT world, but it&#8217;s not uncommon to have to interact with objects outside that world from time to time.<\/p>\n<p>It is a common pattern for COM methods to return objects through a pair of parameters <code>REFIID<\/code> and <code>void**<\/code>. The first parameter describes how you would like to receive the object, and the second parameter is where the object is returned. (Some functions which don&#8217;t follow this pattern <a title=\"SHOpenRegStream does not mix with smart pointers\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20181227-00\/?p=100575\"> have come to regret the decision<\/a>.)<\/p>\n<p>If you are calling a COM ABI method that follows this pattern, you can put the result into a C++\/WinRT smart pointer object with the help of the <code>capture<\/code> function.<\/p>\n<p>There are actually four flavors of <code>capture<\/code>, and they fall neatly into a table.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>\u00a0<\/th>\n<th>Functor<\/th>\n<th>Method pointer<\/th>\n<\/tr>\n<tr>\n<th>Instance method<\/th>\n<td><code>winrt::com_ptr&lt;I&gt; p;<br \/>\np.capture(Functor, args...);<\/code><\/td>\n<td><code>winrt::com_ptr&lt;I&gt; p;<br \/>\np.capture(q, &amp;I2::Method, args...);<\/code><\/td>\n<\/tr>\n<tr>\n<th>Free function<\/th>\n<td><code>auto p =<br \/>\n\u00a0\u00a0winrt::capture&lt;I&gt;(Functor, args...);<\/code><\/td>\n<td><code>auto p =<br \/>\n\u00a0\u00a0winrt::capture&lt;I&gt;(q, &amp;I2::Method, args...);<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In all cases, <code>capture<\/code> throws a <code>winrt::hresult_error<\/code> exception if the call fails.<\/p>\n<p>Today, we&#8217;ll look at the first column: The functor.<\/p>\n<p><i>Functor<\/i> is C++-speak for &#8220;anything you can call as if it were a function.&#8221; The functor could be an actual function, like <code>CoGetObjectContext<\/code>, or it could be something that has an overloaded <code>operator()<\/code>, such as a lambda.<\/p>\n<p>After the functor comes an optional list of additional parameters.<\/p>\n<p>In the functor form, <code>capture<\/code> invokes the functor by passing the additional parameters, followed by a <code>REFIID<\/code> and <code>void**<\/code>.<\/p>\n<p>The functor is expected to return in the <code>void**<\/code> parameter a pointer to object whose type is specified by the <code>REFIID<\/code> parameter, and that pointer shall have it reference count incremented by one, indicating ownership. Fortunately, this convention is standard in COM, so you&#8217;re already in good shape on that front.<\/p>\n<p>Here&#8217;s an example of the most basic case:<\/p>\n<pre>\/\/ Capture into an existing variable.\r\nwinrt::com_ptr&lt;IContextCallback&gt; context;\r\ncontext.capture(CoGetObjectContext);\r\n\r\n\/\/ Create a variable and capture in one step.\r\nauto p = winrt::capture&lt;IContextCallback&gt;(CoGetObjectContext);\r\n<\/pre>\n<p>This calls the <code>CoGetObjectContext<\/code> function and puts the result into an existing variable <code>p<\/code> or returns the result directly, which we place into a new variable <code>p<\/code>.<\/p>\n<p>If you pass additional parameters, those are passed to the functor as well, and the <code>REFIID<\/code> and <code>void**<\/code> parameters go at the end.<\/p>\n<pre>\/\/ Capture into existing object.\r\nwinrt::com_ptr&lt;IWidget&gt; widget;\r\nwidget.capture(CoUnmarshalInterface, stream.get());\r\n\r\n\/\/ Capture into new object.\r\nauto widget = winrt::capture&lt;IWidget&gt;(CoUnmarshalInterface, stream.get());\r\n<\/pre>\n<p>This will perform a<\/p>\n<pre>CoUnmarshalInterface(stream.get(), riid, ppv)\r\n<\/pre>\n<p>where the <code>riid<\/code> and <code>ppv<\/code> receive the object.<\/p>\n<p>If you use <code>capture<\/code> as a free function, you aren&#8217;t required to store the result into a variable. You could just use the result right away:<\/p>\n<pre>HRESULT hr = winrt::capture&lt;IWidget&gt;(CoUnmarshalInterface, stream.get())-&gt;Toggle();\r\nif (FAILED(hr)) throw_hresult(hr);\r\n\r\n\/\/ Or equivalently\r\nwinrt::check_hresult(winrt::capture&lt;IWidget&gt;(CoUnmarshalInterface, stream.get())-&gt;Toggle());\r\n<\/pre>\n<p>You can pass a lambda or other callable object if you want to do something that isn&#8217;t expressible as a simple function. For example, the <code>OleCreate<\/code> function does not place both the <code>riid<\/code> and <code>void**<\/code> parameters at the end of the parameter list. It doesn&#8217;t fit the pattern required by <code>capture<\/code>, but you can fix that with a lambda that rearranges the parameters.\u00b2<\/p>\n<pre>o.capture(\r\n    [](auto&amp;&amp; a, auto&amp;&amp; b, auto&amp;&amp; c, auto&amp;&amp; d, auto&amp;&amp; e, REFIID riid, void** ppv)\r\n    { return OleCreate(a, riid, b, c, d, e, ppv); },\r\n    rclsid, renderopt, pFormatEtc, pClientSite, pStg);\r\n<\/pre>\n<p>If you&#8217;re going to be doing this a lot, you may want to factor the lambda into a helper function.<\/p>\n<pre>inline HRESULT CapturableOleCreate(\r\n  IN REFCLSID        rclsid,\r\n  IN DWORD           renderopt,\r\n  IN LPFORMATETC     pFormatEtc,\r\n  IN LPOLECLIENTSITE pClientSite,\r\n  IN LPSTORAGE       pStg,\r\n  IN REFIID          riid,\r\n  OUT LPVOID         *ppvObj)\r\n{\r\n    return OleCreate(rclsid, riid, renderopt, pFormatEtc, pClientSite, pStg, riid, ppv);\r\n}\r\n\r\no.capture(CapturableOleCreate, rclsid, renderopt, pFormatEtc, pClientSite, pStg);\r\n<\/pre>\n<p>After all this discussion, the second column is going to sound anticlimactic.<\/p>\n<p>If you want to obtain the object from a method call on an existing object, you can use the method pointer version of the <code>capture<\/code> function. For example, suppose you have a <code>IGlobalInterfaceTable<\/code> and you want to call <code>GetInterfaceFromGlobal<\/code> to obtain an object from the global interface table.<\/p>\n<pre>auto git = winrt::create_instance&lt;\r\n    IGlobalInterfaceTable&gt;(CLSID_StdGlobalInterfaceTable);\r\n\r\n\/\/ Capture into existing object.\r\nwinrt::com_ptr&lt;IWidget&gt; widget;\r\nwidget.capture(git, &amp;IGlobalInterfaceTable::GetInterfaceFromGlobal, dwCookie);\r\n\r\n\/\/ Capture into new object.\r\nauto widget = winrt::capture&lt;IWidget&gt;(\r\n    git, &amp;IGlobalInterfaceTable::GetInterfaceFromGlobal, dwCookie);\r\n<\/pre>\n<p><b>Bonus chatter<\/b>: C++\/WinRT comes with a premade capture wrapper for <code>CoCreateInstance<\/code>:<\/p>\n<pre>template &lt;typename Interface&gt;\r\nauto create_instance(guid const&amp; clsid, uint32_t context = 0x1 \/*CLSCTX_INPROC_SERVER*\/, void* outer = nullptr);\r\n{\r\n    return capture(WINRT_IMPL_CoCreateInstance\u00b9, clsid, outer, context);\r\n}\r\n<\/pre>\n<p>The default context is &#8220;in-process server&#8221;, and there is no aggregation by default. You can use it like this:<\/p>\n<pre>auto shellWindows = winrt::create_instance&lt;IShellWindows&gt;(CLSID_ShellWindows, CLSCTX_ALL);\r\n<\/pre>\n<p><b>Bonus bonus chatter<\/b>: The definition of <code>capture<\/code> is quite simple:<\/p>\n<pre>template &lt;typename T&gt;\r\nstruct com_ptr\r\n{\r\n    ...\r\n\r\n    template &lt;typename F, typename...Args&gt;\r\n    void capture(F function, Args&amp;&amp;...args)\r\n    {\r\n        check_hresult(function(args..., guid_of&lt;T&gt;(), put_void()));        \r\n    }\r\n\r\n    template &lt;typename O, typename M, typename...Args&gt;\r\n    void capture(com_ptr&lt;O&gt; const&amp; object, M method, Args&amp;&amp;...args)\r\n    {\r\n        check_hresult((object.get()-&gt;*(method))(args..., guid_of&lt;T&gt;(), put_void()));\r\n    }\r\n}\r\n\r\ntemplate &lt;typename T, typename F, typename...Args&gt;\r\nauto capture(F function, Args&amp;&amp;...args)\r\n{\r\n    impl::com_ref&lt;T&gt; result{ nullptr };\r\n    check_hresult(function(args..., guid_of&lt;T&gt;(), reinterpret_cast&lt;void**&gt;(put_abi(result))));\r\n    return result;\r\n}\r\n\r\ntemplate &lt;typename T, typename O, typename M, typename...Args&gt;\r\nauto capture(com_ptr&lt;O&gt; const&amp; object, M method, Args&amp;&amp;...args)\r\n{\r\n    impl::com_ref&lt;T&gt; result{ nullptr };\r\n    check_hresult((object.get()-&gt;*(method))(args..., guid_of&lt;T&gt;(), reinterpret_cast&lt;void**&gt;(put_abi(result))));\r\n    return result;\r\n}\r\n<\/pre>\n<p>This entire series is based on me reverse-engineering these four functions.<\/p>\n<p>\u00b9 The C++\/WinRT library is freestanding, so it contains its own private redeclaration of the <code>CoCreateInstance<\/code> function. This redeclared version is for internal use only. Don&#8217;t use it from your own code. Basically, anything named <code>WINRT_IMPL<\/code> or in the <code>winrt::impl<\/code> namespace is reserved for internal use.<\/p>\n<p>\u00b2 I guess you could also have captured the parameters into the lambda:<\/p>\n<pre>o.capture(\r\n    [&amp;](REFIID riid, void** ppv)\r\n    { return OleCreate(rclsid, riid, renderopt, pFormatEtc, pClientSite, pStg, ppv); });\r\n<\/pre>\n<p>This could be handy if you are creating the same object many times with the same parameters. For example, if you were creating five objects from the stream, you could do this:<\/p>\n<pre>auto create = [&amp;](REFIID riid, void** ppv)\r\n    { return OleCreate(rclsid, riid, renderopt, pFormatEtc, pClientSite, pStg, ppv); });\r\n\r\no1.capture(create);\r\no2.capture(create);\r\no3.capture(create);\r\no4.capture(create);\r\no5.capture(create);\r\n<\/pre>\n<p>Note that the lambda is passed <i>by value<\/i> to <code>capture<\/code>, so a copy is made each time you use it. This means that your lambda probably shouldn&#8217;t have any mutable state, because the mutations are made to a copy of the original. It also means that your lambda probably shouldn&#8217;t capture objects with nontrivial copy constructors or destructors, because they will be invoked each time you use it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Simplifying a common COM pattern.<\/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-104230","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Simplifying a common COM pattern.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104230","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=104230"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104230\/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=104230"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104230"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104230"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}