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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 18:48:03