windows C++-winrt的错误处理(上)

写在前面的话: winrt可能和常规的C++和之前的SEH稍微有一些不一样,对于这项技术,我是以了解为主,坦白说,这可能和中间版本C++/CX技术有关,winrt的前一个版本是C++/CX,但是那个很快就被弃用了。

避免捕获和抛出异常

建议继续编写异常安全代码,但最好尽量避免捕获和抛出异常。 如果没有异常处理程序,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 。

  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值