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);
}
}