Skip to content

Error handling customization

Raymond Chen edited this page Aug 20, 2021 · 4 revisions

WIL provides a number of facilities for altering its error handling behavior, some automatic and some explicit.

A common usage for these features is to add custom error logging behavior; see the page on error logging and observation for more detailed discussion of those particular usages with examples.

Unless otherwise noted, customizations are intended to be established at module initialization before any WIL functions have been called, and left unchanged for the lifetime of the module. Changing the customization on the fly is likely to result in race conditions.

Unless otherwise noted, the Set...Callback functions permit clearing the callback by passing nullptr, but you cannot change the callback from one non-null value to another. (The fact that race conditions can occur if callbacks are changed on the fly means that this limitation is unlikely to be relevant in practice.)

Custom result logging

void wil::SetResultLoggingCallback(callback);

Sets a callback function that is called whenever WIL logs a failure. The callback function should be declared as follows:

void __stdcall CustomLoggingCallback(wil::FailureInfo const& failure) noexcept;

Debugging information only: You can check in the debugger whether a custom result logging callback is enabled by inspecting the variable wil::details::g_pfnLoggingCallback.

Example.

Custom error message generation

void wil::SetResultMessageCallback(callback);

Sets a callback function that is called whenever WIL logs a failure. The callback function should be declared as follows:

void __stdcall CustomResultMessageCallback(
    _Inout_ wil::FailureInfo* failure,
    _Inout_updates_opt_z_(cchDebugMessage) PWSTR pszDebugMessage,
    _Pre_satisfies_(cchDebugMessage > 0) size_t cchDebugMessage) noexcept;

The callback may optionally generate a debug message in pszDebugMessage based on the information in failure. If callback does not put a message in pszDebugMessage, then the default message is used.

Even though the failure parameter is passed as a non-const pointer, you must not modify the FailureInfo.

Debugging information only: You can check in the debugger whether a custom result logging callback is enabled by inspecting the variable wil::g_pfnResultLoggingCallback.

Custom exception types

We recommend that your custom exceptions derive from wil::ResultException. If you do that, then the existing WIL library will support your custom exception automatically. However, it may be the case that you are using an existing library which throws its own exceptions, in which case you can teach WIL about those custom exceptions.

void wil::SetResultFromCaughtExceptionCallback(callback)

Sets a callback function that is used to convert an exception to an HRESULT. The callback function should be declared as follows:

HRESULT __stdcall CustomResultFromCaughtException() noexcept;

The custom exception handler is called from inside a catch block. The expected pattern is

HRESULT __stdcall CustomResultFromCaughtException() noexcept
{
    try
    {
        throw;
    }
    catch (custom_exception1 const& ex)
    {
        return ex.ExtractHResult();
    }
    catch (custom_exception2 const& ex)
    {
        return ex.ExtractHResult();
    }
    catch (...)
    {
        return S_OK;
    }
}

The callback function should return S_OK for any exceptions it does not understand, in which case the exception is treated as an unknown exception. (See "Custom behavior for unknown exceptions.")

Custom behavior for unrecognized exceptions

See Exception Guards for a description of the exceptions that WIL supports by default. See Custom exception types for information on adding support for your custom exception types.

If an exception goes unrecognized, WIL fails-fast by default, because throwing an unrecognized exception is a programming error, not a runtime error.

bool wil::g_fResultSupportStdException = true;
bool wil::g_fResultFailFastUnknownExceptions = true;

If you don't want WIL to recognize std::exception, then set wil::g_fResultSupportStdException to false.

If you don't want WIL to fail fast when an unknown exception is detected, set wil::g_fResultFailFastUnknownExceptions to false. In that case, WIL behaves as follows:

  • The HRESULT for an unrecognized exception is HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION).
  • The debug text is normally empty, but by default, WIL will use the std::exception::what() string for std::exception-derived classes.

Custom debugger detection

bool(__stdcall *wil::g_pfnIsDebuggerPresent)() noexcept = nullptr;

Set wil::g_pfnIsDebuggerPresent to a pointer to a function that returns true if a debugger is present or false if not. If not customized, then WIL uses the system IsDebuggerPresent() function.

bool wil::g_fIsDebuggerPresent = false;

Records whether WIL believes a debugger is present.

Debugging information only: You can patch this value to true in the debugger to retroactively change the value calculated by g_pfnIsDebuggerPresent. This is convenient if debugging a user-mode program from the kernel debugger, because the kernel debugger is not detected by IsDebuggerPresent().

Custom fail-fast behavior

bool (__stdcall *wil::g_pfnWilFailFast)(wil::FailureInfo const& info) noexcept = nullptr;

Set wil::g_pfnWilFailFast to a pointer to a function that is called to fail fast. If the pointer is nullptr or the function returns, then WIL generates its own fail-fast exception.

The return value of the function is not used.

Other customizations

bool wil::g_fResultOutputDebugString = true;

Set wil::g_fResultOutputDebugString to false to suppress the generation of failure debug messages to the debugging console.

bool wil::g_fBreakOnFailure = false;

Set wil::g_fBreakOnFailure = true to call DebugBreak() (or its customized replacement) when a failure occurs.

C++/CX support

If compiled in C++/CX mode (MSVC /GW compiler flag), the WIL error handling library automatically adds support for thrown Platform::Exception^ exceptions.

You can suppress this behavior by defining the symbol RESULT_SUPPRESS_STATIC_INITIALIZERS before including any WIL headers.

Debugging information only: You can check in the debugger whether C++/CX support is enabled by inspecting the variables wil::details::g_pfnResultFromCaughtException_WinRt, wil::details::g_pfnResultFromKnownExceptions_WinRt, and wil::details::g_pfnThrowPlatformException. They will be null if C++/CX support is disabled and non-null if C++/CX support is enabled.

If compiled in C++/CX mode, the WIL THROW_XXX macros throw Platform::Exception^ objects by default. To force them to throw wil::ResultException, set wil::g_fResultThrowPlatformException to false.

bool wil::g_fResultThrowPlatformException = true;
Clone this wiki locally