翻译《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…

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

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

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虚拟试穿技术,…

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;充分展现了六大核心特性的卓越与全面。这一方…

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;越来越多的数学领域被严格地分解为各…

如何手动实现multiSetIfAbsent、multiExpire

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

2024年大数据、区块链与物联网国际会议(ICBDBLT 2024)

2024 International Conference on Big Data, Blockchain, and Internet of Things 【1】大会信息 会议简称&#xff1a;ICBDBLT 2024 大会地点&#xff1a;中国青岛 审稿通知&#xff1a;投稿后2-3日内通知 会议官网&#xff1a;www.icbdblt.com 【2】会议简介 即将召开的…

DDoS攻击:企业与个人都应了解的基本知识

DDoS攻击&#xff0c;全称分布式拒绝服务攻击&#xff08;Distributed Denial of Service attack&#xff09;&#xff0c;是一种常见的网络安全攻击方式。以下是对DDoS攻击的详细解释: DDoS攻击是指攻击者利用大量被控制的计算机或设备&#xff08;通常称为“僵尸网络”或“傀…

签到的二维码怎么制作?快速实现制作二维码签到的方法

现在很多活动会采用二维码的方式来做登记、报名、签到等&#xff0c;通过二维码可以快速获取用户信息&#xff0c;并且对于用户填写内容也提升了便利性&#xff0c;而且还能够节约成本&#xff0c;通过后台就可以查看用户登记的数据&#xff0c;方便后期的分析和信息管理。 想…

考研计组chap3存储系统

目录 一、存储器的基本概念 80 1.按照层次结构 2.按照各种分类 &#xff08;41&#xff09;存储介质 &#xff08;2&#xff09;存取方式 &#xff08;3&#xff09;内存是否可更改 &#xff08;4&#xff09;信息的可保存性 &#xff08;5&#xff09;读出之后data是否…

SwaggerSpy:一款针对SwaggerHub的自动化OSINT安全工具

关于SwaggerSpy SwaggerSpy是一款针对SwaggerHub的自动化公开资源情报&#xff08;OSINT&#xff09;安全工具&#xff0c;该工具专为网络安全研究人员设计&#xff0c;旨在简化广大红队研究人员从SwaggerHub上收集已归档API信息的过程&#xff0c;而这些OSINT信息可以为安全人…

【全网瞩目】最强文生图模型,Stable Diffusion 3技术报告解禁

12号&#xff0c;终于在Hugging Face上出现了 Stable Diffusion 3 Medium。没错&#xff0c;正如他所承诺的&#xff0c;最强文生图模型真的开源了。而且此次开源不仅是以SD2的比较下性能得到了更好的升级&#xff0c;同时也向我们展示了最前沿的DiT技术——MMDiT。 是什么让 S…

【乐吾乐2D可视化组态编辑器】导出HTML,下载离线部署包

乐吾乐2D可视化组态编辑器地址&#xff1a;https://2d.le5le.com/ 使用步骤 1. 从“文件”菜单导出HTML 导出为 HTML 需要一定的开发能力&#xff0c;后续不再维护&#xff0c;即将下线&#xff0c;推荐使用 下载离线部署包&#xff08;html&#xff09; 2. 解压 3. 下载后端…

阿里云运维第一步(监控):开箱即用的监控

作者&#xff1a;仲阳 这是云的时代&#xff0c;现在云计算已经在各行各业广泛的应用。但是上云对于大多数客户来说&#xff0c;依然有很大的学习成本&#xff0c;如下图仅是阿里云都有几百款产品&#xff0c;怎么选择&#xff1f;怎么用&#xff1f;对于客户来说都是问题。“…

手撕设计模式——计划生育之单例模式

1.业务需求 ​ 大家好&#xff0c;我是菠菜啊。80、90后还记得计划生育这个国策吗&#xff1f;估计同龄的小伙伴们&#xff0c;小时候常常被”只生一个好“”少生、优生“等宣传标语洗脑&#xff0c;如今国家已经放开并鼓励生育了。话说回来&#xff0c;现实生活中有计划生育&…