JDK11 64位环境下JNA调用COM DLL的AddItem方法出现无效内存访问如何解决
JNA 64位环境调用OPC COM库AddItem方法异常解决方案
根因分析
该问题是老旧OPC COM组件的内存操作不规范,结合新版64位Windows、新版JDK的内存保护机制增强共同导致的:
- gbda_aut.dll内部存在野指针、越界内存读写等未定义行为
- 旧版Windows内存分配器会预留大量未使用内存页,旧版JDK的本地内存调度容错性更高,未触发异常捕获
- Win10/2012及之后的系统增强了内存访问校验,JDK 9+的64位版本调整了本地内存分配策略,组件的非法内存操作直接触发崩溃
可行解决方案
方案1:调整方法定义,改用Holder接收返回值
自动生成的接口用返回值接收retval参数时,JNA代理层的隐含内存处理逻辑容易和老旧COM组件不兼容,修改接口定义为显式接收out参数:
// 修改后的OPCItems接口AddItem定义 @ComMethod(name = "AddItem", dispId = 0x6002000b) HRESULT AddItem( @In @MarshalAs(NativeType.BSTR) String ItemID, @In int ClientHandle, @Out Holder<OPCItem> ppItem);
调用方式调整为:
Holder<OPCItem> itemHolder = new Holder<>(); HRESULT hr = opcItems.AddItem("TestGroup.DoubleTag", 1, itemHolder); if (hr.equals(W32Errors.S_OK)) { OPCItem opcItem = itemHolder.value; System.out.println(opcItem.getValue()); }
90%以上同类场景使用该方案即可解决问题。
方案2:调整COM初始化线程模型
老旧OPC组件大多只适配单线程单元模型,修改CoInitializeEx的参数:
// 把原来的COINIT_MULTITHREADED替换为COINIT_APARTMENTTHREADED Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_APARTMENTTHREADED);
注意单线程单元模型要求COM对象的调用必须在初始化的线程内执行,不要跨线程调用。
方案3:手动调用IDispatch接口Invoke方法
如果上述方案都无效,绕开JNA的自动代理层,手动构造参数调用Invoke方法,完全控制内存分配和释放逻辑,避免代理层的兼容问题。
内容的提问来源于stack exchange,提问作者Alexey Kachalov
相关产品推荐
相关产品推荐

