{"id":111366,"date":"2025-07-10T07:00:00","date_gmt":"2025-07-10T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111366"},"modified":"2025-07-10T08:15:22","modified_gmt":"2025-07-10T15:15:22","slug":"20250710-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250710-00\/?p=111366","title":{"rendered":"Our first attempt to detect and report all unhandled C++ exceptions as well as all unhandled structured exceptions"},"content":{"rendered":"<p>Last time, we saw that <a title=\"When I install an unhandled structured exception filter, why doesn't std::terminate get called?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250709-00\/?p=111360\"> installing an unhandled structured exception filter prevents the Microsoft Visual C++ runtime from converting unhandled C++ exceptions into <code>std::<wbr \/>terminate<\/code><\/a>, because the C++ runtime itself uses the unhandled structured exception filter to do that conversion.<\/p>\n<p>One option is to detect that your unhandled structured exception handler is observing an unhandled C++ exception, in which case you allow the call to pass through to the C++ unhandled exception handler so it can turn it into a <code>std::<wbr \/>terminate<\/code>.<\/p>\n<pre>#define MSVC_EXCEPTION_CODE 0xE06D7363U\r\n\r\nLPTOP_LEVEL_EXCEPTION_FILTER previousFilter;\r\nstd::terminate_handler previousTerminate;\r\n\r\nLONG CALLBACK MyUnhandledExceptionFilter(\r\n    EXCEPTION_POINTERS *ExceptionInfo)\r\n{\r\n    if (ExceptionInfo-&gt;ExceptionRecord.ExceptionCode ==\r\n            MSVC_EXCEPTION_CODE) {\r\n        return previousFilter(ExceptionInfo);\r\n    }\r\n    CaptureDump(ExceptionInfo,\r\n                UnhandledException::Structured);\r\n    return EXCEPTION_EXECUTE_HANDLER;\r\n}\r\n\r\nvoid __cdecl my_terminate()\r\n{\r\n    CaptureDump(nullptr,\r\n                UnhandledException::Cpp);\r\n    if (previousTerminate) {\r\n        previousTerminate();\r\n    } else {\r\n        std::abort();\r\n    }\r\n}\r\n\r\nvoid InstallCustomUnhandledExceptionFilters()\r\n{\r\n    previousFilter = SetUnhandledExceptionFilter(\r\n                        MyUnhandledExceptionFilter);\r\n\r\n    previousTerminate = std::set_terminate(my_terminate);\r\n}\r\n<\/pre>\n<p>The diagram looks like this:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">Exception raised<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">Frame handler<\/td>\n<td>&nbsp;<\/td>\n<td>(not handled)<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">Frame handler<\/td>\n<td>&nbsp;<\/td>\n<td>(not handled)<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">\u22ee<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">Frame handler<\/td>\n<td>&nbsp;<\/td>\n<td>(not handled)<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">MyUnhandled-<br \/>\nExceptionFilter<\/td>\n<td style=\"text-align: center;\">not C++<br \/>\n\u2192<\/td>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">CaptureDump(Structured)<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">CppUnhandled-<br \/>\nExceptionFilter<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">std::terminate<\/td>\n<td style=\"text-align: center;\">\u2192<\/td>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">CaptureDump(Cpp)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>But wait, why pass the C++ exception through to the runtime&#8217;s unhandled exception handler if we know that it&#8217;s just going to call our our custom terminate handler?\u00b9 We can call the custom terminate handler directly.<\/p>\n<pre>#define MSVC_EXCEPTION_CODE 0xE06D7363U\r\n\r\nLONG CALLBACK MyUnhandledExceptionFilter(\r\n    EXCEPTION_POINTERS *ExceptionInfo)\r\n{\r\n    if (ExceptionInfo-&gt;ExceptionRecord.ExceptionCode ==\r\n            MSVC_EXCEPTION_CODE) {\r\n        CaptureDump(ExceptionInfo,\r\n                    UnhandledException::Structured);\r\n    } else {\r\n        CaptureDump(ExceptionInfo,\r\n                    UnhandledException::Cpp);\r\n    }\r\n    return EXCEPTION_EXECUTE_HANDLER;\r\n}\r\n\r\nvoid InstallCustomUnhandledExceptionFilters()\r\n{\r\n    previousFilter = SetUnhandledExceptionFilter(\r\n                        MyUnhandledExceptionFilter);\r\n}\r\n<\/pre>\n<p>Now the diagram looks like this:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">Exception raised<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">Frame handler<\/td>\n<td>&nbsp;<\/td>\n<td>(not handled)<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">Frame handler<\/td>\n<td>&nbsp;<\/td>\n<td>(not handled)<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">\u22ee<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">Frame handler<\/td>\n<td>&nbsp;<\/td>\n<td>(not handled)<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">MyUnhandled-<br \/>\nExceptionFilter<\/td>\n<td style=\"text-align: center;\">\u2192<\/td>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">CaptureDump(Cpp)<\/td>\n<td style=\"width: 1em;\">\u00a0<\/td>\n<td>If not C++<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td style=\"text-align: center;\">\u2198<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px currentcolor; text-align: center;\">CaptureDump(Cpp)<\/td>\n<td style=\"width: 1em;\">\u00a0<\/td>\n<td>If C++<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>But wait, this is still not complete. There are ways for a program to terminate with an unhandled exception that don&#8217;t go through the unhandled exception filter. We&#8217;ll look at those next time.<\/p>\n<p>\u00b9 On reason is that from the custom terminate handler, you can use <code>std::<wbr \/>current_<wbr \/>exception()<\/code> to snoop on the exception that caused the termination (if there is one).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Identifying and classifying the exit points.<\/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-111366","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Identifying and classifying the exit points.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111366","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=111366"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111366\/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=111366"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111366"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111366"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}