为什么std::string构造函数正在重置GetLastError < >我用C++代码调用Windows APIs,我有一个帮助方法来处理格式消息> /Cuth>,并抛出异常来处理错误。函数的签名是 void throw_system_error(const std::string& msg_prefix, DWORD messageid)

为什么std::string构造函数正在重置GetLastError < >我用C++代码调用Windows APIs,我有一个帮助方法来处理格式消息> /Cuth>,并抛出异常来处理错误。函数的签名是 void throw_system_error(const std::string& msg_prefix, DWORD messageid),c++,winapi,error-handling,C++,Winapi,Error Handling,我注意到了一些奇怪的事情。此代码无法正常工作: handle = ...; if (handle == NULL) { throw_system_error("something bad happened", GetLastError()); } 传递给throw\u system\u error的错误代码始终为零 这样做很好: handle = ...; if (handle == NULL) { DWORD error = GetLastError(); throw

我注意到了一些奇怪的事情。此代码无法正常工作:

handle = ...;
if (handle == NULL) {
    throw_system_error("something bad happened", GetLastError());
}
传递给
throw\u system\u error
的错误代码始终为零

这样做很好:

handle = ...;
if (handle == NULL) {
    DWORD error = GetLastError();
    throw_system_error("something bad happened", error);
}
进一步调查表明,该版本存在相同的问题:

handle = ...;
if (handle == NULL) {
    std::string msg("something bad happened");
    DWORD error = GetLastError();
    throw_system_error(msg, error);
}
它看起来像是
std::string
的构造函数正在重置错误代码

我猜想,
std::string
正在内部分配内存,这会导致一些系统调用,然后将最后一个错误设置回零

有人知道这里到底发生了什么吗

VisualC++ 2015,64位.< /P> < P>这是正常的,“最后一个错误”可以通过任何函数调用间接设置。 某些函数在成功时将其设置为“无错误”,因此如果您想可靠地使用它,则需要在执行任何其他操作之前立即存储它


如果您遇到过“发生严重错误:操作成功完成”对话框,这可能就是原因。

请参阅

文档中用于设置最后一个错误代码的每个函数的返回值部分会记录函数设置最后一个错误代码的条件。大多数设置线程最后一个错误代码的函数在失败时设置它但是,有些函数在成功时也会设置最后一个错误代码。如果没有记录该函数以设置最后一个错误代码,则该函数返回的值仅为最近设置的最后一个错误代码某些函数在成功时将最后一个错误代码设置为0,而其他函数则没有设置。

在构造
std::string
的过程中,会调用
SetLastError
。windows上的标准库使用Win32调用作为其实现的一部分

第二种方法(有效)是使用
GetLastError

当函数的返回值指示此类调用将返回有用数据时,应立即调用GetLastError函数。这是因为某些函数在成功调用SetLastError时,会使用零来清除最近失败的函数设置的错误代码

让我们看看文档:

大多数设置线程最后一个错误代码的函数在 失败。但是,某些函数在运行时也会设置最后一个错误代码 成功

当发生错误时,应立即调用GetLastError函数 函数的返回值表示这样的调用将返回有用的 数据。这是因为有些函数调用SetLastError时使用零 当它们成功时,将清除最近 失败的函数

因此有一个函数调用SetLastError,很可能是一个分配内存的函数: 构造字符串时,将调用
new
来分配内存

现在,让我们看看
new
在vc++中的实现。在堆栈溢出中有一个很好的答案:

这取决于您是处于调试模式还是发布模式。在发布模式下,有HeapAlloc/HeapFree,它们是内核函数

在调试模式下(使用visual studio),有一个手写的 免费和malloc版本(新/删除重新定向到该版本) 线程锁和更多异常检测,以便您可以检测 当堆指针出现错误时,更容易 在调试模式下运行代码

因此,在释放模式下,调用的函数是,它不调用SetLastError。从文件中:

如果函数失败,它不会调用SetLastError

因此,代码应该在发布模式下正常工作。 但是,在调试实现中,调用函数FlsGetValue,该函数在成功时调用SetLastError

这很容易检查

#包括
#包括
int main(){
DWORD t=FlsAlloc(空PTR);
SetLastError(23);//将error设置为23
DWORD error1=GetLastError();//存储错误
FlsGetValue(t);//如果成功,它将把error设置为0
DWORD error2=GetLastError();//存储第二个错误代码

std::cout是的,我可以猜到那么多。但是在我所知道的std::string文档中,没有任何地方注意到设置最后一个错误代码的条件,所以根据您引用的文档,它确实不应该这样做。另外,分配内存可能会通过HeapAlloc,它明确表示不调用SetLastError.@Stefan
HeapAlloc
的文档没有这么说。你能画的只是“应用程序不能调用
GetLastError
来获取扩展的错误信息。”@Stefan我同意,关于设置
GetLastError
的文档不是很好。不过,文档确实考虑到了这一点(第二个引号)。您不应该依赖于它被记录,并且应该在需要时立即调用
GetLastError
,因为任何东西都可以设置它。@Stefan:“但是在我所知道的std::string的文档中,它没有注意到设置最后一个错误代码的条件”它不需要,因为C++标准库从不记录实现细节。而且也不需要,因为文档的内容很清楚:“当函数返回值指示调用将返回有用数据时,您应该立即调用GETLASTError函数。”不是任何函数。只有记录此行为的函数。请参阅Ics的答案。关于错误代码,文档通常是错误的。仅仅因为函数没有记录它设置了错误代码,并不保证它无论如何不会。MS说“如果函数没有记录为设置最后一个错误代码,则返回值
23
0
#include <iostream>
#include <Windows.h>
int main() {

    DWORD t = FlsAlloc(nullptr);
    SetLastError(23); //Set error to 23
    DWORD error1 = GetLastError(); //store error

    int* test = new int; //allocate int

    DWORD error2 = GetLastError(); //store second error code

    std::cout << error1 << std::endl; //output errors
    std::cout << error2 << std::endl;

    delete test; //free allocated memory
    system("PAUSE");
    return 0;
}
23
0
23
23