zoukankan      html  css  js  c++  java
  • Java通过JNI调用dll详细过程(转)

    源:Java通过JNI调用dll详细过程

      最近项目有这样一个需求,在已有的CS软件中添加一个链接,将当前登录用户的用户名加密后放在url地址中,在BS的login方法里通过解密判断,如果为合法用户则无需再次登录直接进入平台,CS软件方提供了一个加密解密的dll文件,我们需要在action中通过该dll解密,那么就涉及到java调用dll的问题。

      首先我选择了JNI方式(因为网上说的另两种方式Jawin, Jacob更不会),大体流程如下:

      1、写一个java的class,在类里声明所调用的库名称和需要使用的函数(注意:需要对方法做本地声明,关键字为native。且只需要声明,而不需要具体实现)

    package com;
    public class javacall
    {
            static
            {
                    System.loadLibrary("htgsjencrypt");
            }
           
            public native static String DecodeString(char[] szSrc);
            public native static String EncodeString(char[] szSrc);
    
            private static void printCharArray(char[] content)
            {
                String temp=new String(content);
                System.out.println(temp);
            }
    
            public static void main(String[] args)
            {
              String s="123";
              char[] src=new char[100];
              src=s.toCharArray();
              String encode="";
              printCharArray(src);
              encode=javacall.EncodeString(src);
              System.out.println("encode="+encode);
              String decode="";
              src=encode.toCharArray();
              decode=javacall.DecodeString(src);
              System.out.println("decode="+decode);
              
            }
    }

      这个地方需要提一下,新建这个class时最好不要建在默认包中,将来对这个工程打包后,在引用的工程中无法找到默认包中的class(也许是我写的不对,不过写在默认包中确实会带来不必要的麻烦)

      2、对于以上编译好的class文件通过使用javah命令生成头文件javacall.h,这个文件需要被C++程序调用来生成所需的库文件

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include "jni.h"
    /* Header for class com_javacall */
    
    #ifndef _Included_com_javacall
    #define _Included_com_javacall
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_javacall
     * Method:    DecodeString
     * Signature: ([C)Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_com_javacall_DecodeString
      (JNIEnv *, jclass, jcharArray);
    
    /*
     * Class:     com_javacall
     * Method:    EncodeString
     * Signature: ([C)Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_com_javacall_EncodeString
      (JNIEnv *, jclass, jcharArray);
    
    #ifdef __cplusplus
    }
    #endif
    #endif

      这里需要提到一点,默认生成的头文件中写的是

    #include <jni.h>

      在C++的calss中引用时编译报错找不到jni.h,可以去jdk安装包的include文件夹中拷贝jni.h、jni_md.h、jawt_md.h三个文件到程序目录,这时再编译可能还报找不到jni.h的错误,可以将#include <jni.h>改为#include "jni.h",因为前者是引用系统头文件的写法

      3、在VC中新建一个库文件htgsjencrypt,在新建的class文件中实现java头文件中声明的两个加密解密方法,因为第三方没有提供.lib文件,也没有.h文件,那么只能用动态使用链接库的方式来调用dll了,具体代码如下:

    #include "gsjencrypt.h"
    #include "com_javacall.h"
    #include "windows.h"
    #include <iostream>
    //////////////////////////////////////////////////////////////////////
    // Construction/Destruction
    //////////////////////////////////////////////////////////////////////
    
    gsjencrypt::gsjencrypt()
    {
    
    }
    
    gsjencrypt::~gsjencrypt()
    {
    
    }
    typedef int (WINAPI *FDecodeString)(char szSrc[100], char szDest[100]);
    typedef int (WINAPI *FEncodeString)(char szSrc[100], char szDest[100]); 
    
    JNIEXPORT jstring JNICALL Java_com_javacall_EncodeString(JNIEnv * env, jclass jobject, jcharArray src)
    {
        HINSTANCE hDLL;
        hDLL=LoadLibrary("gsjencrypt.dll");//加载动态链接库gsjencrypt.dll文件;
        if(hDLL==NULL)
            return 0;
        FEncodeString encodeString=(FEncodeString)GetProcAddress(hDLL,"EncodeString");
        jsize size = (env)->GetArrayLength(src);
    
        jchar * arrayBody = (env)->GetCharArrayElements(src,0); 
        //char * csrc=(char *)arrayBody;
        
        char csrctemp[100]="";
        int k=0;
        while(size!=0)
        {
            csrctemp[k]=*arrayBody;
            *arrayBody++;
            size--;
            k++;
        }
        char cdesttemp[100]="";
        encodeString(csrctemp,cdesttemp);
        (env)->ReleaseCharArrayElements(src,arrayBody,0);
        return (env)->NewStringUTF(cdesttemp);
    }
    JNIEXPORT jstring JNICALL Java_com_javacall_DecodeString(JNIEnv * env, jclass jobject, jcharArray src)
    {
        HINSTANCE hDLL;
        hDLL=LoadLibrary("gsjencrypt.dll");//加载动态链接库gsjencrypt.dll文件;
        if(hDLL==NULL)
            return 0;
        FDecodeString decodeString=(FDecodeString)GetProcAddress(hDLL,"DecodeString");
        jsize size = (env)->GetArrayLength(src);
        jchar * arrayBody = (env)->GetCharArrayElements(src,0); 
        char * csrc=(char *)arrayBody;
        char csrctemp[100]="";
        int k=0;
        while(size!=0)
        {
            csrctemp[k]=*arrayBody;
            *arrayBody++;
            size--;
            k++;
        }
        //arrayBody=(env)->GetCharArrayElements(dest,0); 
        //char * cdest=(char *)arrayBody;
        char cdesttemp[100]="";
        decodeString(csrctemp,cdesttemp);
        (env)->ReleaseCharArrayElements(src,arrayBody,0);
        return (env)->NewStringUTF(cdesttemp);
    }

      这里需要注意的是

    typedef int (WINAPI *FDecodeString)(char szSrc[100], char szDest[100]);
    typedef int (WINAPI *FEncodeString)(char szSrc[100], char szDest[100]); 

      以上两个方法是原始dll中提供给外界调用的函数接口,声明时一定要记得加WINAPI,否则调用时始终报错。

      4、最后将第三方提供的gsjencrypt.dll和新生成的htgsjencrypt.dll同时拷贝到java.library.path里(jdk或者jre的bin文件中),然后将最开始写的java程序打包即可被别的工程调用。

      记录一下最难解决的问题,就是不知道怎样在c++中返回给java解密后的串,因为总是想把指针的概念与java中的某个byte数组或者char数组关联起来,始终不能成功,最后尝试使用NewStringUTF才解决问题,哎,C++已经六年没用过了,真是费劲啊

  • 相关阅读:
    js表单验证
    i利用图片按钮 和 input type="image" 为背景提交表单
    php 时间转换
    select 响应时间 js
    浏览器兼容
    选修课程的级联。
    时间显示的位置
    广告飘动
    C++--引用
    ubuntu中可以ping通IP地址但是ping不通域名的问题(www.baidu.com)
  • 原文地址:https://www.cnblogs.com/LittleTiger/p/4660650.html
Copyright © 2011-2022 走看看