我正在使用mingw g ++ 4.6.1和-O0,WinXP SP2。
Minimal working example is here.
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()
被调用了两次,但调试显示它正如预期的那样被调用一次。
发生了什么事?
答案 0 :(得分:10)
设置throw
的代码可能会在某处调用Win32 API函数,将Last-Error值重置为0.这可能发生在之前你的调用到error_message()
。
调用GetLastError()
不会自动将Last-Error值重置为0,因此可以安全地调用两次。
您的编译器/运行时是否生成调用Win32 API函数的代码将由您的特定运行时决定。为了安全而不依赖于此,请使用双语句版本:
const string msg = error_message();
throw runtime_error(msg);
更好的是,对于未来的代码读者,在GetLastError()
之外调用error_message()
会很有用:
const string msg = error_message(GetLastError());
throw runtime_error(msg);
这样,读者会在相应的Win32 API调用之后立即看到GetLastError()
调用。
答案 1 :(得分:8)
如果查看生成的汇编代码,就会清楚发生了什么。以下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
等全局状态进行错误处理的主要问题之一。