分享

java 调用第三方dll学习心得

 血色残阳梦 2011-06-03
     最近由于搞毕业设计的需要,使用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文件添加到头文件,另外还要把jdk中include文件夹下的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);//返回用name和buffer中的信息,中间用”|”分割开
}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);
}
}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多