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

C#替代VB6 OCX后COM事件无法转发至JNI监听器的问题

问题:C#替代VB6 OCX后COM事件无法转发至JNI监听器

用C#替换VB6 OCX项目,基础功能已实现,但COM事件无法转发到JNI监听器。VB6控件提供C++定义的manager和device两个COM对象代理,manager的PlugPlayConnectionChange事件能被C#捕获执行,但无法传递给JNI。

IDL定义

[
    object,
    uuid(GUID),
    dual,
    helpstring("IManager Interface"),
    pointer_default(unique)
]
interface IManager : IDispatch
{
    [propget, id(DISPID_VALUE), helpstring("property device")]
        HRESULT Device(
            [in] long lIndex,
            [out,retval] IDispatch ** ppDevice );
};

enum
{
    DISPID_ONPLUGPLAYCONNECTIONCHANGE = 1
};
[
    uuid(GUID),
    nonextensible,
    helpstring("_IManagerEvents")
]
dispinterface _IManagerEvents
{
    properties:
    methods:
        [id(DISPID_ONPLUGPLAYCONNECTIONCHANGE)]
            HRESULT PlugPlayConnectionChange(
                long laType,
                BSTR bstrID,
                VARIANT_BOOL bAdd);
};

当前C#实现代码

public delegate void PlugPlayConnectionChangeEventHandler(int aType, string id, bool add);

[ComVisible(true)]
[Guid("GUID")]
[ProgId("ManagerProxy")]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[ComSourceInterfacesAttribute("_IManagerEvents")]

public class ManagerProxy : IManager
{
    public static event PlugPlayConnectionChangeEventHandler PlugPlayConnectionChange;

    public ManagerProxy() : base()
    {
            manager = new Manager();
            manager.PlugPlayConnectionChange += MGR_PlugPlayConnectionChange;
    }

    internal void MGR_PlugPlayConnectionChange(int aType, string id, bool add)
    {
        ...
        PlugPlayConnectionChange?.Invoke(aType, id, add);
    }
}

问题原因分析

  • 静态事件不符合COM事件模型:COM事件基于对象实例订阅,静态事件不属于任何实例,JNI作为COM客户端无法绑定订阅
  • ComSourceInterfacesAttribute配置无效:单纯使用字符串_IManagerEvents缺少命名空间或GUID匹配,CLR无法定位到正确的事件接口
  • 未实现JNI适配类:JNI生成的_ManagerProxy和__ManagerProxy是桥接COM事件到Java层的关键包装类,未实现会导致事件无法传递到JNI

解决方案建议

  1. 将静态事件改为实例级事件
    移除事件的static修饰符,确保事件属于对象实例,符合COM事件模型要求:

    public event PlugPlayConnectionChangeEventHandler PlugPlayConnectionChange;
    
  2. 正确配置ComSourceInterfacesAttribute

    • 优先使用类型引用(若C#项目中可直接引用_IManagerEvents接口):
      [ComSourceInterfaces(typeof(_IManagerEvents))]
      
    • 若只能用字符串,需指定完整命名空间+接口名或接口GUID:
      [ComSourceInterfaces("YourNamespace._IManagerEvents")]
      // 或使用GUID:[ComSourceInterfaces("{你的_IManagerEvents的GUID}")]
      
  3. 实现JNI适配的事件接收器
    参考JNI生成的_ManagerProxy和__ManagerProxy,实现事件转发逻辑:

    // 实现_IManagerEvents接口作为事件接收器,转发到JNI
    [ComVisible(true)]
    [Guid("你的_IManagerEvents的GUID")]
    public class ManagerEventsSink : _IManagerEvents
    {
        private readonly IntPtr _jniCallbackHandle;
    
        public ManagerEventsSink(IntPtr jniCallbackHandle)
        {
            _jniCallbackHandle = jniCallbackHandle;
        }
    
        public void PlugPlayConnectionChange(int laType, string bstrID, bool bAdd)
        {
            // 调用JNI方法触发Java层回调,示例逻辑(需根据实际JNI环境调整)
            // JNIEnv.CallVoidMethod(_jniCallbackHandle, _callbackMethodId, laType, bstrID, bAdd);
        }
    }
    
    // 在ManagerProxy中添加JNI订阅方法
    public void RegisterJNIListener(IntPtr jniCallbackHandle)
    {
        var sink = new ManagerEventsSink(jniCallbackHandle);
        PlugPlayConnectionChange += sink.PlugPlayConnectionChange;
    }
    
  4. 对齐事件DISPID
    在C#事件上添加DispId属性,确保与IDL中定义的DISPID_ONPLUGPLAYCONNECTIONCHANGE = 1一致:

    [DispId(1)]
    public event PlugPlayConnectionChangeEventHandler PlugPlayConnectionChange;
    
  5. 重新注册COM组件
    使用regasm.exe重新注册C#组件,确保元数据正确:

    regasm.exe YourAssembly.dll /codebase /tlb
    

内容的提问来源于stack exchange,提问作者Bill Packard

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 17:32:13