You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中如何重写IDispatch的Invoke方法及包装类暴露Test接口?

解决方案

一、重写IDispatch.Invoke方法

当使用ClassInterfaceType.AutoDual时,.NET会自动生成COM可见的双接口并实现IDispatch,但如果要自定义Invoke逻辑,不能依赖自动生成的实现,需要显式实现IDispatch接口,同时调整类的特性配置:

  1. 将ClassInterface特性改为ClassInterfaceType.None,避免自动生成接口冲突;
  2. 显式实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 12:45:11