能否使用JNA调用IMAPI这类复杂Windows DLL及相关实现问题求助
问题根因与修正方案
- 你对COM组件的调用逻辑理解有误,IMAPI是标准COM组件,不需要通过
Native.load加载DLL定义导出函数,COM运行时会根据CLSID自动加载对应依赖,你定义的Imapi2 extends Library部分完全冗余,可以直接删除。 - 你写的
create()是IDiscMaster2实例的唯一合法创建入口,直接调用空构造生成的对象没有绑定真实COM实例指针,调用任何方法都会出错,必须用create()获取实例。 getCount方法的参数处理逻辑完全错误,该方法的入参是[out]类型的输出指针,需要用JNA的LongByReference接收返回值,不能直接把变量值转成Pointer传入。- 调用任何COM接口前必须先初始化当前线程的COM环境,否则
CoCreateInstance会直接返回失败。
修正后代码示例
首先确保你的项目引入了JNA及JNA Platform依赖,COM相关的封装都在Platform包中。
IDiscMaster2定义
import com.sun.jna.platform.win32.COM.COMUtils; import com.sun.jna.platform.win32.COM.Dispatch; import com.sun.jna.platform.win32.Guid.CLSID; import com.sun.jna.platform.win32.Guid.IID; import com.sun.jna.platform.win32.Ole32; import com.sun.jna.platform.win32.WinError; import com.sun.jna.platform.win32.WinNT; import com.sun.jna.platform.win32.WTypes; import com.sun.jna.ptr.LongByReference; import com.sun.jna.PointerByReference; public class IDiscMaster2 extends Dispatch { public static final CLSID CLSID_MsftDiscMaster2 = new CLSID("2735412F-7F64-5B0F-8F00-5D77AFBE261E"); // IDiscMaster2接口IID,CoCreateInstance必须指定要查询的接口ID public static final IID IID_IDiscMaster2 = new IID("2735412E-7F64-5B0F-8F00-5D77AFBE261E"); private IDiscMaster2(Pointer pvInstance) { super(pvInstance); } public static IDiscMaster2 create() { PointerByReference pbr = new PointerByReference(); // 初始化COM环境,单线程内仅需调用一次 WinNT.HRESULT coInitRes = Ole32.INSTANCE.CoInitializeEx(null, Ole32.COINIT_APARTMENTTHREADED); if (COMUtils.FAILED(coInitRes) && coInitRes.intValue() != WinError.RPC_E_CHANGED_MODE) { System.out.println("COM初始化失败,错误码:" + coInitRes.intValue()); return null; } WinNT.HRESULT hres = Ole32.INSTANCE.CoCreateInstance(CLSID_MsftDiscMaster2, null, WTypes.CLSCTX_ALL, IID_IDiscMaster2, pbr); if (COMUtils.FAILED(hres)) { System.out.println("创建实例失败,错误码:" + hres.intValue()); return null; } return new IDiscMaster2(pbr.getValue()); } public long getCount() { LongByReference countRef = new LongByReference(); // vtable索引2对应get_Count方法,索引取值正确 WinNT.HRESULT result = (WinNT.HRESULT) _invokeNativeObject(2, new Object[]{this.getPointer(), countRef}, WinNT.HRESULT.class); COMUtils.checkRC(result); return countRef.getValue(); } // 用完实例后调用释放COM资源 public void release() { this.Release(); Ole32.INSTANCE.CoUninitialize(); } }
调用逻辑
public class TestImapi { public static void main(String[] args) { IDiscMaster2 recorderList = IDiscMaster2.create(); if (recorderList != null) { System.out.println("Found " + recorderList.getCount() + " Recorders"); recorderList.release(); } } }
后续适配说明
- IMAPI的接口都是标准IDispatch派生接口,JNA可以完全适配,包括返回SAFEARRAY的方法,JNA Platform包已经提供了
SAFEARRAY封装类,后续对应方法直接用该类型接收参数即可 - 不需要手动处理imapi2fs.dll的加载,COM运行时会根据你创建的CLSID自动加载依赖组件
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

