{"id":107429,"date":"2022-11-21T07:00:00","date_gmt":"2022-11-21T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=107429"},"modified":"2022-11-19T19:36:10","modified_gmt":"2022-11-20T03:36:10","slug":"20221121-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20221121-00\/?p=107429","title":{"rendered":"What kind of caller diagnostic information can I get from exceptions thrown by C++\/WinRT and wil?"},"content":{"rendered":"<p>C++\/WinRT throws <code>winrt::<wbr \/>hresult_<wbr \/>error<\/code> to represent COM exceptions. The <a href=\"https:\/\/github.com\/microsoft\/wil\">wil<\/a> framework throws <code>Result\u00adException<\/code> for this purpose. How do they interact, and what diagnostic information do they provide?<\/p>\n<p>C++\/WinRT <code>hresult_<wbr \/>error<\/code> uses <code>Ro\u00adOriginate\u00adError<\/code> to generate a <i>stowed exception<\/i> which records, among other things, a stack trace for the current thread. This stack trace is stored as part of the <code>hresult_<wbr \/>error<\/code> in the form of a <code>IRestrictedErrorInfo<\/code>.<\/p>\n<p>The wil framework by default does not use <code>Ro\u00adOriginate\u00adError<\/code>, so there is no captured stack trace. However, it does capture the file name and line number in the <code>Failure\u00adInfo<\/code> that is stored in the <code>Result\u00adException<\/code>. The <code>Failure\u00adInfo<\/code> also contains information to let you correlate multiple failures and see which ones are manifestations of the same underlying failure.<\/p>\n<p>Here&#8217;s a little table of what we have so far:<\/p>\n<table style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>\u00a0<\/th>\n<th>C++\/WinRT<\/th>\n<th>wil<\/th>\n<\/tr>\n<tr>\n<td>Thrown type<\/td>\n<td><code>hresult_<wbr \/>error<\/code><\/td>\n<td><code>Result\u00adException<\/code><\/td>\n<\/tr>\n<tr>\n<td>Stack trace in thrown object<\/td>\n<td>Yes<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<td>File\/line number in thrown object<\/td>\n<td>No<\/td>\n<td>Yes<\/td>\n<\/tr>\n<tr>\n<td>Recorded in wil error log<\/td>\n<td>No<\/td>\n<td>Yes<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Things get more complicated if you include <code>wil\/<wbr \/>result_originate.h<\/code>: This tells wil to call <code>Ro\u00adOriginate\u00adError<\/code> before throwing the exception, thereby capturing a stack trace. The stack trace is not explicitly saved in the exception object, however. It is stored in a thread-local object that can be retrieved via <code>GetErrorInfo()<\/code>, and many parts of the system (including C++\/WinRT) understand how to retrieve and preserve this extended information, though determining whether any specific scenario preserves the extended information requires investigation.<\/p>\n<p>So now we have this:<\/p>\n<table style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>\u00a0<\/th>\n<th>C++\/WinRT<\/th>\n<th>wil<\/th>\n<\/tr>\n<tr>\n<td>Thrown type<\/td>\n<td><code>hresult_<wbr \/>error<\/code><\/td>\n<td><code>Result\u00adException<\/code><\/td>\n<\/tr>\n<tr>\n<td>Stack trace in thrown object<\/td>\n<td>Yes<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<td>Stack trace in thread data<\/td>\n<td>Yes<\/td>\n<td>Requires <code>result_<wbr \/>originate.h<\/code><\/td>\n<\/tr>\n<tr>\n<td>File\/line number in thrown object<\/td>\n<td>No<\/td>\n<td>Yes<\/td>\n<\/tr>\n<tr>\n<td>Recorded in wil error log<\/td>\n<td>No<\/td>\n<td>Yes<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>But wait, we&#8217;re not done yet. There&#8217;s another header file that affects how wil throws exceptions, and that&#8217;s <code>wil\/<wbr \/>cppwinrt.h<\/code>. This header file enables various C++\/WinRT+wil interop features, including exception handling. Exceptions propagated by the C++\/WinRT library (for example, by <code>check_<wbr \/>hresult()<\/code>) are filtered through wil, which logs them through its own error logging channel. However, since the file and line number were generated from the <code>__FILE__<\/code> and <code>__LINE__<\/code> preprocessor symbols captured by the <code>THROW_<wbr \/>IF_<wbr \/>FAILED<\/code> macro, C++\/WinRT cannot capture file and line number information about the origination point, so you don&#8217;t get line number information in your wil trace log. But you still get a stack trace in the <code>hresult_<wbr \/>error<\/code> object.<\/p>\n<p>Exceptions that are thrown explicitly via <code>throw hresult_error()<\/code> do not go through wil filtering.<\/p>\n<table style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th rowspan=\"3\">\u00a0<\/th>\n<th colspan=\"3\">C++\/WinRT<\/th>\n<th>wil<\/th>\n<\/tr>\n<tr>\n<th rowspan=\"2\">no <code>wil\/<wbr \/>cppwinrt.h<\/code><\/th>\n<th colspan=\"2\">with <code>wil\/<wbr \/>cppwinrt.h<\/code><\/th>\n<th>\u00a0<\/th>\n<\/tr>\n<tr>\n<th><code>throw hresult_<wbr \/>error<\/code><\/th>\n<th><code>check_<wbr \/>hresult<\/code><\/th>\n<th><code>THROW_<wbr \/>IF_<wbr \/>FAILED<\/code><\/th>\n<\/tr>\n<tr>\n<td>Thrown type<\/td>\n<td><code>hresult_<wbr \/>error<\/code><\/td>\n<td><code>hresult_<wbr \/>error<\/code><\/td>\n<td><code>hresult_<wbr \/>error<\/code><\/td>\n<td><code>Result\u00adException<\/code><\/td>\n<\/tr>\n<tr>\n<td>Stack trace in thrown object<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<td>Stack trace in thread data<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<td>Requires <code>result_originate.h<\/code><\/td>\n<\/tr>\n<tr>\n<td>File\/line number in thrown object<\/td>\n<td>No<\/td>\n<td>No<\/td>\n<td>No<\/td>\n<td>Yes<\/td>\n<\/tr>\n<tr>\n<td>Recorded in wil error log<\/td>\n<td>No<\/td>\n<td>No<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>But wait, we&#8217;re not finished yet. The wil framework alters its behavior if C++\/CX is enabled. If so, then it throws a <code>Platform::<wbr \/>Exception^<\/code> instead of a <code>wil::<wbr \/>Result\u00adException<\/code>. The <code>Platform::<wbr \/>Exception^<\/code> captures a stack trace but not file\/line number information.<\/p>\n<table style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th rowspan=\"3\">\u00a0<\/th>\n<th colspan=\"3\">C++\/WinRT<\/th>\n<th colspan=\"2\">wil<\/th>\n<\/tr>\n<tr>\n<th rowspan=\"2\">no <code>wil\/<wbr \/>cppwinrt.h<\/code><\/th>\n<th colspan=\"2\">with <code>wil\/<wbr \/>cppwinrt.h<\/code><\/th>\n<th>no C++\/CX<\/th>\n<th>with C++\/CX<\/th>\n<\/tr>\n<tr>\n<th><code>throw hresult_<wbr \/>error<\/code><\/th>\n<th><code>check_<wbr \/>hresult<\/code><\/th>\n<th><code>THROW_<wbr \/>IF_<wbr \/>FAILED<\/code><\/th>\n<th><code>THROW_<wbr \/>IF_<wbr \/>FAILED<\/code><\/th>\n<\/tr>\n<tr>\n<td>Thrown type<\/td>\n<td><code>hresult_<wbr \/>error<\/code><\/td>\n<td><code>hresult_<wbr \/>error<\/code><\/td>\n<td><code>hresult_<wbr \/>error<\/code><\/td>\n<td><code>Result\u00adException<\/code><\/td>\n<td><code>Exception^<\/code><\/td>\n<\/tr>\n<tr>\n<td>Stack trace in thrown object<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<td>No<\/td>\n<td>Yes<\/td>\n<\/tr>\n<tr>\n<td>Stack trace in thread data<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<td>Requires <code>result_<wbr \/>originate.h<\/code><\/td>\n<td>Yes<\/td>\n<\/tr>\n<tr>\n<td>File\/line number in thrown object<\/td>\n<td>No<\/td>\n<td>No<\/td>\n<td>No<\/td>\n<td>Yes<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<td>Recorded in wil error log<\/td>\n<td>No<\/td>\n<td>No<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n","protected":false},"excerpt":{"rendered":"<p>A survey of the current state of the art.<\/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-107429","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A survey of the current state of the art.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107429","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=107429"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107429\/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=107429"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=107429"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=107429"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}