【问题标题】:How do I put a void* into a COM VARIANT so that it gets marshalled as IntPtr by COM-Interop to .NET?如何将 void* 放入 COM VARIANT 中,以便它被 COM-Interop 编组为 IntPtr 到 .NET?
【发布时间】:2012-06-02 07:22:48
【问题描述】:

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

我读过的一个主题是this one at MSDN Social。他们提出了这样的解决方案:

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

这个解决方案的问题是,V_INT(成员intVal)是32 bit integer,并强制指针为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,它向前映射到System.Int32
  • VT_I8 映射到 System.Int64 但不是 System.IntPtr

那么VARENUM 标志会指定System.IntPtr

【问题讨论】:

    标签: c++ .net com marshalling variant


    【解决方案1】:

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

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

    【讨论】:

    • 简单地转移价值是一个问题,现在应该解决了。 (我已经为我的问题添加了解决方案。)另一方面,.NET 无法将其识别为 System.IntPtr without cast 的问题仍然存在。
    【解决方案2】:

    目前我找到了解决全部问题的临时解决方案:为了在调用方法时显式定义参数类型,我将使用另一种调用机制。因此,我使用以下方法,它几乎包装了类型调用方法的参数,但另外检查类型并相应地转换它们,以便将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 中使用它来传递指针 VARIANTs,它们的填充方式如下所述:v.llVal = 0; v.byref = myPointer; v.vt = VT_INT;

    /已解决 :-)

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

        我遇到了类似的问题。当将 64 位指针放入 PVOID 变体类型时, 它被截断/编组为 32 位指针! 所以我然后使用了 VT_I8 变体类型并将指针放入它的 llVal 成员中,一切都很好。

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-05-11
            • 1970-01-01
            • 2011-10-12
            • 2014-12-17
            • 2013-09-11
            • 2018-10-26
            • 1970-01-01
            相关资源
            最近更新 更多