C++/WinRT教程(第四篇)WinRT 的错误和异常处理

目录

前言

避免捕获和抛出异常

捕获异常

抛出异常

编辑API时抛出异常

使用 noexcept 时如何调试

调用同步代码

快速失败

断言


前言

本文主要介绍 C++/WinRT 中的异常如何使用以及使用原则,如果你刚开始接触WinRT,建议先阅读第一篇。

C++/WinRT教程(第一篇)-CSDN博客

C++/WinRT教程(第二篇)基础类型的使用-CSDN博客

C++/WinRT教程(第三篇)API的使用-CSDN博客

其他资料:

现代 C++ 处理异常和错误的最佳做法 | Microsoft Learn

操作说明:异常安全性设计 | Microsoft Learn

避免捕获和抛出异常

最好尽量避免捕获和抛出异常。 如果没有异常处理程序,Windows 将自动生成错误报告(包括故障的小型转储),以便跟踪问题所在位置。

应仅在发生意外运行时错误时抛出异常,并处理带有错误/结果代码的任何其他事项,直接并靠近故障原因。 这样,当异常“被”引发时,你会知道原因是代码中的 bug 还是系统中的异常错误状态。

例如访问 Windows 注册表的场景。 如果你的应用无法从注册表读取值,这是预料之中的,你应该正确处理。 不要抛出异常;而应返回 bool 或 enum 值指示未读取值或原因。 另一方面,无法向注册表写入值很可能表示你的应用程序中存在的问题更大

抛出异常异常往往会比使用错误代码更慢。谷歌和微软代码规范都不提倡使用异常。

捕获异常

在 Windows 运行时 ABI 层出现的错误状态以 HRESULT 值的形式返回。 不过你无需处理代码中的 HRESULT。 为每个使用方的 API 生成的 C++/WinRT 投影代码将检测 ABI 层的错误 HRESULT 代码,并将代码转换为你可以捕获并处理的 winrt::hresult_error 异常。 如果你的确希望处理 HRESULTS,那么请使用“winrt::hresult”类型。

例如,如果用户碰巧,在你的应用程序迭代图片库时,从该集合中删除了图像,那么投影将抛出异常。 这是你必须捕获和处理该异常的一种情况。 下面的代码示例展示了这种情况。

#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.UI.Xaml.Media.Imaging.h>using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Storage;
using namespace Windows::UI::Xaml::Media::Imaging;IAsyncAction MakeThumbnailsAsync()
{auto imageFiles{ co_await KnownFolders::PicturesLibrary().GetFilesAsync() };for (StorageFile const& imageFile : imageFiles){BitmapImage bitmapImage;try{auto thumbnail{ co_await imageFile.GetThumbnailAsync(FileProperties::ThumbnailMode::PicturesView) };if (thumbnail) bitmapImage.SetSource(thumbnail);}catch (winrt::hresult_error const& ex){winrt::hresult hr = ex.code(); // HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND).winrt::hstring message = ex.message(); // The system cannot find the file specified.}}
}

请在调用 co_await 的函数时在协调程序中使用相同模式。 此 HRESULT 到异常转换的另一个示例是,当组件 API 返回 E_OUTOFMEMORY 时,会导致抛出“std::bad_alloc”。

如果只是要浏览 HRESULT 代码,则首选 winrt::hresult_error::code。 另一方面,winrt::hresult_error::to_abi 函数转换为 COM 错误对象,并将状态推送到 COM 线程本地存储。

抛出异常

注:慎重,没捕获成功你的程序会原地崩溃

下方代码示例使用 winrt::handle 值作为从 CreateEvent 返回的 HANDLE 的包装 。 然后将该句柄(从其创建 bool 值)传递到 winrt::check_bool 函数模板。

 “winrt::check_bool”使用 bool 或任何可转换为 false(错误条件)或 true(成功条件)的值。

winrt::handle h{ ::CreateEvent(nullptr, false, false, nullptr) };
winrt::check_bool(bool{ h });
winrt::check_bool(::SetEvent(h.get()));

如果你传递到 winrt::check_bool 的值为 false,那么以下操作序列将生效。

  • “winrt::check_bool”调用 winrt::throw_last_error 函数 。
  • “winrt::throw_last_error”调用 GetLastError 来检索调用线程的最后一个错误代码值,然后调用 winrt::throw_hresult 函数 。
  • “winrt::throw_hresult”使用表示该错误代码的 winrt::hresult_error 对象(或标准对象)抛出异常 。

由于 Windows API 使用各种返回值类型报告运行时的错误,因此除“winrt::check_bool”外,还有其他一些用于检查值和抛出异常的有用的帮助程序函数。

  • winrt::check_hresult。 检查 HRESULT 代码是否表示错误,如果是,则调用“winrt::throw_hresult”。
  • winrt::check_nt。 检查代码是否表示错误,如果是,则调用“winrt::throw_hresult”。
  • winrt::check_pointer。 检查指针是否为 null,如果是,则调用“winrt::throw_last_error”。
  • winrt::check_win32。 检查代码是否表示错误,如果是,则调用“winrt::throw_hresult”。

你可以对常见的返回代码类型使用这些帮助程序函数,也可以响应任何错误条件并调用 winrt::throw_last_error 或 winrt::throw_hresult 。

编辑API时抛出异常

所有 Windows 运行时应用程序二进制接口边界(简称 ABI 边界)必须为 noexcept,即不得有异常。 创作 API 时,应始终使用 C++ noexcept 关键字来标记 ABI 边界。 noexcept 在 C++ 中有特定的行为。 如果 C++ 异常遇到 noexcept 边界,则会调用 std::terminate,导致进程很快失败。

该行为通常是理想的做法,因为未经处理的异常几乎总是意味着进程中出现了未知状态。

由于异常不得跨过 ABI 边界,在实现中出现的错误条件以 HRESULT 错误代码的形式跨 ABI 层返回。 在使用 C++/WinRT 创作 API 时,将生成代码以供你将在实现中抛出的任何异常转换为 HRESULT。 Winrt::to_hresult 函数以与此类似的模式用于生成的代码。

HRESULT DoWork() noexcept
{try{// Shim through to your C++/WinRT implementation.return S_OK;}catch (...){return winrt::to_hresult(); // Convert any exception to an HRESULT.}
}

winrt::to_hresult 处理派生自 std::exception 和 winrt::hresult_error 及其派生类型的异常 。 在你的实现中,最好使用 winrt::hresult_error 或派生类型,以便你的 API 的使用者可以收到丰富的错误信息。 “std::exception”(映射到 E_FAIL)在你使用标准模板库时引发异常的情况下受支持。

注:如果捕获std::exception就捕获不到 winrt::hresult_error ,所以最好就使用winrt::to_hresult 

使用 noexcept 时如何调试

如前所述,如果 C++ 异常遇到 noexcept 边界,则会调用 std::terminate,导致进程很快失败。 这不适用于调试,因为 std::terminate 通常会失去引发的大部分或所有错误或异常上下文,尤其是在涉及协同程序的情况下。

因此,本部分处理的是 ABI 方法(已使用 noexcept 进行适当的批注)使用 co_await 来调用异步 C++/WinRT 投影代码的情况。

建议将对 C++/WinRT 项目代码的调用包装在 winrt::fire_and_forget 中。 这样做就可以在正确的位置将未经处理的异常正确记录为存放异常,大大提高可调试性。

HRESULT MyWinRTObject::MyABI_Method() noexcept
{winrt::com_ptr<Foo> foo{ get_a_foo() };[/*no captures*/](winrt::com_ptr<Foo> foo) -> winrt::fire_and_forget{co_await winrt::resume_background();foo->ABICall();AnotherMethodWithLotsOfProjectionCalls();}(foo);return S_OK;
}

winrt::fire_and_forget 有内置的 unhandled_exception 方法帮助程序,该程序调用 winrt::terminate,后者又调用 RoFailFastWithErrorContext。 这样就可以保证任何上下文(存放异常、错误代码、错误消息、堆栈回溯等)都会得到保存,不管是进行实时调试,还是进行事后转储。 为了方便起见,可以将“发后不理”部分重构成一个单独的可返回 winrt::fire_and_forget 的函数,然后调用它。

调用同步代码

在某些情况下,ABI 方法(同样已使用 noexcept 进行适当的批注)仅调用同步代码。 换而言之,它从不使用 co_await,不管是用来调用异步 Windows 运行时方法,还是用来在前台和后台线程之间切换。 在这种情况下,“ winrt::fire_and_forget”方法仍可使用,但效率不高。 可以改为执行类似下面的代码。 

HRESULT abi() noexcept try
{// ABI code goes here.
} catch (...) { winrt::terminate(); }

快速失败

上一部分的代码仍会快速失败。 从编写的内容来看,该代码不处理任何异常。 任何未经处理的异常都会导致程序终止。

但该形式是很好的,因为它确保了可调试性。 在罕见情况下,可能需要使用 try/catch,并处理某些异常。 但这应该很罕见,因为正如本主题所述,我们反对将异常作为一种流控制机制用于预期的条件。

记住,让未经处理的异常逃脱无包装的 noexcept 上下文是很糟糕的做法。 在该条件下,C++ 运行时会 std::terminate 进程,因此会失去任何存放的由 C++/WinRT 仔细记录的异常信息。

断言

对应用程序中的内部假设,存在断言。 最好尽可能地为编译时验证使用“static_assert”。

WinRT使用带布尔值表达式的 WINRT_ASSERTWINRT_ASSERT 是宏定义,并且扩展到 _ASSERTE。

WINRT_ASSERT(pos < size());

 WINRT_ASSERT 在发布版本中被编译; 在调试版本中,它会在断言所在的代码行上停止调试器中的应用程序。

 不应在析构函数中使用异常。 因此,至少在调试版本中,你可以断言从带有 WINRT_VERIFY(带有布尔值表达式)和 WINRT_VERIFY_(带有预期结果和布尔值表达式)的析构函数调用函数的结果。

WINRT_VERIFY(::CloseHandle(value));
WINRT_VERIFY_(TRUE, ::CloseHandle(value));

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

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

相关文章

67-箭头函数,new.target,模版字符串

1.箭头函数 ES6新增语法&#xff0c;用来简化函数的书写()>{} <script>//箭头函数的基本使用let a (a,b)>{return ab;}let c a(1,2);console.log(c);//输出3</script> 2.简写形式&#xff1a; 2.1参数&#xff1a;只有一个参数时可以省略小括号a>{}&…

面试经典 150 题 ---- 轮转数组

面试经典 150 题 ---- 轮转数组 轮转数组方法一&#xff1a;使用额外的数组方法二&#xff1a;数组翻转 轮转数组 方法一&#xff1a;使用额外的数组 我们可以使用额外的数组来将每个元素放至正确的位置。用 n 表示数组的长度&#xff0c;我们遍历原数组&#xff0c;将原数组…

Java底层自学大纲_JVM篇

JVM专题_自学大纲所属类别学习主题建议课时&#xff08;h&#xff09; A 深入理解Java虚拟机001 JVM类加载器设计原理2.5 A 深入理解Java虚拟机002 基于SPI破解双亲委派机制2.5 A 深入理解Java虚拟机003 JVM内部结构分析2.5 A 深入理解Java虚拟机004 字符串常量池原理2.5 …

【算法】长短期记忆网络(LSTM,Long Short-Term Memory)

这是一种特殊的循环神经网络&#xff0c;能够学习数据中的长期依赖关系&#xff0c;这是因为模型的循环模块具有相互交互的四个层的组合&#xff0c;它可以记忆不定时间长度的数值&#xff0c;区块中有一个gate能够决定input是否重要到能被记住及能不能被输出output。 原理 黄…

37.云原生之springcloud+k8s+GitOps+istio+安全实践

云原生专栏大纲 文章目录 准备工作项目结构介绍配置安全测试ConfigMapSecret使用Secret中数据的方式Deployment使用Secret配置Secret加密 kustomize部署清单ConfigMap改造SecretSealedSecretDeployment改造Serviceistio相关资源DestinationRuleGatewayVirtualServiceServiceAc…

132557-72-3,2,3,3-三甲基-3H-吲哚-5-磺酸,具有优异的反应活性和光学性能

132557-72-3&#xff0c;5-Sulfo-2,3,3-trimethyl indolenine sodium salt&#xff0c;2,3,3-三甲基-3H-吲哚-5-磺酸&#xff0c;具有优异的反应活性和光学性能&#xff0c;一种深棕色粉末 您好&#xff0c;欢迎来到新研之家 文章关键词&#xff1a;132557-72-3&#xff0c;5…

ROS2体系框架

文章目录 1.ROS2的系统架构2.ROS2的编码风格3.细谈初始化和资源释放4.细谈配置文件5.ROS2的一些命令6.ROS2的核心模块6.1 通信模块6.2 功能包6.3 分布式6.4 终端命令和rqt6.5 launch6.6 TF坐标变换6.7 可视化RVIZ 1.ROS2的系统架构 开发者的工作内容一般都在应用层&#xff0c;…

MySQL学习Day24—数据库的设计规范

一、数据库设计的重要性: 1.糟糕的数据库设计产生的问题: (1)数据冗余、信息重复、存储空间浪费 (2)数据更新、插入、删除的异常 (3)无法正确表示信息 (4)丢失有效信息 (5)程序性能差 2.良好的数据库设计有以下优点: (1)节省数据的存储空间 (2)能够保证数据的完整性 …

力扣138.随机链表的复制

给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每个新节点的值都设为其对应的原节点的值。新节点的 n…

编写一个自动合并代码到不同分支的脚本小工具

新建一个 autoMerge.sh 的文件&#xff0c;文件内容如下 # 提示用户确认继续执行 read -p "确认要执行脚本吗&#xff1f;(输入 yes 继续): " userInput# 检查用户输入是否为 "yes" if [ "$userInput" ! "yes" ]; thenecho "用户…

《TCP/IP详解 卷一》第9章 广播和组播

目录 9.1 引言 9.2 广播 9.2.1 使用广播地址 9.2.2 发送广播数据报 9.3 组播 9.3.1 将组播IP地址转换为组播MAC地址 9.3.2 例子 9.3.3 发送组播数据报 9.3.4 接收组播数据报 9.3.5 主机地址过滤 9.4 IGMP协议和MLD协议 9.4.1 组成员的IGMP和MLD处理 9.4.2 组播路由…

可用于智能客服的完全开源免费商用的知识库项目

介绍 FastWiki项目是一个高性能、基于最新技术栈的知识库系统&#xff0c;专为大规模信息检索和智能搜索设计。利用微软Semantic Kernel进行深度学习和自然语言处理&#xff0c;结合.NET 8和MasaBlazor前端框架&#xff0c;后台采用.NET 8MasaFrameworkSemanticKernel&#xff…

嵌入式Linux学习DAY26

管道的作用&#xff1a;进程间的通信 无名管道&#xff1a; 只能在父子进程中进行通信 pipe int pipe(int pipefd[2]); 功能: 创建一个无名管道 参数: pipefd[0]:读管道文件描述符 pipefd[1]:写管道文件描述符 …

【InternLM 实战营笔记】基于 InternLM 和 LangChain 搭建MindSpore知识库

InternLM 模型部署 准备环境 拷贝环境 /root/share/install_conda_env_internlm_base.sh InternLM激活环境 conda activate InternLM安装依赖 # 升级pip python -m pip install --upgrade pippip install modelscope1.9.5 pip install transformers4.35.2 pip install str…

【大厂AI课学习笔记NO.53】2.3深度学习开发任务实例(6)数据采集

这个系列写了53期了&#xff0c;很多朋友收藏&#xff0c;看来还是觉得有用。 后续我会把相关的内容&#xff0c;再次整理&#xff0c;做成一个人工智能专辑。 今天学习到了数据采集的环节。 这里有个问题&#xff0c;数据准备包括什么&#xff0c;还记得吗&#xff1f; 数…

ZStack Cube超融合入选IDC《中国超融合基础架构市场评估》报告

近日&#xff0c;IDC发布了《中国超融合基础架构市场评估&#xff0c;2023》。IDC针对中国超融合基础架构市场的发展现状展开了调研&#xff0c;明确了最终用户构建融合型云平台的痛点和难点&#xff0c;阐述了市场中各技术服务提供商的服务方案和优势&#xff0c;并对未来中国…

vue3+ts+vite数据大屏自适应总结(两种方法)

总结一下我常用的数据大屏自适应方法 目录 1、通过css缩放方案&#xff1a; 利用transform&#xff1a;scale 进行适配2、采用rem布局&#xff0c; 根据屏幕分辨率大小不同&#xff0c;调整根元素html的font-size&#xff0c; 从而达到每个元素宽高自动变化&#xff0c;适配不…

接口测试实战--mock测试、日志模块

一、mock测试 在前后端分离项目中,当后端工程师还没有完成接口开发的时候,前端开发工程师利用Mock技术,自己用mock技术先调用一个虚拟的接口,模拟接口返回的数据,来完成前端页面的开发。 接口测试和前端开发有一个共同点,就是都需要用到后端工程师提供的接口。所以,当…

Redis速学

一、介绍Redis 基本概念和特点 Redis是一个开源的内存数据库&#xff0c;它主要用于数据缓存和持久化。其数据存储在内存中&#xff0c;这使得它具有非常快的读写速度。Redis支持多种数据结构&#xff0c;包括字符串、哈希、列表、集合和有序集合&#xff0c;这使得它非常灵活…

书生·浦语大模型图文对话Demo搭建

前言 本节我们先来搭建几个Demo来感受一下书生浦语大模型 InternLM-Chat-7B 智能对话 Demo 我们将使用 InternStudio 中的 A100(1/4) 机器和 InternLM-Chat-7B 模型部署一个智能对话 Demo 环境准备 在 InternStudio 平台中选择 A100(1/4) 的配置&#xff0c;如下图所示镜像…