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_INSERTS
flag. 자세한 내용은 FORMAT_MESSAGE_IGNORE_INSERTS 플래그의 중요성을 참조하십시오. - 다른 응답과 달리 오류를 자동으로 무시하는 적절한 오류 처리/오류 보고
이 답변은 Stack Overflow Documentation에서 통합되었습니다.stackptr, Ajay, Cody Gray♦, IInspectable 등의 사용자가 이 예에 기여했습니다.
에는 MSDN 가 몇 .FormatMessage()
★★★★★★★★★★★★★★★★★」GetLastError()
together: 최종 에러 코드 취득
FormatMessage는 GetLastError의 정수 반환을 텍스트 메시지로 변환합니다.
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 。FormatMessage
Win32 러 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을 사용하면 오류가 사용자의 언어로 현지화되지 않았더라도 최소한 무언가가 반환될 가능성이 높습니다.
[in] dwLanguageId
요청된 메시지의 언어 ID.dwFlags에 FORMAT_MESSAGE_FROM_STRING이 포함되어 있는 경우 이 파라미터는 무시됩니다.
이 파라미터로 특정 LANGID를 전달하면 FormatMessage는 해당 LANGID에 대한 메시지만 반환합니다.함수는 해당 LANGID 메시지를 찾을 수 없는 경우 Last-Error를 ERRORESOURCES_LANG_NOT_FOUND로 설정합니다.제로를 통과하면 FormatMessage는 다음 순서로 LANGID 메시지를 찾습니다.
- 언어 중립
- 스레드의 로케일 값에 기반한 스레드 LANGID
- 사용자의 기본 로케일 값에 기반한 사용자 기본 LANGID
- 시스템 디폴트 LANGID(시스템 디폴트 로케일 값에 근거함)
- 미국 영어
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
'programing' 카테고리의 다른 글
링크할 때 정적 및 공유 객체 라이브러리를 혼재시킬 수 있습니까? (0) | 2022.07.21 |
---|---|
Docker 컨테이너 localhost에 vue-cli 시작 페이지를 실행하는:.이 사이트에 도달할 수 없습니다. (0) | 2022.07.21 |
Laravel 및 Vuejs에서 액세스할 수 있도록 글로벌 변수를 설정하려면 어떻게 해야 합니까? (0) | 2022.07.21 |
이중 중첩 다이내믹 모듈 등록 - vuex (0) | 2022.07.21 |
memcpy 대신 더 빠른 방법? (0) | 2022.07.21 |