如何处理导致异常的WinRT异常?

7
如果Windows运行时类型引发COM错误,则.NET似乎经常(或总是?)将此错误封装到一个Exception实例中。错误消息包括COM HRESULT错误代码。例如,使用新的加密API与AES-CBC时,错误的缓冲区长度会导致带有消息“所提供的用户缓冲区对于请求的操作无效。(HRESULT异常:0x800706F8)”的Exception

那么我们应该如何处理这些异常呢?我们应该从异常中读取HRESULT代码以了解是什么类型的异常吗?在经典的.NET中,我会得到一个CryptographicException,我可以用它来区分加密错误和其他错误。

另一件我不明白的事情是,微软的代码质量规则指出,永远不应该抛出Exception,而应该始终使用派生类型。原因是没有人应该被迫捕获捕获更致命异常的一般Exception。另一个规则说,在库中永远不要捕获Exception。如果我们被迫在Windows Store应用程序或WinRT库中捕获Exception,我们该如何遵循这些政策呢?

顺便说一句:Clemens Vasters在他的博客中展示了如何捕获异常而避免捕获致命异常。我认为,如果我们能够避免捕获致命异常,那么捕获Exception就不再是坏代码了。


1
关于链接的博客文章,列出的许多“致命”异常无法被托管代码捕获。值得注意的是,StackOverflowException,虽然我相当确定 AV 也无法被捕获(当然,在本机代码中可以捕获这两个异常,但这样做很危险)。还要注意,一些看起来“致命”的异常实际上可能并非如此。例如,许多 COM 组件在特定缓冲区中的空间耗尽时返回 E_OUTOFMEMORY。这个 HRESULT 将被翻译为 OutOfMemoryException,但这并不意味着进程已经耗尽了其整个地址空间。 - James McNellis
似乎这是将非托管世界与托管世界混合的结果,很遗憾(至少他们可以声明类ComException:Exception)。 - Evgeny Gorbovoy
1个回答

4

可以捕获 Exception,通过 HRESULT 开关处理特定错误,并在错误是“意外”的情况下重新抛出 Exception。例如,

try
{
    // ...
}
catch (Exception ex)
{
    switch (ex->HResult)
    {
    case E_INVALID_USER_BUFFER: // 0x800706f8
        // handle invalid buffer case...
        break;
    default:
        // Unexpected exception; re-throw:
        throw;
    }
}

(我想指出,提供无效的缓冲区更像是逻辑错误而不是运行时错误,所以我想知道这个特定异常是否真的应该被捕获。)
另一种更通用的解决方案是编写一个函数或一组函数来处理已知 HRESULT 的异常,并重新抛出更具体的异常。例如:
static void HandleKnownExceptions(Action f)
{
    try
    {
        f();
    }
    catch (Exception ex)
    {
        // Detect expected HRESULTs and throw the more-specific exception
        // type for each.
    }
}

这两种方法在C++和C#中同样适用。

需要注意的是,Exception并不一定是由平台或其他组件直接抛出的。在Windows Runtime ABI层,没有异常:所有的错误都通过HRESULT在ABI边界上报告。CLR将少数已知的HRESULT翻译为更具体的异常类型,但无法执行通用翻译。


谢谢James。这是我在捕获无效缓冲区HRESULT时的方法。我觉得.NET程序员现在必须处理HRESULTS有点奇怪。当尝试解密可能被篡改或简单地裁剪的无效数据时,您很容易遇到无效缓冲区。我认为几乎不可能检查加密数据的正确长度,因为这取决于所使用的算法和密钥。已经很难找出所使用的块大小了。捕获无效缓冲区错误对于确保以用户友好的方式处理无效数据至关重要。 - Jürgen Bayer
啊,那是我的错误。我误解了哪个缓冲区是无效的。你是对的,处理这种错误是一个好主意 :-) - James McNellis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接