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

3 篇文章 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 <jni.h>  修改成  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"); //调用程序目录下须要有此动态库文件
	if(hDll == NULL)
	{
		return -1;
	}
	pSMSStopSericeFun SMSStopSericeFun;
	SMSStopSericeFun = (pSMSStopSericeFun)GetProcAddress(hDll, "SMSStopSerice");
	if(SMSStopSericeFun)
	{
	
		int iflag = SMSStopSericeFun();
		if(iflag != 0)
		{
			printf("Serice stop success   "); 

			FreeLibrary(hDll);

			return 0;
		}
		else
		{
			printf("Serice stop fail   "); 

			FreeLibrary(hDll);

			return -1;
		}
	}
	else
	{
		return -1;

		FreeLibrary(hDll);
	}
}

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

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

	char* msg = NULL;
	msg = jstringToWindows(env,jMsg);
	
	char* phone = NULL;
	phone = jstringToWindows(env,jPhone);
	
	pSMSSendMessageFun SMSSendMessageFun;
	pSMSQueryFun SMSQueryFun;
	SMSSendMessageFun = (pSMSSendMessageFun)GetProcAddress(hDll, "SMSSendMessage");
	SMSQueryFun = (pSMSQueryFun)GetProcAddress(hDll, "SMSQuery");
	if(SMSSendMessageFun && SMSQueryFun)
	{
		SMSSendMessageFun(msg, phone);
		Sleep(2000);
	}
	printf("message send done   ");
	return 0;
}

char* jstringToWindows( JNIEnv *env, jstring jstr )  
{  
	  int length = env->GetStringLength(jstr);  
	  const jchar* jcstr = env->GetStringChars(jstr, 0);  
	  char* rtn = (char*)malloc(length*2+1);  
	  int size = 0;  
	  size = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,(length*2+1), NULL, NULL);  
	  if( size <= 0 )  
		return NULL;  
	  env->ReleaseStringChars(jstr, jcstr);  
	  rtn[size] = 0;  
	  return rtn;  
} 
 5、如果需要在64位环境中运行,需要修改工程配置:
     SMSService属性


字符集设置使用多字节字符集,否则 LoadLibrary("SMSDLL.dll") 或出现错误
运行库设置成空,否则调用会出现错误


重新编译dll,   回到初始的java类,调用Main,测试成功!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值