【Java Native】【JNI技术的实践(使用VS2010)】

来源:互联网 发布:2018优化重组数学答案 编辑:程序博客网 时间:2024/06/05 23:06

        因为现在平台的脚本都从C++迁移到java上来,我们需增加Java对-X读卡器的支持,但是-X读卡器并不是标准的PC/SC设备,里面包含了公司自己的通讯协议。因此我们不能使用现有的开源的jpcsc库来开发,而必须使用Java Native技术来实现软件与-X读卡器的通讯。

       现在手里有-X读卡器通讯的dll文件:wdcrwx.dll。虽然说只有要dll文件就可以开发了,但是实际上这样需要做很多函数的声明,使用起来非常麻烦。因此还需要找到对应的lib文件和头文件:wdcrwx.lib、wdcrwx.h。

       下面开始正式编码:

1.  JAVA端的工作

第1步,在java程序中,首先声明需要在类中所调用的库的名称,如下:

static{

       System.loadLibrary("Promotion_crwx");

    }

在这里,库的扩展名不用写,究竟使用的是DLL还是SO,由系统判断。

第2步,对Native方法进行声明,如下:

publicnative static int readerOpen(String name);

publicnative static int readerClose(int fd);

publicnative static String cardReset(int fd);

publicnative static byte[] sendCmd(int fd,byte[] Cmd);

publicnative static void setNad(int fd, byte nad);

第3步,编译该java程序,生成CLASS。再用javah命令,生成JNI的头文件。

注意,编译的时候一定要加上路径,要不然会提示找不到类。例如上面的类的类名是:CRWXService,包名是:com.watchdata.framework.cardcomm.crwx。因此需要这样编译:

>javac–d ./ CRWXService.java

>javah –classpath./ com.watchdata.framework.cardcomm.crwx.CRWXService

在当前java文件目录下生成com文件夹下,按包路径深入文件夹可以找到CRWXService.class。同时,在当前java文件目录下也生成了对应的头文件:com_watchdata_framework_cardcomm_crwx_CRWXService.h

到这里,java端的工作完毕,开始C/C++端的工作

2.  C/C++端的工作

之前有个jni的工程C/C++端是用VC6.0写的,但是我总觉得没有必要要那么老的IDE吧,虽然经典,但是太丑,又那么不智能,为什么不用visual studio呢。何况小飞他们的java虚拟机的C工程都是用vs2010建的,至少说明vs还是挺好用的吧。

 第1步,在vs2010中建立名称为Promotion_crwx的win32控制台应用程序。如下所示:

第2步,下一步,设置应用程序类型:DLL,附加选项:导出符号。如下图所示:

点击完成。

第3步,将 生成的com_watchdata_framework_cardcomm_crwx_CRWXService.h拷贝到工程下,引入该头文件。这时会报“无法识别符号JNIEXPORT”类似的错误,因为他们是jni头文件声明的关键字。我们需要引入jni的库,或者拷贝相应的头文件到工程下引入。我采用的是第2种:拷贝%jdk%\include\win32\jni_md.h和%jdk%\include\jni.h到工程中引入。

将com_watchdata_framework_cardcomm_crwx_CRWXService.h中#include <jni.h>改为#include “jni.h”。编译通过,生成dll文件。但是dll文件中只有示例中的接口,并不是我们想要的接口。下面开始修改。

第4步,删除Promotion_crwx.h和Promotion_crwx.cpp中的所有内容(这些都是示例代码,我们不需要)。然后在stdafx.h中写入:#include “com_watchdata_framework_cardcomm_crwx_CRWXService.h",在Promotion_crwx.cpp中实现该头文件中函数,为了验证我们是否成功调用,我们在每个函数里都加上一段打印字符串。如下图所示:


         这个时候重新生成Promotion_crwx.dll文件。通过dll查看器,我们看到生成的接口都以””_java”开头,名称中含有包路径和类名,如下图所示:

第5步,为了验证我们的dll是可以被调用的,我们需要先测试一下。

首先,将dll文件拷贝到java.library.path目录下,或者在运行java时设置java.library.path的路径。如下图所示:

(注意:在在eclipse中运行java代码时,java. library.path指向的是我的lib文件夹;但是将java代码打包或者在控制台单独运行的时候,java. library.path包含了system32目录,所以只要将dll文件拷贝到system32下就可以了。如果不确定java. library.path指向的是哪里,可以用

System.out.println("java. library.path=" +System.getProperty("java.library.path"));

打印出来看看。)

然后在main方法中调用java端的native方法,运行结果中输出了我们期望的字符串说明调用成功。如下图所示:

第6步,如果是普通的jni,我们到此就完成了。但因为我们需要调用其他的dll,因此还需要下面这一步。将wdcrwx.dll、wdcrwx.lib、wdcrwx.h拷贝到工程目录下,引入头文件wdcrwx.h,引入资源文件wdcrwx.dll、wdcrwx.lib。并在stdafx.h中写入:#include "wdcrwx.h"。然后我们就可以在Promotion_crwx.cpp中调用dll中的函数了。下面是实现后的一个截图: