Win32 api error code

Learn Win32 API - Each thread will have its own last error code. The Windows API will set the last error code on the calling thread.You should always call...

Each thread will have its own last error code. The Windows API will set the last error code on the calling thread.

You should always call the GetLastError function immediately after checking a Windows API function’s return value.

The majority of Windows API functions set the last error code when they fail. Some will also set the last error code when they succeed. There are a number of functions that do not set the last error code. Always refer to the Windows API function’s documentation.

It is unsafe to use FORMAT_MESSAGE_FROM_SYSTEM without FORMAT_MESSAGE_IGNORE_INSERTS when using the FormatMessage function to get a description of an error code.

Introduction

The Windows API is provided by means of a C-callable interface. Success or failure of an API call is reported strictly through return values. Exceptions aren’t part of the documented contract (although some API implementations can raise SEH exceptions, e.g. when passing a read-only lpCommandLine argument to CreateProcess).

Error reporting roughly falls into one of four categories:

  • Return value only
  • Return value with additional information on failure
  • Return value with additional information on failure and success
  • HRESULT return value

The documentation for each API call explicitly calls out, how errors are reported. Always consult the documentation.

Error reported by return value only

Some API calls return a single failure/success flag, without any additional information (e.g. GetObject):

if ( GetObjectW( obj, 0, NULL ) == 0 ) {
    // Failure: no additional information available.
}

Error reported with additional information on failure

In addition to a failure/success return value, some API calls also set the last error on failure (e.g. CreateWindow). The documentation usually contains the following standard wording for this case:

If the function succeeds, the return value is <API-specific success value>.
If the function fails, the return value is <API-specific error value>. To get extended error information, call GetLastError.

if ( CreateWindowW( ... ) == NULL ) {
    // Failure: get additional information.
    DWORD dwError = GetLastError();
} else {
    // Success: must not call GetLastError.
}

It is vital that you call GetLastError() IMMEDIATELY. The last error code can be overwritten by any other function, so if there’s an extra function call between the function that failed and the call to GetLastError(), the return from GetLastError() will no longer be reliable. Take extra caution when dealing with C++ constructors.

Once you get an error code, you will need to interpret it. You can get a comprehensive list of error codes on MSDN, at the System Error Codes (Windows) page. Alternatively, you can look in your system header files; the file with all the error code constants is winerror.h. (If you have Microsoft’s official SDK for Windows 8 or newer, this is in the shared subfolder of the include folder.)

Notes on calling GetLastError() in other programming languages

.net languages (C#, VB, etc.)

With .net, you should not P/Invoke to GetLastError() directly. This is because the .net runtime will make other Windows API calls on the same thread behind your back. For instance, the garbage collector might call VirtualFree() if it finds enough memory that it is no longer using, and this can happen between your intended function call and your call to GetLastError().

Instead, .net provides the Marshal.GetLastWin32Error() function, which will retrieve the last error from the last P/Invoke call that you yourself made. Use this instead of calling GetLastError() directly.

(.net does not seem to stop you from importing GetLastError() anyway; I’m not sure why.)

Go

The various facilities provided by Go for calling DLL functions (which reside in both package syscall and package golang.org/x/sys/windows) return three values: r1, r2, and err. r2 is never used; you can use the blank identifier there. r1 is the function’s return value. err is the result of calling GetLastError() but converted into a type that implements error, so you can pass it up to calling functions to handle.

Because Go does not know when to call GetLastError() and when not to, it will always return a non-nil error. Therefore, the typical Go error-handling idiom

r1, _, err := syscall.Syscall12(CreateWindowW.Addr(), ...)
if err != nil {
    // handle err
}
// use r1

will not work. Instead, you must check r1, exactly as you would in C, and only use err if that indicates the function returned an error:

r1, _, err := syscall.Syscall12(CreateWindowW.Addr(), ...)
if r1 == 0 {
    // handle err
}
// use r1

Error reported with additional information on failure and success

Some API calls can succeed or fail in more than one way. The APIs commonly return additional information for both successful invocations as well as errors (e.g. 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.
    }
}

Error reported as HRESULT value

HRESULTs are numeric 32-bit values, where bits or bit ranges encode well-defined information. The MSB is a failure/success flag, with the remaining bits storing additional information. Failure or success can be determined using the FAILED or SUCCEEDED macros. HRESULTs are commonly used with COM, but appear in non-COM implementations as well (e.g. 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).
        ...
    }
}

Converting an error code into a message string

GetLastError returns a numerical error code. To obtain a descriptive error message (e.g., to display to a user), you can call 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);
}

In C++, you can simplify the interface considerably by using the std::string class:

#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.");
    }
}

NOTE: These functions also work for HRESULT values. Just change the first parameter from DWORD dwErrorCode to HRESULT hResult. The rest of the code can remain unchanged.

Differences between and best practices for WINERROR, HRESULT, RPC_STATUS, SECURITY_STATUS and NTSTATUS?

Why would a developer care?

These error types and their values are an often ignored part of the API contract. Using the return value incorrectly has the same type of consequences as using a cast to pass the wrong data type to a parameter of an API, code just doesn’t work, and often fails first in the wild.

I recently began running into code like this:


// ERROR_SUCCESS is a WINERROR value
NTSTATUS status = ERROR_SUCCESS;
WCHAR* buffy;

buffy = (WCHAR*)malloc(1);
if (buffy == NULL)
{
// this is an HRESULT value
status = E_OUTOFMEMORY;
}

// not only is this a WINERROR value, but HRESULT and
// NTSTATUS have many success values, not just one
if (status != ERROR_SUCCESS)
{
return status;
}

I went searching for information about what each type is and the relationships between them and how to use them correctly. Here is what I learned.

They are all 32bit values, some signed some unsigned. Some are defined in terms of bit fields and require macros or inline-functions to check their current state. Others are straight enumerations.

One of the complexities is best demonstrated with the HRESULT type. Lets look at the out of memory condition. Here are some of the possible encodings:

Expression HRESULT value
E_OUTOFMEMORY 0x8007000E
HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY) 0x8007000E
HRESULT_FROM_NT(STATUS_NO_MEMORY) 0xD0000017
HRESULT_FROM_NT(
NTSTATUS_FROM_WIN32(ERROR_OUTOFMEMORY))
0xD007000E
STG_E_INSUFFICIENTMEMORY 0x80030008
NTE_NO_MEMORY 0x8009000E

There was some effort to make the values agree across the different expressions.

Many Api’s will pick a set of return codes and then build internal maps from all the errors they receive from other Api’s to the set they have decided to return. Thus most code calling Api’s can expect that only one encoding will be returned.

Primary Windows Error Types

There will be subsequent posts that expand on each of these types. Here is a summary:

WINERROR

Include winerror.h
Type DWORD
Structure values in the range 0-16384
Success value == ERROR_SUCCESS
Failure value != ERROR_SUCCESS
Conversions HRESULT_FROM_WIN32(value)
NTSTATUS_FROM_WIN32(value)

HRESULT

Include winerror.h
Type HRESULT
Structure [1bit Severity]
[1bit Reserved]
[1bit Customer]
[1bit FACILITY_NT_BIT]
[12bit Facility]
[16bit Code]
Success SUCCEEDED(value)
Failure FAILED(value)
Conversions HRESULT is a superset
of all the other error types.
They all have lossless conversions
to HRESULT but it doesn’t have
lossless conversions to anything else

NTSTATUS

Include

#define WIN32_NO_STATUS
#include <windows.h>
#undef WIN32_NO_STATUS

#include <winternl.h>
#include <ntstatus.h>

Type NTSTATUS
Structure [2bit Severity]
[1bit Customer]
[1bit Reserved]
[12bit Facility]
[16bit Code]
Success NT_SUCCESS(value)
Failure !NT_SUCCESS(value)
Other
Tests
NT_INFORMATIONAL(value)
NT_WARNING(value)
NT_ERROR(value)
Conversions HRESULT_FROM_NT(value)

Derived/Subset Windows Error Types

RPC_STATUS

Include rpcnterr.h
Type RPC_STATUS
Structure A subset of the WINERROR values, redefined as RPC_S_…
Success value == RPC_S_OK
Failure value != RPC_S_OK
Conversions HRESULT_FROM_WIN32(value)
NTSTATUS_FROM_WIN32(value)

SECURITY_STATUS

Include winerror.h
Type SECURITY_STATUS or HRESULT
Structure A subset of the HRESULT values (from FACILITY_NULL and FACILITY_SECURITY)
Success SUCCEEDED(value)
Failure FAILED(value)
Conversions

It is vital that you call GetLastError() IMMEDIATELY. The last error code can be overwritten by any other function, so if there’s an extra function call between the function that failed and the call to GetLastError() , the return from GetLastError() will no longer be reliable. Take extra caution when dealing with C++ constructors.

Once you get an error code, you will need to interpret it. You can get a comprehensive list of error codes on MSDN, at the System Error Codes (Windows) page. Alternatively, you can look in your system header files; the file with all the error code constants is winerror.h . (If you have Microsoft’s official SDK for Windows 8 or newer, this is in the shared subfolder of the include folder.)

Notes on calling GetLastError() in other programming languages

.net languages (C#, VB, etc.)

With .net, you should not P/Invoke to GetLastError() directly. This is because the .net runtime will make other Windows API calls on the same thread behind your back. For instance, the garbage collector might call VirtualFree() if it finds enough memory that it is no longer using, and this can happen between your intended function call and your call to GetLastError() .

Instead, .net provides the Marshal.GetLastWin32Error() function, which will retrieve the last error from the last P/Invoke call that you yourself made. Use this instead of calling GetLastError() directly.

(.net does not seem to stop you from importing GetLastError() anyway; I’m not sure why.)

The various facilities provided by Go for calling DLL functions (which reside in both package syscall and package golang.org/x/sys/windows ) return three values: r1 , r2 , and err . r2 is never used; you can use the blank identifier there. r1 is the function’s return value. err is the result of calling GetLastError() but converted into a type that implements error , so you can pass it up to calling functions to handle.

Because Go does not know when to call GetLastError() and when not to, it will always return a non- nil error. Therefore, the typical Go error-handling idiom

will not work. Instead, you must check r1 , exactly as you would in C, and only use err if that indicates the function returned an error:

Error reported with additional information on failure and success

Some API calls can succeed or fail in more than one way. The APIs commonly return additional information for both successful invocations as well as errors (e.g. CreateMutex).

Error reported as HRESULT value

HRESULTs are numeric 32-bit values, where bits or bit ranges encode well-defined information. The MSB is a failure/success flag, with the remaining bits storing additional information. Failure or success can be determined using the FAILED or SUCCEEDED macros. HRESULT s are commonly used with COM, but appear in non-COM implementations as well (e.g. StringCchPrintf).

Converting an error code into a message string

GetLastError returns a numerical error code. To obtain a descriptive error message (e.g., to display to a user), you can call FormatMessage :

In C++, you can simplify the interface considerably by using the std::string class:

NOTE: These functions also work for HRESULT values. Just change the first parameter from DWORD dwErrorCode to HRESULT hResult . The rest of the code can remain unchanged.

Источник

Win32 API
Сообщение об ошибках и обработка

замечания

Каждый поток будет иметь свой последний код ошибки. Windows API установит последний код ошибки в вызывающем потоке.

Вы всегда должны вызывать GetLastError сразу после проверки возвращаемого значения функции API Windows.

Большинство функций Windows API устанавливают последний код ошибки, когда они терпят неудачу. Некоторые также установят последний код ошибки, когда они преуспеют. Существует ряд функций, которые не устанавливают последний код ошибки. Всегда обращайтесь к документации по функциям Windows API.

Невозможно использовать FORMAT_MESSAGE_FROM_SYSTEM без FORMAT_MESSAGE_IGNORE_INSERTS при использовании функции FormatMessage для получения описания кода ошибки.

Вступление

API Windows предоставляется с помощью C-вызываемого интерфейса. Успех или сбой вызова API сообщаются строго через возвращаемые значения. Исключения не являются частью документированного контракта (хотя некоторые реализации API могут вызывать исключения SEH , например, при передаче аргумента lpCommandLine только для чтения в CreateProcess ).

Сообщение об ошибке грубо относится к одной из четырех категорий:

Документация для каждого вызова API явно вызывается, как сообщаются ошибки. Всегда обращайтесь к документации.

Ошибка, сообщенная только возвратным значением

Некоторые вызовы API возвращают единый флаг отказа / успеха без какой-либо дополнительной информации (например, GetObject ):

Сообщается об ошибке с сообщением об ошибке

В дополнение к возвращаемому значению отказа / успеха некоторые вызовы API также устанавливают последнюю ошибку при сбое (например, CreateWindow ). Документация обычно содержит следующую стандартную формулировку для этого случая:

Если функция завершается успешно, возвращаемое значение .
Если функция не работает, возвращаемое значение . Чтобы получить расширенную информацию об ошибке, вызовите GetLastError .

Очень важно, что вы вызываете GetLastError() НЕМЕДЛЕННО. Последний код ошибки может быть перезаписан любой другой функцией, поэтому, если есть дополнительная функция вызова между неудавшейся функцией и вызовом GetLastError() , возврат из GetLastError() больше не будет надежным. Будьте особенно осторожны при работе с конструкторами C ++.

Как только вы получите код ошибки, вам нужно будет его интерпретировать. Вы можете получить полный список кодов ошибок в MSDN на странице Системные коды ошибок (Windows) . Кроме того, вы можете посмотреть в своих файлах заголовков системы; файл со всеми константами кода ошибки — winerror.h . (Если у вас есть официальный SDK от Microsoft для Windows 8 или новее, это находится в shared папке с папкой include.)

Заметки о вызове GetLastError() на других языках программирования

.net (C #, VB и т. д.)

С .net вы не должны P / Invoke в GetLastError() напрямую. Это связано с тем, что среда выполнения .net сделает другие вызовы Windows API одним и тем же потоком за вашей спиной. Например, сборщик мусора может вызвать VirtualFree() если он найдет достаточно памяти, которую он больше не использует, и это может произойти между вашим назначенным вызовом функции и вашим вызовом GetLastError() .

Вместо этого .net предоставляет Marshal.GetLastWin32Error() , которая будет извлекать последнюю ошибку из последнего вызова P / Invoke, который вы сами сделали. Используйте это вместо прямого вызова GetLastError() .

(.net, похоже, не мешает вам импортировать GetLastError() любом случае, я не уверен, почему.)

Различные средства, предоставляемые Go для вызова DLL-функций (которые находятся как в syscall пакета, syscall и в пакете golang.org/x/sys/windows ), возвращают три значения: r1 , r2 и err . r2 никогда не используется; вы можете использовать пустой идентификатор. r1 — возвращаемое значение функции. err является результатом вызова GetLastError() но преобразуется в тип, реализующий error , поэтому вы можете передать его вызывающим функциям для обработки.

Поскольку Go не знает, когда вызывать GetLastError() а когда нет, он всегда будет возвращать ошибку nil . Поэтому типичная идиома обработки ошибок Go

не будет работать. Вместо этого вы должны проверить r1 точно так же, как и на C, и использовать только err если это указывает, что функция возвратила ошибку:

Сообщается об ошибке с дополнительной информацией о сбоях и успехах

Некоторые вызовы API могут преуспеть или сбой более чем одним способом. API обычно возвращают дополнительную информацию как для успешных вызовов, так и для ошибок (например, CreateMutex ).

Ошибка, сообщенная как значение HRESULT

HRESULT s — числовые 32-битные значения, где биты или диапазоны бит кодируют четко определенную информацию. MSB — это флаг отказа / успеха, а остальные бит хранят дополнительную информацию. Отказ или успех можно определить с помощью макросов FAILED или SUCCEEDED . HRESULT s обычно используются совместно с COM, но также отображаются в реализациях, отличных от COM (например, StringCchPrintf ).

Преобразование кода ошибки в строку сообщения

GetLastError возвращает числовой код ошибки. Чтобы получить описательное сообщение об ошибке ( например , для отображения пользователю), вы можете вызвать FormatMessage :

В C ++ вы можете значительно упростить интерфейс, используя класс std::string :

ПРИМЕЧАНИЕ. Эти функции также работают для значений HRESULT . Просто измените первый параметр из DWORD dwErrorCode на HRESULT hResult . Остальная часть кода может оставаться неизменной.

Источник

Win32 get error message

1. _com_error is just a wrapper around FormatMessage.

2. Higher versions of MS VS contain buggy implementation of _com_error.

See here for details. Sign In· View Thread

conversion error when compiling ehaerim 23-Dec-07 22:44
In an ATL COM project,

if (FAILED(hr))
<
::MessageBox(NULL, _com_error(hr).ErrorMessage, _T(«FinalConstruct»), MB_ICONSTOP);
>

cause the following error message and I couldn’t make it compile OK.

error C2664: ‘MessageBoxA’ : cannot convert parameter 2 from ‘const char *(void) const’ to ‘const char *’
There is no context in which this conversion is possible

How to fix this?

HaeRim

Sign In· View Thread
Can we use this with WSAGetLastError () for reporting WinSock errors? Siva Ranganeni 24-May-06 5:17
Hi,

This is a great article and very helpful.

Can we use the same with WSAGetLastError()? Will the following code give Winsock error message()?

_com_error( HRESULT_FROM_WIN32(WSAGetLastError() ).ErrorMessage()

Thanks in Advance.

Siva

Sign In· View Thread
Re: Can we use this with WSAGetLastError () for reporting WinSock errors? jwpfish 16-Jul-07 18:16

You can do that, e.g.:

puts(_com_error(WSAENOTSOCK).ErrorMessage());

Sign In· View Thread
That is great! Thank you! Ferne Li 8-May-06 16:32
That is great! Thank you!
Sign In· View Thread
Excellent. kimjim 20-Mar-06 18:11
Excellent article. I implemented this in my project. Thank you. A few words about passing GetLastError() as an argument.

As far as I know all the function’s arguments are evaluated before the actual function call is made. Let us take a case where some other win32 or other functions are called in the constructor of the _com_error or like class object. But we are passing the GetLastError as a parameter. So before even the constructor which is afterall a function, starts its execution. So I dont think this will be a problem. If at all the problem comes just make a workover. Create a function alike:

and put all your _com_error calls in that(using the ‘hr’ above). Pass GetLastError as a parameter to CallComErrMsgBox(). This should solve the problem.

Again excellent article. This kind of articles teaches again and again the time that can be saved by stopping «reinventing the wheel»

Thanks,
Jim

Sign In· View Thread
Very good Sam NG 20-Feb-06 17:02
Thank you I am not aware of the _com_error class. I do the error handling by my own macro, not a bad solution, but I think if the compiler already have it, better not «reinventing» the wheel.

Besides, I agree with ARB70 that display the «system» error message may not be the most difficult part, providing a more meaningful message specific to your application and your target user group is a even more challenging task. And I am always confused about what should be display in case of error

«Error initialize the program» seem good enough for general users but it won’t help in finding the reason why the program won’t start.

Sam

Sign In· View Thread
Re: Very good Brian C Hart 21-Feb-06 9:13

«Error initialize the program» seem good enough for general users but it won’t help in finding the reason why the program won’t start.

Perhaps. but all these strings are mapped to specific E_ or other constant values in code. and can be looked up in the docs. That’s the beauty of it. So just append, to the _com_error.ErrorMessage() output a string saying «nnEmail Tech support with the exact wording of this error string, and what you were doing when this happened.» Then you can work backwards from there as to what SDK calls you were making at that point, and from the error string you can work back to which error code the SDK call returned, and then you can look the SDK call up in the docs and work backward to figure out when said call returns that specific code.

Sincerely Yours,
Brian Hart
Department of Physics and Astronomy
University of California, Irvine

Sign In· View Thread
One problem this article does not solve. ARB70 3-Jan-06 12:43
The fact that both Win32 and COM error messages are frequently cryptic and unhelpful.

I have used _com_error a lot in the past for dealing with HRESULTS. If you import a COM type library in C++ and call methods on the generated smart pointers they always throw _com_error when something goes wrong, so having a catch handler that deals with them is nearly always mandatory. I was not aware that you could also use them for dealing with Win32 calls and that is very helpful and useful. Thanks.

However both Win32 and COM produce extremely «techie», unhelpful error messages and I nearly always find that they require massaging if they are going to be of any help to a «normal» user. In you example «The system cannot find the file specified» is not terribly helpful. 100 different files may have been specified! «Class not registered» is even more cryptic!

So unlike some other posters I won’t be changing all my code to use a one liner approach but will continue using support classes and other code to provide more meaningful error messages. Maybe this is not such a problem if all of your users are «propellor heads».

ARB70

Sign In· View Thread
Re: One problem this article does not solve. Brian C Hart 3-Jan-06 13:12

So unlike some other posters I won’t be changing all my code to use a one liner approach but will continue using support classes and other code to provide more meaningful error messages. Maybe this is not such a problem if all of your users are «propellor heads».

Conceded. You have to do what you think is best for your particular application, and the user base you will be serving.

Sincerely Yours,
Brian Hart
Department of Physics and Astronomy
University of California, Irvine

Sign In· View Thread
Re: One problem this article does not solve. Paul Sanders (the other one) 20-Oct-07 8:45

Nearly two years after the event is perhaps a little late to comment on this, but you never know, someone may read it.

I think that reporting ‘bare metal’ WIN32 errors is extremely important when something happens to your program that you did not anticipate when you wrote it. I do accept, though, that it is no substitute for handling error conditions that you know, when you are writing the program, might reasonably arise (such as ‘file not found’, to take a trivial example).

I guess what I am trying to say is that unexpected things ALWAYS happen to your software, and the more helpful *to the developer* the error message is in such circumstances, the more likely he/she is to be able to get to the bottom of it (and hence fix it in the next release). «The operation could not be completed» just doesn’t cut it. Encourage your users to copy cryptic error messages to the clipboard (provide a button that does it) and email them to you, perhaps.

Of course, the ultimate error message is ‘assert failed’

Sign In· View Thread
Re: One problem this article does not solve. Brian C Hart 21-Oct-07 16:38
Common Gotcha’s with GetLastError() Doug Schmidt 3-Jan-06 6:07
The article above is good, but readers should be cautioned not to embed a call to GetLastError() as an argument to another function. Resist the temptation to do everything in one line.

The only 100% reliable way of calling GetLastError() is on a line by itself, immediately after the WIN32 API call.

The reasoning is that GetLastError() retrieves the last error encountered by the last Win32 API call on each thread. Some C++ class contructors (like _bstr_t ) will make WIN32 API calls to allocate resources, and if the arguments to the error formatting function use these objects, GetLastError() may actually return 0 (SUCCESS).

So if your app formats every error with «ERROR DETECTED: %s», then you run the risk of displaying the embarassing «ERROR DETECTED: No error detected.», since «No error detected» is the message string for SUCCESS.

The rather large I am working on has hit this problem many times, and usually it is because a temporary string was construtcted in between the Win32 API call and the call to GetLastError() .

So you should prefer:

over this more terse equivalent of

Cheers,
Doug

Sign In· View Thread
Re: Common Gotcha’s with GetLastError() Brian C Hart 3-Jan-06 8:48

The above article is good, but readers should be cautioned not to embed a call to GetLastError() as an argument to another function. Resist the temptation to do everything in one line.

The only 100% reliable way of calling GetLastError() is on a line by itself, immediately after the WIN32 API call.

I concur that one should be careful; however, I have used this approach in multithreaded programs, with the _com_error class only, and have not had any problems. I assume that _com_error is somewhat thread-safe since it is designed to also be thrown as an exception type.

Sincerely Yours,
Brian Hart
Department of Physics and Astronomy
University of California, Irvine

Sign In· View Thread
Re: Common Gotcha’s with GetLastError() Karim AM 16-Jan-06 16:05

I disagree with Doug Schmidt on this one — I’d expect you to be fine passing GetLastError() on its own into a function call or constructor, as the first thing that will happen is that GetLastError()’s return result will be assigned (and therefore fixed) into the function/constructor parameter.

I would however be careful with macros, or fns/c’tors that have multiple parameters, where AFAIK it’s undefined in what order the parameters will be evaluated.

Karim

Sign In· View Thread
Nice article. However. W. Kleinschmit 1-Jan-06 22:40
Quote:
. to determine if it is anything other than the ‘success value,’ which is S_OK. .

Sorry, but this is wrong. By definition every negative HRESULT is an error. So the FAILED(. ) macro only tests for bit 31 of the HRESULT set.
There are other possible return values (S_FALSE for example), that are sometimes used to indicate special situations but are not considered an error.
FAILED(S_FALSE) returns FALSE.

Anyway. Your article is one of the more usefull ones I have seen on CodeProject lately.

Hang on.

Sign In· View Thread
Re: Nice article. However. Brian C Hart 2-Jan-06 9:23

Sorry, but this is wrong. By definition every negative HRESULT is an error. So the FAILED(. ) macro only tests for bit 31 of the HRESULT set.
There are other possible return values (S_FALSE for example), that are sometimes used to indicate special situations but are not considered an error.
FAILED(S_FALSE) returns FALSE.

I stand corrected.

Sincerely Yours,
Brian Hart
Department of Physics and Astronomy
University of California, Irvine

Sign In· View Thread
How do you correct this AlexEvans 2-Jan-06 15:33

Nice work Brian

Do intend to post a fix? Or maybe a way to fix this issue?

Thanks for sharing
Alex

Sign In· View Thread
Re: How do you correct this Brian C Hart 2-Jan-06 15:39

As you can see, I have revised the article. This invites readers to look up FAILED and SUCCEEDED macros in the docs. The way you check for whether your COM calls have succeeded or fails really depends on your particular application.

Sincerely Yours,
Brian Hart
Department of Physics and Astronomy
University of California, Irvine

Sign In· View Thread
Immediate usage Randor 1-Jan-06 16:23
There are some articles I read here on codeproject that have an immediate effect on my active projects, and motivate me to update older projects. This article is one of them.

Many thanks.

Sign In· View Thread
Re: Immediate usage Brian C Hart 1-Jan-06 18:23

I am humbled by your compliment. Thank you.

Sincerely Yours,
Brian Hart
Department of Physics and Astronomy
University of California, Irvine

Sign In· View Thread
Re: Immediate usage JPaulson 3-Jan-06 8:39

I agree. I immediately went through and updated the project I was working on as well. This is why I subscribe to the CodeProject newsletter.

Sign In· View Thread
Two words for you Rob Manderson 1-Jan-06 10:33
Bloody brilliant!

And exactly what I needed for a COM object I’m writing. You got my 5.

I’m working on a version for Visual Lisp++

My blog http://blogs.wdevs.com/ultramaroon/[^]

Sign In· View Thread
Re: Two words for you Brian C Hart 1-Jan-06 19:50

And exactly what I needed for a COM object I’m writing. You got my 5.

Such a pleasure to receive such kind words. Thank you. Happy New Year!

Sincerely Yours,
Brian Hart
Department of Physics and Astronomy
University of California, Irvine

Sign In· View Thread
Last Visit: 31-Dec-99 19:00 Last Update: 15-Jan-23 23:43 Refresh 1 2 Next ᐅ

General News Suggestion Question Bug Answer Joke Praise Rant Admin

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Источник

замечания

Каждый поток будет иметь свой последний код ошибки. Windows API установит последний код ошибки в вызывающем потоке.

Вы всегда должны вызывать GetLastError сразу после проверки возвращаемого значения функции API Windows.

Большинство функций Windows API устанавливают последний код ошибки, когда они терпят неудачу. Некоторые также установят последний код ошибки, когда они преуспеют. Существует ряд функций, которые не устанавливают последний код ошибки. Всегда обращайтесь к документации по функциям Windows API.

Невозможно использовать FORMAT_MESSAGE_FROM_SYSTEM без FORMAT_MESSAGE_IGNORE_INSERTS при использовании функции FormatMessage для получения описания кода ошибки.

Вступление

API Windows предоставляется с помощью C-вызываемого интерфейса. Успех или сбой вызова API сообщаются строго через возвращаемые значения. Исключения не являются частью документированного контракта (хотя некоторые реализации API могут вызывать исключения SEH , например, при передаче аргумента lpCommandLine только для чтения в CreateProcess ).

Сообщение об ошибке грубо относится к одной из четырех категорий:

  • Только возвращаемое значение
  • Возвращаемое значение с дополнительной информацией о сбое
  • Возвращаемое значение с дополнительной информацией об отказе и успехе
  • Возвращаемое значение HRESULT

Документация для каждого вызова 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 . (Если у вас есть официальный SDK от Microsoft для Windows 8 или новее, это находится в shared папке с папкой include.)

Заметки о вызове GetLastError() на других языках программирования

.net (C #, VB и т. д.)

С .net вы не должны P / Invoke в GetLastError() напрямую. Это связано с тем, что среда выполнения .net сделает другие вызовы Windows API одним и тем же потоком за вашей спиной. Например, сборщик мусора может вызвать VirtualFree() если он найдет достаточно памяти, которую он больше не использует, и это может произойти между вашим назначенным вызовом функции и вашим вызовом GetLastError() .

Вместо этого .net предоставляет Marshal.GetLastWin32Error() , которая будет извлекать последнюю ошибку из последнего вызова P / Invoke, который вы сами сделали. Используйте это вместо прямого вызова GetLastError() .

(.net, похоже, не мешает вам импортировать GetLastError() любом случае, я не уверен, почему.)

Идти

Различные средства, предоставляемые Go для вызова DLL-функций (которые находятся как в syscall пакета, syscall и в пакете golang.org/x/sys/windows ), возвращают три значения: r1 , r2 и err . r2 никогда не используется; вы можете использовать пустой идентификатор. r1 — возвращаемое значение функции. err является результатом вызова GetLastError() но преобразуется в тип, реализующий error , поэтому вы можете передать его вызывающим функциям для обработки.

Поскольку Go не знает, когда вызывать GetLastError() а когда нет, он всегда будет возвращать ошибку nil . Поэтому типичная идиома обработки ошибок Go

r1, _, err := syscall.Syscall12(CreateWindowW.Addr(), ...)
if err != nil {
    // handle err
}
// use r1

не будет работать. Вместо этого вы должны проверить r1 точно так же, как и на C, и использовать только 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 s — числовые 32-битные значения, где биты или диапазоны бит кодируют четко определенную информацию. MSB — это флаг отказа / успеха, а остальные бит хранят дополнительную информацию. Отказ или успех можно определить с помощью макросов FAILED или SUCCEEDED . HRESULT s обычно используются совместно с 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 . Остальная часть кода может оставаться неизменной.

Опубликовано: 10 августа 2017

Исправлено: 10 августа 2017

Версия документа: 1.0

Когда ты вызываешь функцию Windows, она проверяет переданные ей параметры, а затем пытается выполнить свою работу. Если ты передал недопустимый параметр или если данную операцию нельзя выполнить по какой‐то другой причине, она возвращает значение, свидетельствующее об ошибке.

Типы данных для возвращаемых значений

Большинство функций Windows возвращают следующие типы:

Процедуры:
Подпрограммы‐процедуры Sub не возвращают значений. Такие функции всегда (или почти всегда) выполняется успешно, хотя их количество в Windows очень мало. Пример: функция ExitProcess.
BOOL или Boolean:
Если вызов функции оканчивается неудачей, то возвращается ложь False (она же 0), в остальных случаях возвращается любое другое число, отличное от нуля. Однако не пытайся сравнить это число с True, лучше просто сравнивать с нулём.
HANDLE:
Если вызов функции оканчивается неудачей, то обычно возвращается NULL, что эквивалентно нулю, в остальных случаях возвращаемое значение идентифицирует объект, которым ты можешь манипулировать. Однако некоторые функции вместо NULL в случае ошибки возвращают константу INVALID_HANDLE_VALUE, например, функция CreateFile. В документации для каждой функции чётко указано, что именно она возвращает при ошибке: NULL или INVALID_HANDLE_VALUE.
PVOID или Any Ptr:
Если вызов функции оканчивается неудачей, то возвращается NULL, в остальных случаях PVOID сообщает адрес блока данных в памяти.
HRESULT:
Если вызов функции оканчивается неудачей, то возвращается ошибочный код HRESULT, в остальных случаях значение говорит об успехе операции. Подробнее о HRESULT →
Integer, Long или DWORD:
Это значение — «крепкий орешек». Функции, которые возвращают значения каких‐либо счётчиков, обычно возвращают Integer, Long или DWORD. Если по какой‐либо причине функция не сумела сосчитать то, что ты хотел, она обычно возвращает 0 или -1, всё зависит от конкретной функции. Лучше всего проверь в документации, каким именно значением функция уведомляет об ошибке.

Почему же произошла ошибка?

При возникновении ошибки необходимо разобраться почему вызов данной функции оказался неудачен. За каждой ошибкой закреплён свой код — 32‐битное целое число.

Функция Windows, обнаружив ошибку, через механизм локальной памяти потока сопоставляет соответствующий код ошибки с вызывающим потоком. Это позволяет потокам работать независимо друг от друга, не вмешиваясь в чужие ошибки. Когда функция вернёт управление, её возвращаемое значение будет указывать на то, что произошла какая‐то ошибка. Какая именно — можно узнать, вызвав функцию GetLastError.

Declare Function GetLastError()As DWORD

Она просто возвращает числовое значение, характеризующее код ошибки.

Список кодов ошибок лежит в заголовочной файле winwinerror.bi. Здесь приведена его небольшая часть, чтобы примерно представлять, на что он похож:

Const ERROR_SUCCESS = 0
Const NO_ERROR = 0
Const ERROR_INVALID_FUNCTION = 1
Const ERROR_FILE_NOT_FOUND = 2
Const ERROR_PATH_NOT_FOUND = 3
Const ERROR_TOO_MANY_OPEN_FILES = 4
Const ERROR_ACCESS_DENIED = 5
Const ERROR_INVALID_HANDLE = 6

Функцию GetLastError необходимо вызывать сразу же после неудачного вызова функции Windows, иначе код ошибки может быть потерян.

Некоторые функции Windows всегда завершаются успешно, но по разным причинам. Например, попытка создать объект ядра «событие» с определённым именем может быть успешна потому, что оно действительно создано, либо потому, что такой объект уже существует. Но иногда нужно знать причину успеха. Для возврата этой информации корпорация Microsoft предпочла использовать механизм установки кода последней ошибки. Так что и при успешном выполнении некоторых функций ты можешь использовать GetLastError и получать дополнительную информацию. К таким функциям относится, например, CreateEvent.

Ты наверняка спросишь, составит ли корпорация Microsoft полный список всех кодов ошибок, возможных в каждой функции. Ответ: нет. Такого списка никогда не будет, уж слишком сложно его составлять и поддерживать для всё новых и новых версий системы.

Проблема с подобным списком ещё и в том, что ты вызываешь одну функцию, а она может обратиться к другой, та — к следующей и так далее. Любая из этих функций может завершиться неудачно и по самым разным причинам. Иногда функция более высокого уровня сама справляется с ошибкой в одной из вызванных ею функций, и в конечном счёте выполняет то, что от неё хотели. В общем, для создания такого списка пришлось бы проследить цепочки вызовов в каждой функции, что очень трудно. А с появлением новой версии операционной системы цепочки вызовов пришлось бы пересматривать заново.

Определение собственных кодов ошибок

Механизм установки кода ошибки можно использовать и в собственных функциях. Предположим, ты пишешь библиотечную функцию, к которой будут обращаться другие части программы или вообще другие программы. Вызов этой функции по каким‐либо причинам может оказаться неудачным и тебе придётся тоже сообщать об этом. С этой целью ты просто устанавливаешь код последней ошибки в потоке и возвращаешь значение False, INVALID_HANDLE_VALUE, NULL или что‐то другое, более подходящее в твоём случае.

SetLastError

Чтобы установить код последней ошибки вызывай функцию SetLastError и передай ей нужной число.

Declare Sub SetLastError( _
    ByVal dwErrorCode As DWORD _
)

Можно использовать коды ошибок, уже определённые в winerror.bi, если они подходят. Если ты считаешь, что ни один из кодов ошибок из winerror.bi не годится для ошибки, возможной в твоей функции, можно определить свой код.

Формат кода ошибки

Код ошибки представляет 32‐битное беззнаковое число, которое разбито на поля:

Биты числа 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Описание Степень тяжести Кем определён Зарезервировано Подсистема (facility code) Код ошибки
Биты 30 и 31:
Представляют собой степень тяжести ошибки. В двоичном виде: 00 — успех, 01 — информация, 10 — предупреждение, 11 — ошибка.
Бит 29:
Корпорация Microsoft обещала, что никогда не будет его устанавливать. Следовательно, если ты определяешь собственный код ошибки, то установи этот бит в 1 для гарантии, что твой код ошибки не будет конфликтовать с кодами, определёнными Microsoft.
Бит 28:
Зарезервирован. Должен быть 0.
Биты с 16 по 27:
Код подсистемы (facility code). Определяется корпорацией Microsoft. Указывает на компонент операционной системы, вызвавший ошибку.
Биты с 0 по 15:
Код ошибки. Определяется корпорацией Microsoft или пользователем.

Подробнее об этих полях будет рассказано в следующих статьях. На данный момент единственное важное для тебя поле — это бит 29. Чтобы гарантировать непересекаемость кодов ошибок от Microsoft, установи его в 1. В переводе на числа это означает, что твой код ошибки должен быть больше, чем &h20000000 или 536870912 в десятичном виде.

Получение описания ошибки

Для получения текстового описания ошибки подойдёт функция FormatMessage. Использовать её можно так:

#include "windows.bi"

Const BufferSize As Integer = 4096 - 1

' Строка с ошибкой
Dim ErrorMessage As WString * (BufferSize + 1) = Any

' Вызов функции с неправильным параметром
GetProcessId(NULL)

' Получить код ошибки
Dim dwError As DWORD = GetLastError()

' Получить строку по коду ошибки
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), @ErrorMessage, BufferSize, NULL)

' Выводим описание ошибки на консоль
Print ErrorMessage

Понравилась статья? Поделить с друзьями:
  • Win32 api error 193 lightroom
  • Win32 api error 126 не найден указанный модуль lightroom windows 10
  • Win не удается установить необходимые файлы код ошибки
  • Win не удается подключиться к принтеру ошибка 0х00000011b
  • Win xp ошибка отложенной записи