jna调用原生代码——传递参数

来源:互联网 发布:网络维护员 编辑:程序博客网 时间:2024/06/06 04:47

用java做个小玩意,需要调用windows操作系统的动态链接库提供的API函数。

选来选去最终决定用jna实现调用,简单方便。

 

这个过程中遇到的最大障碍就是怎么传递函数的参数!

ps:原生代码在本文中指x86cpu下运行的c\c++代码。

jre也必须是32位的jre,否则可能发生不明原因的错误。

整理了一个jna与windous API(__stdcall调用约定)的库的数据类型对应表

 

 

其中值得注意的:

1:

c\c++中有无符号整数,java中没有,传递接受无符号的数据时要做转换。

例子:unsigned int xx(unsigned int);

你传递参数-1(0xffffffff),函数xx中会认为你传递的参数的值为4294967295

函数返回2147483648(0x80000000),java会认为函数返回的是-2147483648

2:

windows系统提供的api一般都有两个版本:asci 和 Unicode版本,在调用这些api时可能要明确指定调用哪个版本的api.

例如:GetClassName函数就分别有

int GetClassNameA(HWND hWnd, LPSTR IpClassName, int nMaxCount);

int GetClassNameW(HWND hWnd, LPTSTR IpClassName, int nMaxCount);

两个函数,其中的形参IpClassName就分别是asci 编码和 Unicode编码,都可以支持中文。

jna的人已经在jna-platform中定义了大部分windows API、windows数据类型。用起来很方便。

3:

windows API asci版以java的String替换本地的char。

windows API Unicode版以java的WString替换本地的wchar。

windows API 接受的字符数据默认都是用小端模式存储的,java程序中所有的字符和字符串都被utf-16be转码为java的char储存。

强烈建议严格遵循数据类型的替换表。自己实现这些转换很麻烦。

eclipse可以指定源文件的编码,但编码改变后有可能改不回来,建议保持eclipse的默认设置。

4:

c\c++的结构在java中用Structure表示,如果我想用HWND WindowFromPoint(POINT Point)这个函数,

需要在java代码文件中某接口中定义函数HWND WindowFromPoint(POINT Point);

并定义一个结构,最精简的定义为:

import java.util.ArrayList;import java.util.List;import com.sun.jna.Structure;public class POINT extends Structure{  public int x;  public int y;  @Override  protected List<String> getFieldOrder() {   // 元素的添加顺序必须与结构元素声明的顺序相同,添加的顺序一定不能写错(不管是基本变量还是结构体变量,或者是数组什么的,只需要添加名称就可以   // 这个list返回的是封装结构体中的变量名称   List<String> a = new ArrayList<String>();   a.add("x");   a.add("y");   return a;  }}

注意:getFieldOrder()返回的List集合存储的字符串代表的属性的访问权限必须是public,

在本例中体现为int x;   int y;的访问权限必须是public。

5:

在实际使用jna的过程中中,请严格按照表替换,否则容易出现让人恼火弱智问题。

我就曾出过一些很弱智的问题,

例如:c\c++中定义了一种BOOL类型,jna也定义了一个BOOL类,但如果java中声明BOOL类型为某函数的返回值类型,这个函数对应c\c++返回值为BOOL的函数。

调用这个函数,无论接不接收返回值,都会报错 java.lang.IllegalArgumentException: Unsupported size: 0。正确的java函数的返回值类型可以是int或 boolean。

例如:原生代码有一种与平台相关的long类型,jna也定义了一种LONG类型,这很容易混淆,但正确的匹配类型是jna的NativeLong。

 

如果原生代码类型没有相应的java类型替换,或者嫌麻烦不想用相应的java类型可以用数组类型代替。

java数组类型可以替换大部分的本地代码的参数的数据类型,但是相应的就必须自行完成部分大小端的转换。不推荐

 

jna到本地代码函数的过程中可能涉及到数据在java大端模式(BIG-ENDIAN)到x86cpu小端模式(LITTLE-ENDIAN)的相互转换。

 

6:

jna还可以支持函数指针和回调函数,回调函数在jan-platform中有定义。

其余的我没有用到,不做讨论。