C#中如何重写IDispatch的Invoke方法及包装类暴露Test接口?
解决方案
一、重写IDispatch.Invoke方法
当使用ClassInterfaceType.AutoDual时,.NET会自动生成COM可见的双接口并实现IDispatch,但如果要自定义Invoke逻辑,不能依赖自动生成的实现,需要显式实现IDispatch接口,同时调整类的特性配置:
- 将
ClassInterface特性改为ClassInterfaceType.None,避免自动生成接口冲突; - 显式实现
IDispatch的所有方法(GetIDsOfNames、GetTypeInfo、GetTypeInfoCount、Invoke),重点重写Invoke逻辑。
示例代码:
[ClassInterface(ClassInterfaceType.None)] [ComVisible(true)] public class Test : IDispatch { // 自定义方法,供COM调用 public void OriginalFunc() { Console.WriteLine("OriginalFunc called"); } // 实现IDispatch的其他方法,复用默认逻辑 public void GetIDsOfNames(ref Guid riid, IntPtr rgszNames, uint cNames, uint lcid, IntPtr rgDispId) { var type = GetType(); System.Runtime.InteropServices.ComTypes.IDispatch dispatch = (System.Runtime.InteropServices.ComTypes.IDispatch)type.InvokeMember( null, System.Reflection.BindingFlags.CreateInstance, null, null, null); dispatch.GetIDsOfNames(ref riid, rgszNames, cNames, lcid, rgDispId); } public void GetTypeInfo(uint iTInfo, uint lcid, IntPtr ppTInfo) { var type = GetType(); System.Runtime.InteropServices.ComTypes.IDispatch dispatch = (System.Runtime.InteropServices.ComTypes.IDispatch)type.InvokeMember( null, System.Reflection.BindingFlags.CreateInstance, null, null, null); dispatch.GetTypeInfo(iTInfo, lcid, ppTInfo); } public uint GetTypeInfoCount(out uint pcTInfo) { var type = GetType(); System.Runtime.InteropServices.ComTypes.IDispatch dispatch = (System.Runtime.InteropServices.ComTypes.IDispatch)type.InvokeMember( null, System.Reflection.BindingFlags.CreateInstance, null, null, null); return dispatch.GetTypeInfoCount(out pcTInfo); } // 重写Invoke方法,添加自定义逻辑 public void Invoke(int dispIdMember, ref Guid riid, uint lcid, ushort wFlags, IntPtr pDispParams, IntPtr pVarResult, IntPtr pExcepInfo, IntPtr puArgErr) { // 自定义前置逻辑 Console.WriteLine("Invoke called before target method"); // 调用原始Invoke实现 var type = GetType(); System.Runtime.InteropServices.ComTypes.IDispatch dispatch = (System.Runtime.InteropServices.ComTypes.IDispatch)type.InvokeMember( null, System.Reflection.BindingFlags.CreateInstance, null, null, null); dispatch.Invoke(dispIdMember, ref riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); // 自定义后置逻辑 Console.WriteLine("Invoke called after target method"); } }
关键点说明:
- 改用
ClassInterfaceType.None后,类的公共方法默认依然是COM可见的,除非显式标记[ComVisible(false)]; - 示例中复用了.NET自动生成的IDispatch实现处理其他方法,你也可以完全自定义这些方法的逻辑;
- 在Invoke方法中可以添加拦截、日志、参数修改等自定义逻辑。
二、通过TestWrapper暴露Test的COM接口
由于不能继承Test,需要让TestWrapper的IDispatch实现先尝试处理自身方法,找不到时转发给内部的_test实例。核心是实现IDispatch的GetIDsOfNames和Invoke方法,完成方法路由。
示例代码:
[ClassInterface(ClassInterfaceType.None)] [ComVisible(true)] public class TestWrapper : IDispatch { private readonly Test _test; private readonly System.Runtime.InteropServices.ComTypes.IDispatch _testDispatch; public TestWrapper() { _test = new Test(); // 将_test转换为IDispatch接口,用于转发调用 _testDispatch = (System.Runtime.InteropServices.ComTypes.IDispatch)_test; } // 自身的扩展方法 public void NewFunc() { Console.WriteLine("NewFunc called from TestWrapper"); } public void GetIDsOfNames(ref Guid riid, IntPtr rgszNames, uint cNames, uint lcid, IntPtr rgDispId) { // 先尝试在自身查找方法 try { var type = GetType(); System.Runtime.InteropServices.ComTypes.IDispatch selfDispatch = (System.Runtime.InteropServices.ComTypes.IDispatch)type.InvokeMember( null, System.Reflection.BindingFlags.CreateInstance, null, null, null); selfDispatch.GetIDsOfNames(ref riid, rgszNames, cNames, lcid, rgDispId); return; } catch (Exception) { // 自身找不到,转发给_test _testDispatch.GetIDsOfNames(ref riid, rgszNames, cNames, lcid, rgDispId); } } public void GetTypeInfo(uint iTInfo, uint lcid, IntPtr ppTInfo) { // 返回自身的类型信息,也可合并Test的类型信息 var type = GetType(); System.Runtime.InteropServices.ComTypes.IDispatch selfDispatch = (System.Runtime.InteropServices.ComTypes.IDispatch)type.InvokeMember( null, System.Reflection.BindingFlags.CreateInstance, null, null, null); selfDispatch.GetTypeInfo(iTInfo, lcid, ppTInfo); } public uint GetTypeInfoCount(out uint pcTInfo) { var type = GetType(); System.Runtime.InteropServices.ComTypes.IDispatch selfDispatch = (System.Runtime.InteropServices.ComTypes.IDispatch)type.InvokeMember( null, System.Reflection.BindingFlags.CreateInstance, null, null, null); return selfDispatch.GetTypeInfoCount(out pcTInfo); } public void Invoke(int dispIdMember, ref Guid riid, uint lcid, ushort wFlags, IntPtr pDispParams, IntPtr pVarResult, IntPtr pExcepInfo, IntPtr puArgErr) { // 先尝试调用自身方法 try { var type = GetType(); System.Runtime.InteropServices.ComTypes.IDispatch selfDispatch = (System.Runtime.InteropServices.ComTypes.IDispatch)type.InvokeMember( null, System.Reflection.BindingFlags.CreateInstance, null, null, null); selfDispatch.Invoke(dispIdMember, ref riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); return; } catch (Exception) { // 自身找不到对应的DispId,转发给_test _testDispatch.Invoke(dispIdMember, ref riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); } } }
关键点说明:
- 将内部
_test转换为System.Runtime.InteropServices.ComTypes.IDispatch接口,方便转发COM调用; - GetIDsOfNames中先尝试在TestWrapper自身查找方法名对应的DispId,失败则转发给_test;
- Invoke方法同理,先尝试调用自身方法,失败则转发给_test的Invoke实现;
- 如果需要更严谨的类型信息处理,可以合并TestWrapper和Test的TypeInfo,大部分场景下上述逻辑足够满足需求。
内容的提问来源于stack exchange,提问作者yunate
相关产品推荐
相关产品推荐

