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

news2025/1/9 1:52:45

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 @ 0x000000002b1ef170

Stowed Exception #1 @ 0x000000001ce068e8
    0x80070005 (FACILITY_WIN32 - Win32 Undecorated Error Codes):
    E_ACCESSDENIED - General access denied error

    Stack    : 0x2b214de0
    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

Stowed Exception #2 @ 0x000000001ce02378
    0x80070005 (FACILITY_WIN32 - Win32 Undecorated Error Codes):
    E_ACCESSDENIED - General access denied error

    Stack    : 0x12cda890
    litware!winrt::hresult_error::hresult_error+0x12c
    litware!winrt::throw_hresult+0x83
    litware!winrt::LitWare::implementation::IconProvider::LoadIcon+0x90
    litware!winrt::impl::produce<winrt::LitWare::implementation::IconProvider,
        winrt::LitWare::IIconProvider>::LoadIcon+0x1b
    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

Stowed Exception #3 @ 0x000000001ce04fa8
    0x80070005 (FACILITY_WIN32 - Win32 Undecorated Error Codes):
    E_ACCESSDENIED - General access denied error

    Stack    : 0x1d94b410
    combase!RoOriginateError+0x51
    contoso!wil::details::RaiseRoOriginateOnWilExceptions+0x137
    contoso!wil::details::ReportFailure_Return<1>+0x1b8
    contoso!wil::details::ReportFailure_Win32<1>+0x70
    contoso!wil::details::in1diag3::Return_Win32+0x18
    contoso!Internal::ContosoSettingsStorage::Save+0xdc729
    contoso!Internal::ContosoSettings::SaveToDefaultLocalStorage+0xf1
    contoso!Internal::ContosoSettings::Save+0x4ef
    contoso!Contoso::AppSettings::save+0x4ef
    contoso!std::_Func_impl_no_alloc<<lambda_f4300885c0b58e31cf789c4999ed9d7a>,
        void>::_Do_call+0x2b
    contoso!std::_Func_impl_no_alloc<<lambda_052e919cc0e5399df76dff3972c0cac1>,
        unsigned char>::_Do_call+0x28
    contoso!Concurrency::task<unsigned char>::_InitialTaskHandle<void,
        <lambda_f4300885c0b58e31cf789c4999ed9d7a>,
        Concurrency::details::_TypeSelectorNoAsync>::_Init+0xc3
    contoso!Concurrency::details::_PPLTaskHandle<unsigned char,
        Concurrency::task<unsigned char>::_InitialTaskHandle<void,
        <lambda_f4300885c0b58e31cf789c4999ed9d7a>,
        Concurrency::details::_TypeSelectorNoAsync>,
        Concurrency::details::_TaskProcHandle>::invoke+0x55
    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+0x686
    kernel32!BaseThreadInitThunk+0x10
    ntdll!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.coloradmin.cn/o/1820640.html

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

相关文章

LogicFlow 学习笔记——4. LogicFlow 基础 边 Edge

边 Edge 和节点一样&#xff0c;LogicFlow 也内置一些基础的边。LogicFlow 的内置边包括&#xff1a; 直线 - line直角折现 - polyline贝塞尔曲线 - bezier 新建 src/views/Example/LogicFlow/Example08.vue 并编写如下代码&#xff1a; <script setup lang"ts&quo…

c#中上传超过30mb的文件,接口一直报404,小于30mb的却可以上传成功

在一次前端实现上传视频文件时,超过30mb的文件上传,访问接口一直报404,但是在Swagger中直接访问接口确是正常的,且在后端控制器中添加了限制特性,如下 但是却仍然报404,在apifox中请求接口也是报404, 网上说: 在ASP.NET Core中,配置请求过来的文件上传的大小限制通常…

FPGA - 全局时钟资源

全局时钟资源是指FPGA内部为实现系统时钟到达FPGA内部各 CLB、IOB&#xff0c;以及BSRAM&#xff08;Block Select RAM&#xff0c;选择性BRAM&#xff09;等基本逻辑单元的延时和抖动最小化&#xff0c;采用全铜层工艺设计和实现的专用缓冲与驱动结构。 由于全局时钟资源的布线…

电子制造业数字化整体解决方案

电子制造行业有特殊的着重点&#xff1a; 高精度要求&#xff1a;电子制造需要极高的精度和质量控制&#xff0c;因为电子组件和电路板的尺寸通常非常小&#xff0c;且对错误和缺陷非常敏感。 快速技术迭代&#xff1a;电子行业的技术迅速发展&#xff0c;产品生命周期短&…

RabbitMQ概述

RabbitMQ RabbitMQ概述 RabbitMQ是一个开源的消息代理&#xff08;message broker&#xff09;系统&#xff0c;最初由Rabbit Technologies Ltd开发&#xff0c;并在开源社区的支持下不断发展和完善。它提供了强大的消息传递机制&#xff0c;被广泛应用于构建分布式系统和应用…

vue3 proxy对象转为原始对象

https://cn.vuejs.org/api/reactivity-advanced.html#toraw import { toRaw } from "vue";const foo {} const reactiveFoo reactive(foo)console.log(toRaw(reactiveFoo) foo) // true 人工智能学习网站 https://chat.xutongbao.top

基于LangChain-Chatchat实现的RAG-本地知识库的问答应用[1]-最新版快速实践并部署(检索增强生成RAG大模型)

基于LangChain-Chatchat实现的RAG-本地知识库的问答应用[1]-最新版快速实践并部署(检索增强生成RAG大模型) 基于 ChatGLM 等大语言模型与 Langchain 等应用框架实现,开源、可离线部署的检索增强生成(RAG)大模型知识库项目。 1.介绍 一种利用 langchain思想实现的基于本地知…

『原型资源』Axure自带图标库不够用,第三方经典图标库来袭

​今天小编为大家带来第三方经典图标库&#xff0c;己确认内容可用现推荐给大家。直接上手就可不用自己画哈~ 获取原型文档请与班主任联系&#xff01; 先睹为快&#xff0c;合适再拿走不谢&#xff1a; 图标太多&#xff0c;截取部分给大家参考o(*&#xffe3;︶&#xffe3;*…

毕业了!给学计算机朋友的 10 条血泪建议

大家好&#xff0c;我是程序员鱼皮。最近高考结束了&#xff0c;也有很多同学毕业了&#xff0c;首先祝福这些朋友在人生的新阶段一帆风顺。 刚参加完高考的朋友&#xff0c;面临的最大问题就是选专业&#xff0c;这段时间也有一些家长向我咨询&#xff1a;还能不能选计算机啦…

2024 年 19 种最佳大型语言模型

大型语言模型是 2023 年生成式人工智能热潮背后的推动力。然而&#xff0c;它们已经存在了一段时间了。 LLM是黑盒 AI 系统&#xff0c;它使用深度学习对超大数据集进行处理&#xff0c;以理解和生成新文本。现代 LLM 开始成型于 2014 年&#xff0c;当时一篇题为“通过联合学…

鸿蒙轻内核A核源码分析系列七 进程管理 (1)

本文开始继续分析OpenHarmony LiteOS-A内核的源代码&#xff0c;接下来会分析进程和任务管理模块。本文中所涉及的源码&#xff0c;以OpenHarmony LiteOS-A内核为例&#xff0c;均可以在开源站点 https://gitee.com/openharmony/kernel_liteos_a 获取。如果涉及开发板&#xff…

排名前五的 Android 数据恢复软件

正在寻找数据恢复软件来从 Android 设备恢复数据&#xff1f;本指南将为您提供 5 款最佳 Android 数据恢复软件。浏览这些软件&#xff0c;然后选择您喜欢的一款来恢复 Android 数据。 ndroid 设备上的数据丢失可能是一种令人沮丧的经历&#xff0c;无论是由于意外删除、系统崩…

Sm4【国密4加密解密】

当我们开发金融、国企、政府信息系统时&#xff0c;不仅要符合网络安全的等保二级、等保三级&#xff0c;还要求符合国密的安全要求&#xff0c;等保测评已经实行很久了&#xff0c;而国密测评近两年才刚开始。那什么是密码/国密&#xff1f;什么是密评&#xff1f;本文就关于密…

vs2019 c++20规范 STL 库中头文件 <atomic> 源码注释及探讨几个知识点

&#xff08;1 探讨一&#xff09; 模板类 atomic 的继承关系与数据结构如下&#xff1a; (2 探讨二 ) 可见 atomic 的 fetch_xx 函数&#xff0c;返回的都是 atomic 中存储的旧值。测试如下&#xff1a; 谢谢

Weighted A* 改进型(1):XDP

本文的主要内容来自于文献[1]&#xff0c;总的来说这篇文献给我的感觉就是理论证明非常精妙&#xff0c;最后的实际效果也是提升的非常明显。 在Introduction中作者给出了一般Best first search&#xff08;BFS&#xff0c;常用的包括A *&#xff0c;weighted A * &#xff0c…

FPGA - 滤波器 - FIR滤波器设计

一&#xff0c;数字滤波器 滤波器是一种用来减少或消除干扰的器件&#xff0c;其功能是对输入信号进行过滤处理得到所需的信号。滤波器最常见的用法是对特定频率的频点或该频点以外的频率信号进行有效滤除&#xff0c;从而实现消除干扰、获取某特定频率信号的功能。一种更广泛的…

一杯咖啡的艺术 | 如何利用数字孪生技术做出完美的意式浓缩咖啡?

若您对数据分析以及人工智能感兴趣&#xff0c;欢迎与我们一起站在全球视野关注人工智能的发展&#xff0c;与Forrester 、德勤、麦肯锡等全球知名企业共探AI如何加速制造进程&#xff0c; 共同参与6月20日由Altair主办的面向工程师的全球线上人工智能会议“AI for Engineers”…

考研计组chap3存储系统

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

Sui Bridge在测试网上线并推出10万SUI激励计划

是一种为Sui设计的原生桥接协议&#xff0c;专门用于在Sui与其他网络之间桥接资产和数据。今天&#xff0c;Sui Bridge宣布在测试网上线。作为一种原生协议&#xff0c;Sui Bridge能够在Ethereum和Sui之间轻松且安全地转移ETH、wBTC、USDC和USDT&#xff0c;使其成为Sui基础设施…

法考报名必看,99%高过审率证件照片电子版制作技巧

在2024年&#xff0c;法考备战已经如火如荼进行中&#xff0c;作为进入法律行业的第一步&#xff0c;参加法考的重要性不言而喻。而作为报名过程中必不可少的一环&#xff0c;报名照片要求以及证件照制作技巧更是需要我们特别重视的部分。想要在这个过程中顺利通过审核&#xf…