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

时间:2011-12-29 18:01:36

标签: c++ c winapi exception mingw

我正在使用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()被调用了两次,但调试显示它正如预期的那样被调用一次。

发生了什么事?

2 个答案:

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