作为IUnknown的'object []'的托管到非托管的Marshaling

时间:2012-04-25 08:22:34

标签: .net arrays pinvoke marshalling iunknown

我正在使用P / Invoke从C#调用非托管C函数,传递一个对象数组。 在非托管代码中,我查询IDUatch的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(...)来解决这个问题吗? 或者还有另一种方法可以让它发挥作用吗?

1 个答案:

答案 0 :(得分:2)

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

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

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