WindowLib Error Handling

This page describes my approach to catching and handling errors in my C++ Win32 Library. The more I use this library for my own applications, the more concerned I become about turning it into a professional-grade library. Here's what I'm currently doing to find and deal with errors.

The Approach

The first order of business is making the library fail early and often to smoke out errors. I am accomplishing this by intentionally making the Win32 API brittle. Most of the API functions return FALSE or NULL when something goes wrong. If these return values are left unchecked, Windows will try to cover the error and let the developer muddle through blissfully unaware of any problems.

What I've done is wrap each API call in a method named CheckWin32. Actually, it's a pair of methods:

template<class T> inline T CheckWin32(T result)
{
    if (!result && ::GetLastError())
    {
        throw Win32Error();
    }

    return result;
}

inline BOOL CheckWin32(BOOL result)
{
    if (::GetLastError())
    {
        throw Win32Error();
    }

    return result;
}

// Elsewhere in the library...
void Yadda()
{
    HINSTANCE hInstance = CheckWin32(::GetModuleHandle(NULL));
}

The first version of CheckWin32 is a template to check the validity of various types of return values and pass the values through. If the value is 0, NULL, FALSE, or anything else that will evaluate to a boolean false (say, an override of operator! in the unlikely event a Win32 API ever returns a reference to a C++ class), then the second half of the if statment is evaluated. If ::GetLastError returns non-zero, the function assumes that an error indeed occurred and it throws an exception. The function makes this call to deal with the few API calls that return non-zero even when the method was successful.

The second version deals with API functions that return a boolean value that does not necessarily correlate with the success or failure of the call. This one always checks ::GetLastError to determine if an error occurred. Of course, this means that the developer will occasionally need to explicitly set the last error to 0 before calling the function, but this is a small price to pay for good error checking.

Now that the API calls fail every time they hit an error condition the developer must decide how to handle the exceptions. I have chosen to not provide exception handling inside the library for most cases in order to keep clients of the library from becoming lazy. I think it's a good habit to expose errors and deal with them rather than hide errors and pretend they're benign.

Aren't You Overdoing It A Tad?

Maybe I am. Particularly down in the bowels of Splitter, HorizontalSplitter, and VerticalSplitter I might be able to lay off checking every single GDI call. Since I'm not sure, though, I'll keep them in place for now. At the very least, I'll make a concession to crank back a little bit in release builds, but debug builds will have all the error checking I can possibly cram into them.

Error Checking To the Rescue

Speaking of splitter classes, I have noticed an elusive exception occurring in the SplitterWindow sample application that never appeared before I dropped in this error-checking code. Occasionally when I resize the application window smaller than a particular size, then slowly make it bigger again, somewhere along the line an exception is being thrown that has to do with "Access denied." I don't know what it is yet, but I do know that I may never have found it without obsessive error checking.