【问题标题】:DllImport - PreserverSig and SetLastError attributesDllImport - PreserverSig 和 SetLastError 属性
【发布时间】:2010-10-20 07:23:20
【问题描述】:

在 MSDN 上,我找到了两个属性的以下描述:

PreserveSig 将 PreserveSig 字段设置为 true 以直接转换具有 HRESULT 或 retval 值的非托管签名;将其设置为 false 以自动将 HRESULT 或 retval 值转换为异常。默认情况下,PreserveSig 字段为 true。

SetLastError 使调用者能够使用 Marshal.GetLastWin32Error API 函数来确定在执行方法时是否发生错误。在 Visual Basic 中,默认值为 true(这会增加一些开销);在 C# 和 C++ 中,默认值为 false。

我的问题是:这两者如何相互关联?假设我将 PreserveSig 设置为 'false' - 这意味着我应该将 HRESULT 转换为异常 - 如果非托管函数返回指示错误或未发生错误的整数,如何将其转换为异常?

如果我设法使用 PreserveSig 提取异常,为什么还需要调用 GetLastWin32Error 方法?

亲切的问候 PK

【问题讨论】:

    标签: interop dllimport


    【解决方案1】:

    Win32 函数几乎从不返回 HRESULT。相反,它们返回 BOOL 或使用特殊值来指示错误(例如 CreateFile 返回 INVALID_HANDLE_VALUE)。它们将错误代码存储在每个线程变量中,您可以使用GetLastError() 读取该变量。 SetLastError=true 指示封送拆收器在本机函数返回后读取此变量,并将错误代码存储在您以后可以使用 Marshal.GetLastWin32Error() 读取的位置。这个想法是 .NET 运行时可能会在幕后调用其他 Win32 函数,这会在您有机会检查之前弄乱 p/invoke 调用中的错误代码。

    返回 HRESULT(或等价物,例如 NTSTATUS)的函数与 Win32 函数属于不同的抽象级别。通常,这些函数与 COM 相关(高于 Win32)或来自ntdll(低于 Win32),因此它们不使用 Win32 上一个错误代码(尽管它们可能在内部调用 Win32 函数)。

    PreserveSig=false 指示封送器检查返回 HRESULT,如果它不是成功代码,则创建并抛出包含 HRESULT 的异常。然后,DllImported 函数的托管声明将 void 作为其返回类型。

    请记住,C# 或 VB 编译器无法检查 DllImported 函数的非托管签名,因此它必须信任您告诉它的任何内容。如果你把PreserveSig=false 放在一个返回HRESULT 以外的东西的函数上,你会得到奇怪的结果(例如随机异常)。如果您将SetLastError=true 放在未设置最后一个 Win32 错误代码的函数上,您将得到垃圾而不是有用的错误代码。

    【讨论】:

    • 我没有使用 COM 对象的经验,所以让我再问一个关于创建方法签名的问题。问题是:当我看到 COM 函数返回 HRESULT 时,我可以将我的方法标记为返回 void 并设置 PreserveSig=false (如您所说),或者设置 PreserveSig=true 并将我的方法标记为返回 IntPtr 以手动检查返回的代码?
    • 是的,这是正确的,除了 HRESULT 是 UInt32,而不是 IntPtrs。
    • 谢谢你 - 你很有帮助。
    猜你喜欢
    • 2011-02-18
    • 1970-01-01
    • 1970-01-01
    • 2011-02-03
    • 1970-01-01
    • 2017-07-21
    • 1970-01-01
    • 2013-03-26
    • 1970-01-01
    相关资源
    最近更新 更多