I'm calling Windows APIs from C++ code and I have a helper method to do the FormatMessage
stuff and throw an exception for error handling. The signature of the function is
void throw_system_error(const std::string& msg_prefix, DWORD messageid)
I've noticed something strange. This code does not work properly:
handle = ...;
if (handle == NULL) {
throw_system_error("something bad happened", GetLastError());
}
The error code that is passed to throw_system_error
is always zero.
Where as this works just fine:
handle = ...;
if (handle == NULL) {
DWORD error = GetLastError();
throw_system_error("something bad happened", error);
}
Some more investigation showed that this version has the same problem:
handle = ...;
if (handle == NULL) {
std::string msg("something bad happened");
DWORD error = GetLastError();
throw_system_error(msg, error);
}
It looks for all the world as if the constructor of std::string
is resetting the error code.
My guess would be that std::string
is allocating memory internally which causes some system call that then sets the last error back to zero.
Anyone knows what is actually going on here?
Visual C++ 2015, 64bit.
Let's see the GetLastError documentation:
So there is one function calling SetLastError, very likely one that allocates memory: When you construct a string,
new
is called to allocate memory.Now, let's see
new
's implementation in vc++. There is a very good answer to that question in Stack Overflow : https://softwareengineering.stackexchange.com/a/293209So in release mode, the function called is HeapAlloc, which does NOT call SetLastError. From the documentation:
So the code should work properly in release mode. However, in the debug implementation, the function FlsGetValue is called, and that function calls SetLastError when succeeded.
It's very easy to check this,
It outputs the following:
So FlsGetValue has called SetLastError(). To prove that it is called only on debug we can do the following test:
If you run it in debug mode, it will give you, because it calls FlsGetValue:
However, if you run it in release mode, it produces, because it calls HeapAlloc:
Per the documentation for GetLastError
At some point during the construction of
std::string
,SetLastError
is called. The standard library on windows uses Win32 calls as part of its implementation.Your second method (that works) is the correct way to use
GetLastError
This is normal - the "last error" can be set indirectly through any function call.
Some functions set it to "no error" on success, so if you want to use it reliably you need to store it immediately before you do anything else.
If you've ever encountered an "A serious error occurred: The operation completed successfully" dialogue, this is probably the reason.