Sunday, November 21, 2010

Windows error reporting in C++

This post describes how to convert GetLastError() result to text message.

The pure WinAPI-only code is
#include <Windows.h>

/* Shows MessageBox with the last-error text.
 * Default caption is "error".
 */
void show_system_error(const wchar_t* caption = NULL)
{
    const size_t BUF_SIZE = 1024;
    wchar_t errorMsgBuf[BUF_SIZE];
    FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM + FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, GetLastError(), NULL, errorMsgBuf, BUF_SIZE, NULL);
    MessageBoxW(0, errorMsgBuf, caption, MB_ICONERROR);
}

int main()
{
    SetLastError(ERROR_FILE_NOT_FOUND);
    show_system_error();
}

If we use boost, we got smaller code:
#include <iostream>
#include <string>
#include <locale>
#include <boost/system/error_code.hpp>
#include <Windows.h>

int main()
{
    setlocale(LC_ALL, "");
    SetLastError(ERROR_FILE_NOT_FOUND);

    auto text = boost::system::system_category()(
        GetLastError(), boost::system::system_category()).message();
    // or, just text = boost::system::system_category().message(GetLastError());

    std::cout << text << '\n';
}

Also we can throw an exception, using boost::system::system_error instead of error_code:
#include <iostream>
#include <locale>
#include <boost/system/system_error.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <Windows.h>

int main()
{
    setlocale(LC_ALL, "");

    try
    {
        SetLastError(ERROR_FILE_NOT_FOUND);

        throw boost::system::system_error(
            GetLastError(), boost::system::system_category(),
            __FUNCTION__ " #" BOOST_PP_STRINGIZE(__LINE__));
    }
    catch(std::exception& e)
    {
        std::cerr << e.what() << '\n';
    }
}


Update:
C++0x standard library contains <system_error> header file, with same classes as in boost.system library.
#include <iostream>
#include <string>
#include <system_error>
#include <Windows.h>

int main()
{
    SetLastError(ERROR_FILE_NOT_FOUND);

    auto text = std::system_category().message(GetLastError());

    std::cout << text << '\n';
}
In MSVC2010, it differs from boost's version - message isn't localized, so we don't have to use setlocale().

#include <iostream>
#include <string>
#include <system_error>
#include <Windows.h>

int main()
{
    try
    {
        SetLastError(ERROR_FILE_NOT_FOUND);

        throw std::system_error(GetLastError(), std::system_category(), "test");
    }
    catch(std::system_error& e)
    {
        std::cerr << e.what() << ": " << e.code().message() << '\n';
    }
}
MSVC2010's std::system_error is broken. It's what() method doesn't concatenate error code and message parameters, so we should do it manually. It will be fixed in VC11 (link).

No comments:

Post a Comment