p/invoke operation ends up with executing another function

167 views Asked by At

Not sure why this is happening, but when i execute one of my c# function, which could be defined by the following c# interface:

[ComImport, Guid("EA5435EA-AA5C-455d-BF97-5F19DC9C29AD"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IClosedCaptionsDecoder2 
{
    [PreserveSig]
    int SetConfig([In] ref ClosedCaptionsDecoderConfig config);
    [PreserveSig]
    int GetConfig([Out] out ClosedCaptionsDecoderConfig config);
}

and c++ interface:

    interface __declspec(uuid("{EA5435EA-AA5C-455d-BF97-5F19DC9C29AD}"))
    IClosedCaptionsDecoder2 : public IClosedCaptionsDecoder
    {
        STDMETHOD(SetConfig)(IN CLOSEDCAPTIONSDECODERCONFIG& config) PURE;
        STDMETHOD(GetConfig)(OUT CLOSEDCAPTIONSDECODERCONFIG* pConfig) PURE;
    };

im redirected to another function declared by a 'prior' interface. when i try to execute the following command for example: config->SetConfig(....). the function im redirected to(or next command to execute), is implemented by the base classs of IClosedCaptionsDecoder2, which's called IClosedCaptionsDecoder.

the c++ decleration of this interface is:

interface __declspec(uuid("{26B8D7F1-7DD8-4a59-9663-8D00C03135F7}"))
        IClosedCaptionsDecoder : public IUnknown
        {
            STDMETHOD(xxx)(IExternalCCObserver* pObserver, LONG lFlags) PURE;
        };

so config->SetConfig() actually invokes config->xxx(), my guess is that something is wrong with the offests of the functions.

i even tried to define the entire relation in the c# side(the inheritance and so on) but that didn't work either.

i would appreciate any help. thanks!

Edit: when i tried to call GetConfig(), it actually executed SetConfig(). so im definitly having problems with pointers offset or so. each function, calls the previous one in the decleration order, how's it possible??

Edit2: i managed to solve this case by adding all functions to the IClosedCaptionsDecoder2 interface.

1

There are 1 answers

0
Hans Passant On

This is a side-effect of a flaw in the way COM interop is implemented in the CLR. It doesn't properly map methods of an interface to v-table slots when the interface derives from another interface other than IUnknown or IDispatch. It maps the first method to the first available slot even though it is already occupied by the methods of the inherited interface in the concrete coclass implementation. A side-effect of not supporting multiple inheritance. So what goes wrong is that when client code calls IClosedCaptionsDecoder::xxx(), it ends up calling IClosedCaptionsDecoder2::SetConfig() instead.

The workaround is straight-forward though unpleasant, you have to flatten the interface so it includes the inherited methods. In your case that will be:

[ComImport, Guid("EA5435EA-AA5C-455d-BF97-5F19DC9C29AD"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IClosedCaptionsDecoder2 
{
    // Methods inherited from IClosedCaptionsDecoder:
    [PreserveSig]
    int xxx(whatever...);
    // Methods specific to IClosedCaptionsDecoder2
    [PreserveSig]
    int SetConfig([In] ref ClosedCaptionsDecoderConfig config);
    [PreserveSig]
    int GetConfig([Out] out ClosedCaptionsDecoderConfig config);
}

This becomes law in the USA on September 30th, only 6 weeks left to get this working ;)