programing

GetLastError()에 의해 반환된 오류 코드에서 오류 메시지를 얻는 방법

randomtip 2022. 7. 21. 21:51
반응형

GetLastError()에 의해 반환된 오류 코드에서 오류 메시지를 얻는 방법

Windows API 호출 후 텍스트 형식으로 마지막 오류 메시지를 받으려면 어떻게 해야 합니까?

GetLastError()텍스트 메시지가 아닌 정수 값을 반환합니다.

//Returns the last Win32 error, in string format. Returns an empty string if there is no error.
std::string GetLastErrorAsString()
{
    //Get the error message ID, if any.
    DWORD errorMessageID = ::GetLastError();
    if(errorMessageID == 0) {
        return std::string(); //No error message has been recorded
    }
    
    LPSTR messageBuffer = nullptr;

    //Ask Win32 to give us the string version of that message ID.
    //The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be).
    size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                                 NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
    
    //Copy the error message into a std::string.
    std::string message(messageBuffer, size);
    
    //Free the Win32's string's buffer.
    LocalFree(messageBuffer);
            
    return message;
}

부터는 c++11 대신 할 수 .FormatMessage:

#include <system_error>

if (!SomeWin32Function()){
   std::string message = std::system_category().message(::GetLastError);
   ...
}

코멘트를 고려하여(2017년 11월) 갱신되었습니다.

간단한 예:

wchar_t buf[256];
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
               NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
               buf, (sizeof(buf) / sizeof(wchar_t)), NULL);

GetLastError는 숫자 오류 코드를 반환합니다.설명적인 오류 메시지(예를 들어 사용자에게 표시)를 얻으려면 FormatMessage를 호출합니다.

// This functions fills a caller-defined character buffer (pBuffer)
// of max length (cchBufferLength) with the human-readable error message
// for a Win32 error code (dwErrorCode).
// 
// Returns TRUE if successful, or FALSE otherwise.
// If successful, pBuffer is guaranteed to be NUL-terminated.
// On failure, the contents of pBuffer are undefined.
BOOL GetErrorMessage(DWORD dwErrorCode, LPTSTR pBuffer, DWORD cchBufferLength)
{
    if (cchBufferLength == 0)
    {
        return FALSE;
    }

    DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                                 NULL,  /* (not used with FORMAT_MESSAGE_FROM_SYSTEM) */
                                 dwErrorCode,
                                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                 pBuffer,
                                 cchBufferLength,
                                 NULL);
    return (cchMsg > 0);
}

C++에서는 std::string 클래스를 사용하여 인터페이스를 대폭 간소화할 수 있습니다.

#include <Windows.h>
#include <system_error>
#include <memory>
#include <string>
typedef std::basic_string<TCHAR> String;

String GetErrorMessage(DWORD dwErrorCode)
{
    LPTSTR psz{ nullptr };
    const DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
                                         | FORMAT_MESSAGE_IGNORE_INSERTS
                                         | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                                       NULL, // (not used with FORMAT_MESSAGE_FROM_SYSTEM)
                                       dwErrorCode,
                                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                       reinterpret_cast<LPTSTR>(&psz),
                                       0,
                                       NULL);
    if (cchMsg > 0)
    {
        // Assign buffer to smart pointer with custom deleter so that memory gets released
        // in case String's c'tor throws an exception.
        auto deleter = [](void* p) { ::LocalFree(p); };
        std::unique_ptr<TCHAR, decltype(deleter)> ptrBuffer(psz, deleter);
        return String(ptrBuffer.get(), cchMsg);
    }
    else
    {
        auto error_code{ ::GetLastError() };
        throw std::system_error( error_code, std::system_category(),
                                 "Failed to retrieve error message string.");
    }
}

메모: 이러한 함수는 HRESULT 값에도 적용됩니다.첫 번째 파라미터를 DWORD dwErrorCode에서HRESULT hResult로 변경합니다.나머지 코드는 변경되지 않을 수 있습니다.


These implementations provide the following improvements over the existing answers:
  • 호출할 API에 대한 참조뿐만 아니라 샘플 코드를 완성합니다.
  • C 와 C++ 의 양쪽 모두의 실장을 제공합니다.
  • Unicode 및 MBCS 프로젝트 설정 모두에 대해 작동합니다.
  • 오류 코드를 입력 매개 변수로 사용합니다.스레드의 마지막 오류 코드는 잘 정의된 지점에서만 유효하기 때문에 이는 중요합니다.입력 파라미터에 의해 발신자는 문서화된 계약에 따를 수 있습니다.
  • 적절한 예외 안전성을 구현합니다.예외를 암묵적으로 사용하는 다른 모든 솔루션과 달리 이 구현에서는 반환값 구성 중에 예외가 발생해도 메모리가 누출되지 않습니다.
  • 「 」의 FORMAT_MESSAGE_IGNORE_INSERTSflag. 자세한 내용은 FORMAT_MESSAGE_IGNORE_INSERTS 플래그의 중요성을 참조하십시오.
  • 다른 응답과 달리 오류를 자동으로 무시하는 적절한 오류 처리/오류 보고

이 답변은 Stack Overflow Documentation에서 통합되었습니다.stackptr, Ajay, Cody Gray♦, IInspectable 등의 사용자가 이 예에 기여했습니다.

에는 MSDN 가 몇 .FormatMessage() ★★★★★★★★★★★★★★★★★」GetLastError()together: 최종 에러 코드 취득

FormatMessageGetLastError의 정수 반환을 텍스트 메시지로 변환합니다.

void WinErrorCodeToString(DWORD ErrorCode, string& Message)
{
char* locbuffer = NULL;
DWORD count = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, ErrorCode,
    0, (LPSTR)&locbuffer, 0, nullptr);
if (locbuffer)
{
    if (count)
    {
        int c;
        int back = 0;
        //
        // strip any trailing "\r\n"s and replace by a single "\n"
        //
        while (((c = *CharPrevA(locbuffer, locbuffer + count)) == '\r') ||
            (c == '\n')) {
            count--;
            back++;
        }

        if (back) {
            locbuffer[count++] = '\n';
            locbuffer[count] = '\0';
        }

        Message = "Error: ";
        Message += locbuffer;
    }
    LocalFree(locbuffer);
}
else
{
    Message = "Unknown error code: " + to_string(ErrorCode);
}
}

c# 를 사용하고 있는 경우는, 다음의 코드를 사용할 수 있습니다.

using System.Runtime.InteropServices;

public static class WinErrors
{
    #region definitions
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr LocalFree(IntPtr hMem);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern int FormatMessage(FormatMessageFlags dwFlags, IntPtr lpSource, uint dwMessageId, uint dwLanguageId, ref IntPtr lpBuffer, uint nSize, IntPtr Arguments);

    [Flags]
    private enum FormatMessageFlags : uint
    {
        FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100,
        FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200,
        FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000,
        FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000,
        FORMAT_MESSAGE_FROM_HMODULE = 0x00000800,
        FORMAT_MESSAGE_FROM_STRING = 0x00000400,
    }
    #endregion

    /// <summary>
    /// Gets a user friendly string message for a system error code
    /// </summary>
    /// <param name="errorCode">System error code</param>
    /// <returns>Error string</returns>
    public static string GetSystemMessage(int errorCode)
    {
        try
        {
            IntPtr lpMsgBuf = IntPtr.Zero;

            int dwChars = FormatMessage(
                FormatMessageFlags.FORMAT_MESSAGE_ALLOCATE_BUFFER | FormatMessageFlags.FORMAT_MESSAGE_FROM_SYSTEM | FormatMessageFlags.FORMAT_MESSAGE_IGNORE_INSERTS,
                IntPtr.Zero,
                (uint) errorCode,
                0, // Default language
                ref lpMsgBuf,
                0,
                IntPtr.Zero);
            if (dwChars == 0)
            {
                // Handle the error.
                int le = Marshal.GetLastWin32Error();
                return "Unable to get error code string from System - Error " + le.ToString();
            }

            string sRet = Marshal.PtrToStringAnsi(lpMsgBuf);

            // Free the buffer.
            lpMsgBuf = LocalFree(lpMsgBuf);
            return sRet;
        }
        catch (Exception e)
        {
            return "Unable to get error code string from System -> " + e.ToString();
        }
    }
}

반반, in반, in in in in 。FormatMessageWin32 러 win win win win win win win win win 。

MSDN 의 메뉴얼로부터, 다음의 것을 참조해 주세요.

메시지 문자열을 포맷합니다.함수에는 메시지 정의가 입력으로 필요합니다.메시지 정의는 함수에 전달된 버퍼에서 가져올 수 있습니다.이미 로드된 모듈의 메시지테이블 리소스에서 가져올 수 있습니다.또는 발신자는 이 함수에 대해 메시지 정의를 위해 시스템의 메시지테이블 리소스를 검색하도록 요청할 수 있습니다.이 함수는 메시지 식별자와 언어 식별자를 기반으로 메시지 테이블 리소스에서 메시지 정의를 찾습니다.이 함수는 포맷된 메시지 텍스트를 출력 버퍼에 복사하여 요청 시 내장된 삽입 시퀀스를 처리합니다.

Format Message 선언:

DWORD WINAPI FormatMessage(
  __in      DWORD dwFlags,
  __in_opt  LPCVOID lpSource,
  __in      DWORD dwMessageId, // your error code
  __in      DWORD dwLanguageId,
  __out     LPTSTR lpBuffer,
  __in      DWORD nSize,
  __in_opt  va_list *Arguments
);

나중에 써야 하니까 여기 놔둘게요.어셈블리에서 동일하게 동작하는 소형 바이너리 호환 툴인 C와 C++의 소스입니다.

GetErrorMessageLib.c(GetErrorMessageLib.dll로 컴파일)

#include <Windows.h>

/***
 * returns 0 if there was enough space, size of buffer in bytes needed
 * to fit the result, if there wasn't enough space. -1 on error.
 */
__declspec(dllexport)
int GetErrorMessageA(DWORD dwErrorCode, LPSTR lpResult, DWORD dwBytes)
{    
    LPSTR tmp;
    DWORD result_len;

    result_len = FormatMessageA (
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
        NULL,
        dwErrorCode,
        LANG_SYSTEM_DEFAULT,
        (LPSTR)&tmp,
        0,
        NULL
    );        

    if (result_len == 0) {
        return -1;
    }

    // FormatMessage's return is 1 character too short.
    ++result_len;

    strncpy(lpResult, tmp, dwBytes);

    lpResult[dwBytes - 1] = 0;
    LocalFree((HLOCAL)tmp);

    if (result_len <= dwBytes) {
        return 0;
    } else {
        return result_len;
    }
}

/***
 * returns 0 if there was enough space, size of buffer in bytes needed
 * to fit the result, if there wasn't enough space. -1 on error.
 */
__declspec(dllexport)
int GetErrorMessageW(DWORD dwErrorCode, LPWSTR lpResult, DWORD dwBytes)
{   
    LPWSTR tmp;
    DWORD nchars;
    DWORD result_bytes;

    nchars = dwBytes >> 1;

    result_bytes = 2 * FormatMessageW (
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
        NULL,
        dwErrorCode,
        LANG_SYSTEM_DEFAULT,
        (LPWSTR)&tmp,
        0,
        NULL
    );    

    if (result_bytes == 0) {
        return -1;
    } 

    // FormatMessage's return is 1 character too short.
    result_bytes += 2;

    wcsncpy(lpResult, tmp, nchars);
    lpResult[nchars - 1] = 0;
    LocalFree((HLOCAL)tmp);

    if (result_bytes <= dwBytes) {
        return 0;
    } else {
        return result_bytes * 2;
    }
}

인라인 버전(GetErrorMessage).h) :

#ifndef GetErrorMessage_H 
#define GetErrorMessage_H 
#include <Windows.h>    

/***
 * returns 0 if there was enough space, size of buffer in bytes needed
 * to fit the result, if there wasn't enough space. -1 on error.
 */
static inline int GetErrorMessageA(DWORD dwErrorCode, LPSTR lpResult, DWORD dwBytes)
{    
    LPSTR tmp;
    DWORD result_len;

    result_len = FormatMessageA (
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
        NULL,
        dwErrorCode,
        LANG_SYSTEM_DEFAULT,
        (LPSTR)&tmp,
        0,
        NULL
    );        

    if (result_len == 0) {
        return -1;
    }

    // FormatMessage's return is 1 character too short.
    ++result_len;

    strncpy(lpResult, tmp, dwBytes);

    lpResult[dwBytes - 1] = 0;
    LocalFree((HLOCAL)tmp);

    if (result_len <= dwBytes) {
        return 0;
    } else {
        return result_len;
    }
}

/***
 * returns 0 if there was enough space, size of buffer in bytes needed
 * to fit the result, if there wasn't enough space. -1 on error.
 */
static inline int GetErrorMessageW(DWORD dwErrorCode, LPWSTR lpResult, DWORD dwBytes)
{   
    LPWSTR tmp;
    DWORD nchars;
    DWORD result_bytes;

    nchars = dwBytes >> 1;

    result_bytes = 2 * FormatMessageW (
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
        NULL,
        dwErrorCode,
        LANG_SYSTEM_DEFAULT,
        (LPWSTR)&tmp,
        0,
        NULL
    );    

    if (result_bytes == 0) {
        return -1;
    } 

    // FormatMessage's return is 1 character too short.
    result_bytes += 2;

    wcsncpy(lpResult, tmp, nchars);
    lpResult[nchars - 1] = 0;
    LocalFree((HLOCAL)tmp);

    if (result_bytes <= dwBytes) {
        return 0;
    } else {
        return result_bytes * 2;
    }
}

#endif /* GetErrorMessage_H */

dynamic use case(에러 코드가 유효한지 확인합니다.그렇지 않으면 -1 체크가 필요합니다.

#include <Windows.h>
#include <Winbase.h>
#include <assert.h>
#include <stdio.h>

int main(int argc, char **argv)
{   
    int (*GetErrorMessageA)(DWORD, LPSTR, DWORD);
    int (*GetErrorMessageW)(DWORD, LPWSTR, DWORD);
    char result1[260];
    wchar_t result2[260];

    assert(LoadLibraryA("GetErrorMessageLib.dll"));

    GetErrorMessageA = (int (*)(DWORD, LPSTR, DWORD))GetProcAddress (
        GetModuleHandle("GetErrorMessageLib.dll"),
        "GetErrorMessageA"
    );        
    GetErrorMessageW = (int (*)(DWORD, LPWSTR, DWORD))GetProcAddress (
        GetModuleHandle("GetErrorMessageLib.dll"),
        "GetErrorMessageW"
    );        

    GetErrorMessageA(33, result1, sizeof(result1));
    GetErrorMessageW(33, result2, sizeof(result2));

    puts(result1);
    _putws(result2);

    return 0;
}

일반 사용 사례(오류 코드는 유효하며, 그렇지 않으면 -1 반품 체크 필요):

#include <stdio.h>
#include "GetErrorMessage.h"
#include <stdio.h>

int main(int argc, char **argv)
{
    char result1[260];
    wchar_t result2[260];

    GetErrorMessageA(33, result1, sizeof(result1));
    puts(result1);

    GetErrorMessageW(33, result2, sizeof(result2));
    _putws(result2);

    return 0;
}

예를 들어 어셈블리의 gnu를 MinGW32와 같이 사용하고 있습니다(이 예에서는 에러 코드가 유효하다고 가정하고, 그렇지 않으면 -1 체크가 필요).

    .global _WinMain@16

    .section .text
_WinMain@16:
    // eax = LoadLibraryA("GetErrorMessageLib.dll")
    push $sz0
    call _LoadLibraryA@4 // stdcall, no cleanup needed

    // eax = GetProcAddress(eax, "GetErrorMessageW")
    push $sz1
    push %eax
    call _GetProcAddress@8 // stdcall, no cleanup needed

    // (*eax)(errorCode, szErrorMessage)
    push $200
    push $szErrorMessage
    push errorCode       
    call *%eax // cdecl, cleanup needed
    add $12, %esp

    push $szErrorMessage
    call __putws // cdecl, cleanup needed
    add $4, %esp

    ret $16

    .section .rodata
sz0: .asciz "GetErrorMessageLib.dll"    
sz1: .asciz "GetErrorMessageW"
errorCode: .long 33

    .section .data
szErrorMessage: .space 200

★★★★★The process cannot access the file because another process has locked a portion of the file.

유니코드뿐만 아니라 MBCS를 지원해야 한다면 Mr.C64의 답변으로는 충분하지 않습니다.버퍼를 TCHAR로 선언하고 LPTSTR에 캐스트해야 합니다.이 코드는, Microsoft 가 에러 메세지에 추가한 귀찮은 줄바꿈에는 대응하고 있지 않습니다.

CString FormatErrorMessage(DWORD ErrorCode)
{
    TCHAR   *pMsgBuf = NULL;
    DWORD   nMsgLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        reinterpret_cast<LPTSTR>(&pMsgBuf), 0, NULL);
    if (!nMsgLen)
        return _T("FormatMessage fail");
    CString sMsg(pMsgBuf, nMsgLen);
    LocalFree(pMsgBuf);
    return sMsg;
}

또, 간결하게 하기 위해서, 다음의 방법이 도움이 됩니다.

CString GetLastErrorString()
{
    return FormatErrorMessage(GetLastError());
}

솔루션

다음으로 std::string/wstring을 사용한 최소한의 C++ 예를 나타냅니다.

  • Unicode와 MBCS 모두 지원
  • MSVC 6.0 -> VS2022 및 GCC/MinGW (-lstdc++ 와 호환)아마 클랑도 그랬을 거야
  • C++11 불필요
  • Windows XP 이후에 동작합니다.
#include <windows.h>
#include <string>

typedef std::basic_string<TCHAR> String;

const String errorMessage(DWORD dwError)
{
    LPTSTR lpBuffer = NULL;
    String ret = TEXT("");
    if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwError, 0, (LPTSTR)&lpBuffer, 0, NULL))
        ret = String(lpBuffer);
    LocalFree(lpBuffer);
    return ret;
}

단, 에러 체크는 없고, 지정한 에러를 찾을 수 없는 경우는 빈 문자열만 반환됩니다.필요에 따라서, 독자적인 에러 체크를 실장할 수 있습니다.

로트 코드를 작성하는 데 시간을 낭비하는 이유는 무엇입니까?


추가 정보

다른 답변에서는 MAKELANGID 매크로가 더 이상 사용되지 않으며 일부 언어에서는 전혀 작동하지 않으므로 사용하지 않아야 하므로 dwLanguageId는 올바른 방법이기 때문에 0으로 지정합니다.

다음은 Windows SDK 10.0.19041.0(2020-05-12)의 winnt.h에서 발췌한 것입니다.

//
// ** DEPRECATED ** DEPRECATED ** DEPRECATED ** DEPRECATED ** DEPRECATED **
//
//  DEPRECATED: The LCID/LANGID/SORTID concept is deprecated, please use
//  Locale Names instead, eg: "en-US" instead of an LCID like 0x0409.
//  See the documentation for GetLocaleInfoEx.
//
//  A language ID is a 16 bit value which is the combination of a
//  primary language ID and a secondary language ID.  The bits are
//  allocated as follows:
//
//       +-----------------------+-------------------------+
//       |     Sublanguage ID    |   Primary Language ID   |
//       +-----------------------+-------------------------+
//        15                   10 9                       0   bit
//
//  WARNING:  This pattern is broken and not followed for all languages.
//            Serbian, Bosnian & Croatian are a few examples.
//
//  WARNING:  There are > 6000 human languages.  The PRIMARYLANGID construct
//            cannot support all languages your application may encounter.
//            Please use Language Names, such as "en".
//
//  WARNING:  There are > 200 country-regions.  The SUBLANGID construct cannot
//            represent all valid dialects of languages such as English.
//            Please use Locale Names, such as "en-US".
//
//  WARNING:  Some languages may have more than one PRIMARYLANGID.  Please
//            use Locale Names, such as "en-FJ".
//
//  WARNING:  Some languages do not have assigned LANGIDs.  Please use
//            Locale Names, such as "tlh-Piqd".
//
//  It is recommended that applications test for locale names rather than
//  attempting to construct/deconstruct LANGID/PRIMARYLANGID/SUBLANGID
//
//  Language ID creation/extraction macros:
//
//    MAKELANGID    - construct language id from a primary language id and
//                    a sublanguage id.
//    PRIMARYLANGID - extract primary language id from a language id.
//    SUBLANGID     - extract sublanguage id from a language id.
//
//  Note that the LANG, SUBLANG construction is not always consistent.
//  The named locale APIs (eg GetLocaleInfoEx) are recommended.
//
//  DEPRECATED: Language IDs do not exist for all locales
//
// ** DEPRECATED ** DEPRECATED ** DEPRECATED ** DEPRECATED ** DEPRECATED **
//

아직 MAKELANGID의 MSDN 공식 문서에 정보가 전달되지 않은 것 같습니다.

올바르게 동작해도 지정된 Lang에서 오류 문자열을 검색하려고 하기 때문에 더 나쁜 옵션입니다.아이디 하나뿐이고 존재하지 않으면 실패한다.대신 0을 사용하면 오류가 사용자의 언어로 현지화되지 않았더라도 최소한 무언가가 반환될 가능성이 높습니다.

MSDN Format Message에서 인용W:

[in] dwLanguageId

요청된 메시지의 언어 ID.dwFlags에 FORMAT_MESSAGE_FROM_STRING이 포함되어 있는 경우 이 파라미터는 무시됩니다.

이 파라미터로 특정 LANGID를 전달하면 FormatMessage는 해당 LANGID에 대한 메시지만 반환합니다.함수는 해당 LANGID 메시지를 찾을 수 없는 경우 Last-Error를 ERRORESOURCES_LANG_NOT_FOUND로 설정합니다.제로를 통과하면 FormatMessage는 다음 순서로 LANGID 메시지를 찾습니다.

  1. 언어 중립
  2. 스레드의 로케일 값에 기반한 스레드 LANGID
  3. 사용자의 기본 로케일 값에 기반한 사용자 기본 LANGID
  4. 시스템 디폴트 LANGID(시스템 디폴트 로케일 값에 근거함)
  5. 미국 영어

Format Message가 이전 LANGID 메시지를 찾지 못한 경우 존재하는 언어 메시지 문자열을 반환합니다.실패하면 ERRORESOURCE_LANG_NOT_FOUND가 반환됩니다.

언급URL : https://stackoverflow.com/questions/1387064/how-to-get-the-error-message-from-the-error-code-returned-by-getlasterror

반응형