Если вы посмотрите на сгенерированный код сборки, станет ясно, что происходит.Следующий код 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
.