How to handle WinRT exceptions that result in Exception?
Asked Answered
S

1

7

If a Windows runtime type raises a COM error .NET seems to wrap this error often (or always?) just into an Exception instance. The error message includes the COM HRESULT error code. When using the new Cryptographic API with AES-CBC for example a wrong buffer length results in an Exception with the message "The supplied user buffer is not valid for the requested operation. (Exception from HRESULT: 0x800706F8)".

Well, How are we supposed to handle those exceptions? Should we read the HRESULT code from the exception to get an idea what kind of exception that was? In classic .NET I would get a CryptographicException that I could use to distinguish cryptographic errors from other errors.

Another thing that I do not understand is that the Microsoft code quality rules state that one should never throw Exception but always derived types. The reason is that no one should be forced to catch the general Exception that catches more fatal exceptions like OutOfMemoryException as well. Another rule says that one should never, ever catch Exception in libraries. How could we follow these policies if we are forced to catch Exception in Windows Store apps or WinRT libraries?

By the way: Clemens Vasters shows in his blog how we can catch Exception while avoiding to catch fatal exception. I assume catching Exception is no longer bad code then.

Saponaceous answered 25/9, 2012 at 15:39 Comment(2)
With respect to the linked blog entry, many of the "fatal" exceptions listed cannot be caught by managed code. Notably, StackOverflowException, though I'm fairly certain that AVs can't be caught either (both can be caught in native code, of course, but doing so is perilous). Note also that some exceptions that appear fatal may not in fact be so. For example, many COM components return E_OUTOFMEMORY when space in a particular buffer is exhausted. This HRESULT will be translated as an OutOfMemoryException, but it doesn't mean that the process has exhausted its entire address space.Colchester
Seems that is the result of mixing unmanaged world into managed world, sad( At least they could declare class ComException : Exception.Ultrasonic
C
4

It is possible to catch Exception, handle particular errors by switching on the HRESULT, and re-throwing the Exception if the error was "unexpected." For example,

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

(I would note that providing an invalid buffer sounds more like a logic error than a runtime error, so I wonder whether this particular exception should really be caught.)

Alternatively, a more general solution would be to write a function or set of functions that handle Exception for known HRESULTs and re-throws a more specific exception. For example,

static void HandleKnownExceptions(Action f)
{
    try
    {
        f();
    }
    catch (Exception ex)
    {
        // Detect expected HRESULTs and throw the more-specific exception
        // type for each.
    }
}

Both of these approaches work equally well in both C++ and C#.

Note that it isn't necessarily the case that Exception is thrown directly by the platform or other components. At the Windows Runtime ABI layer, there are no exceptions: all errors are reported across the ABI boundary by HRESULT. The CLR translates a handful of known HRESULTs to more specific exception types, but it cannot perform a general translation.

Colchester answered 25/9, 2012 at 18:6 Comment(2)
Thanks James. That was my approach when catching the invalid buffer HRESULT. I find it a bit strange that .NET programmers now must deal with HRESULTS though. You easily get an invalid buffer when trying to decrypt invalid data that may have been tempered with or was simply cropped. I think that it is nearly impossible to check encrypted data for the right length because this depends on the used algorithm and key. It's already hard enough to find out the used block size. Catching the invalid buffer error is vital to ensure that invalid data is treated in a user friendly manner.Believe
Ah, my mistake then. I misunderstood what buffer was invalid. You are right, handling that sort of error is a good idea :-)Colchester

© 2022 - 2024 — McMap. All rights reserved.