JNA java调用c/c++代码

12 篇文章 0 订阅

最近在搞一个小程序,需要用java调用DLL。用到的技术是JNA。

具体的内容在网上一搜都有,但是很多帖子内容都差不多,而且都有些问题,也不知道是不是版本的问题,反正代码拿来一copy各种问题,倒腾了好久,终于弄出点眉目出来,写来与大家分享下。


首先,c/c++代码如下:

[cpp]  view plain copy
  1. extern "C" _declspec(dllexportint add(int first, int second);  
实现代码:

[cpp]  view plain copy
  1. int add(int first, int second) {  
  2.     printf("(c) test jna : %d + %d = %d\n", first, second, first + second);  
  3.     return first + second;  
  4. }  

封装成DLL,copy到java工程的bin目录下,

在java中使用时,首先需要导入jna的包,过程网上都有,就不多说了,然后在代码中需要添加:

[java]  view plain copy
  1. import com.sun.jna.Library;  
  2. import com.sun.jna.Native;  
  3. import com.sun.jna.NativeLong;  
  4. import com.sun.jna.Platform;  
  5. import com.sun.jna.Pointer;  
  6. import com.sun.jna.Structure;  

调用代码如下:

[cpp]  view plain copy
  1. public class Dll {  
  2.       public interface TestJnaLib extends Library  
  3.       {  
  4.           TestJnaLib INSTANCE = (TestJnaLib)Native.loadLibrary("DLL.dll", TestJnaLib.class);  
  5.           int add(int first, int second);   
  6.       }  
  7.       public static void main(String[] args) {  
  8.           
  9.         TestJnaLib.INSTANCE.add(23, 34);  
  10.            }  

好,第一个没有任何问题,通过。

现在试试结构体:

给DLL 工程中添加这些(教程里用的):

[cpp]  view plain copy
  1. struct UserStruct{  
  2. int id;  
  3. char* name;  
  4. int age;  
  5. };  
  6. extern "C" __declspecdllexport )void sayUser(UserStruct* pUserStruct)  
  7. {  
  8.     printf("%ld %s %d\n",pUserStruct->id,pUserStruct->name,pUserStruct->age);  
  9. }  

按照教程的说法,封装以及调用规则如下:

[java]  view plain copy
  1. public static class UserStruct extends Structure {  
  2.             public int id;  
  3.             public String name;  
  4.             public int age;  
  5.             public static class ByReference extends UserStruct  implements Structure.ByReference { }  
  6.             public static class ByValue extends UserStruct implements Structure.ByValue{ }  
  7.             @Override  
  8.             protected List getFieldOrder() {  
  9.                 List a = new ArrayList();  
  10.                 a.add("id");  
  11.                 a.add("name");  
  12.                 a.add("age");  
  13.                 return a;  
  14.             }  
  15.         }  


注意在教程里没有提到getFieldOrder这个函数,所以编译会报错,只好实现一下,查了一下,这个list返回的是封装结构体中的变量名称,这个一定不能写反(不管是基本变量还是结构体变量,或者是数组什么的,只需要添加名称就可以)。

调用函数声明:

[cpp]  view plain copy
  1. void sayUser(UserStruct.ByReference struct);  

关于:

[java]  view plain copy
  1. public static class ByReference extends UserStruct  implements Structure.ByReference { }  
  2. public static class ByValue extends UserStruct implements Structure.ByValue{ }  


这两个需要说一下,教程里是说显示的说明是值或引用,如果去掉也不影响,前提是定义变量的时候应该这样写:

[java]  view plain copy
  1. TestJnaLib.UserStruct userStruct=new TestJnaLib.UserStruct();  


我测试过了,这个写对结果也没有什么影响,DLL调用成功,不过还是建议加上,这样显得更正确些。

测试代码:

[java]  view plain copy
  1. TestJnaLib.UserStruct.ByReference userStruct=new TestJnaLib.UserStruct.ByReference();  
  2.           userStruct.id=100;  
  3.           userStruct.age=30;  
  4.           userStruct.name=new String("奥巴马");  
  5.           TestJnaLib.INSTANCE.sayUser(userStruct);  

看声明,是传递引用,所以显示的使用了ByReference。

一定注意,在定义变量的时候使用的是UserStruct.ByReference ,因为函数声明的是ByReference。前后一定要一致。

下面是问题最多的一块:结构体数组:

定义如下:

[cpp]  view plain copy
  1. struct CompanyStruct{  
  2. int id;  
  3. char* name;  
  4. int count;  
  5. UserStruct users[10];  
  6. };  
  7. extern "C" __declspecdllexport )void sayCom(CompanyStruct* pCompanyStruct)  
  8. {  
  9.     printf("%ld %s %d\n",pCompanyStruct->id,pCompanyStruct->name,pCompanyStruct->count);  
  10.     for(int i=0;i<10;i++)  
  11.         printf("%ld %s %d\n",pCompanyStruct->users[i].id,pCompanyStruct->users[i].name,pCompanyStruct->users[i].age);  
  12. }   

嵌套结构体:

教程里是这样封装调用的:

[java]  view plain copy
  1. public static class CompanyStruct extends Structure{  
  2. public NativeLong id;  
  3. public WString name;  
  4. public UserStruct.ByValue[] users=new  
  5. UserStruct.ByValue[100];  
  6. public int count;  
  7. }  

但是会报错(除了那个getFiledOrder函数外)。

回头看看,可能是因为版本的问题吧,帖子和教程都是08年的,现在JNA更新到了4.0了,可能对某些东西做了改动,所以会报错。

尝试着这样定义:使用原始的方法定义:

[java]  view plain copy
  1.  public static class CompanyStruct extends Structure{  
  2.   public int id;  
  3.   public String name;  
  4.   public int count;  
  5.   public UserStruct [] users=new UserStruct[10];  
  6.   public static class ByReference extends CompanyStruct implements Structure.ByReference { }  
  7.         public static class ByValue extends CompanyStruct implements Structure.ByValue{ }  
  8. @Override  
  9. protected List getFieldOrder() {  
  10.     List a = new ArrayList();  
  11.        a.add("id");  
  12.        a.add("name");  
  13.        a.add("count");  
  14.        a.add("users");            
  15.        return a;  
  16. }  
  17.  }  
不用byValue了。如果这里用到了byValue或者byReferfece都会报错,即使编译过了,运行也是错的(其中有什么invalid memory 访问等一大堆各种奇葩的问题)。

想想,不管怎样,在c模式下,数组其实跟指针是“相通”的。

函数声明:

[java]  view plain copy
  1. void sayCom(CompanyStruct.ByReference struct);  

调用如下:

[cpp]  view plain copy
  1. TestJnaLib.CompanyStruct.ByReference companyStruct2=new TestJnaLib.CompanyStruct.ByReference();  
  2.      
  3.           companyStruct2.id=1000;  
  4.           companyStruct2.name=new String("xueerfei");  
  5.           companyStruct2.count=10;  
  6.           TestJnaLib.UserStruct pUserStruct=new TestJnaLib.UserStruct();  
  7.           pUserStruct.id=90;  
  8.           pUserStruct.name=new String("xueerfei");  
  9.           pUserStruct.age=99;  
  10.           pUserStruct.write();                  
  11.           int offset = 0;  
  12.           for(int i=0;i<companyStruct2.users.length;i++){  
  13.               companyStruct2.users[i]=pUserStruct;  
  14.           }                
  15.           TestJnaLib.INSTANCE.sayCom(companyStruct2);  


因为函数是Reference声明,所以定义使用ByReference。(注意,如果在java里函数声明是一般声明,那么定义变量时也是用一般声明,虽然在DLL中是指针参数,但是调用DLL经测试是成功的,这点我很纳闷,可能在转换的时候java已经自动识别了吧,保险一下,还是显示的使用声明吧)

注意这里:

[java]  view plain copy
  1. TestJnaLib.UserStruct pUserStruct=new TestJnaLib.UserStruct();  
不用显示的使用Reference,因为在定义的时候使用的是普通的new,所以这里也而且必须使用普通的变量定义。

这样调用就成功了。

其他的说明教程里都有讲的比较详细,就不多说了。

因为技术也在发展,所以教程里的某些东西可能因为更新而被修改,所以阅读的时候还需要自己再实际的测试使用才行。


转载于:http://blog.csdn.net/xueerfei008/article/details/9898657


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值