Win32 API
오류보고 및 처리
수색…
비고
각 스레드는 고유의 마지막 오류 코드를 갖습니다. Windows API는 호출 스레드의 마지막 오류 코드를 설정합니다.
Windows API 함수의 반환 값을 확인한 후에는 항상 GetLastError
함수를 호출해야합니다.
대부분의 Windows API 함수는 실패 할 때 마지막 오류 코드를 설정합니다. 일부는 성공할 때 마지막 오류 코드도 설정합니다. 마지막 오류 코드를 설정하지 않는 여러 가지 기능이 있습니다. 항상 Windows API 함수의 설명서를 참조하십시오.
FormatMessage
함수를 사용하여 오류 코드에 대한 설명을 가져올 때 FORMAT_MESSAGE_FROM_SYSTEM
없이 FORMAT_MESSAGE_IGNORE_INSERTS
을 사용하는 것은 안전하지 FORMAT_MESSAGE_IGNORE_INSERTS
.
소개
Windows API는 C 호출 가능 인터페이스를 통해 제공됩니다. API 호출의 성공 또는 실패는 반환 값을 통해보고됩니다. 일부 API 구현 은 읽기 전용 lpCommandLine 인수를 CreateProcess에 전달할 때 SEH 예외를 발생시킬 수 있지만 예외는 문서화 된 계약의 일부가 아닙니다.
오류보고는 크게 네 범주 중 하나에 속합니다.
각 API 호출에 대한 문서는 명시 적으로 오류를보고하는 방법을 호출합니다. 항상 설명서를 참조하십시오.
반환 값에 의해서만보고 된 오류
일부 API 호출은 추가 정보 (예 : GetObject )없이 단일 실패 / 성공 플래그를 반환합니다.
if ( GetObjectW( obj, 0, NULL ) == 0 ) {
// Failure: no additional information available.
}
오류에 대한 추가 정보가있는 오류가보고되었습니다.
실패 / 성공 반환 값 외에도 일부 API 호출은 실패시 마지막 오류 (예 : CreateWindow )를 설정합니다. 문서에는 대개이 경우 다음과 같은 표준 문구가 포함됩니다.
함수가 성공하면 반환 값은 <API 관련 성공 값> 입니다.
함수가 실패하면 반환 값은 <API 관련 오류 값> 입니다. 확장 된 오류 정보를 얻으려면 GetLastError를 호출하십시오.
if ( CreateWindowW( ... ) == NULL ) {
// Failure: get additional information.
DWORD dwError = GetLastError();
} else {
// Success: must not call GetLastError.
}
GetLastError()
즉시 호출하는 것이 중요합니다. 실패한 기능과 호출 사이에 추가 함수 호출이 있다면, 그래서 마지막 오류 코드가 다른 기능에 의해 덮어 쓸 수 GetLastError()
에서 반환 GetLastError()
더 이상 신뢰할 수 없습니다. C ++ 생성자를 다룰 때는 특히주의하십시오.
오류 코드를 받으면 해석해야합니다. MSDN의 시스템 오류 코드 (Windows) 페이지 에서 포괄적 인 오류 코드 목록을 얻을 수 있습니다. 또는 시스템 헤더 파일을 볼 수도 있습니다. 모든 오류 코드 상수를 가진 파일은 winerror.h
입니다. (Windows 8 용 Microsoft의 공식 SDK가있는 경우이 폴더는 include 폴더의 shared
하위 폴더에 있습니다.)
다른 프로그래밍 언어로 GetLastError()
를 호출 할 때의주의 사항
.net 언어 (C #, VB 등)
.net을 사용하면 GetLastError()
P / Invoke를 직접 해서는 안됩니다 . NET 런타임이 등 뒤의 동일한 스레드에서 다른 Windows API 호출을 수행하기 때문입니다. 예를 들어 가비지 컬렉터는 더 이상 사용하지 않는 메모리를 발견하면 VirtualFree()
호출 할 수 있으며 , 이는 의도 한 함수 호출과 GetLastError()
호출 사이에서 발생할 수 있습니다 .
그 대신 .net은 마지막 P / Invoke 호출에서 마지막으로 발생한 오류를 검색하는 Marshal.GetLastWin32Error()
함수를 제공합니다. GetLastError()
직접 호출하는 대신이 GetLastError()
사용하십시오.
(.net 어쨌든 GetLastError()
가져 오기 중지하지 않는 것, 왜 모르겠어요.)
가기
DLL 함수를 호출하기 위해 Go가 제공하는 다양한 기능 (패키지 syscall
과 패키지 golang.org/x/sys/windows
모두에 golang.org/x/sys/windows
)은 r1
, r2
및 err
세 가지 값을 반환합니다. r2
는 사용되지 않습니다. 빈 식별자를 사용할 수 있습니다. r1
은 함수의 반환 값입니다. err
은 GetLastError()
를 호출 한 결과이지만 error
를 구현하는 유형으로 변환되므로 처리 할 호출 함수에 전달할 수 있습니다.
Go는 GetLastError()
를 호출 GetLastError()
알지 못하며 그렇지 않을 경우 항상 nil
이 아닌 오류를 반환합니다. 따라서 일반적인 Go 오류 처리 관용구
r1, _, err := syscall.Syscall12(CreateWindowW.Addr(), ...)
if err != nil {
// handle err
}
// use r1
작동하지 않을 것이다. 대신, C에서와 똑같이 r1
검사해야하며, 함수가 오류를 반환 했다는 것을 나타내는 경우에만 err
사용하십시오.
r1, _, err := syscall.Syscall12(CreateWindowW.Addr(), ...)
if r1 == 0 {
// handle err
}
// use r1
실패 및 성공에 대한 추가 정보가있는 오류보고
일부 API 호출은 여러 가지 방법으로 성공하거나 실패 할 수 있습니다. API는 일반적으로 성공적인 호출과 오류 (예 : CreateMutex )에 대한 추가 정보를 반환합니다.
if ( CreateMutexW( NULL, TRUE, L"Global\\MyNamedMutex" ) == NULL ) {
// Failure: get additional information.
DWORD dwError = GetLastError();
} else {
// Success: Determine which mutex was returned.
if ( GetLastError() == ERROR_ALREADY_EXISTS ) {
// Existing mutex object returned.
} else {
// Newly created mutex object returned.
}
}
HRESULT 값으로보고 된 오류
HRESULT 는 숫자 32 비트 값이며 비트 또는 비트 범위는 잘 정의 된 정보를 인코딩합니다. MSB는 실패 / 성공 플래그이며 나머지 비트는 추가 정보를 저장합니다. 실패 또는 성공은 FAILED 또는 SUCCEEDED 매크로를 사용하여 판별 할 수 있습니다. HRESULT
는 일반적으로 COM과 함께 사용되지만 non-COM 구현에도 사용됩니다 (예 : StringCchPrintf ).
const size_t cchBuf = 5;
wchar_t buffer[cchBuf] = { 0 };
HRESULT hr = StringCchPrintfW( buffer, cchBuf, L"%s", L"Hello, world!" );
if ( FAILED( hr ) ) {
// Failure: Determine specific reason.
switch ( hr ) {
case STRSAFE_E_INSUFFICIENT_BUFFER:
// Buffer too small; increase buffer and retry.
...
case STRSAFE_E_INVALID_PARAMETER:
// Invalid parameter; implement custom error handling (e.g. logging).
...
default:
// Some other error code; implement custom error handling (e.g. logging).
...
}
}
오류 코드를 메시지 문자열로 변환
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 <exception>
#include <stdexcept>
#include <memory>
#include <string>
typedef std::basic_string<TCHAR> String;
String GetErrorMessage(DWORD dwErrorCode)
{
LPTSTR psz = NULL;
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) { ::HeapFree(::GetProcessHeap(), 0, p); };
std::unique_ptr<TCHAR, decltype(deleter)> ptrBuffer(psz, deleter);
return String(ptrBuffer.get(), cchMsg);
}
else
{
throw std::runtime_error("Failed to retrieve error message string.");
}
}
참고 : 이러한 함수는 HRESULT
값에 대해서도 작동 합니다 . DWORD dwErrorCode
에서 HRESULT hResult
첫 번째 매개 변수를 변경하기 DWORD dwErrorCode
됩니다. 나머지 코드는 변경되지 않습니다.