Win32 API
エラーの報告と処理
サーチ…
備考
各スレッドには独自の最後のエラーコードがあります。 Windows APIは、呼び出し側のスレッドで最後のエラーコードを設定します。
Windows API関数の戻り値をチェックした直後にGetLastError
関数を呼び出す必要があります。
大部分のWindows API関数は、失敗したときに最後のエラーコードを設定します。成功したときに最後のエラーコードを設定するものもあります。最後のエラーコードを設定しない関数がいくつかあります。常にWindows API関数のドキュメントを参照してください。
これを使うのは安全ではないFORMAT_MESSAGE_FROM_SYSTEM
せずにFORMAT_MESSAGE_IGNORE_INSERTS
使用した場合FormatMessage
エラーコードの説明を取得する機能を。
前書き
Windows APIは、C呼び出し可能なインターフェイスによって提供されます。 API呼び出しの成功または失敗は、厳密には戻り値によって報告されます。例外は文書化された契約の一部ではありません(一部のAPI 実装では、たとえばCreateProcessに読み取り専用のlpCommandLine引数を渡すときにSEH例外が発生する可能性があります)。
エラー報告は、大まかに次の4つのカテゴリのいずれかに分類されます。
各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
です。 (Microsoftの公式SDK for Windowsが8以上の場合、これはインクルードフォルダのshared
サブフォルダにあります。)
他のプログラミング言語でのGetLastError()
呼び出しに関する注意
.net言語(C#、VBなど)
.netでは、P / InvokeをGetLastError()
直接行うべきではありません 。これは、.NETランタイムが背後の同じスレッドで他のWindows API呼び出しを行うためです。たとえば、使用しなくなったメモリが十分に見つかった場合、ガーベッジコレクタはVirtualFree()
呼び出すことがあります。これは、目的の関数呼び出しとGetLastError()
呼び出しの間で発生します 。
代わりに、.netは自分で作成した最後のP / Invoke呼び出しから最後のエラーを取得するMarshal.GetLastWin32Error()
関数を提供します。 GetLastError()
直接呼び出す代わりにこれを使用します。
(.netとにかくGetLastError()
をインポートするのを止められないようですが、なぜそうはGetLastError()
ません)。
行こう
DLL関数を呼び出すためにGoによって提供されるさまざまな機能(パッケージsyscall
とパッケージgolang.org/x/sys/windows
両方に存在)は、 r1
、 r2
、およびerr
3つの値を返します。 r2
は使用されません。そこに空白の識別子を使用することができます。 r1
は関数の戻り値です。 err
はGetLastError()
を呼び出した結果ですが、 error
を実装する型に変換されるため、処理する呼び出し関数に渡すことができます。
GoはGetLastError()
を呼び出すタイミングを知りませんし、toにしない場合は常に 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で一般的に使用されますが、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
変更してください。残りのコードは変更されません。