java通过JNA调用动态库
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
java通过JNA调⽤动态库
前⾔
⽼规矩,先说下为什么会有这篇⽂章。
近期对接了⼀个项⽬,应接⼝提供⽅要求,必须通过动态库调⽤,⼀个是为了安全可控,调⽤⽅不⽤知道内部实现,加密、解密、具体的逻辑不需要考虑,只需要调⽤即可;另⼀个是封装了统⼀的GUI界⾯。
总之就是⾮⽤动态库不可,然后我查了很多资料,请教了⼏个⼤佬,最后在运⽓的加持下,终于调通了,但整个过程特别坎坷,所以我觉有必要记录下。
需要说明的是我们这⾥采⽤的是JNA的⽅式
什么是动态库
说实话,⼀般我们不会有调⽤动态库的需求,因为这不是web开发的范畴,出发你涉及到嵌⼊式的开发,或者客户端开发。
动态库也叫动态链接库,英⽂简写DLL,简单来讲,就是Windows下开发的程序模块,类似于java下的jar(不知道可不可以这样理解)。
它是实现Windows应⽤程序共享资源、节省内存空间、提⾼使⽤效率的⼀个重要技术⼿段。
windows下它是以dll结尾的⽂件,⽐如:msctf.dll
百度百科给的解释是这样的:
动态链接库英⽂为DLL*,是Dynamic Link Library*的缩写。
DLL是⼀个包含可由多个程序,同时使⽤的代码和数据的库。
在中,这种⽂件被称为应⽤程序拓展。
例如,在操作系统中,执⾏与对话框有关的常见函数。
因此,每个程序都可以使⽤该 DLL 中包含的功能来实现“打开”对话框。
这有助于避免代码重⽤和促进内存的有效使⽤。
通过使⽤ DLL,程序可以实现模块化,由相对独⽴的组件组成。
例如,⼀个计账程序可以按模块来销售。
可以在运⾏时将各个模块加载到主程序中(如果安装了相应模块)。
因为模块是彼此独⽴的,所以程序的加载速度更快,⽽且模块只在相应的功能被请求时才加载。
咱也不是特别知道,咱也不敢问,你现在只有保证知道动态库这样的东西就⾏了。
开整
Talk is cheap. Show me the code。
先上代码,然后再解释
public class DllTest {
static {
String filePath = "D:\\dll\\"; // 这⾥是你的动态库所在⽂件夹的绝对路径
// 这⾥引⽤动态库和他的依赖
System.load(filePath + "mfc100.dll");
System.load(filePath + "mydll.dll");
}
public static void main(String[] args) {
String strUrl = "http://127.0.0.1/test";
String InData = "{\"data\":{\"operatorId\":\"test001\",\"operatorName\":\"超级管理员\",\"orgId\":\"123\"},\"orgId\":\"1232\"}";
byte[] OutData = new byte[1024];
String msg = CLibrary.INSTANCE.test(strUrl.getBytes(), InData.getBytes(), OutData);
System.out.println(msg);
try {
System.out.println(new String(OutData, "GBK"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
// 这⾥是最关键的地⽅
public interface CLibrary extends Library {
// FS_CheckCode是动态库名称,前⾯的d://test//是路径
CLibrary INSTANCE = (CLibrary) Native.loadLibrary("mydll", CLibrary.class);
// 我们要调⽤的动态库⾥⾯的⽅法。
String test(byte[] strUrl, byte[] InData, byte[] OutData);
}
}
动态库⾥⾯的⽅法是这么定义的:
char* __stdcall test(char* strUrl,char* InData,char* OutData)
解释
⼩朋友,你是否有很多的问号 没事,接下来我们就详细说明下。
⾸先要关注的是java定义动态库接⼝⽅法,对应代码:
public interface CLibrary extends Library {
// FS_CheckCode是动态库名称,前⾯的d://test//是路径
CLibrary INSTANCE = (CLibrary) Native.loadLibrary("mydll", CLibrary.class);
// 我们要调⽤的动态库⾥⾯的⽅法。
String test(byte[] strUrl, byte[] InData, byte[] OutData);
}
其中,loadLibrary⽅法是创建动态库对象实例,第⼀⼊参是你要调⽤的动态库的名字,test⽅法对应动态库中的⽅法,这⾥需要注意的是jNA 和动态库直接数据类型的对应关系,具体的对应看后⾯的附表。
这⾥还有⼀个需要注意的问题是是,动态库加载的问题:
// 这⾥引⽤动态库和他的依赖
System.load(filePath + "mfc100.dll");
System.load(filePath + "mydll.dll");
如果你没有把动态库放到classpath下,⽽且没有上⾯加载的代码,会报如下错误:
Exception in thread "main" ng.UnsatisfiedLinkError: Unable to load library 'NationECCode':
找不到指定的模块。
遇到的问题
JDK版本
如果完成了相应改造⼯作,你就可以直接运⾏了。
如果你的JDK是64位,但你的动态库是32位(X86),它肯定会报如下错误:
ng.UnsatisfiedLinkError: D:\workspace\learning\github\httputil-demo\dll\mydll.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform
at ng.ClassLoader$NativeLibrary.load(Native Method)
at ng.ClassLoader.loadLibrary0(ClassLoader.java:1941)
at ng.ClassLoader.loadLibrary(ClassLoader.java:1824)
at ng.Runtime.load0(Runtime.java:809)
at ng.System.load(System.java:1086)
或者这样:
Exception in thread "main" ng.UnsatisfiedLinkError: %1 不是有效的 Win32 应⽤程序。
at com.sun.jna.Native.open(Native Method)
at com.sun.jna.NativeLibrary.loadLibrary(NativeLibrary.java:278)
at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:455)
at com.sun.jna.Library$Handler.<init>(Library.java:179)
at com.sun.jna.Native.loadLibrary(Native.java:646)
at com.sun.jna.Native.loadLibrary(Native.java:630)
当然如果你的动态库是64位,JDK是32位(X86),同样也会报错(应该会,没试过)
解决⽅法很简单:
更换JDK版本
联系动态库封装的⼈,重新封装对应的版本
找不到模块
这个问题上⾯已经说过了,就是因为没有加载动态库⽂件,⽽且也没有把它和它的依赖⽂件放到classpath下,就会报这个错。
数据类型错误
这个问题本质上就是没有搞清楚JNA和动态库数据对应关系,我之前也没搞清楚,反复试了好多次才成功。
然后在今天写这篇⽂章的时候,发现了char*作为出参和⼊参对应的类型是不⼀样的,才恍然⼤悟。
希望⼩伙伴在⾃⼰搞的时候⼀定看清楚。
出参未分配空间
和java不⼀样,动态库⽅法是把⼊参传给⽅法的,⽽且需要给出参分配空间,如果不分配内存空间,会报错:
ng.Error: Invalid memory access
这个问题也很好解决,就是给出参分配⾜够的空间:
byte[] OutData = new byte[1024];
到这⾥,所有问题都解决了,动态库也完美运⾏起来了。
总结
收获就⼀句话:对于⾃⼰没有做过的事,要积极尝试,积极思考,积极请教,然后问题解决后要积极分享。
好了,祝⼤家周末愉快!
JNA和动态库类型之间的映射关系:
Native Type Size Java Type Common Windows Types
char8-bit integer byte BYTE, TCHAR
Native Type Size Java Type Common Windows Types short16-bit integer short WORD
wchar_t16/32-bit character char TCHAR
int32-bit integer int DWORD
int boolean value boolean BOOL
long32/64-bit integer NativeLong LONG
long long64-bit integer long__int64
float32-bit FP float
double64-bit FP double
char* C string String LPCSTR
void*pointer Pointer LPVOID, HANDLE, LP XXX
补充表:
Native Type Java Type Native Representation char byte8-bit integer
wchar_t char16/32-bit character
short short16-bit integer
int int32-bit integer
int boolean32-bit integer (customizable) long, __int64long64-bit integer
long long long64-bit integer
float float32-bit FP
double double64-bit FP
pointer Buffer/Pointer
pointer array[] (array of primitive type)
char*String
wchar_t*WString
char**String[]
wchar_t**WString[]
void*Pointer
void **PointerByReference
int&IntByReference
int*IntByReference
struct Structure
(*fp)()Callback
varies NativeMapped
long NativeLong
pointer PointerType
然后⼜找到⼀些补充资料:
C语⾔Java
char*String (作为⼊⼝参数)/ byte[] (作为出⼝参数)
unsigned char*String (作为⼊⼝参数)(不确定,没具体使⽤过)/ Pointer (作为出⼝参数)
int*IntByReference。