我有一组用于Win32API和MFC的异常类,它们捕获当前的Win32错误代码(GetLastError()
)并制定一个人类可读的消息,说明它发生的位置。
我依赖于在ctor开始工作之前捕获当前错误代码的能力,假设必须在调用ctor之前解析ctor的参数。
但是我在当前构建中遇到了问题,在VS2013中将编译工具从120_xp切换到120(我不是100%确定这是变更的来源 - 它可能已经休眠了一段时间无关到平台工具集的变化)。
但是,我认为这些都不相关 - C ++会要求首先解析所有参数,然后执行函数调用,以便下面的默认参数error
始终具有当前值调用ctor之前的错误代码(可能会改变它):
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)
{
}
这是调用上下文:
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());
如果我更改调用上下文以强制捕获当前错误代码,我实际上会得到错误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
很简单,它保留了当前的错误代码:
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;
}
我遇到的问题是我看到错误122:提供给系统调用的缓冲区太小。但是,在调用此异常时的错误代码是错误2:找不到文件。
我更希望我的软件报告“找不到文件”,这更加正确且可操作。
所以:
1.我错了C ++,保证在调用函数调用(ctor)之前解析所有参数?
2.还有哪些调用可能会改变当前的错误代码(CString ctor(s)是在CWin32APIErrorException::CWin32APIErrorException()
ctor之前调用的唯一其他内容。
这意味着CString ctor显然正在改变当前的错误?!啊!
有人能让我知道我的理解错在哪里吗?
谢谢!
答案 0 :(得分:1)
我将把它留在这里,以防它有其他类似的问题:
就我上面的情况而言,我正在编译UNICODE,我的CWin32APIErrorException::CWin32APIErrorException(const CString & source, ...
正在使用throw CWin32APIErrorException(__FUNCTION__, ...
进行调用,然后调用CStringW(const char *)
构造函数,内部调用MultiByteToWideChar()
使用_MBCS
第一次调用null缓冲区以获取所需缓冲区大小的模式,然后第二次调用缓冲区分配给该大小。
因此,第一个调用总是将当前错误设置为122:缓冲区太小。
因此我所看到的行为。
我们在编译__FUNCTION__
时没有看到此问题,因为CStringA(const char *)
字面值未在内部转换 - CString
未覆盖当前错误代码,因为无需转换
可能在未来的任何时候CWin32APIErrorException
构造函数可能会因任何其他原因而随意改变当前错误的副作用......因此让if (!SomeWinAPIFunction(...))
{
auto error = GetLastError(); // make sure we report the correct error!
throw CWin32APIErrorException(__FUNCTION__, "SomeWinAPIFunction", error);
}
采取任何形式构造对象类型意味着它本身很容易得到错误的错误代码,除非调用者确保首先捕获它并传递捕获的值:
CWin32APIErrorException
可以更改const char *
ctors以const wchar_t *
和单独 if (!::DestroyAcceleratorTable(m_hAccel))
if (!m_hAccel)
{
auto error = GetLastError();
throw CWinAPIErrorException(__FUNCTION__, "DestroyAcceleratorTable", FormatString(_T("%X"), m_hAccel), error);
}
,以避免在捕获当前错误代码之前进行CString构造。
这种方法的问题在于排列快速加起来(窄,窄),(窄,宽),(宽,窄),(宽,宽)。这只是两个论点。我的实际课程有三个(参数中有一个可选)。
最后一个最好留作CString,因为它很可能在运行时动态创建,在编译时不知道,与前两个参数不同。因此很可能会出现更改当前错误代码的可能性,即使ctor接口需要简单的char *或wchar_t * ...这意味着此问题仍未解决。
最终,在以有保证的方式捕获错误报告所需的其他参数之前,我无法想出捕获当前错误的方法 - 所以我只是将接口更改为使用显式参数(no默认值)强迫我查看所有代码,并确保我们在调用站点捕获正确的错误代码并传递它。
例如:
PATH