现在的位置: 首页 > 综合 > 正文

java 调用第三方dll学习心得

2013年10月11日 ⁄ 综合 ⁄ 共 4794字 ⁄ 字号 评论关闭

     最近由于搞毕业设计的需要,使用J2EE做一个实验预约系统,其中涉及到一卡通和IC卡读卡器,弄来一个刷卡机,厂商只提供了一个用C编写的Windows动态链接库SmartCom411SFJ.dll,我需要使用java程序调用这个dll文件来获取一卡通中的信息。其实我用到的函数很简单,这有三个:

串口初始化函数:int  IniCom(int ComPort,int BaudRate)

读卡信息函数:int ReadPersonalInfo(int ComPort,unsigned char *Name,unsigned char *buffer)

关闭串口函数:int CloseCom(int ComPort)

上网查过很多资料,得知可以使用JNI调用本地DLL文件,网上也有很多朋友提出类似问题,但是看了很多网友的回答结果还是不能解决自己遇到的问题,好多回答都是从别人的网页上copy一些代码,没有讲述如何调用第三方的DLL文件,打开很多网页发现里面讲的内容都是一样的,甚至还有很多代码贴出来都是错误的,根本就没有对提问者的问题做出回答,真正要找的解决办法却很难。

我开始也尝试网上说的办法来做:

1.   java编写一个类,类中使用System.LoadLibrary方法调用动态链接库,同时声明动态链接库中个各个方法。

2.   然后用javac编译成class文件,再用javah生成.h文件。

3.   编写一个C/C++程序,生成java可以直接调用的DLL文件。

4.   把生成的DLL文件何java文件放在一块,重新运行开始写的java程序。

但是问题是很多C中使用的数据类型在java中不能使用,如unsigned char *HANDLE等,如何转换呢?我觉得这是很常用的啊,怎么很少有人回答这种问题呢?也许是我的搜索能力太差了吧,呵呵!

通过几天的努力我终于把问题解决了,我把在编写过程中遇到的一写问题列出来,虽然我的程序有点简单,想跟大家分享一下,希望与我一样困惑的朋友能够用得上。

1.   JAVA程序中,首先声明java要调用的库名称,库的扩展名字可以不用写出来,该库名称不是商家提供的库,名字可以随便去,最好不要和商家提供的库名称一样,否则会出错。还需要对将要调用的方法做本地声明,使用关键字native,只需声明不要具体实现,方法名和参数不需要和商家提供的库中方法一样,况且一些C参数类型也没办法使用java语言表示。例如我的程序SmartCard.java内容如下:

public class SmartCard{

static{

           System.loadLibrary("SmartCard");//后面使用C/C++编写的JAVA能直接调用的库

}

 

//java中需要用到的本地方法声明,从安全上考虑最好把它设成私有

private native int iniCom(int ComPort,int BaudRate);

private native int closeCom(int ComPort);

private native String readPersonalInfo(int ComPort);

 

//外部类能调用的方法

public int iniComTemp(int ComPort,int BaudRate){

           return this.iniCom(ComPort,BaudRate);

}

public int closeComTemp(int ComPort){

           return this.closeCom(ComPort);

}

public String readPersonalInfoTemp(int ComPort){

           return this.readPersonalInfo(ComPort);

}

}

2.   使用javac SmartCard编译生成CLASS文件,再调用javah SmartCard生成C/C++的头文件

比如我的程序生成的.h文件内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class SmartCard */

 

#ifndef _Included_SmartCard

#define _Included_SmartCard

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:     SmartCard

 * Method:    iniCom

 * Signature: (II)I

 */

JNIEXPORT jint JNICALL Java_SmartCard_iniCom

  (JNIEnv *, jobject, jint, jint);

 

/*

 * Class:     SmartCard

 * Method:    closeCom

 * Signature: (I)I

 */

JNIEXPORT jint JNICALL Java_SmartCard_closeCom

  (JNIEnv *, jobject, jint);

 

/*

 * Class:     SmartCard

 * Method:    readPersonalInfo

 * Signature: (I)Ljava/lang/String;

 */

JNIEXPORT jstring JNICALL Java_SmartCard_readPersonalInfo

  (JNIEnv *, jobject, jint);

 

#ifdef __cplusplus

}

#endif

#endif

3.   C/C++中所需做的工作,对于已生成的.h文件,C/C++所需要做的就是把它的各个方法具体实现,然后连接成库文件即可,在方法实现过程中需要用到商家提供的第三方DLL文件,以及转化数据类型。编写是需要把刚才生成的.h文件添加到头文件,另外还要把jdkinclude文件夹下的jni.h以及include/ win32下的jni_md.h添加到编译器中的include中,或者何源文件放在一起,又是会提示jnih找不到,这是你可以把使用javah生成的.h文件中的<jni.h>改成“jni.h”。类型转换及如何调用商家提供的库可分析一下代码,要注意的是这里使用的DLL文件不能与开始java中使用的DLL文件同名。

#include "stdafx.h"

#include "windows.h"

#include "string.h"

#include "SmartCard.h"//该头文件须被包含进来

 

typedef int (_stdcall *INICOM)(int ComPort,int BaudRate);//参数需要何商家提供的DLL文件中方法的参数一致

typedef int (_stdcall *CLOSECOM)(int ComPort);

typedef int (_stdcall *IDREAD)(int ComPort,unsigned char *Name,unsigned char *buffer);

 

HINSTANCE dllHandle;

int result;

//初始化串口方法实现

JNIEXPORT jint JNICALL Java_SmartCard_iniCom(JNIEnv *env, jobject jo, jint ComPort, jint BaudRate){

INICOM pIniCom;

dllHandle = LoadLibrary("SmartCom411SFJ.dll");//商家提供的库文件

pIniCom = (INICOM)GetProcAddress(dllHandle,"IniCom");//寻找商家提供库中对应的方法名

result = pIniCom(ComPort,BaudRate);

FreeLibrary(dllHandle);

return result;

}

//关闭串口方法实现

JNIEXPORT jint JNICALL Java_SmartCard_closeCom(JNIEnv *env, jobject jo, jint ComPort){

CLOSECOM pCloseCom;

dllHandle = LoadLibrary("SmartCom411SFJ.dll");

pCloseCom = (CLOSECOM)GetProcAddress(dllHandle,"CloseCom");

result = pCloseCom(ComPort);

FreeLibrary(dllHandle);

return result;

}

 

JNIEXPORT jstring JNICALL Java_SmartCard_readPersonalInfo(JNIEnv *env, jobject jo, jint ComPort){

IDREAD pIdRead;

unsigned char name[8]="",*na=name;

unsigned char buffer[20]="put card on it",*buf=buffer;

char splitLetter[]="|";

jstring jstr;

 

dllHandle = LoadLibrary("SmartCom411SFJ.dll");

pIdRead = (IDREAD)GetProcAddress(dllHandle,"ReadPersonalInfo");

result = pIdRead(ComPort,name,buffer);

char resultStr[29]="",*reTemp=resultStr;

if(result==0){

           for(int i=0;i<8;i++){

                    *(reTemp+i)=*(na+i);

           }

         *(reTemp+8)=splitLetter[0];

           for(i=9;i<29;i++){

                    *(reTemp+i)=*(buf+i);

           }

           jstr=env->NewStringUTF(resultStr);//返回用namebuffer中的信息,中间用”|”分割开

}else if(result==-6){

           jstr=env->NewStringUTF("-6");

}else if(result==1){

           jstr=env->NewStringUTF("1");

}else if(result==16){

           jstr=env->NewStringUTF("16");

}else{

           jstr=env->NewStringUTF("-2");

}

 

FreeLibrary(dllHandle);

return jstr;

}

4.   编译生成库文件,生成的库文件名需和第一步中的库名一致。在需要使用商家提供的库中方法的java类中调用第一步中声明的对应方法即可。我编写的一个测试类如下:

public class test{

public static void main(String[] args){

           SmartCard sc = new SmartCard();

           int i = sc.iniComTemp(2,0);

           int j = sc.closeComTemp(2);

           String str = sc.readPersonalInfoTemp(2);

           System.out.println(i);

           System.out.println(j);

           System.out.println(str);

}

}

抱歉!评论已关闭.