【问题标题】:Passing NULL to ref/out parameter of a COM interface method将 NULL 传递给 COM 接口方法的 ref/out 参数
【发布时间】:2013-09-24 07:11:54
【问题描述】:

如果COM接口方法的参数定义为[In, Out] ref int pchEaten,如何将NULL传递给?

例如,考虑以下接口:

[ComImport, Guid ("000214E6-0000-0000-C000-000000000046")]
[InterfaceType (ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellFolder
{
    void ParseDisplayName (
        [In] IntPtr hwnd,
        [In] IBindCtx pbc,
        [In, MarshalAs (UnmanagedType.LPWStr)] string pszDisplayName,
        [In, Out] ref uint pchEaten,
        [Out] out PIDLIST ppidl,
        [In, Out] ref SFGAO pdwAttributes);
    // ...
}

MSDN 对pchEaten 参数进行了如下说明:一个指向 ULONG 值的指针,该值接收已解析的显示名称的字符数。如果您的应用程序不需要此信息,请将 pchEaten 设置为 NULL,并且不会返回任何值。 pdwAttributes 参数也可以设置为 NULL。

但是,当我从 C# 调用 ParseDisplayName 方法时,我看不到将 null 传递给 ref 参数的方法。

如果我从 DLL 调用函数,我可以多次导入一个函数:一个带有 IntPtr 参数,一个带有适当的签名,然后根据我是否需要传递和接收值来选择重载。但是,如果我尝试在 COM 中多次导入相同的方法,它不会起作用,因为方法的顺序很重要,而且函数指针会发生变化。

问题:如何使用 in/out 参数的值和 NULL 来调用 COM 方法?

注意:这只是一个例子。我知道我可以创建一个虚拟变量,将其传递给ref 参数并忽略返回值。但是,方法的行为可能取决于值是否为 NULL,非 null 值可能会产生性能成本等,所以我想避免它。

【问题讨论】:

  • @FlorianGl 这个问题和答案不包括我的情况,一个COM接口。正如我已经提到的,我知道 DLL 案例的解决方案。
  • 这并不能解决你的问题,但是这个参数必须是out,而不是ref
  • @sharptooth 在 IDL 中是 [in, out, unique, annotation("__reserved")] ULONG *pchEaten,但在文档中是 [out] ULONG *pchEaten。奇怪的。逻辑上应该是out,没错。
  • 哎哟。 IDL 与文档不匹配有点令人担忧。

标签: c# com null interop com-interop


【解决方案1】:

您可以像这样重新定义界面,例如:

internal interface IShellFolder
{
    void ParseDisplayName (
        [In] IntPtr hwnd,
        [In] IBindCtx pbc,
        [In, MarshalAs (UnmanagedType.LPWStr)] string pszDisplayName,
        IntPtr pchEaten,
        [Out] out PIDLIST ppidl,
        [In, Out] ref SFGAO pdwAttributes);
    // ...
}

如果你想传递 null,就这样调用它:

ParseDisplayName(...., IntPtr.Zero, ...);

如果你想传递一个 uint 值,或者像这样:

IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(uint)));
uint i = 1234;
Marshal.StructureToPtr(i, p, false);
ParseDisplayName(...., p, ...);
i = (uint)Marshal.PtrToStructure(p, typeof(uint)); // read the value back
Marshal.FreeHGlobal(p);

【讨论】:

  • +1,Marshal.ReadInt32 可以读取返回值吗?
  • 是否需要分配内存?如果我正确理解了 MSDN (Blittable and Non-Blittable Types),则 blittable 类型将被固定而不是复制,int 是 blittable。理想情况下,我想复制这种行为。
  • @Groo - 好话,我已经更新了我的帖子。您可以使用 Marshal.ReadInt32(并强制转换为 uint - 未选中),但我已经放置了一个代码,如果您将 uint 替换为任何结构,则该代码将起作用。
  • @Athari - int 是 blittable,但您想将(可能为 null)指针/引用传递给 int(或此处为 uint),而不是 int。
【解决方案2】:

令我震惊的是,您可能过于努力地避免使用指针。 C# 语言很好地支持它们,您可以将参数声明为[In] uint* pchEaten。现在您拥有母语中的所有可用选项,您可以传递&localnull。其中local 应该是调用方法的局部变量,这样指针就可以稳定,您不必担心固定。

但是是的,您必须声明该方法不安全。当然不是当你正确编程时:)

【讨论】:

    猜你喜欢
    • 2012-08-29
    • 1970-01-01
    • 1970-01-01
    • 2018-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-25
    • 1970-01-01
    相关资源
    最近更新 更多