I have a set of exception classes for use on Win32API and MFC which capture the current Win32 error code (GetLastError()
) and formulate a human readable message about where it occurred.
I rely upon the ability to capture the current error code before the ctor begin doing their work, under the assumption that surely the arguments to the ctor must be resolved before the ctor is invoked.
But I'm running into issues in my current build, having switched compilation tooling from 120_xp to 120 in VS2013 (I am not 100% certain that this is the source of the change - it may have lain dormant for some time unrelated to the platform toolset change).
However, I would have thought that none of that is relevant - that C++ would require that all arguments are resolved first, then the function call is executed, so that the default argument error
below would always have the current error code before invoking the ctor (which could potentially change it):
CWin32APIErrorException::CWin32APIErrorException(const CString & source, const CString & api, DWORD error /*= GetLastError()*/)
: CContextException(FormatString(_T("In %s, %s() returned "), source, api), error)
, m_source(source)
, m_api(api)
{
}
Here's the calling context:
m_file = CreateFile(filename, FILE_GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (!m_file) // NOTE: m_file is smart handle and it's operator bool() knows that the invalid file handle is "false"
throw CWin32APIErrorException(__FUNCTION__, _T("CreateFile"), GetLastError());
If I change the calling context to force the capture of the current error code, I do in fact get error 2:
m_file = CreateFile(filename, FILE_GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (!m_file)
{
auto error = GetLastError();
throw CWin32APIErrorException(__FUNCTION__, _T("CreateFile"), error);
}
FormatString
is simple, and it preserves the current error code:
CStringW FormatString(const wchar_t * format, ...)
{
const DWORD dwError = ::GetLastError();
va_list args;
va_start(args, format);
CStringW str;
str.FormatV(format, args);
::SetLastError(dwError);
return str;
}
The problem I'm experiencing is that I'm seeing error 122: buffer supplied to system call is too small. But the error code at the point of invoking this exception is error 2: file not found.
I would far prefer that my software report "file not found" which is more correct and actionable.
So:
1. Am I wrong about C++ guaranteeing that all arguments are resolved before the function call (the ctor) is invoked?
2. Where else could calls be getting made that could change the current error code (CString ctor(s) are the only other things being invoked before the CWin32APIErrorException::CWin32APIErrorException()
ctor.
Meaning that CString ctor is apparently changing the current error?! ugh!
Can someone let me know where I'm wrong in my understanding?
Thank you!