为什么GetLastError()根据调用方式返回0或2 ?

Why does GetLastError() return 0 or 2 depending on how it is called?

本文关键字:返回 方式 调用 GetLastError 为什么      更新时间:2023-10-16

我使用的是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等全局状态进行错误处理的主要问题之一。