Question

This is a follow up from my previous post. Read that post for context. Note that it is not strict COM interop - but the C++ interfaces are COM compatible.

Im trying to implement this C++ interface in C#

class IPluginFactory : public FUnknown
{
    virtual tresult PLUGIN_API createInstance (FIDString cid, FIDString iid, void** obj) = 0;
};

My C# code looks like this:

[ComImport]
[Guid(Interfaces.IPluginFactory)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPluginFactory
{
    [PreserveSig]
    [return: MarshalAs(UnmanagedType.Error)]
    Int32 CreateInstance([In] ref Guid classId, [In] ref Guid interfaceId, [MarshalAs(UnmanagedType.IUnknown), In, Out] ref object instance);
}

The implementation assigns a new object instance to the 'instance' parameter and returns 0 (S_OK). I even cast to the expected (managed) interface.

instance = (IPluginBase)new PluginBase();
return 0;

The object returned is represented by this C++ interface:

class IPluginBase: public FUnknown
{
public:
    virtual tresult PLUGIN_API initialize (FUnknown* context) = 0;
    virtual tresult PLUGIN_API terminate () = 0;
};

Which looks like this in my C# implementation:

[ComImport]
[Guid(Interfaces.IPluginBase)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPluginBase
{
    [PreserveSig]
    [return: MarshalAs(UnmanagedType.Error)]
    Int32 Initialize([MarshalAs(UnmanagedType.IUnknown), In] object context);

    [PreserveSig]
    [return: MarshalAs(UnmanagedType.Error)]
    Int32 Terminate();
}

In the unmanaged C++ test application I wrote, I can successfully call createInstance and receive a non-null pointer the code uses as a IPluginBase*.

The problem comes when I try to call the 'initialize' method on this IPluginBase* pointer. It never reaches the managed code (no breakpoint is hit - other breakpoint work fine) and the return code is 0x80004003. It looks like the wrapper does some intercepting here...

My question is: is the declaration of the managed representation of CreateInstance correct? What am I missing here? (this should be plain vanilla interop, should it not?)

Other suggestions on declaration 'style' are also welcome. Thanx, Marc.

EDIT: The problem seems to lie with the createInstance method. I am unable to get the interface returned that is asked for by the iid parameter.

[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
Int32 CreateInstance([In] ref Guid classId, [In] ref Guid interfaceId, [MarshalAs(UnmanagedType.IUnknown, IidParameterIndex = 1), In, Out] ref object instance);

I have also tried UnmanagedType.Interface in combination with the IidParameterIndex but both result in a IUnknown being marshaled back. If I re-query the IPluginBase interface the IPluginBase::initialize method works (breakpoint in managed code hits).

Was it helpful?

Solution

EDIT: The problem seems to lie with the createInstance method. I am unable to get the interface returned that is asked for by the iid parameter.

Specifying IidParameterIndex doesn't help here. Your implementation of createInstance should look like this:

public int createInstance(ref Guid classId, ref Guid riid, ref IntPtr instance)
{
    if (instance != IntPtr.Zero)
        return E_POINTER;

    // substitute your actual object creation code for CreateObject
    object obj = CreateObject(classId) 

    // return the correct interface
    IntPtr unk = Marshal.GetIUnknownForObject(obj);
    try
    {
        return Marshal.QueryInterface(unk, ref riid, out instance);
    }
    finally
    {
        Marshal.Release(unk);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top