C ++ / CLI和GetLastError

时间:2015-08-07 08:05:59

标签: visual-c++ c++-cli

我在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会起作用。

1 个答案:

答案 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()的包装函数,然后在必要时抛出异常。