PInvoke能够将[out]参数转换为返回值吗?

3

我是PInvoke的新手,需要一些帮助。

我想使用PInvoke访问IEnumGUID,并在pinvoke.net上找到了一个代码块。

 [ComImport, Guid("0002E000-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), ComVisible(false)]
 public interface IEnumGUID
 {
     int Next(int celt, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)]Guid[] rgelt);
     void Skip(int celt);
     void Reset();
     [return: MarshalAs(UnmanagedType.Interface)]
     IEnumGUID Clone();
 }

这里的克隆函数将 IEnumGUID 作为 返回值,而原始 C++ 接口中的函数将其作为输出参数
HRESULT Clone(
  [out]  IEnumGUID **ppenum
);

我了解到PInvoke会自动将HRESULT转换为COMException,但我不知道PInvoke如何将“out参数”转换为“返回值”。
请给出一些解释,以便我以后可以正确使用这种方法。
1个回答

5

pinvoke.net 网站并不完美,它有很多错误,这些错误与使用声明的人数成反比。对于你找到的这个声明来说,赌注不高,它被严重破坏了。

这与 pinvoke 无关,COM interop 是 .NET 中的一个特色。将参数转换为返回值的能力非常特定于 COM Automation,并且非常特定于用 [out, retval] 属性装饰的参数。是 [retval] 提供了提示给 COM 客户端运行时支持库,表明该参数可以被视为返回值。这是非常普遍的,例如任何脚本语言都可以利用这一点。除非使用 [PreserveSig] 属性显式禁止转换,否则任何 .NET 语言都会自动进行转换。

IEnumGUID::Clone() 的参数没有 [retval] 属性,因此不应该像具有该属性一样声明。这不是唯一的错误,其他成员需要 [PreserveSig] 属性,因为您需要知道它们的返回值才能正确使用它们。正确的声明方式如下:

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("0002E000-0000-0000-C000-000000000046")]
public interface IEnumGUID
{
    [PreserveSig]
    int Next(int celt, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] Guid[] rgelt, out int pceltFetched);
    [PreserveSig]
    int Skip(int celt);
    [PreserveSig]
    int Reset();
    void Clone(out IEnumGUID ppenum);
}

COM的IEnumXxxx接口非常常见,它们只是由枚举器返回的元素类型不同而已。在.NET Framework中存在几个这样的接口,例如与此接口进行比较。


太好了!谢谢您告诉我关于[retval]的内容,并纠正了声明!顺便说一下,MSDN文档中提到Next、Skip、Reset函数都返回HRESULT。他们说错了吗?http://msdn.microsoft.com/en-us/library/windows/desktop/ms688533(v=vs.85).aspx - Kagami Sascha Rosylight
我记错了。修正后,实际上只对Next()非常重要。你需要知道何时没有下一个。 - Hans Passant
我明白了!你帮了我很多。谢谢 :) - Kagami Sascha Rosylight
1
为什么没有使用[retval]进行封送的函数不能使用返回值?毕竟,这个属性只是一个提示,不是吗? - IS4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接