如何将 void* 放入 COM 变体中,以便它被 COM-Interop 编组为 IntPtr,到 .NET

How do I put a void* into a COM VARIANT so that it gets marshalled as IntPtr by COM-Interop to .NET?

本文关键字:COM-Interop NET IntPtr 放入 void COM      更新时间:2023-10-16

如何放置一个空指针(又名。 void*或句柄)转换为 COM VARIANT -Type。包装类_variant_t错误地将其转换为布尔值。但是我需要将其作为指针放入,以便 .NET 的 COM-Marshaller 将其识别为 IntPtr

我读过的其中一个帖子是MSDN Social的这个。他们提出了这样的解决方案:

VARIANTARG Arg; 
VariantInit(&Arg);
V_VT(&Arg) = VT_INT;
V_INT(&Arg) = (int)m_hWnd; 

此解决方案的问题是,V_INT(成员 intVal )是一个 32 位整数,并强制指针为 32 位指针。换句话说:我无法传输 64 位指针。这个问题有什么解决方案吗?还有其他方法可以将其转换为 64 位整数吗?

我已经通过一些代码对此进行了测试。因此,我使用一个 .NET 方法来接收任何System.Object并给出其值和类型。

public static void TakePtr(object ptr)
{
    Console.WriteLine("Received a " + ptr.GetType() + " with value " + ptr);
}

我的VARIANTv.llVal = 0; v.byref = myPointer;填充以兼容,因此它应该始终正确编译 32/64 位指针。此外,我需要设置变体类型,以便 .NET 将其应用到System.IntPtr而不强制转换。(因为对于真正的程序集,我无法更改代码,也不想包装它。这将增加主要的复杂性。

  • .NET将System.IntPtr向后传输为VT_INT,从而将mapps奖励到System.Int32
  • VT_I8 mapp System.Int64但不是System.IntPtr

那么VARENUM标志会指定System.IntPtr吗?

这是不可能的,VARIANT 是一种自动化类型。 自动化不喜欢处理指向未知类型的指针,没有安全的方法来取消引用它们。 唯一支持的指针类型是 IUnknown* 和 IDispatch*,即接口指针。

充其量你可以把它存储为一个VT_I8,编组到一个长。 然后,您可以将其转换为 IntPtr。

目前我找到了一个临时解决方案来解决整个问题: 为了在调用方法时显式定义参数类型,我将使用另一种调用机制。因此,我使用以下方法,它几乎破坏了类型invoke方法的参数,但另外检查类型并相应地强制转换它们,以便将System.IntPtr识别为这样,而不是System.Int32System.Int64

public static object InvokeMember(this Type type, object obj, String name, System.Reflection.BindingFlags invokeAttr, Type[] argTypes, object[] argValues)
{
    if (argTypes.Length != argValues.Length) throw new InvalidOperationException("Mismatching count of types an parameters.");
    for (int i = 0; i < argTypes.Length; i++) if (argTypes[i] == typeof(IntPtr)) argValues[i] = new IntPtr(Convert.ToInt64(argValues[i]));
    return type.InvokeMember(name, invokeAttr, null, obj, argValues);
}

此方法的调用可能如下所示:

typeof(ExampleLib.HelloNET).InvokeMember(
    null,
    "TakePtr",
    BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod,
    new Type[] { typeof(IntPtr) },
    new object[] { 42 });

最后,必须从 COM 使用它来传递指针 VARIANT,这些指针按如下所述填充: v.llVal = 0; v.byref = myPointer; v.vt = VT_INT;

/已解决:-)

变体的PVOID byref;成员看起来很有帮助。 但我不确定应该设置什么类型来伴随它。

你可以按照 Hans 的建议使用LONGLONG llVal,因为即使是 32 位平台也有 IntPtr(Int64) 构造函数。 应该工作,直到有人发明 128 位指针。

我也有类似的问题。当将 64 位指针放入 PVOID 变体类型时,它被截断/编组为 32 位指针!因此,我随后使用了VT_I8变体类型并将指针放入其llVal成员中,一切正常。

我使用了VT_VOID和byref参数,几乎没有任何问题。几乎是因为在某些情况下,.NET 会引发有关从本机对象到托管对象的错误转换的 MDA 错误(InvalidVariant 错误)

我还无法检查问题是否出在 byref 为 0 时,因为它并不总是出现。 :)