翻译《The Old New Thing》- The case of the exception that a catch (…) didn’t catch

The case of the exception that a catch (...) didn't catch - The Old New Thing (microsoft.com)icon-default.png?t=N7T8https://devblogs.microsoft.com/oldnewthing/20240405-00/?p=109621

Raymond Chen 2024年04月05日


        一位客户认为他们修复了一个bug,但他们仍然因为这个bug而崩溃。

        根据!analyze的输出,问题来自于这个堆栈:

contoso!winrt::hresult_error::hresult_error+0x143
contoso!winrt::throw_hresult+0x132
contoso!winrt::impl::consume_LitWare_IIconProvider<winrt::LitWare::IIconProvider>::LoadIcon+0x3b
contoso!winrt::Contoso::implementation::IconDataModel::ReloadIcon$_ResumeCoro$1+0x214
contoso!winrt::impl::resume_background_callback+0x10
ntdll!TppSimplepExecuteCallback+0xa3
ntdll!TppWorkerThread+0x8f6
kernel32!BaseThreadInitThunk+0x1d
ntdll!RtlUserThreadStart+0x28

         这令人困惑,因为“我们已经修复了那个bug!”文件版本号和时间戳确认ReloadIcon的代码确实捕获了异常:

    try{icon = m_provider.LoadIcon(); // ⇐ blamed frame}catch(...){// There was a problem getting the new icon.// Just stick with the old one.LOG_CAUGHT_EXCEPTION();co_return;}

        让我们看看崩溃时的堆栈:

KERNELBASE!RaiseFailFastException+0x152
combase!RoFailFastWithErrorContextInternal2+0x4d9
contoso!wil::details::FailfastWithContextCallback+0xc1
contoso!wil::details::WilFailFast+0x47
contoso!wil::details::ReportFailure_NoReturn<3>+0x2df
contoso!wil::details::ReportFailure_Base<3,0>+0x30
contoso!wil::details::ReportFailure_CaughtExceptionCommonNoReturnBase<3>+0xa7
contoso!wil::details::ReportFailure_CaughtExceptionCommon+0x22
contoso!wil::details::ReportFailure_CaughtException<3>+0x40
contoso!wil::details::in1diag3::FailFast_CaughtException+0x13
contoso!`<lambda_f370031fe3623a0b308de0bbdeb2db76>::operator()'::`1'::catch$2+0x22
ucrtbase!_CallSettingFrame_LookupContinuationIndex+0x20
ucrtbase!__FrameHandler4::CxxCallCatchBlock+0x115
ntdll!RcFrameConsolidation+0x6
contoso!<lambda_f370031fe3623a0b308de0bbdeb2db76>::operator()+0x1a
contoso!std::invoke+0x24
contoso!std::_Invoker_ret<void,1>::_Call+0x24
contoso!std::_Func_impl_no_alloc<<lambda_f370031fe3623a0b308de0bbdeb2db76>,void,Concurrency::task<void> >::_Do_call+0x28
contoso!std::_Func_class<void,Concurrency::task<void> >::operator()+0x31
contoso!Concurrency::details::_MakeTToUnitFunc::__l2::<lambda_64124396551846798083ef48cd389b4a>::operator()+0x46
contoso!std::invoke+0x66
contoso!std::_Invoker_ret<unsigned char,0>::_Call+0x66
contoso!std::_Func_impl_no_alloc<<lambda_64124396551846798083ef48cd389b4a>,unsigned char,Concurrency::task<void> >::_Do_call+0x72
contoso!std::_Func_class<unsigned char,Concurrency::task<void> >::operator()+0x32
contoso!Concurrency::task<void>::_ContinuationTaskHandle<void,void,std::function<void __cdecl(Concurrency::task<void>)>,std::integral_constant<bool,1>,Concurrency::details::_TypeSelectorNoAsync>::_LogWorkItemAndInvokeUserLambda<std::function<unsigned char __cdecl(Concurrency::task<void>)>,Concurrency::task<void> >+0x8b
contoso!Concurrency::task<void>::_ContinuationTaskHandle<void,void,std::function<void __cdecl(Concurrency::task<void>)>,std::integral_constant<bool,1>,Concurrency::details::_TypeSelectorNoAsync>::_Continue+0x8c
contoso!Concurrency::task<void>::_ContinuationTaskHandle<void,void,std::function<void __cdecl(Concurrency::task<void>)>,std::integral_constant<bool,1>,Concurrency::details::_TypeSelectorNoAsync>::_Perform+0x8
contoso!Concurrency::details::_PPLTaskHandle<unsigned char,Concurrency::task<void>::_ContinuationTaskHandle<void,void,std::function<void __cdecl(Concurrency::task<void>)>,std::integral_constant<bool,1>,Concurrency::details::_TypeSelectorNoAsync>,Concurrency::details::_ContinuationTaskHandleBase>::invoke+0x37
contoso!Concurrency::details::_TaskProcHandle::_RunChoreBridge+0x25
contoso!Concurrency::details::_DefaultPPLTaskScheduler::_PPLTaskChore::_Callback+0x26
msvcp140!Concurrency::details::`anonymous namespace'::_Task_scheduler_callback+0x5d
ntdll!TppWorkpExecuteCallback+0x13a
ntdll!TppWorkerThread+0x8f6
kernel32!BaseThreadInitThunk+0x1d
ntdll!RtlUserThreadStart+0x28

        嘿,等等,这看起来一点也不像!analyze报告的堆栈!发生了什么?

        !analyze使用了第一次存储的异常堆栈。你可以使用!pde.dse命令转储所有存储的异常。

0:076> !pde.dse
Stowed Exception Array @ 0x000000002b1ef170Stowed Exception #1 @ 0x000000001ce068e80x80070005 (FACILITY_WIN32 - Win32 Undecorated Error Codes):E_ACCESSDENIED - General access denied errorStack    : 0x2b214de0contoso!winrt::hresult_error::hresult_error+0x143contoso!winrt::throw_hresult+0x132contoso!winrt::impl::consume_LitWare_IIconProvider<winrt::LitWare::IIconProvider>::LoadIcon+0x3bcontoso!winrt::Contoso::implementation::IconDataModel::ReloadIcon$_ResumeCoro$1+0x214contoso!winrt::impl::resume_background_callback+0x10ntdll!TppSimplepExecuteCallback+0xa3ntdll!TppWorkerThread+0x8f6kernel32!BaseThreadInitThunk+0x1dntdll!RtlUserThreadStart+0x28Stowed Exception #2 @ 0x000000001ce023780x80070005 (FACILITY_WIN32 - Win32 Undecorated Error Codes):E_ACCESSDENIED - General access denied errorStack    : 0x12cda890litware!winrt::hresult_error::hresult_error+0x12clitware!winrt::throw_hresult+0x83litware!winrt::LitWare::implementation::IconProvider::LoadIcon+0x90litware!winrt::impl::produce<winrt::LitWare::implementation::IconProvider,winrt::LitWare::IIconProvider>::LoadIcon+0x1bcontoso!winrt::impl::consume_LitWare_IIconProvider<winrt::LitWare::IIconProvider>::LoadIcon+0x3bcontoso!winrt::Contoso::implementation::IconDataModel::ReloadIcon$_ResumeCoro$1+0x214contoso!winrt::impl::resume_background_callback+0x10ntdll!TppSimplepExecuteCallback+0xa3ntdll!TppWorkerThread+0x8f6kernel32!BaseThreadInitThunk+0x1dntdll!RtlUserThreadStart+0x28Stowed Exception #3 @ 0x000000001ce04fa80x80070005 (FACILITY_WIN32 - Win32 Undecorated Error Codes):E_ACCESSDENIED - General access denied errorStack    : 0x1d94b410combase!RoOriginateError+0x51contoso!wil::details::RaiseRoOriginateOnWilExceptions+0x137contoso!wil::details::ReportFailure_Return<1>+0x1b8contoso!wil::details::ReportFailure_Win32<1>+0x70contoso!wil::details::in1diag3::Return_Win32+0x18contoso!Internal::ContosoSettingsStorage::Save+0xdc729contoso!Internal::ContosoSettings::SaveToDefaultLocalStorage+0xf1contoso!Internal::ContosoSettings::Save+0x4efcontoso!Contoso::AppSettings::save+0x4efcontoso!std::_Func_impl_no_alloc<<lambda_f4300885c0b58e31cf789c4999ed9d7a>,void>::_Do_call+0x2bcontoso!std::_Func_impl_no_alloc<<lambda_052e919cc0e5399df76dff3972c0cac1>,unsigned char>::_Do_call+0x28contoso!Concurrency::task<unsigned char>::_InitialTaskHandle<void,<lambda_f4300885c0b58e31cf789c4999ed9d7a>,Concurrency::details::_TypeSelectorNoAsync>::_Init+0xc3contoso!Concurrency::details::_PPLTaskHandle<unsigned char,Concurrency::task<unsigned char>::_InitialTaskHandle<void,<lambda_f4300885c0b58e31cf789c4999ed9d7a>,Concurrency::details::_TypeSelectorNoAsync>,Concurrency::details::_TaskProcHandle>::invoke+0x55contoso!Concurrency::details::_TaskProcHandle::_RunChoreBridge+0x25contoso!Concurrency::details::_DefaultPPLTaskScheduler::_PPLTaskChore::_Callback+0x26msvcp140!Concurrency::details::`anonymous namespace'::_Task_scheduler_callback+0x5dntdll!TppWorkpExecuteCallback+0x13antdll!TppWorkerThread+0x686kernel32!BaseThreadInitThunk+0x10ntdll!RtlUserThreadStart+0x2b

        现在事情开始清晰起来了。

        抛出 Windows 运行时异常的经验法则是,在抛出异常或返回失败的HRESULT之前,你调用RoOriginateError来捕获堆栈和其他上下文。在处理 Windows 运行时常常用到的是异常被捕获并保存(“存储”),通常在IAsyncAction或类似的接口中,然后稍后,当调用者执行co_await或类似的操作时,异常被重新抛出。

        当异常被重新抛出时,原始堆栈已经被展开,所以堆栈上没有东西可以追踪。调用RoOriginateError在为时已晚之前捕获失败点的堆栈。然后这些信息可以用来“拼接”异常的生命周期,从抛出异常的代码开始,到尝试(并失败)捕获它的代码结束。

        系统通过在每个线程的数据中存储错误历史来完成这种堆栈拼接,允许组件捕获该历史并将其传输到另一个线程,当任务的错误状态在线程之间移动时,并寻找具有相同HRESULT的错误。如果有一个最近的捕获堆栈与未处理的异常的HRESULT匹配,那么系统会说,“我敢打赌这两个属于一起。”

        通常,所有这些堆栈拼接工作都很好,因为我们的 API 设计原则说,不应该为可恢复的错误抛出异常。这意味着通常没有大量的异常流量,所以误报的比率很低。

        但在这个案例中,我们有一个误报:IconDataModel调用了IconProvider::LoadIcon(),它以E_ACCESSDENIED失败。这个异常随后被捕获并处理。我们从前两个存储的异常中看到了这一点,使用我们刚才学到的关于拼接多个错误堆栈来获得导致失败的更完整的画面。

        在这种情况下,IconProvider::LoadIcon()明确地使用throw_hresult抛出了一个异常(存储的异常 #2),然后在 ABI 边界处将异常从 C++ 异常转换为HRESULT,然后在另一边,C++/WinRT 将HRESULT重新转换为异常并重新抛出(存储的异常 #1)。这个重新抛出的异常随后被catch(...)捕获,这就是那个异常的结束。

        但这并不是导致我们崩溃的原因。

         当前活动的堆栈显示我们从 lambda 表达式中引发了一个快速失败异常。调试器告诉我们是这个 lambda: 

void ViewPreferences::SaveChanges()
{m_settings.save_async().then([](concurrency::task<void> precedingTask) {try{precedingTask.get();}CATCH_FAIL_FAST();});
}

        代码保存设置,并在操作失败时立即失败。

        我们在第三个堆栈中看到了这个失败,也就是ContosoSettingsStorage::Save那个。那个Save操作以E_ACCESSDENIED失败,并记录在了失败历史中。

        发生的事情是,大约在同一时间发生了两个E_ACCESSDENIED错误,!analyze试图弄清楚哪个堆栈属于哪个序列,并没有完全成功,它认为当前的失败与m_provider.LoadIcon()失败相匹配。但我们使用我们的人类大脑,看到m_provider.LoadIcon()异常被处理了,真正的罪魁祸首是存储的异常 #3。

        你可以调用函数RoTransformError如果你的代码接收一个错误代码并返回一个不同的错误代码。这告诉 COM 错误跟踪这两个错误序列应该拼接在一起形成一个大的错误序列。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/26917.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python django初步搭建(一)

记录一次简单的python django使用&#xff0c;后续调用api相关的暂时不想写。。。 一、环境 windows python 3.11.7 django 二、初步搭建 2.1 新建空文件夹 为了方便本次记录&#xff0c;新建了一个空的文件夹来使用。 直接在这里输入cmd 然后按下回车 2.2 安装virtual…

华为数通——奇偶数子网划分与流量控制(QoS)

在华为设备上使用 QoS&#xff08;Quality of Service&#xff09;配置来实现将 IP 地址段 192.168.1.0/24 分成奇数和偶数两个子网&#xff0c;并将它们定向到不同的下一跳&#xff0c;可以通过以下步骤进行配置&#xff1a; 配置 ACL配置流量分类器配置流量行为配置流量策略应…

Vim安装

Vim安装的过程可以根据您使用的操作系统有所不同。以下是在不同系统上安装Vim的详细步骤&#xff1a; 1. Linux系统&#xff08;如Debian/Ubuntu&#xff09; 1.1 检测Vim是否已安装 打开终端&#xff0c;输入vi&#xff0c;然后按两次Tab键。 如果显示了Vim的相关命令&…

前端 JS 经典:package.json 属性详解

前言&#xff1a;package.json 里的字段大致分两部分&#xff0c;一部分标准字段&#xff0c;一部分非标字段。标准字段就是官方定义好的字段&#xff0c;非标字段包括作者自定义字段 1. 标准字段 1.1 name 包名&#xff0c;就是我们用 npm 去下载的名字&#xff0c;就定义在…

vue页面和 iframe多页面无刷新方案和并行存在解决方案

面临问题 : back的后台以jsp嵌套iframe为主, 所以在前端框架要把iframe无刷新嵌套和vue页面进行并行使用,vue的keep-alive只能对虚拟dom树 vtree 进行缓存无法缓存iframe,所以要对iframe进行处理 tab标签的切换效果具体参考若依框架的tab切换,可以去若依看源码,若依源码没有实…

DES文件加密解密(增强版)

1.引入hutool-all的maven依赖 <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.28</version> </depend…

C++设计模式——Proxy代理模式

一&#xff0c;代理模式简介 代理模式是一种 结构型设计模式&#xff0c;该模式通过引入一个新的代理对象Proxy&#xff0c;来间接访问原始对象&#xff0c;从而使访问方式变得灵活和可控。 代理对象的设定减少了客户端与真实对象之间的直接交互。 通过引入代理对象来间接访问原…

农资投入品系统架构:数字化农业的技术支撑与创新

在当今数字化时代&#xff0c;农业领域也在迅速迈向数字化和智能化的新阶段。农资投入品系统作为农业生产的重要支撑&#xff0c;其系统架构的设计与创新对于提高农业生产效率、保障粮食安全具有重要意义。本文将探讨农资投入品系统架构的设计原则、核心模块以及未来发展趋势。…

OrangePi AIpro测评:性能、应用与开发者体验解析

一、OrangePi AIpro介绍 OrangePi AIpro(8T)采用昇腾AI技术路线&#xff0c;具体为4核64位处理器AI处理器&#xff0c;集成图形处理器&#xff0c;支持8TOPS AI算力&#xff0c;拥有8GB/16GB LPDDR4X&#xff0c;可以外接32GB/64GB/128GB/256GB eMMC模块&#xff0c;支持双4K高…

AI虚拟试穿技术:开启高保真、多场景、多样化服装组合的试穿应用

随着电子商务的快速发展,消费者对于在线购物体验的要求越来越高。特别是在服装领域,消费者渴望能够在购买前直观地了解服装的试穿效果。传统的虚拟试穿技术虽然已有一定的发展,但在不同场景下的高保真度和鲁棒性方面仍面临挑战。为此,我们研发了一种全新的AI虚拟试穿技术,…

StarkNet架构之L1-L2消息传递机制

文章目录 StarkNet架构之L1-L2消息传递机制L2 → L1消息L2 → L1消息结构L2 → L1消息哈希L1 → L2消息L1 → L2消息取消L1 → L2报文费用L1 → L2哈希额外资源StarkNet架构之L1-L2消息传递机制 原文地址:https://docs.starknet.io/architecture-and-concepts/network-archit…

2.spring cloud gateway 源码编译

spring cloud gateway编译 1.编译 命令 mvn clean compile -U2.报错 报错信息 核心信息 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-checkstyle-plugin:3.1.2:check (checkstyle-validation) on project spring-cloud-gateway-mvc: Failed during …

【分布式技术专题】「OceanBase深度解析」 探索OceanBase产品矩阵与核心设计

探索OceanBase产品矩阵与核心设计 OceanBase的六大特性高扩展高可用多租户&#xff08;资源隔离&#xff09;OceanBase架构和功能OceanBase广泛的数据源支持 OceanBase的六大特性 OceanBase以其卓越的产品平台整合方案&#xff0c;充分展现了六大核心特性的卓越与全面。这一方…

深入解析 Google Cloud Platform (GCP) 负载均衡:全面指南

目录 概述 GCP 负载均衡的类型 HTTP(S) 负载均衡 TCP/SSL 代理负载均衡 内部 HTTP(S) 负载均衡 内部 TCP/UDP 负载均衡 网络 TCP/UDP 负载均衡 配置 GCP 负载均衡 步骤 1:创建后端服务 步骤 2:配置 URL 地图和目标 HTTP 代理 步骤 3:配置全局转发规则 步骤 4:验…

C++ | Leetcode C++题解之第150题逆波兰表达式求值

题目&#xff1a; 题解&#xff1a; class Solution { public:int evalRPN(vector<string>& tokens) {int n tokens.size();vector<int> stk((n 1) / 2);int index -1;for (int i 0; i < n; i) {string& token tokens[i];if (token.length() >…

人工智能将成为数学家的“副驾驶”

人工智能将成为数学家的“副驾驶” 数学传统上是一门独立的科学。1986年&#xff0c;安德鲁怀尔斯为了证明费马定理&#xff0c;退到书房里呆了7年。由此产生的证明往往很难让同事们理解&#xff0c;有些至今仍有争议。但近年来&#xff0c;越来越多的数学领域被严格地分解为各…

winhttp劫持dll

转发类 #include "pch.h" #include "WinHttpForward.h"void WinHttpForward::InitWinHttpForward() {HMODULE hValve LoadLibrary(L"winhttp.dll");//导入系统路径下的if (GetLastError() ! ERROR_MOD_NOT_FOUND && hValve ! NULL) {G…

如何手动实现multiSetIfAbsent、multiExpire

👽System.out.println(“👋🏼嗨,大家好,我是代码不会敲的小符,目前工作于上海某电商服务公司…”); 📚System.out.println(“🎈如果文章中有错误的地方,恳请大家指正!共同进步,共同成长✊”); 🌟System.out.println(“💡如果文章对您有所帮助,希望您可以三…

Django-filter

准备工作 首先&#xff0c;确保你已经安装了django-filter包。如果没有&#xff0c;请使用以下命令安装&#xff1a; pip install django-filter然后&#xff0c;在你的settings.py文件中添加django_filters到INSTALLED_APPS列表中&#xff1a; INSTALLED_APPS [# ...djang…

深入解析JVM之类的生命周期

在线工具站 推荐一个程序员在线工具站&#xff1a;程序员常用工具&#xff08;http://cxytools.com&#xff09;&#xff0c;有时间戳、JSON格式化、文本对比、HASH生成、UUID生成等常用工具&#xff0c;效率加倍嘎嘎好用。 程序员资料站 推荐一个程序员编程资料站&#xff1a;…