Search code examples
midlc++-winrt

Reverse C++/WinRT ABI parameter order for MIDL 3.0 array parameter?


I have an existing interface that I'm trying to define using MIDL 3.0. One of it's methods has this C++ signature:

HRESULT GetArray(struct FOO** outArray, uint32_t* outSize);

I tried translating this to IDL like so:

namespace Examples {
    struct Foo {
         Int32 n1;
         Int32 n2;
    };
    interface IExample {
        void GetArray(out Foo[] array);
    }
}

However, the resulting C++/WinRT ABI has the parameters in the opposite order:

template <> struct abi<Examples::IExample>{ struct type : IInspectable
{
    virtual HRESULT __stdcall GetArray(uint32_t* __arraySize, struct struct_Examples_Foo** array) noexcept = 0;
};};

This does make sense considering that is the recommended order. Unfortunately, I don't have the ability to change the parameter order of the existing interface. Instead, I figured I might be able to work around it using "classic" style:

namespace Examples {
    [uuid("d7675bdc-7b6e-4936-a4a0-f113c1a3ef70"), version(1)]
    interface IExample {
        HRESULT GetArray(
            [out, size_is(, *size)] Foo** array,
            [out] unsigned long* size
        );
    }
}

But, this is rejected by the MIDL compiler:

MIDL4058: [msg]The size parameter of an array parameter must appear directly before the array parameter. [context]size

How do I write this interface in IDL in such a way that results in the correct ABI?


Solution

  • WinRT has a strict ABI definition for the ordering of array parameters, and as you have discovered it is (size, pointer) and not the other way around. There is no way to change this, since all the projections (such as .NET, JavaScript, and C++/CX) expect this order and would fail catastrophically if passed in the wrong order.

    If you cannot change the ordering, can you write a wrapper class that exposes the correct ordering and simply forwards the calls onto your existing code with the parameters reversed?

    Failing that, there is another way to support this if you only care about C++ (and maybe C# clients). That is, rather than defining a WinRT interface for this method, you can define a classic-COM interface and have your WinRT object implement that interface as well. Then the clients of your WinRT object QI for that COM interface and can pass the arguments in the order you require.