用java如何调用vc写的DLL

解决方案 »

  1.   

     Java语言本身具有跨平台性,如果通过Java调用DLL的技术方便易用,使用Java开发前台界面可以更快速,也能带来跨平台性。    Java调用C/C++写好的DLL库时,由于基本数据类型不同、使用字节序列可能有差异,所以在参数传递过程中容易出现问题。    使用Java调用DLL动态链接库的方案通常有三种:JNI, Jawin, Jacob. 其中JNI(Java Native Interface)是Java语言本身提供的调用本地已编译的函数库的方法,本身具有跨平台性,可以在不同的机器上调用不同的本地库。Jawin和Jacob都是sourceforge.net的开源项目,都是基于JNI技术的依赖Windows的实现,使得在Windows平台下使用COM和DLL的更加方便。
        三、JNI    sun相关文档:http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/jniTOC.html    JNI的完整例子 :http://www.pconline.com.cn/pcedu/empolder/gj/java/0506/642328.html    JNI的应用方案是基于Java类和本地函数相映射的。其使用DLL的步骤还是相对比较麻烦,不但涉及到Java编程,还涉及到C/C++编程。    JNI的使用步骤是:    1.编写Java类,用该类将DLL对外提供的函数服务进行声明,其中的Java方法均声明为native,其方法签名可以自定义,不用实现函数体。    2.用Javah工具将该Java类生成对应的.h头文件。    3.最重要的比较麻烦的一步:编写C/C++代码实现.h头文件中声明的函数,该C/C++代码中包含jni.h头文件,并且编写代码时使用其中定义好的数据类型作为函数的输入和返回数据类型进行编程。用这种方法实现数据类型转换。例如数据类型:boolean(java) à jboolean(jni.h: typedef unsigned char jboolean),在自己编写的C/C++代码中使用数据类型jboolean映射Java中的boolean类型。在该步骤中,可以在C/C++代码中调用已经存在的DLL库。    4.另外编写的Java代码时就可以使用该Java类了。    在第3步中,编写C/C++函数时,可以使用一个叫interface pointer的env指针来调用JNI提供的一系列(很多)函数,用这些函数来访问JVM的对象和数据。    使用JNI的缺点:使用比较麻烦,需要对已有的DLL进行封装,需要对C/C++比较了解。    使用JNI的优点:可以跨平台调用本地库。    四、Jawin    官方网站:http://jawinproject.sourceforge.net/    官方文档(Jawin介绍): http://jawinproject.sourceforge.net/jawin.html    官方文档(Jawin使用DLL):http://jawinproject.sourceforge.net/jawinuserguide_dll.html    官方文档(Jawin数据指令): http://jawinproject.sourceforge.net/instruction_docs.html    Jawin的应用方案是基于函数调用时采用原始字节流传递数据的。就是在Java中指明一个DLL中的某个函数后,通过原始字节流(需要考虑参数数据类型所占的存储字节数及系统使用的字节序列)传递给该DLL函数需要的参数,其返回值也是通过原始字节流解析的方式获得正确的值。    Jawin的使用步骤:    1.环境配置:下载Jawin;Jawin.dll放入工程目录下;Jawin.jar相关jar文件加入到运行库中(LibPath或者Eclipse下配置工程的BuildPath-AddLibrary)。    2.获得函数指针:new FuncPtr("DllFileName.DLL", "dllFunctionName");    3.用LittleEndianOutputStream将函数需要的参数写入到一个原始字节流NakedByteStream。    4.最重要的一步:调用FuncPtr.invoke()。传入参数比较复杂。    5.解析上一步的返回值(字节数组)。    第4步中传入的参数包括:    1.指令字符串。一个"XXX:Y:ZZZ"格式的字符串。其含义分别是传入参数中的每个字节的数据类型意义、返回值的类型、需要从传入指针中读取的数据(inout类型参数)。比如:    函数签名int func(int, int, struct s*, char*); //其中struct s*调用完函数后需要读出,struct s所占字节数为16。    其指令字符串为:IIP16G:I:L4L4n16L4。该字符串在解析返回值(字节数组)时,首先应该是返回类型I对应的4个字节,然后是inout类型的参数中n16对应的16个字节。    其中字符串的意义可以在Jawin提供的文件instructions.h中找到,或者在官方文档(Jawin数据指令)中找到常用的一些指令字符串的意义。    2.传入参数的总字节大小。    3.前面写好的传入参数的原始字节流。    4.一个object数组。    5.ReturnFlags,用以根据C/C++返回值将C/C++的错误转换为Java的异常并抛出。其中CHECK_NONE表示不检查;CHECK_FALSE和CHECK_WIN32分别表示返回0是FALSE和SUCCESS,根据是否出错决定是否抛出异常;CHECK_HRESULT表示使用COM模型中的HRESULT作为返回值,其错误码可以配置。    使用Jawin的缺点:不方便调试,几乎所有的错误都抛出同样的异常COMException;需要对数据类型的转换比较了解;不能跨平台,对Windows的依赖性比较强。    使用Jawin的优点:方便使用,不用进行C/C++开发,不用对原始DLL进行封装就可以方便使用。    五、Jacob    官方文档:http://danadler.com/jacob/    Jacob是Java-Com Bridge的缩写,也可以用来调用DLL。其底层也是使用JNI实现,也具有Windows 的平台依赖性,由于网上有人反映其易用性不如jawin,所以没有深入了解。
      

  2.   

    除了楼上的,JNA也是可选方案
      

  3.   

    还是推荐JNA,无须任何其它额外的工作
      

  4.   

    http://download.csdn.net/source/1228859 
    下载有例子/* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class cn_longhaul_common_Hdinfo */#ifndef _Included_cn_longhaul_common_Hdinfo
    #define _Included_cn_longhaul_common_Hdinfo
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     cn_longhaul_common_Hdinfo
     * Method:    getCpuid
     * Signature: ()Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_cn_longhaul_common_Hdinfo_getCpuid
      (JNIEnv *, jobject);/*
     * Class:     cn_longhaul_common_Hdinfo
     * Method:    getCpuName
     * Signature: ()Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_cn_longhaul_common_Hdinfo_getCpuName
      (JNIEnv *, jobject);/*
     * Class:     cn_longhaul_common_Hdinfo
     * Method:    getCpuFamily
     * Signature: ()Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_cn_longhaul_common_Hdinfo_getCpuFamily
      (JNIEnv *, jobject);#ifdef __cplusplus
    }
    #endif
    #endif
      

  5.   

     
    l        Jni程序开发的一般操作步骤如下:
    1.      编写java中的调用类,并用javac   包名.*.java 命令 得到该调用类的class文件
    2.      用javah 包名.* 生成c/c++原生函数的头文件
    3.        c/c++中调用需要的其他函数功能,实现原生函数(原则上可以调用任何资源)
    4.        将项目依赖的所有原生库和资源加入到java项目的java.library.path
    5.      生成java调用类对应的dll库
    6.      发布java应用和dll库
     
     
    l        Jni程序开发示例:
    1、 在eclipse项目中新建一个TestHello.java,输入以下内容:
    public class TestHello {
     
     static {
     System.loadLibrary("TestHello");//加载名为TestHello.dll的动态链接库文件
     }
     
    //本地方法声明
     public static native void hello(String msg);
     
     public static void main(String[] args) {
     
    //本地方法的调用
     hello("Hello,Kimm!");
     
     }
     
    }
    编译生成TestHello.class文件。
    2、 在命令行下使用javah TestHello命令,生成TestHello.h头文件(就是jni代理stub的接口)。
    3、 在VC6中新建一个项目TestHello, 项目类型为Win32 Dynamic-Link Library。点击OK。
     
     
      在弹出的窗口中选择A simple DLL project,点击Finish。
     
     打开项目所在的文件目录,将步骤2中生成的TestHello.h文件复制到此目录。点击左边中间的FileView,切换到文件浏览模式。在Header Files上点击右键,选择Add Files to Folder…。
    选择TestHello.h文件,点击OK。
     
     
     打开StdAfx.h文件,再最后面添加:
    #include <jni.h>
    #include "TestHello.h"
     
     
    打开TestHello.cpp文件,在最后面添加一段代码:
    JNIEXPORT void JNICALL Java_TestHello_hello(JNIEnv * env, jclass obj, jstring jMsg)
    {
     const char *strMsgPtr = env->GetStringUTFChars( jMsg , 0);
     
     MessageBox( 0, strMsgPtr,"Message box from VC++ ", 0 );
     
     env->ReleaseStringUTFChars( jMsg, strMsgPtr);
     
    }
     在VC的菜单上选择Tools-Options…,打开选项对话框。在Directories文件夹,添加上jdk所在文件夹下的include和include\win32文件夹。
     点击VC上的菜单项Build-Build All,生成TestHello.dll。或者在在Header Files上点击右键,选择Add Files to Folder分别找到jdk目录下的文件:如以我jdk目录为例,把C:\Program Files\Java\jdk1.6.0\include\jni.h、C:\Program Files\Java\jdk1.6.0\include\win32\jni_md.h这连个头文件导入也是同样效果
     
    4、 将VC项目Debug文件夹中的TestHello.dll复制到TestHello.class所在的文件夹下。
     
    如果出现以下异常说明:java.library.path没有设置或者设置不正确
    Exception in thread "main" java.lang.UnsatisfiedLinkError: no TESTHello in java.library.path
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1682)
        at java.lang.Runtime.loadLibrary0(Runtime.java:823)
       at java.lang.System.loadLibrary(System.java:1030)
     
    不过以上异常也无所谓,我们只需把TestHello.h放到C:\WINDOWS\system32目录下就可以了,系统会自动找到这个dll的
     
    C:\WINDOWS\system32
    5、 在命令行下输入java TestHello,弹出MessageBox对话框。调用win32 api成功。
      

  6.   

    简单JNI例子示例
    功能:JAVA程序通过调用C语言编写的2个整数相加程序,并把结果返回到JAVA程序中。
    步骤:
    Step1.用记事本编写以下代码,保存为:Sum.java
    代码如下:
    public class Sum{
    public native int add(int x,int y);  //本地方法申明
    static{
    System.loadLibrary("add");  //当加载类时,伴随加载该库
    }
    public static void main(String[] args) {
    System.out.println(new Sum().add(1,2));
    }
    }Step2.用命令javac Sum.java编译,保证编译通过,并生成Sum.class文件。
    Step3.用命令javah Sum 生成Sum.h文件。
    内容如下:
    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class Sum */
    #ifndef _Included_Sum
    #define _Included_Sum
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
      * Class:     Sum
      * Method:    add
      * Signature: (II)I
      */
    JNIEXPORT jint JNICALL Java_Sum_add
       (JNIEnv *, jobject, jint, jint);
    #ifdef __cplusplus
    }
    #endif
    #endif
    Step4.用Microsoft Visual C++创建动态连接库,即add.dll文件。
    具体方法如下:
    1)、打开VC++6.0,文件->新建-> 工程 -> win32 Dynamic-Link Library .在向导中选择空工程.
    2)、文件->新建-> C++ Source File,新建一文件。
    该文件内容如下:#include "stdio.h"
    #include "Sum.h"
    #include "jni.h"
    JNIEXPORT jint JNICALL Java_Sum_add (JNIEnv *, jobject, jint x, jint y) 
    { return x+y;
    }
          3)细心的读者可能已经注意到,上一个文件中导入了其它的头文件,像jni.h,Sum.h
    ,所以得先导入到VC++才能使用,否则出错。Jni.h这个文件在哪呢?其头就在JDK安装目录下include文件中,例如我本机上的路径是:C:\Program Files\Java\jdk1.6.0_12\include,把include文件下.h结尾的文件和其子目录win32下的2个.h文件全部导入到VC++库内。
      Step5、 编译并生成DLL文件,把生成的DLL文件复制到与Sum.java同一目录。
      Step6、用java Sum命令解释执行。