Почему GetLastError () возвращает 0 или 2 в зависимости от того, как он вызывается? - PullRequest
5 голосов
/ 29 декабря 2011

Я использую mingw g ++ 4.6.1 с -O0, WinXP SP2.

Здесь приведен минимальный рабочий пример.

g ++ настроен с --disable-sjlj-exceptions --with-dwarf2.

GetLastError() возвращает 0 или 2 в зависимости от того, как сгенерировано исключение:

throw runtime_error(error_message());

фальшивый "код ошибки: 0" напечатан, и

const string msg = error_message();

throw runtime_error(msg);

печатает "код ошибки: 2", как и ожидалось.

Сначала я думал, что GetLastError() вызывается дважды, но отладка показывает, что он вызывается ровно один раз, как и ожидалось.

Что происходит?

Ответы [ 2 ]

10 голосов
/ 29 декабря 2011

Возможно, что код, который устанавливает throw, вызывает где-то внутри себя функцию Win32 API, которая сбрасывает значение Last-Error в 0. Это может происходить до вашего вызова error_message()*код, который вызывает функцию Win32 API, будет зависеть от вашей конкретной среды выполнения.Чтобы быть в безопасности и не зависеть от этого, используйте версию с двумя утверждениями:

const string msg = error_message();
throw runtime_error(msg);

Еще лучше, для будущих читателей вашего кода было бы полезно вызвать GetLastError() за пределами error_message():

const string msg = error_message(GetLastError());
throw runtime_error(msg);

Таким образом, читатели увидят вызов GetLastError() сразу после соответствующего вызова Win32 API, где он принадлежит.

8 голосов
/ 29 декабря 2011

Если вы посмотрите на сгенерированный код сборки, станет ясно, что происходит.Следующий код C ++:

hDevice = CreateFileA(path, // drive to open
    // etc...
    );

if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive  
{                                                              
    throw runtime_error(error_message());
}

Генерирует фрагмент кода сборки (по крайней мере, с использованием оптимизации по умолчанию):

    call    _CreateFileA@28  #
LEHE4:
    sub esp, 28  #,
    mov DWORD PTR [ebp-12], eax  # hDevice, D.51673
    cmp DWORD PTR [ebp-12], -1   # hDevice,
    jne L5   #,
    mov DWORD PTR [esp], 8   #,

    call    ___cxa_allocate_exception    # // <--- this call is made between the 
                                             # //    CreateFile() call and the 
                                             # //    error_message() call

    mov ebx, eax     # D.50764,
    lea eax, [ebp-16]    # tmp66,
    mov DWORD PTR [esp], eax     #, tmp66
LEHB5:
    call    __Z13error_messagev  #

Вы видите вызов, сделанный ___cxa_allocate_exception для выделения некоторой памятиблок для исключения бросается.Этот вызов функции изменяет состояние GetLastError().

Когда код C ++ выглядит следующим образом:

hDevice = CreateFileA(path, // drive to open
    // etc...
    );

if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive  
{                                                              
    const string msg = error_message();

    throw runtime_error(msg);
}

Тогда вы получите следующую сгенерированную сборку:

    call    _CreateFileA@28  #
    sub esp, 28  #,
    mov DWORD PTR [ebp-12], eax  # hDevice, D.51674
    cmp DWORD PTR [ebp-12], -1   # hDevice,
    jne L5   #,
    lea eax, [ebp-16]    # tmp66,
    mov DWORD PTR [esp], eax     #, tmp66
    call    __Z13error_messagev  #
LEHE4:
    sub esp, 4   #,
    mov DWORD PTR [esp], 8   #,

    call    ___cxa_allocate_exception  # // <--- now this happens *after*
                                         //     error_message() has been called

которая не вызывает внешнюю функцию между ошибочным вызовом CreateFile() и вызовом error_message().

Этот тип проблемы является одной из основных проблем при обработке ошибок с использованием некоторого глобального состояния, такого как GetLastError() илиerrno.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...