c++comerror-handlingiwebbrowser2

IWebBrowser2::Navigate2 error handling


What is the correct way to handle errors? I've code like following, handling all error codes I've found on MSDN and still, sometimes, I've received error "Unknown".

HRESULT hr = pwb->Navigate2(&URL, &Flag, &TargetFrameName, &PostData, &Headers);
if(FAILED(hr))
{
    std::string message("Navigate2 failed with reason: ");
    switch(hr)
    {
        // The operation was successful.
        case S_OK:
            message.append("S_OK");
            break;
        // One or more parameters are invalid.
        case E_INVALIDARG:
            message.append("E_INVALIDARG");
            break;
        // Out of memory.
        case E_OUTOFMEMORY:
            message.append("E_OUTOFMEMORY");
            break;
        // The operation failed.
        case E_FAIL:
            message.append("E_FAIL");
            break;
        case E_ACCESSDENIED:
            message.append("E_ACCESSDENIED");
            break;
        case E_POINTER:
            message.append("E_POINTER");
            break;
        case E_UNEXPECTED:
            message.append("E_UNEXPECTED");
            break;
        default:
            message.append("Unknown");
    }
}

Solution

  • There's quite a few variations on how to handle COM errors. Strategies include use of cascading if SUCCEEDED() or centralized function error handling using goto on FAILED() among others. There's some good information on MSDN.

    As to interpreting error codes, FormatMessage() will often make your work easier - here's an example from MSDN (included below for clarity)

    [Source: http://msdn.microsoft.com/en-us/library/windows/desktop/ms687061(v=vs.85).aspx]

        #include <stdio.h>
        #include <windows.h>
        #include <tchar.h>
    
        void ErrorDescription(HRESULT hr) 
        { 
             if(FACILITY_WINDOWS == HRESULT_FACILITY(hr)) 
                 hr = HRESULT_CODE(hr); 
             TCHAR* szErrMsg; 
    
             if(FormatMessage( 
               FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, 
               NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
               (LPTSTR)&szErrMsg, 0, NULL) != 0) 
             { 
                 _tprintf(TEXT("%s"), szErrMsg); 
                 LocalFree(szErrMsg); 
             } else 
                 _tprintf( TEXT("[Could not find a description for error # %#x.]\n"), hr); 
        }
    

    In the call above, the flags indicate that windows will allocate memory for error messages (which you have to free - use LocalFree()) and that it will look for error messages in the system message tables (FORMAT_MESSAGE_FROM_SYSTEM). Sometimes, (or often depending on the kind of libraries you use) - no corresponding error descriptions will be found in the system message tables.

    In cases like these you may either handle the error description yourself (as you have done in your example) or attempt to load a library's message tables directly. To do this, use the FORMAT_MESSAGE_FROM_HMODULE and supply a module handle as the lpSource parameter to the FormatMessage() function.

    Here's an example:

    std::wstring StackExample::getLastError( HRESULT hr ) 
    {
        LPWSTR lpMsgBuf;
        DWORD ret;
        std::wstring def(L"(UNKNOWN)");
        ret = FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER | 
            FORMAT_MESSAGE_FROM_HMODULE,
            GetModuleHandle(TEXT("imapi2.dll")),
            hr,
            0,
            (LPWSTR) &lpMsgBuf,
            0, NULL );
    
        if(ret)
        {
            std::wstring last(lpMsgBuf);
            LocalFree(lpMsgBuf);
            return last;
        }
        return def;
    }