{"id":106350,"date":"2022-03-15T07:00:00","date_gmt":"2022-03-15T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=106350"},"modified":"2023-11-12T17:21:43","modified_gmt":"2023-11-13T01:21:43","slug":"20220315-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220315-00\/?p=106350","title":{"rendered":"Reducing chattiness by querying for multiple interfaces at once, part 1"},"content":{"rendered":"<p>During performance analysis, you may discover that your usage of a remote COM object is too chatty, meaning that too much time spent communicating back and forth at the expense of actual work. We saw some time ago how you can <a title=\"We batched up our COM requests and return a single stream of results, but the performance is still slow\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20160212-00\/?p=93013\"> marshal a buffer by value instead of by reference<\/a>, so that there is only one round trip to the server to get the buffer.<\/p>\n<p>But maybe your chattiness problem is with the <code>Query\u00adInterface<\/code> calls:<\/p>\n<pre>\/\/ Error checking elided for expository purposes.\r\n\r\n\/\/ Create a widget.\r\nwil::com_ptr&lt;IWidget&gt; widget;\r\nCoCreateInstance(CLSID_Widget, nullptr,\r\n    CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&amp;widget));\r\n\r\n\/\/ Set ourselves as its site.\r\nwil::com_ptr&lt;IObjectWithSite&gt; objectWithSite;\r\nwidget-&gt;QueryInterface(IID_PPV_ARGS(&amp;objectWithSite));\r\nobjectWithSite-&gt;SetSite(this);\r\n\r\n\/\/ Load it from a file.\r\nwil::com_ptr&lt;IPersistFile&gt; persistFile;\r\nwidget-&gt;QueryInterface(IID_PPV_ARGS(&amp;persistFile));\r\npersistFile-&gt;Load(fileName, STGM_READ);\r\n\r\n\/\/ Ready to do widget things.\r\nwidget-&gt;DoSomething();\r\n<\/pre>\n<p>Let&#8217;s count the number of calls to the server, and how many of them are performing actual work.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Call<\/th>\n<th>Purpose<\/th>\n<th>Nature<\/th>\n<\/tr>\n<tr>\n<td><code>Co\u00adCreate\u00adInstance<\/code><\/td>\n<td>Create the widget<\/td>\n<td>Real work<\/td>\n<\/tr>\n<tr>\n<td><code>QueryInterface<\/code><\/td>\n<td>Get the <code>IObjectWithSite<\/code><\/td>\n<td>Bookkeeping<\/td>\n<\/tr>\n<tr>\n<td><code>SetSite<\/code><\/td>\n<td>Set the site<\/td>\n<td>Configuration<\/td>\n<\/tr>\n<tr>\n<td><code>QueryInterface<\/code><\/td>\n<td>Get the <code>IPersistFile<\/code><\/td>\n<td>Bookkeeping<\/td>\n<\/tr>\n<tr>\n<td><code>Load<\/code><\/td>\n<td>Load the widget<\/td>\n<td>Initialization<\/td>\n<\/tr>\n<tr>\n<td><code>DoSomething<\/code><\/td>\n<td>Do something<\/td>\n<td>Activity<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>There are six calls to the server, and a third of them are just bookkeeping.<\/p>\n<p>We can batch together the <code>QueryInterface<\/code> by using <code>IMultiQI<\/code>:<\/p>\n<pre>\/\/ Error checking elided for expository purposes.\r\n\r\n\/\/ Create a widget.\r\nwil::com_ptr&lt;IWidget&gt; widget;\r\nCoCreateInstance(CLSID_Widget, nullptr,\r\n    CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&amp;widget));\r\n\r\n<span style=\"border: solid 1px currentcolor; border-bottom: none;\">\/\/ Get two interfaces with one call.                  <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">wil::com_ptr&lt;IMultiQI&gt; multiQI;                       <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">widget-&gt;QueryInterface(IID_PPV_ARGS(&amp;multiQI));       <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                                     <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">MULTI_QI mqi[2] = {                                   <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    { &amp;__uuidof(IObjectWithSite), nullptr, 0 },       <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    { &amp;__uuidof(IPersistFile), nullptr, 0 },          <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">};                                                    <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">HRESULT hr = multiQI-&gt;QueryMultipleInterfaces(2, mqi);<\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                                     <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">wil::com_ptr&lt;IObjectWithSite&gt; objectWithSite;         <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">objectWithSite.attach(mqi[0].pItf);                   <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                                     <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">wil::com_ptr&lt;IPersistFile&gt; persistFile;               <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">persistFile.attach(mqi[1].pItf);                      <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                                     <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">if (hr != S_OK) {                                     <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    \/\/ Failed to get at least one interface.          <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    return;                                           <\/span>\r\n<span style=\"border: solid 1px currentcolor; border-top: none;\">}                                                     <\/span>\r\n\r\n\/\/ Set ourselves as its site.\r\nobjectWithSite-&gt;SetSite(this);\r\n\r\n\/\/ Load it from a file.\r\npersistFile-&gt;Load(fileName, STGM_READ);\r\n\r\n\/\/ Ready to do widget things.\r\nwidget-&gt;DoSomething();\r\n<\/pre>\n<p>We were able to combine the two <code>Query\u00adInterface<\/code> calls into one by issuing a batch query. Note that the <code>Query\u00adInterface<\/code> for <code>IMultiQI<\/code> is not a server call: The <code>IMultiQI<\/code> interface is implemented locally on the proxy.<\/p>\n<p>But wait, we can do even better: We can use <code>Co\u00adCreate\u00adInstance\u00adEx<\/code> to obtain all thread interfaces as part of the initial creation:<\/p>\n<pre>\/\/ Error checking elided for expository purposes.\r\n\r\n<span style=\"border: solid 1px currentcolor; border-bottom: none;\">\/\/ Create a widget and request three interfaces.<\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                               <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">MULTI_QI mqi[3] = {                             <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    { &amp;__uuidof(IWidget), nullptr, 0 },         <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    { &amp;__uuidof(IObjectWithSite), nullptr, 0 }, <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    { &amp;__uuidof(IPersistFile), nullptr, 0 },    <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">};                                              <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">HRESULT hr = CoCreateInstanceEx(                <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    CLSID_Widget, nullptr, CLSCTX_LOCAL_SERVER, <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    nullptr, 3, mqi);                           <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                               <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">wil::com_ptr&lt;IWidget&gt; widget;                   <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">widget.attach(mqi[0].pItf);                     <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                               <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">wil::com_ptr&lt;IObjectWithSite&gt; objectWithSite;   <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">objectWithSite.attach(mqi[1].pItf);             <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                               <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">wil::com_ptr&lt;IPersistFile&gt; persistFile;         <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">persistFile.attach(mqi[2].pItf);                <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                               <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">if (hr != S_OK) {                               <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    \/\/ Failed to get at least one interface.    <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    return;                                     <\/span>\r\n<span style=\"border: solid 1px currentcolor; border-top: none;\">}                                               <\/span>\r\n\r\n\/\/ Set ourselves as its site.\r\nobjectWithSite-&gt;SetSite(this);\r\n\r\n\/\/ Load it from a file.\r\npersistFile-&gt;Load(fileName, STGM_READ);\r\n\r\n\/\/ Ready to do widget things.\r\nwidget-&gt;DoSomething();\r\n<\/pre>\n<p>Now we have gotten rid of all of the bookkeeping calls.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Call<\/th>\n<th>Purpose<\/th>\n<th>Nature<\/th>\n<\/tr>\n<tr>\n<td><code>Co\u00adCreate\u00adInstance\u00adEx<\/code><\/td>\n<td>Create the widget and get interfaces<\/td>\n<td>Real work<\/td>\n<\/tr>\n<tr>\n<td><code>SetSite<\/code><\/td>\n<td>Set the site<\/td>\n<td>Configuration<\/td>\n<\/tr>\n<tr>\n<td><code>Load<\/code><\/td>\n<td>Load the widget<\/td>\n<td>Initialization<\/td>\n<\/tr>\n<tr>\n<td><code>DoSomething<\/code><\/td>\n<td>Do something<\/td>\n<td>Activity<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Okay, so now we get to talk about error checking.<\/p>\n<p>There are three classes of results related to whether the interfaces could be obtained.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Interfaces obtained<\/th>\n<th>QueryMultipleInterfaces<\/th>\n<th>CoCreateInstanceEx<\/th>\n<\/tr>\n<tr>\n<td>All<\/td>\n<td colspan=\"2\" align=\"center\"><code>S_OK<\/code><\/td>\n<\/tr>\n<tr>\n<td>Some but not all<\/td>\n<td align=\"center\"><code>S_FALSE<\/code><\/td>\n<td align=\"center\"><code>CO_<wbr \/>S_<wbr \/>NOT\u00adALL\u00adINTERFACES<\/code><\/td>\n<\/tr>\n<tr>\n<td>None<\/td>\n<td colspan=\"2\" align=\"center\">Error<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Note that for <code>Co\u00adCreate\u00adInstance\u00adEx<\/code>, there are other errors possible to report any problems creating the object, but I&#8217;m looking at the interface-related errors.<\/p>\n<p>In our case, we need all of the interfaces, so anything that isn&#8217;t <code>S_OK<\/code> is bad news, and we give up immediately.<\/p>\n<p>There may be other cases where you are probing for an interface and will take advantage of it if present, but its absence should not be considered a failure. In that case, you would dig into the <code>MULTI_QI<\/code> to find out which interfaces could be obtained and which failed. You can use <code>SUCCEEDED(hr)<\/code> as a shortcut to detect that <i>something<\/i> was obtained.<\/p>\n<p>Note that in our sample code above, the obtained interfaces are immediately transferred to smart pointers so that they will be released properly, even in the case where not all interfaces were obtained.<\/p>\n<p>Now, it may be that the various calls to <code>Query\u00adInterface<\/code> are scattered through the code, and it is unwieldy to query them at creation and then pass them around to all the places that query for them. We&#8217;ll look at that case next time.<\/p>\n<p><b>Bonus chatter<\/b>: Note that the batched interface query is a significant improvement only for remote objects. For local objects, calls to the object occur directly, so there&#8217;s no marshaling overhead. Furthermore, local objects are unlikely to support the <code>IMulti\u00adQI<\/code> interface at all.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The multiple-query alternatives.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-106350","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The multiple-query alternatives.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106350","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=106350"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106350\/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=106350"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=106350"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=106350"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}