[C++/Win32] GetLastError, FormatMessage : 에러 코드를 메시지로 바꾸기

[C++/Win32] GetLastError, FormatMessage : 에러 코드를 메시지로 바꾸기

개요

안녕하세요. 이번 글에서는 Win32 API로 개발할 때 꼭 필요한 GetLastError와 FormatMessage에 대해 알아보겠습니다. 대부분의 Win32 함수는 실패하면 FALSE나 NULL만 돌려줄 뿐, 왜 실패했는지는 알려주지 않습니다. 그 이유는 에러 코드로 따로 저장되는데, 이 코드를 사람이 읽을 수 있는 문장으로 바꿔 주는 것이 FormatMessage입니다.

GetLastError — 마지막 에러 코드

Win32 함수가 실패하면 내부적으로 마지막 에러 코드를 저장합니다. 이 값을 가져오는 함수가 GetLastError이며, 반환형은 DWORD(정수)입니다.

#include <windows.h>

HANDLE h = CreateFile(L"없는파일.txt", GENERIC_READ, 0, NULL,
                      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
{
    DWORD err = GetLastError();   // 예: 2 (ERROR_FILE_NOT_FOUND)
    // 이 숫자만으로는 무슨 뜻인지 알기 어렵다
}

주의: GetLastError는 실패를 확인한 직후 바로 호출해야 합니다. 중간에 다른 API를 호출하면 에러 코드가 덮어써질 수 있습니다.

FormatMessage — 코드를 메시지로

2, 5 같은 숫자 코드만으로는 의미를 알기 어렵습니다. FormatMessage에 FORMAT_MESSAGE_FROM_SYSTEM 플래그를 주면, 시스템이 가진 설명 문자열(“지정된 파일을 찾을 수 없습니다” 등)을 돌려줍니다.

버퍼를 직접 잡지 않고 FORMAT_MESSAGE_ALLOCATE_BUFFER로 함수가 알아서 할당하게 한 뒤, 다 쓰면 LocalFree로 해제하는 방식이 편합니다.

#include <windows.h>
#include <string>

std::wstring GetErrorMessage(DWORD err)
{
    LPWSTR buf = nullptr;
    DWORD len = FormatMessageW(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |  // 버퍼를 함수가 할당
        FORMAT_MESSAGE_FROM_SYSTEM     |  // 시스템 메시지에서 찾기
        FORMAT_MESSAGE_IGNORE_INSERTS,    // %1 같은 치환 무시
        NULL,
        err,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 기본 언어
        (LPWSTR)&buf,   // 주소를 넘겨 버퍼 포인터를 받음
        0,
        NULL);

    std::wstring msg = (len && buf) ? buf : L"알 수 없는 에러";
    if (buf) LocalFree(buf);   // 반드시 해제
    return msg;
}

FORMAT_MESSAGE_ALLOCATE_BUFFER를 쓸 때는 buf의 주소(&buf)를 넘긴다는 점에 주의합니다. 함수가 내부에서 메모리를 할당해 그 포인터를 채워 줍니다.

실제 사용

앞의 두 함수를 합치면, 실패 원인을 바로 문장으로 확인할 수 있습니다.

HANDLE h = CreateFile(L"없는파일.txt", GENERIC_READ, 0, NULL,
                      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
{
    DWORD err = GetLastError();
    std::wstring msg = GetErrorMessage(err);
    wprintf(L"에러 %lu: %s\n", err, msg.c_str());
    // 출력 예) 에러 2: 지정된 파일을 찾을 수 없습니다.
}

이렇게 하면 로그나 메시지 박스에 “에러 2” 대신 “지정된 파일을 찾을 수 없습니다”처럼 원인을 바로 보여 줄 수 있어 디버깅이 훨씬 편해집니다.

정리

  • Win32 함수가 실패하면 GetLastError로 에러 코드(DWORD)를 얻는다. 실패 직후 바로 호출할 것.
  • 숫자 코드는 FormatMessage + FORMAT_MESSAGE_FROM_SYSTEM으로 읽을 수 있는 문장으로 바꾼다.
  • FORMAT_MESSAGE_ALLOCATE_BUFFER로 버퍼를 자동 할당하고, 사용 후 LocalFree로 해제한다.
  • 헬퍼 함수 하나로 만들어 두면 모든 Win32 에러 처리에 재사용할 수 있다.

에러 코드를 그대로 두면 원인을 알기 어렵지만, FormatMessage 한 번이면 바로 사람이 읽을 수 있는 메시지가 됩니다. Win32 개발에서 거의 항상 쓰이는 조합이니 헬퍼로 만들어 두길 권합니다. 이상으로 GetLastError와 FormatMessage에 대해 간단히 알아보았습니다.

참고