2016-05-31 82 views
2

我有一套在Win32API和MFC上使用的异常类,它们捕获当前的Win32错误代码(GetLastError())并且制定一条关于它发生的位置的人类可读信息。管理参数默认分配的规则是什么?

在ctor开始工作之前,我依赖于捕获当前错误代码的能力,假设在调用ctor之前必须解析ctor的参数。

但是我遇到了当前版本中的问题,在VS2013中将编译工具从120_xp切换到120(我不是100%确定这是更改的来源 - 它可能会暂时搁置一段时间与平台工具集更改无关)。但是,我会认为没有一个是相关的 - 即C++会要求首先解决所有参数,然后执行函数调用,以便下面的默认参数error始终具有当前的错误代码调用构造函数(这可能会改变它):

​​

这里的调用上下文:

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的构造函数(S)是唯一的其他东西CWin32APIErrorException::CWin32APIErrorException()构造函数。

含义是CString的构造函数显然是改变当前错误之前被调用? !唉

有人可以让我知道我错了,在我的理解

谢谢

+4

参数评测的顺序是不确定的,所以是的,CString的构造函数可以让你的方式。 –

+0

谢谢雷蒙德。我刚刚有一个预感 - 事实证明,这是因为我们正在编译unicode - 所以'__FUNCTION__'正在被转换为CString ctor中的一个宽字符串,这引发了一些导致内部错误122的事情。 ( – Mordachai

+0

)这会比较容易修复,因为你将字符串文字传递给了构造函数,所以它不需要'CString'参数,只需使用'LPCTSTR'(使用预处理器将'__FUNCTION__'宏转换为)解决了构造参数时执行任意代码的问题你是否还有其他调用上下文需要传递动态生成的字符串? –

回答

1

我要去的情况下,离开这里了这一点,帮助别人有类似的问题?!

在我上面的例子中,我正在编译UNICODE,我的CWin32APIErrorException::CWin32APIErrorException(const CString & source, ...被调用时使用throw CWin32APIErrorException(__FUNCTION__, ...,然后调用CStringW(const char *)的构造函数,它在内部调用MultiByteToWideChar(),它使用第一次调用null模式获得所需的缓冲区大小,然后再次调用分配给该大小的缓冲区。

第一次调用总是将当前错误设置为122:缓冲区太小。

因此,我看到的行为。

编译时_MBCS,因为__FUNCTION__字面没有被内部转换,我们没有看到这个问题 - CStringA(const char *)没有覆盖当前的错误代码,因为没有转换是必要的。

这可能是因为在将来的任何时间CString构造函数可以任意地具有改变当前错误的副作用,因为任何其他原因...因此具有任何种类的构造对象类型意味着它固有地是容易得到错误的错误代码,除非来电确保先占领它,并通过对所捕获的值:

if (!SomeWinAPIFunction(...)) 
{ 
    auto error = GetLastError(); // make sure we report the correct error! 
    throw CWin32APIErrorException(__FUNCTION__, "SomeWinAPIFunction", error); 
} 

这有可能改变CWin32APIErrorException构建函数采取const char *并分别const wchar_t *以避免CString的建设在捕获当前错误代码之前发生。

该方法的问题在于排列快速叠加(窄,窄),(窄,宽),(宽,窄),(宽,宽)。这只是两个参数。我的实际类有三个(一个可选的参数)。

而最后一个最好留作CString,因为它非常可能在运行时动态创建,在编译时不知道,与前两个参数不同。因此,即使ctor接口需要简单的char *或wchar_t * ...,这意味着此问题未得到解决,它很可能会导致更改当前错误代码的可能性。

最终,我无法想出一种方式捕捉错误报告所需的其他参数,并保证能够正常工作 - 所以我只是将接口改为使用显式参数(没有默认值)迫使我浏览我们的所有代码,并确保我们在调用网站捕获了正确的错误代码并将其传递。

例如:

if (!::DestroyAcceleratorTable(m_hAccel)) 
     if (!m_hAccel) 
     { 
      auto error = GetLastError(); 
      throw CWinAPIErrorException(__FUNCTION__, "DestroyAcceleratorTable", FormatString(_T("%X"), m_hAccel), error); 
     } 
+0

一个快速提示:我可以使用'_T(__ FUNCTION __)'来避免' CString'转换ctor,并且它会和MBCS编译模型一样工作(也很糟糕)。然而,我认为现在是迫使呼叫者认识到这个问题的更深层次的时候了(尽管如果我想出了一个万无一失的机制,我会把它全部隐藏起来)。 – Mordachai