{"id":106378,"date":"2022-03-23T07:00:00","date_gmt":"2022-03-23T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=106378"},"modified":"2022-03-23T06:44:04","modified_gmt":"2022-03-23T13:44:04","slug":"20220323-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220323-00\/?p=106378","title":{"rendered":"Why does C++\/WinRT say that <CODE>first_interface<\/CODE> is not a member of <CODE>winrt::impl::interface_list&lt;&gt;<\/CODE>?"},"content":{"rendered":"<p>A customer was trying to use C++\/WinRT to implement a classic COM interface, but was getting the error<\/p>\n<pre style=\"white-space: pre-wrap;\">error C2039: 'first_interface': is not a member of 'winrt::impl::interface_list&lt;&gt;'\r\nsee declaration of 'winrt::impl::interface_list&lt;&gt;'\r\nsee reference to class template instantiation 'winrt::impl::implements_default_interface&lt;D,void&gt;' being compiled\r\nwith\r\n[\r\n    D=Widget\r\n]\r\nsee reference to function template instantiation 'auto winrt::make&lt;Widget&gt;()' being compiled\r\n<\/pre>\n<p>from the following code:<\/p>\n<pre>struct Widget : winrt::implements&lt;Widget, IPersist&gt;\r\n{\r\n    Widget(int length) : m_length(length) { }\r\n\r\n    \/\/ IPersist methods\r\n    STDMETHODIMP GetClassID(CLSID* clsid) noexcept override\r\n    { *clsid = CLSID_NULL; return S_OK; }\r\n};\r\n<\/pre>\n<p>What is going on? What does the error mean?<\/p>\n<p>The error message is saying, rather obviously, that an empty list of interfaces has no first interface. Which is true, but where is this empty list of interfaces coming from, and why do we care about it?<\/p>\n<p>The list of interfaces is generated from the template arguments to the <code>winrt::implements<\/code> template type. C++\/WinRT looks through the template type arguments (starting from the second one) and keeps the ones that look like interfaces. Those are the ones that C++\/WinRT will respond to in its <code>Query\u00adInterface<\/code> implementation.<\/p>\n<p>In this case, there&#8217;s only one candidate: <code>IPersist<\/code>. Why didn&#8217;t C++\/WinRT think this was an interface?<\/p>\n<p>C++\/WinRT by default considers something an interface if it derives from <code>winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IUnknown<\/code>. Ah, but <code>IPersist<\/code> doesn&#8217;t derive from <code>winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IUnknown<\/code>. It derives from <code>::IUnknown<\/code>. That doesn&#8217;t count.<\/p>\n<p>Okay, but wait, I&#8217;ve seen people do this. So how come they can do it but I can&#8217;t?<\/p>\n<p>I noted that <i>by default<\/i> C++\/WinRT requires interfaces to derive from <code>winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IUnknown<\/code>. You can activate nondefault behavior by including the <code>unknwn.h<\/code> header file <i>before<\/i> including any C++\/WinRT header files, in which case C++\/WinRT will also recognize types which derives from classic COM <code>IUnknown<\/code> as interfaces.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Include order<\/th>\n<th>Result<\/th>\n<\/tr>\n<tr>\n<td><code>#include &lt;unknwn.h&gt;<\/code><br \/>\n<code>#include &lt;winrt\/base.h&gt;<\/code><\/td>\n<td>Classic COM support active in C++\/WinRT<\/td>\n<\/tr>\n<tr>\n<td><code>#include &lt;winrt\/base.h&gt;<\/code><br \/>\n<code>#include &lt;unknwn.h&gt;<\/code><\/td>\n<td>Classic COM support not active in C++\/WinRT<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>A very common way to get <code>unknwn.h<\/code> is to include <code>windows.h<\/code>, since <code>windows.h<\/code> includes <code>unknwn.h<\/code> by default.<\/p>\n<p>Note that all the code in your module must agree on whether or not classic COM support is active. If there is disagreement, then you have an ODR violation, and the resulting behavior is undefined.<\/p>\n<p>Next time, we&#8217;ll take a peek behind the curtain of C++\/WinRT so we can understand how C++\/WinRT detects which interfaces are implemented.<\/p>\n<p><b>Bonus chatter<\/b>: If you are using the <a href=\"https:\/\/github.com\/microsoft\/wil\"> Windows Implementation Library<\/a>, then you should include <code>wil\/cppwinrt.h<\/code> before including any other C++\/WinRT headers. In addition to making sure that <code>unknwn.h<\/code> is included first, it also activates a bunch of other features in both WIL and C++\/WinRT so that they understand each other&#8217;s exceptions, for example.<\/p>\n<p><b>Bonus bonus chatter<\/b>: <a href=\"https:\/\/github.com\/microsoft\/cppwinrt\/pull\/1022\"> PR #1022<\/a> improves support for classic COM in two ways:<\/p>\n<ul>\n<li>For consuming classic COM interfaces, the ordering requirement between <code>unknwn.h<\/code> and C++\/WinRT header files has been removed. You just have to include <code>unknwn.h<\/code> at some point, either before or after including C++\/WinRT. And in practice, you will, because you need to include that header file in order to declare any classic COM interfaces in the first place!<\/li>\n<li>To implement classic COM interfaces, you still have to include <code>unknwn.h<\/code> before including any C++\/WinRT header files, but now you get a helpful diagnostic if you forget: &#8220;To implement classic COM interfaces, you must #include &lt;unknwn.h&gt; before including C++\/WinRT headers.&#8221;\u00b9<\/li>\n<\/ul>\n<p>\u00b9 It&#8217;s surprising how much work in writing a library consists of <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190530-00\/?p=102529\"> error message hacking<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>An empty list has no first element, but why is the list empty?<\/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-106378","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>An empty list has no first element, but why is the list empty?<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106378","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=106378"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106378\/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=106378"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=106378"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=106378"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}