C# 如何调用基类中定义的私有COM接口的方法?
如何从派生类调用在基类中定义的私有COM接口的方法 例如,下面是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"
IComInterface
(IDL):
下面是来自OldLibrary
程序集中的C#classBaseClass
,它实现了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