Java调用C语言动态库(JNA方式):回调函数、结构体数组传参、结构体数组返回

一、开发环境


系统、开发环境: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代码->声明部分:
  1. import com.sun.jna.Library;
  2. import com.sun.jna.Native;
  3. public class getSDK {
  4. public interface Function extends Library {
  5. Function instanceDll = (Function)Native.loadLibrary( "libSDK.dll",Function.class);
  6. public int System_Init(int version, String encoding);
  7. }
  8. }

java代码->调用部分:

result = getSDK.Function.instanceDll.System_Init(1, "");

参数类型比较简单时,调用也比较容易。

2.回调函数调用

当dll的接口函数需要回调函数作为参数时,使用以下方法实现:
dll接口函数及回调函数类型:

  1. typedef void (*Callback_Status)(char *station_id, char *station_ip, int status);
  2. int Start_Server(char *ip, int port, Callback_Status fun);

java代码->回调函数声明:
  1. import com.sun.jna.win32.StdCallLibrary.StdCallCallback;
  2. public class getSDK {
  3. public interface Callback_Status extends StdCallCallback {
  4. public void Status(String station_id, String station_ip, int status);
  5. }
  6. }
java代码->回调函数实现:
  1. public class getSDK {
  2. public static class Status_Realize implements Callback_Status{
  3. public void Status(String station_id, String station_ip, int status) {
  4. if (status == 2) {
  5. GUI.Station_online(station_id, station_ip, 1, 0);
  6. } else if (status == 3) {
  7. GUI.Station_online(station_id, station_ip, 2, 0);
  8. }
  9. }
  10. }

java代码->dll接口调用:
  1. public static getSDK.Callback_Status callback_status = new getSDK.Status_Realize();
  2. result = getSDK.Function.instanceDll.Start_Server( "0.0.0.0", 1234, callback_status);

3.回调函数含有结构体数组参数

当回调函数其中一个参数是结构体数组时的处理方法:
dll接口函数回调函数类型,结构体类型
  1. typedef struct result_data {
  2. char Station_id[ 3];
  3. char Label_id[ 7];
  4. int Signal;
  5. int Power;
  6. int Status;
  7. }Tstatus, *PTstatus;
  8. typedef void (*Callback_Data)(char *station_id, char *station_ip, Tstatus ret_data[], int count);
  9. int Start_Server (char *server_ip, int listen_port, Callback_Status callback_status, Callback_Data callback_data);
java代码->回调函数、结构体定义:
  1. import com.sun.jna.Structure;
  2. public class getSDK {
  3. public interface Function extends Library {
  4. public static class Tstatus extends Structure {
  5. public byte[] Station_id = new byte[ 3];
  6. public byte[] Label_id = new byte[ 7];
  7. public int Signal;
  8. public int Power;
  9. public int Status;
  10. public static class ByReference extends Tstatus implements
  11. Structure. ByReference {}
  12. public static class ByValue extends Tstatus implements
  13. Structure. ByValue {}
  14. @SuppressWarnings({ "rawtypes", "unchecked" })
  15. @Override
  16. protected List getFieldOrder() {
  17. List a = new ArrayList();
  18. a.add( "Station_id");
  19. a.add( "Label_id");
  20. a.add( "Signal");
  21. a.add( "Power");
  22. a.add( "Status");
  23. return a;
  24. }
  25. }
  26. public int Start_Server(String server_ip, int port, getSDK.Callback_Status Callback_Status,getSDK.Callback_Data Callback_Data);
  27. }
  28. public interface Callback_Data extends StdCallCallback {
  29. public void Data(String station_id, String station_ip, Pointer data, int count);
  30. }
  31. }

java代码->回调函数实现:
  1. public class getSDK {
  2. public static class Data_Realize implements Callback_Data{
  3. public void Data(String station_id, String station_ip, Pointer data, int count) {
  4. int srow, frow;
  5. srow = 0;
  6. frow = 0;
  7. for ( int i = 0; i < count; i++) {
  8. byte[] stationbuf = new byte[ 2];
  9. byte[] labelbuf = new byte[ 6];
  10. int[] signal = new int[ 1];
  11. int[] power = new int[ 1];
  12. int[] status = new int[ 1];
  13. data.read( 0 + i * 24, stationbuf, 0, 2);
  14. data.read( 3 + i * 24, labelbuf, 0, 6);
  15. data.read( 12 + i * 24, signal, 0, 1);
  16. data.read( 16 + i * 24, power, 0, 1);
  17. data.read( 20 + i * 24, status, 0, 1);
  18. String labelID = new String(labelbuf);
  19. String stationID = new String(stationbuf);
  20. GUI.message.append(String.valueOf(power[ 0]));
  21. GUI.message.append(String.valueOf(signal[ 0]));
  22. GUI.message.append(String.valueOf(status[ 0]));
  23. }
  24. }
  25. }
  26. }

java代码->接口调用:
  1. public static getSDK.Callback_Status callback_status = new getSDK.Status_Realize();
  2. public static getSDK.Callback_Data callback_data = new getSDK.Data_Realize();
  3. 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接口函数 类型,结构体类型
  1. typedef unsigned int UINT;
  2. typedef struct code_word {
  3. char Feild[ 8];
  4. BOOL Inverse;
  5. UINT Length;
  6. CHAR Payload[ 255];
  7. }Cword, *pCword;
  8. typedef struct label_data {
  9. char Label_id[ 7];
  10. UINT Count;
  11. Cword NCword[ 50];
  12. }Ldata, *pLdata;
  13. int Start_Update (char *station_id, int count, Ldata data[]);

java代码->结构体定义,接口函数声明:
  1. public class getSDK {
  2. public interface Function extends Library {
  3. public static class Cword extends Structure {
  4. public byte[] Feild = new byte[ 8];
  5. public boolean Inverse;
  6. public int length;
  7. public byte[] Payload = new byte[ 255];
  8. public static class ByValue extends Cword implements
  9. Structure. ByValue {}
  10. @SuppressWarnings({ "rawtypes", "unchecked" })
  11. @Override
  12. protected List getFieldOrder() {
  13. List a = new ArrayList();
  14. a.add( "Feild");
  15. a.add( "X_coord");
  16. a.add( "Y_coord");
  17. a.add( "Font");
  18. a.add( "Inverse");
  19. a.add( "length");
  20. a.add( "Payload");
  21. return a;
  22. }
  23. }
  24. public static class Ldata extends Structure {
  25. public byte[] Label_id = new byte[ 7];
  26. public int Count;
  27. public Cword.ByValue[] NCword = new Cword.ByValue[ 50];
  28. public static class ByReference extends Ldata implements
  29. Structure. ByReference {
  30. }
  31. public static class ByValue extends Ldata implements
  32. Structure. ByValue {
  33. }
  34. @SuppressWarnings({ "rawtypes", "unchecked" })
  35. @Override
  36. protected List getFieldOrder() {
  37. List a = new ArrayList();
  38. a.add( "Label_id");
  39. a.add( "Patten");
  40. a.add( "Count");
  41. a.add( "NCword");
  42. return a;
  43. }
  44. }
  45. public int Start_Update(String station_id, int count, Ldata.ByValue[] data);
  46. }
  47. }

java代码->接口函数调用:
  1. public class GUI {
  2. public static int labelcount = 5;
  3. public static String[] labellist = new String[]{ "000834", "000835", "041908",
  4. "041909", "04190A", "04190B", "04190C", "04190D", "04190E", "04190F", "041910",
  5. "041911", "041912", "041913", "041914", "041915", "041916", "041917", "041918"};
  6. bsend.addActionListener( new ActionListener() {
  7. public void actionPerformed(ActionEvent e) {
  8. int i;
  9. int result;
  10. getSDK.Function.Ldata.ByValue tdata = new getSDK.Function.Ldata.ByValue();
  11. getSDK.Function.Ldata.ByValue[] data = (getSDK.Function.Ldata.ByValue[])tdata.toArray(labelcount);
  12. for (i = 0; i < labelcount; i++) {
  13. data[i].Label_id = labellist[i].getBytes();
  14. }
  15. for (i = 0; i < labelcount; i++) {
  16. data[i].Count = 4;
  17. }
  18. getSDK.Function.Cword.ByValue cdata = new getSDK.Function.Cword.ByValue();
  19. getSDK.Function.Cword.ByValue[] ctdata = (getSDK.Function.Cword.ByValue[])cdata.toArray( 50);
  20. ctdata[ 0].Feild = "Text".getBytes();
  21. ctdata[ 0].Inverse = false;
  22. ctdata[ 0].length = "test".getBytes().length;
  23. ctdata[ 0].Payload = "test".getBytes();
  24. ctdata[ 1].Feild = "Text".getBytes();
  25. ctdata[ 1].Inverse = false;
  26. ctdata[ 1].length = "hello".getBytes().length;
  27. ctdata[ 1].Payload = "hello".getBytes();
  28. ctdata[ 2].Feild = "Text".getBytes();
  29. ctdata[ 2].Inverse = false;
  30. ctdata[ 2].length = "单位".getBytes().length;
  31. ctdata[ 2].Payload = "单位".getBytes();
  32. ctdata[ 3].Feild = "Text".getBytes();
  33. ctdata[ 3].Inverse = false;
  34. ctdata[ 3].length = "规格".getBytes().length;
  35. ctdata[ 3].Payload = "规格".getBytes();
  36. for (i = 0; i < labelcount; i++) {
  37. data[i].NCword = ctdata;
  38. }
  39. result = getSDK.Function.instanceDll.Start_Update((String)station.getSelectedItem(), labelcount, data);
  40. }
  41. });
  42. }

        这里比较关键的是结构体数据的定义,结构体声明中有两个方法:ByValue 和 ByReference, 分别是值和引用,这里使用ByValue方式创建结构体,然后在通过toArray的方式生成结构体数组,为的是保证定义的数组是内存连续的,如果这里直接用:
getSDK.Function.Ldata.ByValue[] tdata = new getSDK.Function.Ldata.ByValue[labelcount];
的方式,得到的内存是不连续的,当你传递这种方式定义的变量给dll时,dll只能取到第一个结构体元素的值,造成数据丢失。

猜你喜欢

转载自blog.csdn.net/wuqianjing/article/details/80867571
今日推荐