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

使用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,其方法   签名可以自定义,不实现函数体。

 

[java][/java]

  1. package com.my.util;
  2.    public class SMSUtil {
  3.        public static void main(String[] args) {
  4.             start(9, 9600);
  5.             sendMsg(“测试”, “189********”);
  6.             stop();
  7.        }
  8.         static
  9.         {
  10.             System.loadLibrary(“SMSService”); //vc++实现的中间件”SMSService.dll”
  11.         }
  12.         public static native int start(int port, int baudRate);
  13.         public static native int stop();
  14.         public static native int sendMsg(String msg, String phoneCode);
  15.     }

2、 编译成class, 在class根目录执行 Javah 生成 .h

 

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

生成 com_my_util_SMSUtil.h

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

 

[java][/java]

  1. JNIEXPORT jint JNICALL Java_com_my_util_SMSUtil_start
  2.   (JNIEnv * env, jclass obj, jint port, jint baudRate);
  3.      JNIEXPORT jint JNICALL Java_com_my_util_SMSUtil_stop
  4.   (JNIEnv * env, jclass obj);
  5.      JNIEXPORT jint JNICALL Java_com_my_util_SMSUtil_sendMsg
  6.   (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

[cpp][/cpp]

  1. #include “stdafx.h”
  2. #include “stdlib.h”
  3. typedef DWORD (_stdcall *pSMSSendMessageFun)(char* Msg,char* PhoneNo);
  4. typedef int (_stdcall *pSMSStartServiceFun)(int nPort,DWORD BaudRate , int Parity, int DataBits ,int StopBits,int FlowControl,char* csca);
  5. typedef int (_stdcall *pSMSStopSericeFun)();
  6. HINSTANCE hDll;
  7. JNIEXPORT jint JNICALL Java_com_my_util_SMSUtil_start
  8.   (JNIEnv * env, jclass obj, jint port, jint baudRate)
  9. {
  10.     hDll = LoadLibrary(“SMSDLL.dll”); //调用程序目录下须要有此动态库文件
  11.     if(hDll == NULL)
  12.     {
  13.         return -1;
  14.     }
  15.     pSMSStartServiceFun SMSStartServiceFun;
  16.     SMSStartServiceFun = (pSMSStartServiceFun)GetProcAddress(hDll, “SMSStartService”);
  17.     if(SMSStartServiceFun)
  18.     {
  19.         int iflag = SMSStartServiceFun(port,baudRate,2,8,0,0,”card”);
  20.         if(iflag != 0)
  21.         {
  22.             printf(“Service start success    “);
  23.             return 0;
  24.         }
  25.         else
  26.         {
  27.             printf(“Service start fail   “);
  28.             pSMSGetLastErrorFun SMSGetLastErrorFun;
  29.             SMSGetLastErrorFun = (pSMSGetLastErrorFun)GetProcAddress(hDll, “SMSGetLastError”);
  30.             char Err[1024];
  31.             memset(Err, 0, 1024);
  32.             if(SMSGetLastErrorFun)
  33.             {
  34.                 int len = SMSGetLastErrorFun(Err);
  35.                 if(len > 0)
  36.                 {
  37.                     printf(Err);
  38.                 }
  39.             }
  40.             return -1;
  41.         }
  42.     }
  43.     else
  44.     {
  45.         return -1;
  46.     }
  47. }
  48. JNIEXPORT jint JNICALL Java_com_my_util_SMSUtil_stop
  49.   (JNIEnv *, jclass)
  50. {
  51.     //HINSTANCE hDll = LoadLibrary(“SMSDLL.dll”); //调用程序目录下须要有此动态库文件
  52.     if(hDll == NULL)
  53.     {
  54.         return -1;
  55.     }
  56.     pSMSStopSericeFun SMSStopSericeFun;
  57.     SMSStopSericeFun = (pSMSStopSericeFun)GetProcAddress(hDll, “SMSStopSerice”);
  58.     if(SMSStopSericeFun)
  59.     {
  60.         int iflag = SMSStopSericeFun();
  61.         if(iflag != 0)
  62.         {
  63.             printf(“Serice stop success   “);
  64.             FreeLibrary(hDll);
  65.             return 0;
  66.         }
  67.         else
  68.         {
  69.             printf(“Serice stop fail   “);
  70.             FreeLibrary(hDll);
  71.             return -1;
  72.         }
  73.     }
  74.     else
  75.     {
  76.         return -1;
  77.         FreeLibrary(hDll);
  78.     }
  79. }
  80. JNIEXPORT jint JNICALL Java_com_my_util_SMSUtil_sendMsg
  81.   (JNIEnv * env, jclass obj, jstring jMsg, jstring jPhone)
  82. {
  83.     //HINSTANCE hDll = LoadLibrary(“SMSDLL.dll”); //调用程序目录下须要有此动态库文件
  84.     if(hDll == NULL)
  85.     {
  86.         return -1;
  87.     }
  88.     char* msg = NULL;
  89.     msg = jstringToWindows(env,jMsg);
  90.     char* phone = NULL;
  91.     phone = jstringToWindows(env,jPhone);
  92.     pSMSSendMessageFun SMSSendMessageFun;
  93.     pSMSQueryFun SMSQueryFun;
  94.     SMSSendMessageFun = (pSMSSendMessageFun)GetProcAddress(hDll, “SMSSendMessage”);
  95.     SMSQueryFun = (pSMSQueryFun)GetProcAddress(hDll, “SMSQuery”);
  96.     if(SMSSendMessageFun && SMSQueryFun)
  97.     {
  98.         SMSSendMessageFun(msg, phone);
  99.         Sleep(2000);
  100.     }
  101.     printf(“message send done   “);
  102.     return 0;
  103. }
  104. char* jstringToWindows( JNIEnv *env, jstring jstr )
  105. {
  106.       int length = env->GetStringLength(jstr);
  107.       const jchar* jcstr = env->GetStringChars(jstr, 0);
  108.       char* rtn = (char*)malloc(length*2+1);
  109.       int size = 0;
  110.       size = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,(length*2+1), NULL, NULL);
  111.       if( size <= 0 )
  112.         return NULL;
  113.       env->ReleaseStringChars(jstr, jcstr);
  114.       rtn[size] = 0;
  115.       return rtn;
  116. }

5、如果需要在64位环境中运行,需要修改工程配置:
SMSService属性

 

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

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

标签