{"id":107360,"date":"2022-11-07T07:00:00","date_gmt":"2022-11-07T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=107360"},"modified":"2022-11-07T09:21:34","modified_gmt":"2022-11-07T17:21:34","slug":"20221107-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20221107-00\/?p=107360","title":{"rendered":"In the debugger, how can I get from a projected type back to the C++\/WinRT implementation?"},"content":{"rendered":"<p>Say you are looking at a crash dump, and you have a pointer to a Windows Runtime object projection, and you know that the Windows Runtime object is implemented in C++\/WinRT, and you want to get to the implementation type so you can look at its private members.<\/p>\n<p>The basic trick of treating the pointer as the start of an object doesn&#8217;t work:<\/p>\n<pre style=\"white-space: pre-wrap;\">0:000&gt; ?? s\r\nstruct winrt::Contoso::Sample\r\n   +0x000 m_ptr : 0x00000205`9ab9cf20 winrt::<wbr \/>impl::<wbr \/>abi&lt;<wbr \/>winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IUnknown,<wbr \/>void&gt;::<wbr \/>type\r\n<\/pre>\n<p>If you dump memory starting at this interface pointer, you get the expected vtable, but the rest does not match the expected implementation type.<\/p>\n<pre>0:000&gt; dps 0x00000205`9ab9cf20\r\n00000205`9ab9cf20  00007ff6`0bef8998 contoso!<wbr \/>winrt::<wbr \/>impl::<wbr \/>produce&lt;<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>implementation::<wbr \/>Sample,<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>ISample&gt;::<wbr \/>`vftable'\r\n00000205`9ab9cf28  00007ff6`0bef8a38 contoso!<wbr \/>winrt::<wbr \/>impl::<wbr \/>produce&lt;<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>implementation::<wbr \/>Sample,<wbr \/>winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IClosable&gt;::<wbr \/>`vftable'\r\n00000205`9ab9cf30  00000000`0000002a\r\n00000205`9ab9cf38  00000205`9ab9dae0\r\n00000205`9ab9cf40  00640065`00720046\r\n00000205`9ab9cf48  00000000`00000000\r\n00000205`9ab9cf50  00000000`00000004\r\n\r\n0:000&gt; dt contoso!<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>implementation::<wbr \/>Sample 0x00000205`9ab9cf20\r\n   +0x010 vtable           : winrt::<wbr \/>impl::<wbr \/>produce&lt;<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>implementation::<wbr \/>Sample,<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>ISample&gt;\r\n   +0x018 vtable           : winrt::<wbr \/>impl::<wbr \/>produce&lt;<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>implementation::<wbr \/>Sample,<wbr \/>winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IClosable&gt;\r\n   +0x000 __VFN_table : 0x00007ff6`0bef8998\r\n   +0x008 m_references     : std::atomic&lt;unsigned __int64&gt;\r\n   +0x020 m_value          : <span style=\"border: solid 1px black;\">0n7471174<\/span> \u2190 garbage\r\n   +0x028 m_name           : std::<wbr \/>basic_string&lt;<wbr \/>wchar_t,<wbr \/>std::<wbr \/>char_traits&lt;<wbr \/>wchar_t&gt;,<wbr \/>std::<wbr \/>allocator&lt;<wbr \/>wchar_t&gt; &gt;\r\n\r\n0:000&gt; ?? ((contoso!<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>implementation::<wbr \/>Sample*)0x00000205`9ab9cf20)-&gt;m_name\r\nclass std::<wbr \/>basic_string&lt;<wbr \/>wchar_t,<wbr \/>std::<wbr \/>char_traits&lt;<wbr \/>wchar_t&gt;,<wbr \/>std::<wbr \/>allocator&lt;<wbr \/>wchar_t&gt; &gt;\r\n   +0x000 _Mypair          : std::<wbr \/>_Compressed_pair&lt;<wbr \/>std::<wbr \/>allocator&lt;wchar_t&gt;,<wbr \/>std::<wbr \/>_String_val&lt;<wbr \/>std::<wbr \/>_Simple_types&lt;<wbr \/>wchar_t&gt; &gt;,1&gt;\r\n\r\n0:000&gt; ?? ((consolecppwinrt!winrt::MyProject::implementation::Sample*)0x00000205`9ab9cf20)-&gt;m_name._Mypair\r\nclass std::<wbr \/>_Compressed_pair&lt;<wbr \/>std::<wbr \/>allocator&lt;wchar_t&gt;,<wbr \/>std::<wbr \/>_String_val&lt;<wbr \/>std::<wbr \/>_Simple_types&lt;<wbr \/>wchar_t&gt; &gt;,1&gt;\r\n   +0x000 _Myval2          : std::<wbr \/>_String_val&lt;<wbr \/>std::<wbr \/>_Simple_types&lt;wchar_t&gt; &gt;\r\n\r\n0:000&gt; ?? ((consolecppwinrt!winrt::MyProject::implementation::Sample*)0x00000205`9ab9cf20)-&gt;m_name._Mypair._Myval2\r\nclass std::<wbr \/>_String_val&lt;<wbr \/>std::<wbr \/>_Simple_types&lt;wchar_t&gt; &gt;\r\n   +0x000 _Myproxy         : <span style=\"border: solid 1px black;\">(null)<\/span> \u2190 garbage\r\n   +0x008 _Bx              : std::_String_val&lt;std::_Simple_types&lt;wchar_t&gt; &gt;::_Bxty\r\n   +0x018 _Mysize          : <span style=\"border: solid 1px black;\">0xabababab`fdfdfdfd<\/span> \u2190 garbage\r\n   +0x020 _Myres           : <span style=\"border: solid 1px black;\">0xabababab`abababab<\/span> \u2190 garbage\r\n<\/pre>\n<p>The reason nothing matches up is that the projection vtable is not at the start of the object. Let&#8217;s look at the initial structure again:<\/p>\n<pre>0:000&gt; dt contoso!<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>implementation::<wbr \/>Sample\r\n   +<span style=\"border: solid 1px black;\">0x010<\/span> vtable           : winrt::<wbr \/>impl::<wbr \/>produce&lt;<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>implementation::<wbr \/>Sample,<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>ISample&gt;\r\n   +0x018 vtable           : winrt::<wbr \/>impl::<wbr \/>produce&lt;<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>implementation::<wbr \/>Sample,<wbr \/>winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IClosable&gt;\r\n   +0x000 __VFN_table : Ptr64\r\n   +0x008 m_references     : std::atomic&lt;unsigned __int64&gt;\r\n   +0x020 m_value          : Int4B\r\n   +0x028 m_name           : std::<wbr \/>basic_string&lt;<wbr \/>wchar_t,<wbr \/>std::<wbr \/>char_traits&lt;<wbr \/>wchar_t&gt;,<wbr \/>std::<wbr \/>allocator&lt;<wbr \/>wchar_t&gt; &gt;\r\n<\/pre>\n<p>Even though the debugger lists the <code>ISample<\/code> projection vtable first, it&#8217;s not actually the start of the object. The offset is <code>0x010<\/code>.<\/p>\n<p>The object starts with its private little vtable <code>__VFN_table<\/code>. In this case, we see that the projection vtable is at offset <code>0x10<\/code>, so we can subtract <code>0x10<\/code> to find the true start of the object:<\/p>\n<pre>0:000&gt; ?? ((consolecppwinrt!winrt::MyProject::implementation::Sample*)0x00000205`9ab9cf10)\r\nstruct winrt::MyProject::implementation::Sample * 0x00000205`9ab9cf20\r\n   +0x010 vtable           : winrt::<wbr \/>impl::<wbr \/>produce&lt;<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>implementation::<wbr \/>Sample,<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>ISample&gt;\r\n   +0x018 vtable           : winrt::<wbr \/>impl::<wbr \/>produce&lt;<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>implementation::<wbr \/>Sample,<wbr \/>winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IClosable&gt;\r\n   +0x000 __VFN_table : 0x00007ff6`0bef8d68\r\n   +0x008 m_references     : std::atomic&lt;unsigned __int64&gt;\r\n   +0x020 m_value          : <span style=\"border: solid 1px black;\">0n42<\/span> \u2190 good value\r\n   +0x028 m_name           : std::<wbr \/>basic_string&lt;<wbr \/>wchar_t,<wbr \/>std::<wbr \/>char_traits&lt;<wbr \/>wchar_t&gt;,<wbr \/>std::<wbr \/>allocator&lt;<wbr \/>wchar_t&gt; &gt;\r\n\r\n0:000&gt; ?? ((consolecppwinrt!winrt::MyProject::implementation::Sample*)0x00000205`9ab9cf10)-&gt;m_name._Mypair._Myval2\r\nclass std::<wbr \/>_String_val&lt;<wbr \/>std::<wbr \/>_Simple_types&lt;wchar_t&gt; &gt;\r\n   +0x000 _Myproxy         : 0x00000205`9ab9dae0 std::_Container_proxy\r\n   +0x008 _Bx              : std::_String_val&lt;std::_Simple_types&lt;wchar_t&gt; &gt;::_Bxty\r\n   +0x018 _Mysize          : <span style=\"border: solid 1px black;\">4<\/span> \u2190 good value\r\n   +0x020 _Myres           : <span style=\"border: solid 1px black;\">7<\/span> \u2190 good value\r\n\r\n0:000&gt; ?? ((consolecppwinrt!winrt::MyProject::implementation::Sample*)0x00000205`9ab9cf10)-&gt;m_name._Mypair._Myval2._Bx\r\nunion std::<wbr \/>_String_val&lt;std::<wbr \/>_Simple_types&lt;wchar_t&gt; &gt;::<wbr \/>_Bxty\r\n   +0x000 _Buf             : [8]  <span style=\"border: solid 1px black;\">\"Fred\"<\/span> \u2190 good value\r\n   +0x000 _Ptr             : 0x00640065`00720046  \"--- memory read error at address 0x00640065`00720046 ---\"\r\n   +0x000 _Alias           : [8]  \"F\"\r\n<\/pre>\n<p>We happened to know that the <code>m_ptr<\/code> came from a projection of the <code>Sample<\/code> runtime class, which means that it&#8217;s a pointer to the default interface, which is <code>ISample<\/code>. If <code>m_ptr<\/code> came from a projection f the <code>IClosable<\/code> interface, then we would have to adjust by <code>0x018<\/code> to get to the start of the object.<\/p>\n<p>My strategy is simply to go backward from the projection pointer and see if a non-projection vtable emerges.<\/p>\n<pre>0:000&gt; dps 0x00000205`9ab9cf20-40 0x00000205`9ab9cf20\r\n00000205`9ab9cee0  00000205`9ab9cdc0\r\n00000205`9ab9cee8  00000205`9ab9dab0\r\n00000205`9ab9cef0  00000000`00000000\r\n00000205`9ab9cef8  00000001`00000000\r\n00000205`9ab9cf00  00000000`00000050\r\n00000205`9ab9cf08  fdfdfdfd`00000118\r\n00000205`9ab9cf10  00007ff6`0bef8d68 <span style=\"border: solid 1px black;\">contoso!<wbr \/>winrt::<wbr \/>impl::<wbr \/>heap_<wbr \/>implements&lt;<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>implementation::<wbr \/>Sample&gt;::<wbr \/>`vftable'<\/span>\r\n00000205`9ab9cf18  00000000`00000001\r\n00000205`9ab9cf20  00007ff6`0bef8998 contoso!<wbr \/>winrt::<wbr \/>impl::<wbr \/>produce&lt;<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>implementation::<wbr \/>Sample,<wbr \/>winrt::<wbr \/>Contoso::<wbr \/>ISample&gt;::<wbr \/>`vftable'\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Look behind you.<\/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-107360","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Look behind you.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107360","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=107360"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107360\/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=107360"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=107360"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=107360"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}