我在Visual Studio 2010中为我的C ++库创建了一个C ++测试项目。测试项目使用C ++ / CLI(/ clr set),我在检索库函数的最后一个错误集时遇到问题。 GetLastError始终返回零。
在下面的例子中,我想测试我的Write函数设置了正确的返回值和最后一个错误:
[TestMethod]
void Write_InvalidHandle_Error()
{
char buffer[] = "Hello";
DWORD actual = -1;
DWORD expected = ERROR_INVALID_HANDLE;
int actualRetVal = 0;
int expectedRetVal = -1;
HANDLE handle = INVALID_HANDLE_VALUE;
actualRetVal = Write(handle, buffer);
actual = GetLastError();
Assert::AreEqual(expectedRetVal, actualRetVal);
Assert::AreEqual(expected, actual);
}
我已经检查了我的Write函数,它确实设置了正确的返回值和最后一个错误,但后者在我的测试方法中没有检索到。即使我将Write函数更改为仅设置错误并返回问题(并且在我的测试方法中调用GetLastError之前我没有调用其他函数):
int Write(HANDLE h, const char* buf)
{
SetLastError(ERROR_INVALID_HANDLE);
return -1;
}
知道如何解决这个问题吗?我假设C ++ / CLI存在问题,因为当我在此测试场景之外使用我的库(纯C ++)时,GetLastError会起作用。
答案 0 :(得分:2)
在托管/非托管边界上依赖GetLastError()/ SetLastError()是有问题的。
使用P / Invoke和DllImport attribute时,您可以(必须)设置SetLastError
属性以访问托管端的本机错误代码。
但是,在使用C ++ / CLI时,编译器会为您处理所有编组,并且显式不会设置该标记。
您可以在this blog post中阅读有关它的更多详情。它的要点是:
如果在C ++中显式使用DllImport,则适用相同的规则 C#。但是,当您直接从托管C ++代码调用非托管API时, GetLastError和Marshal.GetLastWin32Error都不会可靠地运行。
这将在" Expert Visual C ++ / CLI"的第9章中详细介绍。作者:Marcus Heege is available on Google Books:
如前所述,对于这些本机本地函数,C ++ / CLI 在没有lasterror标志的情况下自动生成P / Invoke元数据, 因为使用GetLastError值非常罕见 在项目中传达错误代码。但是,MSDN GetLastError上的文档允许您使用SetLastError和 GetLastError用于您自己的函数。因此,这种优化可以 理论上会导致错误的GetLastError值。
基本上,不要这样做!
我建议使用(本机)C ++异常来传递托管代码和非托管代码之间的错误。 C ++ / CLI非常好地支持这些。如果您无法直接修改Write()
函数,则可以在非托管端创建一个使用GetLastError()
的包装函数,然后在必要时抛出异常。