Java调用DLL动态链接库的JNI方式详解(一)

2014-11-24 07:53:43 · 作者: · 浏览: 0

使用Java调用DLL动态链接库的方案通常有几种方式:JNI, JNative,Jawin, Jacob.其中 JNative,Jawin, 调用方式很简单,google一下即刻解决,但是这两种方式都已不支持64系统, Jacob是Java-Com Bridge的缩写,也可以用来调用DLL。其底层也是使用JNI实现,也具有Windows 的平台依赖性,但是要求dll是可注册的,所以也有一定局限性;

回到最初的调用方式 :JNI(Java Native Interface)是Java语言本身提供的调用本地已编译的函数库的方法,本身具有跨平台性,可以在不同的机器上调用不同的本地库, JNI的应用方案是基于Java类和本地函数相映射的。其使用DLL的步骤还是相对比较麻烦,不但涉及到Java编程,还涉及到C/C++编程,一般来说,第三方提供的dll,我们需要用C++做一个中间件调用它(不建议C#实现),然后再由java去调用这给中间件以达到最终效果。

JNI的使用步骤是:

1、编写Java类,用该类将DLL对外提供的函数服务进行声明,其中的Java方法均声明为native,其方法 签名可以自定义,不实现函数体。

package com.my.util;
   public class SMSUtil {

       public static void main(String[] args) {
        
            start(9, 9600);
            sendMsg("测试", "189********");
            stop();
       }

        static
        {
            System.loadLibrary("SMSService"); //vc++实现的中间件"SMSService.dll"
        }
    
        public static native int start(int port, int baudRate);
    
        public static native int stop();
    
        public static native int sendMsg(String msg, String phoneCode);
    
    }
2、 编译成class, 在class根目录执行 Javah 生成 .h

命令: c:\workspace\smsutil\bin>javah com.my.util

生成 com_my_util_SMSUtil.h

生成的函数定义没有参数名称,所以需要对其做简单的修改:

JNIEXPORT jint JNICALL Java_com_my_util_SMSUtil_start
  (JNIEnv * env, jclass obj, jint port, jint baudRate);

     JNIEXPORT jint JNICALL Java_com_my_util_SMSUtil_stop
  (JNIEnv * env, jclass obj);

     JNIEXPORT jint JNICALL Java_com_my_util_SMSUtil_sendMsg
  (JNIEnv * env, jclass obj, jstring jMsg, jstring jPhone);


3、编写C/C++代码实现.h头文件中声明的函数, 实现接口方法,可以在实现方法中调用第三方DLL库

打开vs2010,建立名称为SMSService的win32控制台应用程序。如下所示:

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

\

点击完成。

复制 %jdk%\include\win32\jni_md.h和%jdk%\include\jni.h 到项目根目录下,复制com_my_util_SMSUtil.h到项目根目录,com_my_util_SMSUtil.h文件的引用 include 修改成 include "jni.h", 然后工程头文件中引用其以上三个文件

删除SMSService.h ,SMSService.cpp 所有代码,然后在stdafx.h中写入:#include “com_my_util_SMSUtil.h", SMSService.cpp引用stdafx.h, 实现定义中的方法, 编译生成SMSService.dll,SMSService.lib, 复制到调用着的 system32目录,至此整个实现过程可以说已经完成。

4、接下来可以在 SMSService.cpp 中调用第三方dll

#include "stdafx.h"
#include "stdlib.h"

typedef DWORD (_stdcall *pSMSSendMessageFun)(char* Msg,char* PhoneNo);
typedef int (_stdcall *pSMSStartServiceFun)(int nPort,DWORD BaudRate , int Parity, int DataBits ,int StopBits,int FlowControl,char* csca);
typedef int (_stdcall *pSMSStopSericeFun)();

HINSTANCE hDll;

JNIEXPORT jint JNICALL Java_com_my_util_SMSUtil_start
  (JNIEnv * env, jclass obj, jint port, jint baudRate)
{
	hDll = LoadLibrary("SMSDLL.dll"); //调用程序目录下须要有此动态库文件

	if(hDll == NULL)
	{
		return -1;
	}

	pSMSStartServiceFun SMSStartServiceFun;
	SMSStartServiceFun = (pSMSStartServiceFun)GetProcAddress(hDll, "SMSStartService");
	if(SMSStartServiceFun)
	{
		int iflag = SMSStartServiceFun(port,baudRate,2,8,0,0,"card");
		if(iflag != 0)
		{
			printf("Service start success    ");
			return 0;
		}
		else
		{
			printf("Service start fail   ");

			pSMSGetLastErrorFun SMSGetLastErrorFun;
			SMSGetLastErrorFun = (pSMSGetLastErrorFun)GetProcAddress(hDll, "SMSGetLastError");
			char Err[1024];
			memset(Err, 0, 1024);
			if(SMSGetLastErrorFun)
			{
				int len = SMSGetLastErrorFun(Err);
				if(len > 0)
				{
					printf(Err);
				}
			}
			return -1;
		}
	}
	else
	{
		return -1;
	}
}

JNIEXPORT jint JNICALL Java_com_my_util_SMSUtil_stop
  (JNIEnv *, jclass)
{
	//HINSTANCE hDll = LoadLibrary("SMSDLL.dll")