为什么GetLastError()根据调用方式返回0或2 ?
Why does GetLastError() return 0 or 2 depending on how it is called?
我使用的是mingw++ 4.6.1 with - 0, WinXP SP2.
这里是最小的工作示例。
g++配置了——disable-sjlj-exceptions——with-dwarf2.
GetLastError()
返回0或2,具体取决于异常的抛出方式:
throw runtime_error(error_message());
输出bogus "error code: 0",
const string msg = error_message();
throw runtime_error(msg);
按预期打印"error code: 2"。
首先,我认为GetLastError()
被调用了两次,但调试显示它只被调用了一次,正如预期的那样。
怎么回事?
设置throw
的代码可能在自身内部某处调用Win32 API函数,将Last-Error值重置为0。这可能在调用error_message()
之前发生。
调用GetLastError()
会使不自动将Last-Error值重置为0,因此可以安全地调用两次。
你的编译器/运行时是否生成调用Win32 API函数的代码将取决于你的特定运行时。为了安全且不依赖于此,请使用双语句版本:
const string msg = error_message();
throw runtime_error(msg);
更好的是,对于将来的代码读者来说,在error_message()
之外调用GetLastError()
会很有用:
const string msg = error_message(GetLastError());
throw runtime_error(msg);
这样,读者将在相应的Win32 API调用之后立即看到GetLastError()
调用,它属于哪里。
如果查看生成的汇编代码,就会清楚发生了什么。下面的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
等全局状态进行错误处理的主要问题之一。
- 如何在C++中以这种方式返回一行文本?
- 以最佳方式返回复制的值
- 返回带或不带 *this 的变量的确切方式不同 |这不是关于*此指针|正确性
- 返回二进制数据的通用方式,而无需原始指针
- 程序流并以安全而雄辩的方式正确返回Main()
- 有没有办法在函数以编程方式返回的引用上多次调用函数? -元组动态访问
- 拥有 std::map 的最佳方式,我可以在其中定义如果没有键时返回的内容
- 在C 中以通用方式构建对象,设置字段并返回对象
- 如何在静态单例类中以编程方式从exec方法返回
- 哪种方式将 HHOOK 消息返回到系统更好
- 返回已过滤集合的便捷方式
- 断言返回值,但以任一方式运行
- 以这种方式返回对新创建的对象的引用,这是一种安全的做法
- 返回指针的最佳方式
- 我可以以这种方式返回迭代器吗?
- 设计一个以统一方式返回多个接口的类
- 为什么不能以与指针相同的方式返回常量引用
- 如何使用返回的临时链接的最佳方式
- 使用c++从函数返回图像或视频文件的最佳方式是什么
- 返回对大小不变的指针数组的引用的最佳方式是什么