Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
317 views
in Technique[技术] by (71.8m points)

c# - How to invoke a method of a private COM interfaces, defined in a base class?

How can I invoke a method of a private COM interface, defined in a base class, from a derived class?

For example, here is the COM interface, IComInterface (IDL):

[
    uuid(9AD16CCE-7588-486C-BC56-F3161FF92EF2),
    oleautomation
]
interface IComInterface: IUnknown
{
    HRESULT ComMethod([in] IUnknown* arg);
}

Here's the C# class BaseClass from OldLibrary assembly, which implements IComInterface like this (note the interface is declared as private):

// Assembly "OldLibrary"
public static class OldLibrary
{
    [ComImport(), Guid("9AD16CCE-7588-486C-BC56-F3161FF92EF2")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IComInterface
    {
        void ComMethod([In, MarshalAs(UnmanagedType.Interface)] object arg);
    }

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    public class BaseClass : IComInterface
    {
        void IComInterface.ComMethod(object arg)
        {
            Console.WriteLine("BaseClass.IComInterface.ComMethod");
        }
    }
}

Finally, here's an improved version, ImprovedClass, which derives from BaseClass, but declares and implement its own version of IComInterface, because the base's OldLibrary.IComInterface is inaccessible:

// Assembly "NewLibrary"
public static class NewLibrary
{
    [ComImport(), Guid("9AD16CCE-7588-486C-BC56-F3161FF92EF2")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IComInterface
    {
        void ComMethod([In, MarshalAs(UnmanagedType.Interface)] object arg);
    }

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    public class ImprovedClass : 
        OldLibrary.BaseClass, 
        IComInterface, 
        ICustomQueryInterface
    {
        // IComInterface
        void IComInterface.ComMethod(object arg)
        {
            Console.WriteLine("ImprovedClass.IComInterface.ComMethod");
            // How do I call base.ComMethod here, 
            // otherwise than via reflection?
        }

        // ICustomQueryInterface
        public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv)
        {
            if (iid == typeof(IComInterface).GUID)
            {
                ppv = Marshal.GetComInterfaceForObject(this, typeof(IComInterface), CustomQueryInterfaceMode.Ignore);
                return CustomQueryInterfaceResult.Handled;
            }
            ppv = IntPtr.Zero;
            return CustomQueryInterfaceResult.NotHandled;
        }   

    }
}

How do I call BaseClass.ComMethod from ImprovedClass.ComMethod without reflection?
I could use reflection, but in the real use case IComInterface is a complex OLE interface with a number of members of complex signatures.

I thought that because both BaseClass.IComInterface and ImprovedClass.IComInterface are both COM interfaces with the same GUID and identical method signatures, and there's COM Type Equivalence in .NET 4.0+, so there has to be a way to do what I'm after without reflection.

Another requirement is that ImprovedClass has to be derived from BaseClass, because the C# client code expects an instance of BaseClass, which it passes to the COM client code. Thus, containment of BaseClass inside ImprovedClass is not an option.

[EDITED] A real-life scenario which involves deriving from WebBrowser and WebBrowserSite is described here.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I'm used to doing this in C++, so I'm mentally translating from C++ to C# here. (I.e., you may have to do some tweaking.)

COM identity rules require the set of interfaces on an object to be static. So, if you can get some interface that's definitely implemented by BaseClass, you can QI off that interface to get BaseClass'es implementation of IComInterface.

So, something like this:

type typeBaseIComInterface = typeof(OldLibrary.BaseClass).GetInterfaces().First((t) => t.GUID == typeof(IComInterface).GUID); 
IntPtr unkBaseIComInterface = Marshal.GetComInterfaceForObject(this, typeBaseIComInterface, CustomQueryInterfaceMode.Ignore);
dynamic baseptr = Marshal.GetTypedObjectForIUnknown(unkBaseIComInterface, typeof(OldLibrary.BaseClass);
baseptr.ComMethod(/* args go here */);

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...