java调用第三方dll文件心得

最近在开发j2ee的项目中,需要读取面部识别考勤终端机中的数据,厂商提供二次开发需要的就是一个开发指南说明书和2dll文件:HwDevComm.dllHDCP_Utils.dll。其中重要的就是HwDevComm.dll

用到的函数有3个:

1、  执行各种命令

int HwDev_Execute(  char * pDevInfoBuf, unsigned long nDevInfoLen,

char * pSendBuf, unsigned long nSendLen,

char ** pRecvBuf, unsigned long * pRecvLen,

FuncTotalDoneTp pFuncTotalDone)

2、  释放存放返回数据的内存

void HwDev_Finish(char **pRecvBuf)

3、  服务器端启动或者关闭对特定端口的监听服务,用来接收考勤机主动上传的信息

int HwDev_Server(int nSwith, char *pServerInfoBuf,

unsigned long nServerInfoLen,

FuncProcessData pFuncProcessData)

Java是使用jni来调用本地dll文件的

通过上网查找和自己的摸索,现就自己在开发过程中遇到的问题和解决方法总结如下(下面仅以HwDev_Execute 方法的实现为例)

开发环境:

我的开发环境是:操作系统:window7

                                     开发语言:javavc++

                                     java开发环境:myeclipse6.0jdk1.6

                                     vc++开发环境:visual studio 2008

第一步:java中的准备工作

1、  在myeclipse中创建一个java项目,新建一个类Face

如下图所示:

这个类可以看作是对上面的3个方法的封装类。具体的实现如下所示:

public class Face {

static {

    // 装载库文件face.dll,不包括库文件的扩展名

    // face.dll必须是在java.library.path这一jvm变量所指向的路径下,

    //该路径可以通过System.getProperty("java.library.path");来获得

    System.loadLibrary("face");

  }

  private native String HwDev_Execute(String sDevInfoBuf, long lDevInfoLen, String sSendBuf, long lSendLen, String[] spRecvBuf, long[] lpRecvLen);

   

  //对外接口

public String HwDev_Execute_temp(String sDevInfoBuf, long lDevInfoLen, String sSendBuf, long lSendLen, String[] spRecvBuf, long[] lpRecvLen) {

   return this.HwDev_Execute(sDevInfoBuf, lDevInfoLen, sSendBuf, lSendLen, spRecvBuf, lpRecvLen);

 }

}

 

说明java中使用System.loadLibrary("face")来装载本地库文件,我们将要在下面步骤中生成face.dll文件,java就是通过调用生成的face.dll文件来获得考勤机上的数据。

native修饰的方法是本地方法,java中不提供方法的实现。

2、  用javac编译生成class文件(如果是用myeclipseIDE开发就会自动编译生成class文件),再用javah命令生成.h的头文件。具体的执行如下(在cmd下):

javac Face.java

javah Face

此时,就会在class文件的路径下生成名为Face.h的头文件,头文件的内容如下所示:

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

#include <jni.h>

/* Header for class Face */

 

#ifndef _Included_Face

#define _Included_Face

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:     Face

 * Method:    HwDev_Execute

 * Signature: (Ljava/lang/String;JLjava/lang/String;J[Ljava/lang/String;[J)Ljava/lang/String;

 */

JNIEXPORT jstring JNICALL Java_Face_HwDev_1Execute

  (JNIEnv *, jobject, jstring, jlong, jstring, jlong, jobjectArray, jlongArray);

 

#ifdef __cplusplus

}

#endif

#endif

第二步:vc++的准备工作

1、  打开visual Studio 2008,新建一个项目,选择Win32>win32项目,项目名称为:face。如下图所示:

点击确定,然后点击下一步,进入应用程序设置的页面,如下图所示:

在应用程序类型下选择”DLL”,在附加选项中选中导出符号,点击完成。


生成的项目的目录结构如下图所示:

2、  将Face.h中的内容复制到vc++项目的face.h的中。在face.h 的头文件中包含了jni.h头文件,所以需要将jdk安装目录下include文件夹下的jni.h头文件和include\win32文件夹下的jawt_md.hjni_md.h头文件拷贝到visual studio的安装目录下vc\include文件夹下。我的路径是:C:\Program Files\Microsoft Visual Studio 9.0\VC\include

也可以把jdk下的那三个头文件拷贝到vc++的项目目录下,我的项目目录路径是:

C:\Users\wangzw\Documents\Visual Studio 2008\Projects\face\face,如果是拷贝到了项目的路径下的话,就需要将face.h头文件中的#include <jni.h>改为#include jni.h

3、  在face.cpp中加入如下代码:

#include "stdafx.h"

#include "face.h"

#include <tchar.h>

#include <iostream>

using namespace std;

//参数需要和商家提供的DLL文件中方法的参数一致

typedef int (CALLBACK FuncTotalDoneTp)( unsigned long nTotal, unsigned long nDone );

//参数需要和商家提供的DLL文件中方法的参数一致

typedef int (_stdcall *EXECUTE)(char * pDevInfoBuf,unsigned long nDevInfoLen,char * pSendBuf,unsigned longnSendLen,char ** pRecvBuf,unsigned long * pRecvLen,FuncTotalDoneTp pFuncTotalDone);

//参数需要和商家提供的DLL文件中方法的参数一致

typedef int (_stdcall *FINISH)(char **pRecvBuf);

HINSTANCE dllHandle;

int result;

 

//将jstring 转换为 char*

char* jstringTostring(JNIEnv* env, jstring jstr)

{

       char* rtn = NULL;

       jclass clsstring = env->FindClass("java/lang/String");

       jstring strencode = env->NewStringUTF("utf-8");

       jmethodID mid = env->GetMethodID(clsstring, "getBytes""(Ljava/lang/String;)[B");

       jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);

       jsize alen = env->GetArrayLength(barr);

       jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);

       if (alen > 0)

       {

                 rtn = (char*)malloc(alen + 1);

                 memcpy(rtn, ba, alen);

                 rtn[alen] = 0;

       }

       env->ReleaseByteArrayElements(barr, ba, 0);

       return rtn;

}

 

 

//将char* 转换为 jstring

jstring stoJstring(JNIEnv* env, const char* pat)

{

       jclass strClass = env->FindClass("Ljava/lang/String;");

       jmethodID ctorID = env->GetMethodID(strClass, "<init>""([BLjava/lang/String;)V");

       jbyteArray bytes = env->NewByteArray(strlen(pat));

       env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);

       jstring encoding = env->NewStringUTF("utf-8");

       return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);

}

 

 

//实现方法

JNIEXPORT jstring JNICALL Java_Face_HwDev_1Execute

(JNIEnv * env, jobject obj, jstring sDevInfoBuf, jlong lDevInfoLen, jstring sSendBuf, jlong lSendLen, jobjectArray spRecvBuf, jlongArray lpRecvLen){

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

EXECUTE pExecute = (EXECUTE)GetProcAddress(dllHandle,"HwDev_Execute");//寻找商家提供库中对应的方法名

FINISH pFinish = (FINISH)GetProcAddress(dllHandle,"HwDev_Finish");//寻找商家提供库中对应的方法名

const char* strDeviceInfo1 = (*env).GetStringUTFChars(sDevInfoBuf, 0);

const char* strCmd1 = (*env).GetStringUTFChars(sSendBuf, 0);

char* strDeviceInfo = jstringTostring(env,sDevInfoBuf);

char* strCmd = jstringTostring(env,sSendBuf);

unsigned long nStrDeviceInfoLen = strlen(strDeviceInfo);

unsigned long nStrCmd = strlen(strCmd);

char* pResult = NULL;

unsigned long iResult = 0;

//执行商家提供的方法

result = pExecute(strDeviceInfo,nStrDeviceInfoLen,strCmd,nStrCmd,&pResult,&iResult,0);

printf( "String1 = [%s]\n", pResult );

jstring returnStr = (*env).NewStringUTF(pResult);

//释放资源

//在使用完你所转换之后的对象之后,需要显示调用ReleaseStringUTFChars方法,让JVM释放转换成UTF-8的string的对象的空间,如果不显示的调用的话,JVM中会一直保存该对象,不会被垃圾回收器回收,因此就会导致内存溢出

(*env).ReleaseStringUTFChars(sDevInfoBuf, strDeviceInfo1);

(*env).ReleaseStringUTFChars(sSendBuf, strCmd1);

pFinish(&pResult);

//释放指定的动态链接库

FreeLibrary(dllHandle);

return returnStr;

}

说明:代码具体的实现涉及到jni中的类型和vc++中类型的对应转换的操作,商家提供的dll中的函数参数是c/c++中的类型,而通过java方法传进来的参数通过jni的处理转变为java和c/c++之间的一种过渡类型,比如jstring等,此时,就需要我们调用jni中的对应方法来转换该类型,具体需参照jni文档。

4、  生成dll文件

如下图所示,在解决方案配置中选择 ”Release”,点击 启动调试 按钮。

这时,就会在工程下的Release文件夹下生成face.dll。我的是在:

C:\Users\wangzw\Documents\Visual Studio 2008\Projects\face\Release路径下。

注:在解决方案配置中如果选择Debug,则会在工程下的Debug文件夹下生成face.dll

5、  将dll文件放到java项目中

将生成的 face.dll和商家提供的 HwDevComm.dll文件拷贝到 java.library.path这一 jvm变量所指向的路径下。我的直接放到项目的目录下,如下图:

如果生成face.dll的时候,解决方案配置选择的是Debug,则还需要把

C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\Debug_NonRedist\x86文件夹下的

Microsoft.VC90.DebugCRT文件夹整个都拷贝到 java工程下,如下图所示:

该文件夹下的文件有:

否则,就会报下面的错误:

Exception in thread "main" java.lang.UnsatisfiedLinkError: D:\workspace\zc_project\face\face.dll: 应用程序无法启动,因为应用程序的并行配置不正确。有关详细信息,请参阅应用程序事件日志,或使用命令行 sxstrace.exe 工具。

    at java.lang.ClassLoader$NativeLibrary.load(Native Method)

    at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1751)

    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1676)

    at java.lang.Runtime.loadLibrary0(Runtime.java:823)

    at java.lang.System.loadLibrary(System.java:1030)

    at Face.<clinit>(Face.java:6)

at Client.main(Client.java:5)

在没有安装Visual Studio的机子上运行也有可能出现上面的错误,原因可能是因为机子上没有安装Microsoft Visual C++ 2008 Redistributable -86这款软件,安装了它就可以在没有安装Visual Studio的机子上运行C++程序了。

6、  在java项目中调用dll

写一个测试类,调用Face类中的HwDev_Execute_temp方法,传入相应的参数,如下所示:

public class Client {

 

    public static void main(String[] args) {

       Face face = new Face();

        String sDevInfoBuf = "DeviceInfo( dev_id = \"1\" comm_type = \"ip\" ip_address = \"192.168.0.2\" password = \"123456\")";

       int lDevInfoLen = sDevInfoBuf.length();

       String sSendBuf = "GetEmployee(id=\"2\")";

       int lSendLen = sSendBuf.length();

        String str = face.HwDev_Execute_temp(sDevInfoBuf, lDevInfoLen, sSendBuf, lSendLen, nullnull);

       System.out.println("*******"+str);

    }

}

注:在运行的时候,有可能出现如下的错误:

# An unexpected error has been detected by Java Runtime Environment:

#

#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x6d895268, pid=7944, tid=7880

#

# Java VM: Java HotSpot(TM) Client VM (1.6.0-b105 mixed mode, sharing)

# Problematic frame:

# V  [jvm.dll+0xd5268]

#

# If you would like to submit a bug report, please visit:

#   http://java.sun.com/webapps/bugreport/crash.jsp

该错误很有可能是由于自己写的dll文件有问题,就需要仔细检查生产dll文件的c++代码是否有问题,特别是在资源的释放问题上。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值