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
解决方案建议
将静态事件改为实例级事件
移除事件的static修饰符,确保事件属于对象实例,符合COM事件模型要求:public event PlugPlayConnectionChangeEventHandler PlugPlayConnectionChange;正确配置
ComSourceInterfacesAttribute- 优先使用类型引用(若C#项目中可直接引用
_IManagerEvents接口):[ComSourceInterfaces(typeof(_IManagerEvents))] - 若只能用字符串,需指定完整命名空间+接口名或接口GUID:
[ComSourceInterfaces("YourNamespace._IManagerEvents")] // 或使用GUID:[ComSourceInterfaces("{你的_IManagerEvents的GUID}")]
- 优先使用类型引用(若C#项目中可直接引用
实现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; }对齐事件DISPID
在C#事件上添加DispId属性,确保与IDL中定义的DISPID_ONPLUGPLAYCONNECTIONCHANGE = 1一致:[DispId(1)] public event PlugPlayConnectionChangeEventHandler PlugPlayConnectionChange;重新注册COM组件
使用regasm.exe重新注册C#组件,确保元数据正确:regasm.exe YourAssembly.dll /codebase /tlb
内容的提问来源于stack exchange,提问作者Bill Packard
相关产品推荐
相关产品推荐

