Вопрос

I'm using P/Invoke to call an unmanaged C function from C#, passing an array of objects. In the unmanaged code I query IUnknown for IDispatch. This works for the simple case, but getting IDispatch fails if one of the objects is an array itself.

managed code:

    [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);
    }

unmanaged code:

    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 is successful most of the time. But, if the managed object is actually a 'System.Object[]', I can't get an IDispatch interface (hr = 0x80004002 = E_NOINTERFACE = 'No such interface supported').

Can I use MarshalAs(...) in some way that will fix this? Or Is there another way to get this to work?

Это было полезно?

Решение

Kind of interesting to see that CLR managed to make the first part of the snippet work at all. It probably managed to generate an IDispatch pointer because the System.Array type is [ComVisible(true)]. So you can get QI to work but there isn't anything you can actually do with the IDispatch pointer without having intimate knowledge of the Array class members. You ran out of luck on the 2nd snippet because there isn't anything in the declaration that forces the CLR to marshal the array element.

You'll need to fix this the right way, an array is not a IDispatch object in COM automation. The default COM marshaling for Object[] is SAFEARRAY*, a safe array whose elements are VARIANT. Change the TakesAnObjectArray argument type from LPUNKNOWN[] to SAFEARRAY*. And change the pinvoke declaration to just plain object[] without the [MarshalAs] attribute.

You'll need to use SafeArrayLock() and SafeArrayGetElement() in your C++ code to access the array elements. The second snippet requires an extra indirection because the first element is a VARIANT that contains a SAFEARRAY itself.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top