Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/266.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何调用基类中定义的私有COM接口的方法?_C#_.net_Reflection_Com_Com Interop - Fatal编程技术网

C# 如何调用基类中定义的私有COM接口的方法?

C# 如何调用基类中定义的私有COM接口的方法?,c#,.net,reflection,com,com-interop,C#,.net,Reflection,Com,Com Interop,如何从派生类调用在基类中定义的私有COM接口的方法 例如,下面是COM接口,IComInterface(IDL): 下面是来自OldLibrary程序集中的C#classBaseClass,它实现了IComInterface,如下所示(注意,接口声明为私有): 最后,这里有一个改进的版本,ImprovedClass,它源于BaseClass,但声明并实现了自己的IComInterface,因为base的旧库。IComInterface不可访问: // Assembly "NewLibrary"

如何从派生类调用在基类中定义的私有COM接口的方法

例如,下面是COM接口,
IComInterface
(IDL):

下面是来自
OldLibrary
程序集中的C#class
BaseClass
,它实现了
IComInterface
,如下所示(注意,接口声明为私有):

最后,这里有一个改进的版本,
ImprovedClass
,它源于
BaseClass
,但声明并实现了自己的
IComInterface
,因为base的
旧库。IComInterface
不可访问:

// 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;
        }   

    }
}
如何在没有反射的情况下从
改进类.ComMethod
调用
基类.ComMethod

我可以使用反射,但在实际用例中
IComInterface
是一个复杂的OLE接口,有许多复杂签名的成员

我认为这是因为
BaseClass.IComInterface
ImprovedClass.IComInterface
都是COM接口,具有相同的GUID和相同的方法签名,并且在.NET 4.0+中存在,所以必须有一种方法来完成我要做的事情而不进行反射

另一个要求是
ImprovedClass
必须从
BaseClass
派生,因为C#client代码需要一个
BaseClass
的实例,并将其传递给COM客户端代码。因此,将
基类
包含在
改进类
中不是一个选项


<强> [编辑] 一个真实的场景,它包含了从和派生的描述。

< P>我习惯于C++中的这一点,所以我在这里从C++到C语言进行心理翻译。(即,您可能需要进行一些调整。)

要求对象上的接口集是静态的。因此,如果您可以得到一些明确由
BaseClass
实现的接口,那么您可以关闭该接口以获得
BaseClass
IComInterface
的实现

比如说:

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 */);
// IComInterface
void IComInterface.ComMethod(object arg)
{
    InvokeBaseMethod(this, "ComMethod", typeof(OldLibrary.BaseClass), typeof(IComInterface), arg);
}

我通过使用一个包含helper的对象(
BaseClassComProxy
)和一个聚合的COM代理对象(使用创建)来解决这个问题。这种方法为我提供了一个具有单独标识的非托管对象,我可以将其强制转换(使用)到我自己的等价版本的
BaseClass.IComInterface
接口,该接口是不可访问的。它适用于由
BaseClass
实现的任何其他私有COM接口

@EricBrown关于COM身份规则的观点对这项研究有很大帮助。谢谢你,埃里克

这里有一个独立的控制台测试应用程序。使用
WebBrowserSite
解决原始问题的代码已发布

使用系统;
使用系统诊断;
使用System.Linq;
使用System.Runtime.InteropServices;
命名空间管理服务器
{
/*
//IComInterface IDL定义
[
uuid(9AD16CCE-7588-486C-BC56-F3161FF92EF2),
油脂自动化
]
接口IComInterface:IUnknown
{
HRESULT方法(IUnknown*arg);
}
*/
//旧图书馆
公共静态类库
{
//专用COM接口IComInterface
[ComImport(),Guid(“9AD16CCE-7588-486C-BC56-F3161FF92EF2”)]
[接口类型(ComInterfaceType.InterfaceSiunknown)]
专用接口IComInterface
{
void ComMethod([In,marshallas(UnmanagedType.Interface)]对象arg);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
公共类基类:IComInterface
{
void IComInterface.ComMethod(对象参数)
{
Console.WriteLine(“BaseClass.IComInterface.ComMethod”);
}
}
}
//新图书馆
公共静态类库
{
//OldLibrary.IComInterface在此不可访问,
//定义新的等效版本
[ComImport(),Guid(“9AD16CCE-7588-486C-BC56-F3161FF92EF2”)]
[接口类型(ComInterfaceType.InterfaceSiunknown)]
专用接口IComInterface
{
void ComMethod([In,marshallas(UnmanagedType.Interface)]对象arg);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
公共课改进课:
OldLibrary.BaseClass,
NewLibrary.IComInterface,
ICustomQueryInterface,
可识别
{
NewLibrary.IComInterface_baseIComInterface;
BaseClassComProxy\u BaseClassComProxy;
//IComInterface
//我们想调用BaseClass.IComInterface.ComMethod,它只能通过COM访问
void IComInterface.ComMethod(对象参数)
{
_baseIComInterface.ComMethod(arg);
Console.WriteLine(“ImprovedClass.IComInterface.ComMethod”);
}
//ICustomQueryInterface
公共CustomQueryInterfaceResult GetInterface(参考Guid iid,out IntPtr ppv)
{
if(iid==typeof(NewLibrary.IComInterface.GUID)
{
//忽略是为了避免QI过程中的无限循环。
ppv=Marshal.GetComInterfaceForObject(这个,typeof(NewLibrary.IComInterface),CustomQueryInterfaceMode.Ignore);
返回CustomQueryInterfaceResult.Handled;
}
ppv=整数零;
返回CustomQueryInterfaceResult.NotHandled;
}
//建造师
公共改进类()
{
//将CCW对象与辅助对象内部对象聚合
_baseClassComProxy=新的baseClassComProxy(此);
_baseIComInterface=\u baseClassComProxy.GetComInterface();
}
~ImprovedClass()
{
处置();
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 */);
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;

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

    // OldLibrary
    public static class OldLibrary
    {
        // private COM interface IComInterface
        [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");
            }
        }
    }

    // NewLibrary 
    public static class NewLibrary
    {
        // OldLibrary.IComInterface is inaccessible here,
        // define a new equivalent version
        [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,
            NewLibrary.IComInterface,
            ICustomQueryInterface,
            IDisposable
        {
            NewLibrary.IComInterface _baseIComInterface;
            BaseClassComProxy _baseClassComProxy;

            // IComInterface
            // we want to call BaseClass.IComInterface.ComMethod which is only accessible via COM
            void IComInterface.ComMethod(object arg)
            {
                _baseIComInterface.ComMethod(arg);
                Console.WriteLine("ImprovedClass.IComInterface.ComMethod");
            }

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

            // constructor
            public ImprovedClass()
            {
                // aggregate the CCW object with the helper Inner object
                _baseClassComProxy = new BaseClassComProxy(this);
                _baseIComInterface = _baseClassComProxy.GetComInterface<IComInterface>();   
            }

            ~ImprovedClass()
            {
                Dispose();
                Console.WriteLine("ImprovedClass finalized.");
            }

            // IDispose
            public void Dispose()
            {
                // we may have recicular COM references to itself
                // e.g., via _baseIComInterface
                // make sure to release all references

                if (_baseIComInterface != null)
                {
                    Marshal.ReleaseComObject(_baseIComInterface);
                    _baseIComInterface = null;
                }

                if (_baseClassComProxy != null)
                {
                    _baseClassComProxy.Dispose();
                    _baseClassComProxy = null;
                }
            }

            // for testing
            public void InvokeComMethod()
            {
                ((NewLibrary.IComInterface)this).ComMethod(null);
            }
        }

        #region BaseClassComProxy
        // Inner as aggregated object
        class BaseClassComProxy :
            ICustomQueryInterface,
            IDisposable
        {
            WeakReference _outer; // avoid circular refs between outer and inner object
            Type[] _interfaces; // the base's private COM interfaces are here
            IntPtr _unkAggregated; // aggregated proxy

            public BaseClassComProxy(object outer)
            {
                _outer = new WeakReference(outer);
                _interfaces = outer.GetType().BaseType.GetInterfaces();
                var unkOuter = Marshal.GetIUnknownForObject(outer);
                try
                {
                    // CreateAggregatedObject does AddRef on this 
                    // se we provide IDispose for proper shutdown
                    _unkAggregated = Marshal.CreateAggregatedObject(unkOuter, this); 
                }
                finally
                {
                    Marshal.Release(unkOuter);
                }
            }

            public T GetComInterface<T>() where T : class
            {
                // cast an outer's base interface to an equivalent outer's interface
                return (T)Marshal.GetTypedObjectForIUnknown(_unkAggregated, typeof(T));
            }

            public void GetComInterface<T>(out T baseInterface) where T : class
            {
                baseInterface = GetComInterface<T>();
            }

            ~BaseClassComProxy()
            {
                Dispose();
                Console.WriteLine("BaseClassComProxy object finalized.");
            }

            // IDispose
            public void Dispose()
            {
                if (_outer != null)
                {
                    _outer = null;
                    _interfaces = null;
                    if (_unkAggregated != IntPtr.Zero)
                    {
                        Marshal.Release(_unkAggregated);
                        _unkAggregated = IntPtr.Zero;
                    }
                }
            }

            // ICustomQueryInterface
            public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv)
            {
                // access to the outer's base private COM interfaces
                if (_outer != null)
                {
                    var ifaceGuid = iid;
                    var iface = _interfaces.FirstOrDefault((i) => i.GUID == ifaceGuid);
                    if (iface != null && iface.IsImport)
                    {
                        // must be a COM interface with ComImport attribute
                        var unk = Marshal.GetComInterfaceForObject(_outer.Target, iface, CustomQueryInterfaceMode.Ignore);
                        if (unk != IntPtr.Zero)
                        {
                            ppv = unk;
                            return CustomQueryInterfaceResult.Handled;
                        }
                    }
                }
                ppv = IntPtr.Zero;
                return CustomQueryInterfaceResult.Failed;
            }
        }
        #endregion

    }

    class Program
    {
        static void Main(string[] args)
        {
            // test
            var improved = new NewLibrary.ImprovedClass();
            improved.InvokeComMethod(); 

            //// COM client
            //var unmanagedObject = (ISimpleUnmanagedObject)Activator.CreateInstance(Type.GetTypeFromProgID("Noseratio.SimpleUnmanagedObject"));
            //unmanagedObject.InvokeComMethod(improved);

            improved.Dispose();
            improved = null;

            // test ref counting
            GC.Collect(generation: GC.MaxGeneration, mode: GCCollectionMode.Forced, blocking: false);
            Console.WriteLine("Press Enter to exit.");
            Console.ReadLine();
        }

        // COM test client interfaces
        [ComImport(), Guid("2EA68065-8890-4F69-A02F-2BC3F0418561")]
        [InterfaceType(ComInterfaceType.InterfaceIsDual)]
        internal interface ISimpleUnmanagedObject
        {
            void InvokeComMethod([In, MarshalAs(UnmanagedType.Interface)] object arg);
            void InvokeComMethodDirect([In] IntPtr comInterface);
        }

    }
}
BaseClass.IComInterface.ComMethod ImprovedClass.IComInterface.ComMethod Press Enter to exit. BaseClassComProxy object finalized. ImprovedClass finalized.
// IComInterface
void IComInterface.ComMethod(object arg)
{
    InvokeBaseMethod(this, "ComMethod", typeof(OldLibrary.BaseClass), typeof(IComInterface), arg);
}
public static object InvokeBaseMethod(object obj, string methodName, Type baseType, Type equivalentBaseInterface, params object[] arguments)
{
    Type baseInterface = baseType.GetInterfaces().First((t) => t.GUID == equivalentBaseInterface.GUID);
    ComMemberType type = ComMemberType.Method;
    int methodSlotNumber = Marshal.GetComSlotForMethodInfo(equivalentBaseInterface.GetMethod(methodName));
    MethodInfo baseMethod = (MethodInfo)Marshal.GetMethodInfoForComSlot(baseInterface, methodSlotNumber, ref type);
    return baseMethod.Invoke(obj, arguments);
}
    [DllImport("mf.dll", ExactSpelling = true), SuppressUnmanagedCodeSecurity]
    static extern HResult MFCreateMediaSession(
        [In] IMFAttributes pConfiguration,
        [Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MFSessionMarshaler))] out IMFMediaSession ppMediaSession
        );
[ComImport, SuppressUnmanagedCodeSecurity, Guid("c6646f0a-3d96-4ac2-9e3f-8ae2a11145ce")]
[ClassInterface(ClassInterfaceType.None)]
public abstract class _session
{
}

public class session : _session, IMFAsyncCallback
{
    HResult IMFAsyncCallback.GetParameters(out MFASync pdwFlags, out MFAsyncCallbackQueue pdwQueue)
    {
        /// add-on interfaces can use explicit implementation...
    }

    public HResult Invoke([In, MarshalAs(UnmanagedType.Interface)] IMFAsyncResult pAsyncResult)
    {
        /// ...or public.
    }
}
class MFSessionMarshaler : ICustomMarshaler
{
    static ICustomMarshaler GetInstance(String _) => new MFSessionMarshaler();

    public Object MarshalNativeToManaged(IntPtr pUnk) => Marshal.GetTypedObjectForIUnknown(pUnk, typeof(session));

    public void CleanUpNativeData(IntPtr pNativeData) => Marshal.Release(pNativeData);

    public int GetNativeDataSize() => -1;
    IntPtr ICustomMarshaler.MarshalManagedToNative(Object _) => IntPtr.Zero;
    void ICustomMarshaler.CleanUpManagedData(Object ManagedObj) { } }
IMFMediaSession pI;
MFCreateMediaSession(null, out pI);  // get magical RCW

var rcw = (session)pI;   // we happen to know what it really is

pI.ClearTopologies();    // you can call IMFMediaSession members...

((IMFAsyncCallback)pI).Invoke(null);  // and also IMFAsyncCallback.
rcw.Invoke(null);        // same thing, via the backing object