Java调用C语言动态库(JNA方式):回调函数、结构体数组传参、结构体数组返回
2013-12-17 16:13
447 查看
一、开发环境
系统、开发环境:win7、eclipse 32位、jdk 32位、jre 32位由于这里使用的dll文件是32位的,而我本身的环境是64位的,包括eclipse、jdk、jre都是64位,所以这里需要开发环境共存(32位、64位共存),如果本来就是32位环境就不用重新搭建环境了。从以下连接分别下载32位软件:
1.eclipse,不用安装,解压后即可使用,解压目录:D:\eclipse
Eclipse IDE for Java EE Developers, 247 MB Windows 32 Bit
http://www.eclipse.org/downloads
2.jdk 32位,安装目录:D:\Program Files\java\jdk32
Windows x86 123.49MB jdk-7u45-windows-i586.exe
http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html
3.jre 32位,安装目录:D:\Program Files\java\jre32
Windows Offline(32-bit) fileSize: 27.7MB
http://www.java.com/en/download/manual.jsp
要让32位和64位eclipse共存,需要修改eclipse目录下的文件:eclipse.ini
在原环境变量(JAVA_HOME指向64位目录)不改变的情况下在上面文件内增加两行:
-vm
D:\Program Files\java\jdk32\bin\javaw.exe
以上的作用使32位eclipse指向32位jdk。
4.JNA下载,这里是放在百度云里面的,下载两个文件:jna-3.5.1.jar、platform-3.5.1.jar,放到目录:D:\Program Files\java\JNA_3.5.1
http://pan.baidu.com/s/1iWobp
至于JNA是什么,它本身是基于JNI技术的,然后进行封装以方便dll调用。这里不再详述,网上很多。
二、调用方法
1.简单函数调用
关于jar包导入就不提了,创建工程:DemoTools,这里使用的dll是:libSDK.dll,把该文件放到工程目录下。dll提供的接口函数:
int System_Init (int version, char* encoding);
java代码->声明部分:
import com.sun.jna.Library; import com.sun.jna.Native; public class getSDK { public interface Function extends Library { Function instanceDll = (Function)Native.loadLibrary("libSDK.dll",Function.class); public int System_Init(int version, String encoding); } }
java代码->调用部分:
result = getSDK.Function.instanceDll.System_Init(1, "");
参数类型比较简单时,调用也比较容易。
2.回调函数调用
当dll的接口函数需要回调函数作为参数时,使用以下方法实现:dll接口函数及回调函数类型:
typedef void (*Callback_Status)(char *station_id, char *station_ip, int status); int Start_Server(char *ip, int port, Callback_Status fun);
java代码->回调函数声明:
import com.sun.jna.win32.StdCallLibrary.StdCallCallback; public class getSDK { public interface Callback_Status extends StdCallCallback { public void Status(String station_id, String station_ip, int status); } }
java代码->回调函数实现:
public class getSDK { public static class Status_Realize implements Callback_Status{ public void Status(String station_id, String station_ip, int status) { if (status == 2) { GUI.Station_online(station_id, station_ip, 1, 0); } else if (status == 3) { GUI.Station_online(station_id, station_ip, 2, 0); } } } }
java代码->dll接口调用:
public static getSDK.Callback_Status callback_status = new getSDK.Status_Realize(); result = getSDK.Function.instanceDll.Start_Server("0.0.0.0", 1234, callback_status);
3.回调函数含有结构体数组参数
当回调函数其中一个参数是结构体数组时的处理方法:dll接口函数、回调函数类型,结构体类型:
typedef struct result_data { char Station_id[3]; char Label_id[7]; int Signal; int Power; int Status; }Tstatus, *PTstatus; typedef void (*Callback_Data)(char *station_id, char *station_ip, Tstatus ret_data[], int count); int Start_Server (char *server_ip, int listen_port, Callback_Status callback_status, Callback_Data callback_data);
java代码->回调函数、结构体定义:
import com.sun.jna.Structure; public class getSDK { public interface Function extends Library { public static class Tstatus extends Structure { public byte[] Station_id = new byte[3]; public byte[] Label_id = new byte[7]; public int Signal; public int Power; public int Status; public static class ByReference extends Tstatus implements Structure.ByReference {} public static class ByValue extends Tstatus implements Structure.ByValue {} @SuppressWarnings({ "rawtypes", "unchecked" }) @Override protected List getFieldOrder() { List a = new ArrayList(); a.add("Station_id"); a.add("Label_id"); a.add("Signal"); a.add("Power"); a.add("Status"); return a; } } public int Start_Server(String server_ip, int port, getSDK.Callback_Status Callback_Status,getSDK.Callback_Data Callback_Data); } public interface Callback_Data extends StdCallCallback { public void Data(String station_id, String station_ip, Pointer data, int count); } }
java代码->回调函数实现:
public class getSDK { public static class Data_Realize implements Callback_Data{ public void Data(String station_id, String station_ip, Pointer data, int count) { int srow, frow; srow = 0; frow = 0; for (int i = 0; i < count; i++) { byte[] stationbuf = new byte[2]; byte[] labelbuf = new byte[6]; int[] signal = new int[1]; int[] power = new int[1]; int[] status = new int[1]; data.read(0 + i * 24, stationbuf, 0, 2); data.read(3 + i * 24, labelbuf, 0, 6); data.read(12 + i * 24, signal, 0, 1); data.read(16 + i * 24, power, 0, 1); data.read(20 + i * 24, status, 0, 1); String labelID = new String(labelbuf); String stationID = new String(stationbuf); GUI.message.append(String.valueOf(power[0])); GUI.message.append(String.valueOf(signal[0])); GUI.message.append(String.valueOf(status[0])); } } } }
java代码->接口调用:
public static getSDK.Callback_Status callback_status = new getSDK.Status_Realize(); public static getSDK.Callback_Data callback_data = new getSDK.Data_Realize(); result = getSDK.Function.instanceDll.Start_Server("0.0.0.0", 1234, callback_status, callback_data);
这里的难点就在处理回调函数的结构体数组参数,参数是从dll传出来的,java里面用指针接收,而获取结构体内部的每个值就需要用读内存的方式读取出来,这里就需要对结构体在内存中的存储方式比较了解才行,因为read函数需要指定:内存起始位置、接收数据数组变量、数组变量的第几个元素、取内存大小,4个参数。
上面源码中i*24作用是取到结构体数组的第二、第三、、、个元素。也就是这里的结构体占内存大小为24,看下面:
在32位系统中char占1个字节,int占4个字节,算下来结构体TStatus应该是22个字节,实际上还有一个内存对齐的概念,结构体前两个char数组共10个字节(是连续存储的),而结构体分配内存的大小必须是该结构体中占字节最大类型的倍数,所以应该分配4字节的倍数即12字节,所以前两个char数组实际分配了12个字节,在取数据时,第三个int的偏移量是12,整个结构体的大小为12 + 3*4(三个int变量)= 24字节
4.接口函数参数为结构体数组
当接口函数的参数为结构体数组,并且结构体成员也为结构体数组时,调用方法:dll接口函数类型,结构体类型:
typedef unsigned int UINT; typedef struct code_word { char Feild[8]; BOOL Inverse; UINT Length; CHAR Payload[255]; }Cword, *pCword; typedef struct label_data { char Label_id[7]; UINT Count; Cword NCword[50]; }Ldata, *pLdata; int Start_Update (char *station_id, int count, Ldata data[]);
java代码->结构体定义,接口函数声明:
public class getSDK { public interface Function extends Library { public static class Cword extends Structure { public byte[] Feild = new byte[8]; public boolean Inverse; public int length; public byte[] Payload = new byte[255]; public static class ByValue extends Cword implements Structure.ByValue {} @SuppressWarnings({ "rawtypes", "unchecked" }) @Override protected List getFieldOrder() { List a = new ArrayList(); a.add("Feild"); a.add("X_coord"); a.add("Y_coord"); a.add("Font"); a.add("Inverse"); a.add("length"); a.add("Payload"); return a; } } public static class Ldata extends Structure { public byte[] Label_id = new byte[7]; public int Count; public Cword.ByValue[] NCword = new Cword.ByValue[50]; public static class ByReference extends Ldata implements Structure.ByReference { } public static class ByValue extends Ldata implements Structure.ByValue { } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override protected List getFieldOrder() { List a = new ArrayList(); a.add("Label_id"); a.add("Patten"); a.add("Count"); a.add("NCword"); return a; } } public int Start_Update(String station_id, int count, Ldata.ByValue[] data); } }
java代码->接口函数调用:
public class GUI { public static int labelcount = 5; public static String[] labellist = new String[]{"000834","000835","041908", "041909","04190A","04190B","04190C","04190D","04190E","04190F","041910", "041911","041912","041913","041914","041915","041916","041917","041918"}; bsend.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int i; int result; getSDK.Function.Ldata.ByValue tdata = new getSDK.Function.Ldata.ByValue(); getSDK.Function.Ldata.ByValue[] data = (getSDK.Function.Ldata.ByValue[])tdata.toArray(labelcount); for (i = 0; i < labelcount; i++) { data[i].Label_id = labellist[i].getBytes(); } for (i = 0; i < labelcount; i++) { data[i].Count = 4; } getSDK.Function.Cword.ByValue cdata = new getSDK.Function.Cword.ByValue(); getSDK.Function.Cword.ByValue[] ctdata = (getSDK.Function.Cword.ByValue[])cdata.toArray(50); ctdata[0].Feild = "Text".getBytes(); ctdata[0].Inverse = false; ctdata[0].length = "test".getBytes().length; ctdata[0].Payload = "test".getBytes(); ctdata[1].Feild = "Text".getBytes(); ctdata[1].Inverse = false; ctdata[1].length = "hello".getBytes().length; ctdata[1].Payload = "hello".getBytes(); ctdata[2].Feild = "Text".getBytes(); ctdata[2].Inverse = false; ctdata[2].length = "单位".getBytes().length; ctdata[2].Payload = "单位".getBytes(); ctdata[3].Feild = "Text".getBytes(); ctdata[3].Inverse = false; ctdata[3].length = "规格".getBytes().length; ctdata[3].Payload = "规格".getBytes(); for (i = 0; i < labelcount; i++) { data[i].NCword = ctdata; } result = getSDK.Function.instanceDll.Start_Update((String)station.getSelectedItem(), labelcount, data); } }); }
这里比较关键的是结构体数据的定义,结构体声明中有两个方法:ByValue 和 ByReference, 分别是值和引用,这里使用ByValue方式创建结构体,然后在通过toArray的方式生成结构体数组,为的是保证定义的数组是内存连续的,如果这里直接用:
getSDK.Function.Ldata.ByValue[] tdata = new getSDK.Function.Ldata.ByValue[labelcount];的方式,得到的内存是不连续的,当你传递这种方式定义的变量给dll时,dll只能取到第一个结构体元素的值,造成数据丢失。
相关文章推荐
- java调用ORACLE存储过程时数组传参,返回数组
- Java 通过 JNA 调用 DLL 返回 char * 字符串乱码问题的解决
- java程序调用C、C++动态库的几种实现方式,即JNI的应用方式
- java通过JNA调用c语言dll
- C语言中结构体的三种初始化方式以及结构体作为传参传递
- 使用jQuery匹配文档中所有的li元素,返回一个jQuery对象,然后通过数组下标的方式读取jQuery集合中第1个DOM元素,此时返回的是DOM对象,然后调用DOM属性innerHTML,读取该元素 包含的文本信息
- java 调用dll的 指针类型结构体及指针数组类型
- Java的native方法返回数组return Array(C语言)
- 使用java传参调用exe并且获取程序进度和返回结果的一种方法
- java 调用存储过程 传入和返回全部是自定义数组
- Java调用C语言Dll库回调函数
- JAVA调用ORACLE带数组输入参数和返回游标结果集的存储过程
- jnaerator:java调用动态库的神器,JNA代码自动生成工具
- JAVA调用C++方式------JNA
- JNA结构体参数传递,Java数组
- MFC应用程序调用控件中返回值之结构体和数组(控件由C++写成)
- java调用webservice天气预报(SOAP请求的方式获取天气信息并解析返回的XML)
- 分享:写了一个 java 调用 C语言 开发的动态库的范例
- Java调用本地C/C++动态库拾遗 JNI/JNA与名称粉碎
- 使用java传参调用exe并且获取程序进度和返回结果的一种方法