为什么GetLastError()会返回0或2,具体取决于它的调用方式?

Ali*_*Ali 5 c c++ winapi mingw exception

我正在使用mingw g ++ 4.6.1和-O0,WinXP SP2.

最小的工作示例在这里.

g ++配置了--disable-sjlj-exceptions --with-dwarf2.

GetLastError() 返回0或2,取决于抛出异常的方式:

throw runtime_error(error_message());
Run Code Online (Sandbox Code Playgroud)

伪造"错误代码:0"打印,并且

const string msg = error_message();

throw runtime_error(msg);
Run Code Online (Sandbox Code Playgroud)

按预期打印"错误代码:2".

首先,我认为GetLastError()调用了两次,但调试显示它正如预期的那样被调用一次.

到底是怎么回事?

Gre*_*ill 10

有可能throw在某个地方设置调用Win32 API函数的代码将Last-Error值重置为0.这可能您调用之前发生error_message().

呼叫GetLastError()没有最后的误差值自动重置为0,因此它是安全地调用两次.

编译器/运行时是否生成调用Win32 API函数的代码将由您的特定运行时决定.为了安全而不依赖于此,请使用双语句版本:

const string msg = error_message();
throw runtime_error(msg);
Run Code Online (Sandbox Code Playgroud)

更好的是,对于未来的代码读者,在GetLastError()外面打电话会很有用error_message():

const string msg = error_message(GetLastError());
throw runtime_error(msg);
Run Code Online (Sandbox Code Playgroud)

这样,读者将GetLastError()在相应的Win32 API调用之后立即看到该调用.


Mic*_*urr 8

如果你看一下生成的汇编代码,就会清楚发生了什么.以下C++代码:

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

if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive  
{                                                              
    throw runtime_error(error_message());
}
Run Code Online (Sandbox Code Playgroud)

生成一段汇编代码(至少使用默认优化):

    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  #
Run Code Online (Sandbox Code Playgroud)

您会看到为调用___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);
}
Run Code Online (Sandbox Code Playgroud)

然后你得到以下生成的程序集:

    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
Run Code Online (Sandbox Code Playgroud)

在失败的CreateFile()呼叫和呼叫之间不调用外部功能error_message().

这种问题是使用某些全局状态(例如GetLastError()或)进行错误处理的主要问题之一errno.