JAVA调用SDK动态链接库
一、案例简述
JA V A作为跨平台语言,开发中难免遇到跨平台调用接口方法,近期在项目定制开发过程中,现场需要JA V A平台通过DLL动态链接库设置DVR设备功能,如设置获取DVR设备白名单功能、开启关闭白名单功能、获取设置DVR设备码流是否加密、设置获取DVR设备的AES码流加密信息(包括加密密码、加密方式、加密瞬间等信息)等功能。
二、案例分析和解决过程
初始化DLL动态库
需要通过接口实体初始化DLL动态库,详细定义格式如下:
public interface HCNetSDK extends StdCallLibrary {
public static final String path =
"C:\\Tools\\HCNetSDK_v4.1.5.61_20151015_WIN64\\lib\\HCNetSDK";
HCNetSDK INSTANCE = (HCNetSDK)Native.loadLibrary(path, HCNetSDK.class);
}
其中:path为DLL动态库的路径,定义DLL动态库方法中的对象INSTANCE为DLL 动态库初始化的实例
动态库方法的对象定义需要实现Structure,并重写Structure中的getFieldOrder方法,详细声明如下:
public static class LPNET_DVR_DEVICEINFO_V40 extends Structure {
public NET_DVR_DEVICEINFO_V30 struDeviceV30;
public byte bySupportLock;
public byte byRetryLoginTime;
public byte byPasswordLevel;
public byte byRes1;
public int dwSurplusLockTime;
public byte[] byRes2 = new byte[256];
@Override
protected List getFieldOrder() {
return Arrays.asList(new String[] {"struDeviceV30", "bySupportLock",
"byRetryLoginTime", "byPasswordLevel",
"byRes1", "dwSurplusLockTime", "byRes2"});
}
}
其中:变量属性中byte[]对字节长度有严格要求,必须长度一致,否则调用时会出现JDK源代码错误。
声明DLL动态库中方法
DLL动态库方法中有变量和指针的区别,JA V A中没有指针变量,在定义DLL方法的时候指针的定义需要特别注意:
NativeLong NET_DVR_Login_V30(String sDVRIP, short wDVRPort, String sUserName, String sPassword, LPNET_DVR_DEVICEINFO_V30 lpDeviceInfo);
NativeLong NET_DVR_Login_V40(LPNET_DVR_USER_LOGIN_INFO lpLoginInfo, LPNET_DVR_DEVICEINFO_V40 lpDeviceInfo);
boolean NET_DVR_GetDVRConfig(NativeLong lUserID, int dwCommand, NativeLong lChannel, Pointer lpOutBuffer, int dwOutBufferSize, IntByReference lpBytesReturned);
其中:Pointer lpOutBuffer变量代表指针参数的传入,在传指针的时候要区别变量和指针的区别。
执行方法调用
实例化DLL动态库对象后,直接调用DLL初始化接口中的方法即可:
static HCNetSDK hCNetSDK = HCNetSDK.INSTANCE;
NET_DVR_STREAM_ENC_PARA v30 = new NET_DVR_STREAM_ENC_PARA();
boolean getDVRConfigSuc = true;
v30.write();
getDVRConfigSuc = https://www.wendangku.net/doc/0d11196854.html,_DVR_GetStreamKey(lUserId, v30);
if (!getDVRConfigSuc) {
int ret = https://www.wendangku.net/doc/0d11196854.html,_DVR_GetLastError();
String errorMsg = https://www.wendangku.net/doc/0d11196854.html,_DVR_GetErrorMsg(null);
log.error("获取DVR设备AES码流密码调用SDK时出现错误,错误代码:{},描述:{}", ret, errorMsg);
result.setSuccess(false);
result.setErrorMsg("获取DVR设备AES码流密码调用SDK时出现错误,错误代码:"+ret);
}
v30.read();
//解析v30对象返回信息
其中:byte[]数组转换成String的时候需要多注意,如byte[]数组为ip地址信息,转换成string
时候首先要将每个byte转换成16进制数字,然后再转换成String变量,否则会出现乱码;boolean指针在传入对象时需要通过定制对象的方法传入,否则方法调用时会提示参数回调写入失败。
三、经验总结、预防措施及建议
通过本案例我们可以认识到,JA V A中初始化DLL动态库并不复杂,只需要通过Native.loadLibrary(path)方法将path指向DLL动态库路径就可以初始化,DLL动态库中定义声明的方法的调用和初始化也不复杂,只需要通过抽象类将方法初始化即可,但DLL 方法中涉及到的变量的定义要求非常严格,参数信息稍有不同就会导致方法调用失败,由于JA V A和C++开发语言的不同,对同一个变量的属性定义不同,对接开发时一定要注意,变量的属性类型定义和长度限制。
鉴于JA V A调用DLL动态库时对参数严格性的要求,在开发过程中,我们应仔细查看DLL动态库对接开发文档,注意文档中参数与指针的区别(name和*name),同时方法调用完成后,注意对资源的释放,避免造成资源没有释放,后来的线程全部挂起处于等待状态导致程序崩溃。
四、从本文可导出的检查项(checklist)*
JA V A跨平台调用DLL动态库方法调用JNative
五、参考文献、标准、案例
JA V A调用DLL动态库(JNI)
JA V A调用动态链接库DLL之JNative学习