使用 C++/WinRT 的错误处理

本主题讨论了处理使用 C++/WinRT 编程时出现的错误的策略。 更多常规信息和背景,请参阅错误和异常处理 (Modern C++)。

避免捕获和抛出异常

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

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

考虑访问 Windows 注册表的场景。 如果你的应用无法从注册表读取值,这是预料之中的,你应该正确处理。 不要抛出异常;而应返回 bool 或 enum 值指示未读取值或原因。 另一方面,无法向注册表写入值很可能表示你的应用程序中存在的问题更大,是你无法明智处理的。 在这种情况下,你不希望应用程序继续,所以导致生成错误报告的异常是阻止应用程序造成任何损害的最快方式。

另一个示例中,请考虑从对 StorageFile.GetThumbnailAsync 的调用检索缩略图图像,然后将该缩略图传递到 BitmapSource.SetSourceAsync。 如果该调用顺序导致你将 nullptr 传递到 SetSourceAsync(无法读取图像文件;或许是文件扩展名使它看似包含图像数据,而实际并非如此),那么你将会导致引发无效的指针异常。 如果你发现自己代码存在这类情况,则不应将这种情况作为异常捕获和处理,而应检查从 GetThumbnailAsync 返回的 nullptr

抛出异常异常往往会比使用错误代码更慢。 如果你仅在出现严重错误时抛出异常,如果一切都正常运行,那么你永远不需要在性能方面妥协。

但更有可能的是,性能下降需要付出运行时开销来确保在不太可能抛出异常的情况下调用相应的析构函数。 这种保障成本不论实际是否抛出异常都会产生。 因此,你应该确保编译器清楚地了解哪些功能可以有抛出异常的可能性。 如果编译器可以证明某些功能不会引发任何异常(noexcept 规范),那么它可以优化所生成的代码。

捕获异常

在 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)在你使用标准模板库时引发异常的情况下受支持。

使用 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 运行时方法,还是用来在前台和后台线程之间切换。 在这种情况下,“发后不理”方法仍可使用,但效率不高。 可以改为执行类似下面的代码。

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

快速失败

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

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

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

断言

对应用程序中的内部假设,存在断言。 最好尽可能地为编译时验证使用“static_assert”。 对于运行时条件,请使用带布尔值表达式的 WINRT_ASSERT WINRT_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/621317.shtml

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

相关文章

国密算法

一、什么是国密算法 国密算法是指中国国家密码管理局推动和标准化的密码算法体系&#xff0c;也称为“中国密码算法”或“国家密码算法”。这些算法被设计用于保护信息安全&#xff0c;涵盖了对称加密、非对称加密、哈希函数等多个领域。 国密算法的主要特点包括&#xff1a; 自…

java面试题(21):private修饰的方法可以通过反射访问,那么private的意义是什么?

1 反射简介 Java&#xff0c;在一个类中&#xff0c;为了不让外界访问到某些属性和方法&#xff0c;通常将其设置为private&#xff0c;用正常的方式&#xff08;对象名.属性名&#xff0c;对象名.方法名&#xff09;将无法访问此属性与方法&#xff0c;但有没有其他方法可以访…

简单的天天酷跑小游戏实现

初级函数实现人物,背景,小乌龟的移动 #include <graphics.h> #include <iostream> #include <Windows.h> #include "tools.h" #include <mmsystem.h> #include <conio.h> #include <time.h>//时间头文件 #include <cstdlib&g…

2024最新最全【DDOS教学】,从零基础入门到精通,看完这一篇就够了

1、互联网安全现状 随着网络世界的高速发展&#xff0c;各行业数字化转型也在如火如荼的进行。但由于TCP/IP网络底层的安全性缺陷&#xff0c;钓鱼网站、木马程序、DDoS攻击等层出不穷的恶意攻击和高危漏洞正随时入侵企业的网络&#xff0c;如何保障网络安全成为网络建设中的刚…

【STK】手把手教你利用STK进行关联分析仿真01-STK/CAT模块介绍

关联分析工具(Conjunction Analysis Tool )主要用于分析航天发射或卫星在轨运行过程中与其他目标之间的接近情况,关联分析包括: 接近分析工具 Close Approach Tool CAT高级接近分析工具 AdvCAT激光接近分析工具 LaserCAT发射窗口分析工具 Launch Window Analysis今天主要介绍…

Mybatis记单词

&#xff08;一&#xff09;Mybatis 1.1 全局配置文件mybatis-config.xml 1、在resources文件夹下新建mybatis-config.xml和db.properties&#xff0c;mybatis-config.xml引入db.properties中关于数据库的配置&#xff1a; <properties resource"db.properties"…

单一附合导线网平差过程

1、导线网图示如下&#xff1a; 2、已知A、B、C、D点坐标和方位角、. 3、设定未知数及近似值。设待定点坐标、、、...、为未知数&#xff0c;则有2n个未知数。坐标近似值为由观测值推算而得的坐标值&#xff0c;分别为、、、...、。改正数分别为、...、。则有&#xff1a;&am…

html+css+Jquery 实现 文本域 文字数量限制、根据输入字数自适应高度

先看效果&#xff1a;初始的效果&#xff0c;样式多少有点问题&#xff0c;不重要&#xff01;重要的是功能&#xff01; 输入后&#xff1a; 根据文字长度&#xff0c;决定文本域长度 限制文字数量 话不多说&#xff0c;直接上代码&#xff01; <!DOCTYPE html> <h…

企业网盘助力数字化教育资源库建设

教育行业数字化是适应社会发展的必然选择&#xff0c;是教育行业的未来重要发展趋势。万事开头难&#xff0c;如何在数字化时代升级转型是教育行业团队正在面临的挑战。Zoho Workdrive企业网盘深耕智慧文件管理服务&#xff0c;为教育行业量身打造集中文件管理库&#xff0c;推…

服务器命令行换源

首先清除默认源 conda config --remove channels default随后添加阿里源 conda config --add channels https://mirrors.aliyun.com/anaconda/pkgs/free/conda config --add channels https://mirrors.aliyun.com/anaconda/pkgs/main/

【嵌入式AI】CanMVk230开发板学习笔记(一)

嵌入式AI学习&#xff1a;CanMVk230开发板学习笔记 官方链接: k230快速入门 github固件下载地址&#xff1a; https://github.com/kendryte/k230_canmv/releases K230的相关软硬件资料&#xff0c;请参考 https://developer.canaan-creative.com/k230/dev/index.html https://g…

Qt屏蔽输出流技巧

Qt程序中调试会加入qDebug语句&#xff0c;但有时候想屏蔽掉&#xff0c;用下面的方法很方便实现。 在Qt项目文件添加以下定义&#xff1a; 屏蔽qInfo() DEFINES QT_NO_INFO_OUTPUT 屏蔽qDebug() DEFINES QT_NO_DEBUG_OUTPUT 屏蔽qWarning() DEFINES QT_NO_WARNING_OUT…

一键批量整理:将相同名称的文件归类至指定文件夹

随着电脑中的文件日益增多&#xff0c;文件管理成为了让人头疼的问题。相似的文件名&#xff0c;难以分类的内容&#xff0c;让你在寻找和整理时耗费大量时间。现在&#xff0c;有了我们的全新工具&#xff0c;这些烦恼全部消失。 第一步&#xff1a;进入文件批量改名高手主页面…

Day29 131分割回文串 93复原ip地址

131分割回文串 给定一个字符串 s&#xff0c;将 s 分割成一些子串&#xff0c;使每个子串都是回文串。 返回 s 所有可能的分割方案。 示例: 输入: "aab" 输出: [ ["aa","b"], ["a","a","b"] ] class Solution …

CnosDB的数据更新和删除

在时序数据中&#xff0c;可能会出现一些数据错误或者异常情况&#xff0c;这时候就需要能够对数据进行清洗修复。如果不支持更新操作&#xff0c;将会变得非常困难。另外&#xff0c;一些业务场景可能会需要对已有数据进行调整&#xff0c;比如设备信息发生变化等。支持数据更…

使用kibana来创建ElasticSearch的索引库与文档的命令

文章目录 &#x1f412;个人主页&#x1f3c5;JavaEE系列专栏&#x1f4d6;前言&#xff1a;&#x1f380;使用kibana来为ElasticSearch创建索引库&#x1f380;使用kibana来为ElasticSearch创建修改文档 &#x1f412;个人主页 &#x1f3c5;JavaEE系列专栏 &#x1f4d6;前言…

VSCode 正则表达式 匹配多行

VS Code 正则表达式匹配多行 (.|\n)*? //test.js const test {str: VS Code 正则表达式匹配多行VS Code 正则表达式匹配多行VS Code 正则表达式匹配多行VS Code 正则表达式匹配多行VS Code 正则表达式匹配多行VS Code 正则表达式匹配多行VS Code 正则表达式匹配多行VS Code …

代码随想录算法训练营day7|454.四数相加II 、383.赎金信、15.三数之和、18.四数之和

454.四数相加II 建议&#xff1a;本题是 使用map 巧妙解决的问题&#xff0c;好好体会一下 哈希法 如何提高程序执行效率&#xff0c;降低时间复杂度&#xff0c;当然使用哈希法 会提高空间复杂度&#xff0c;但一般来说我们都是舍空间 换时间&#xff0c; 工业开发也是这样。…

this关键字和static关键字

一、this关键字 关于java语言当中的this关键字&#xff1a; 1.this是一个关键字&#xff0c;翻译为&#xff1a;这个 2.this是一个引用&#xff0c;this是一个变量&#xff0c;this变量中保存了内存地址指向了了自身&#xff0c; this存储在JVM堆内存java对象内部。 3.创建…

koa2文件的上传下载功能

const Router require(“koa-router”); const upload new Router(); const bodyParser require(“koa-bodyparser”); const multer require("koa/multer"); const path require(“path”); const article require("…/utils/sql"); const { getCur…