0

我正在使用 P/Invoke 从 C# 调用非托管 C 函数,并传递对象数组。在非托管代码中,我为 IDispatch 查询 IUnknown。这适用于简单的情况,但如果其中一个对象是数组本身,则获取 IDispatch 失败。

托管代码:

    [DllImport("NativeDll.dll")]
    static extern void TakesAnObjectArray(int len, 
        [MarshalAs(UnmanagedType.LPArray, 
         ArraySubType = UnmanagedType.IUnknown)]object[] a);

    public static void exec1(int a, object b, string c)
    {
        Object[] info_array;
        Object[] parameters_array;

        parameters_array = new object[4];
        parameters_array[0] = a;
        parameters_array[1] = b;
        parameters_array[2] = c;
        parameters_array[3] = 55;
        // THIS WORKS GREAT
        TakesAnObjectArray(4, parameters_array);

        info_array = new object[6];
        info_array[0] = parameters_array;
        // THIS DOESN'T
        // I CAN'T GET IDISPATCH FOR THE 1ST 'OBJECT'
        TakesAnObjectArray(6, info_array);
    }

非托管代码:

    void TakesAnObjectArray(int len, LPUNKNOWN p[])
    {
        HRESULT hr;
        for (int i=0; i<len; i++)
        {
            IDispatch *disp = NULL;
            hr = p[i]->QueryInterface(IID_IDispatch, (void**)&disp);
        }
    }

QueryInterface 大部分时间都是成功的。但是,如果托管对象实际上是“System.Object[]”,则无法获得 IDispatch 接口(hr = 0x80004002 = E_NOINTERFACE = '不支持此类接口')。

我可以以某种方式使用 MarshalAs(...) 来解决这个问题吗?还是有另一种方法可以让它发挥作用?

4

1 回答 1

3

有趣的是,CLR 设法使代码段的第一部分完全正常工作。它可能设法生成了一个 IDispatch 指针,因为 System.Array 类型是 [ComVisible(true)]。因此,您可以让 QI 工作,但如果不熟悉 Array 类成员,您实际上无法使用 IDispatch 指针执行任何操作。您在第二个片段中运气不佳,因为声明中没有任何内容强制 CLR 编组数组元素。

您需要以正确的方式解决此问题,数组不是 COM 自动化中的 IDispatch 对象。Object[] 的默认 COM 封送处理是 SAFEARRAY*,它是一个元素为 VARIANT 的安全数组。将 TakesAnObjectArray 参数类型从 LPUNKNOWN[] 更改为 SAFEARRAY*。并将 pinvoke 声明更改为没有 [MarshalAs] 属性的纯对象 []。

您需要在 C++ 代码中使用 SafeArrayLock() 和 SafeArrayGetElement() 来访问数组元素。第二个片段需要额外的间接寻址,因为第一个元素是一个包含 SAFEARRAY 本身的 VARIANT。

于 2012-04-25T10:55:29.060 回答