JNI问题:在Java中调用使用第三方dll的Dll

时间:2010-11-21 12:24:18

标签: java dll java-native-interface

我想使用epanet.dll,所以为了调用它我必须创建我的桥dll。

我创建了Java类

public class Epanet {

   //Native method declaration
   native int  ENopen(String fileInput, String fileOutput, String optBinFileOut);
   native int  ENsaveinpfile(String file);
   native int  ENclose();
   native int  ENsolveH();
   native int  ENsaveH();
   native int  ENopenH();
   //native int  ENrunQ(long *);

   //Load the library
   static {
     System.loadLibrary("epanet2");
   }
}

然后javah创建了de .h文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class Epanet */

#ifndef _Included_Epanet
#define _Included_Epanet
#ifdef __cplusplus
extern "C" {
   #endif

   JNIEXPORT jint JNICALL Java_Epanet_ENopen (JNIEnv *, jobject, jstring, jstring, jstring);

   JNIEXPORT jint JNICALL Java_Epanet_ENsaveinpfile (JNIEnv *, jobject, jstring);

   JNIEXPORT jint JNICALL Java_Epanet_ENclose (JNIEnv *, jobject);

   JNIEXPORT jint JNICALL Java_Epanet_ENsolveH (JNIEnv *, jobject);

   .....
   .....

   #ifdef __cplusplus
}
#endif
#endif

然后我创建了应该调用epanet2 dll的.c文件

#include "jni.h"
#include <stdio.h>
#include "myDll.h"
#include "epanet2.h"

JNIEXPORT jint JNICALL Java_Epanet_ENopen
  (JNIEnv *env, jobject obj, jstring fichIn, jstring fichOut, jstring fichBin){

       const char *CStringFichIn = (*env)->GetStringUTFChars(env,fichIn,NULL);
       const char *CStringFichOut =  (*env)->GetStringUTFChars(env,fichOut,NULL);
       const char *CStringFichBin = (*env)->GetStringUTFChars(env,fichBin,NULL);
       int result;

       result =  ENepanet (CStringFichIn, CStringFichOut, CStringFichBin, NULL);

       (*env)->ReleaseStringUTFChars(env, fichIn, CStringFichIn);
       (*env)->ReleaseStringUTFChars(env, fichOut, CStringFichOut);
       (*env)->ReleaseStringUTFChars(env, fichBin, CStringFichBin);

       return result;
}

JNIEXPORT jint JNICALL Java_Epanet_ENsaveinpfile
  (JNIEnv *env, jobject object, jstring fichOut){

       const char *CStringFichOut;
       int result;

       CStringFichOut = (*env)->GetStringUTFChars(env,fichOut,NULL);

       result =  ENsaveinpfile (CStringFichOut);
       return result;
}

JNIEXPORT jint JNICALL Java_Epanet_ENclose
  (JNIEnv *env, jobject object){

       int result;
       result =  ENclose ();
       return result;
}

JNIEXPORT jint JNICALL Java_Epanet_ENsolveH
  (JNIEnv *env, jobject object){

       int result;    
       result =  ENsolveH ();
       return result;
}

JNIEXPORT jint JNICALL Java_Epanet_ENsaveH
  (JNIEnv *env, jobject object){
       int result;
       result =  ENsaveH ();
       return result;
}

JNIEXPORT jint JNICALL Java_Epanet_ENopenH
  (JNIEnv *env, jobject obj){
       int result;
       result =  ENopenH ();
       return result;
}

然后编译。 Visual C ++创建了我的dll。我在system32中复制了两个dll。然后我尝试使用我的dll。

public class NewClass {
     private native void ENopen(String f1, String f2, String f3);

     public static void main(String[] args) {

         System.out.println("started");
         new NewClass().ENopen("C:\\Red2.inp", "C:\\salaida.txt", "");
         System.out.println("finished");
     }

     static {
         System.loadLibrary("myDll");
     }
}

我收到此错误:

    started
 Exception in thread "main" java.lang.UnsatisfiedLinkError: NewClass.epanet(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String)V
            at NewClass.epanet(Native Method)
            at NewClass.main(NewClass.java:18) Java Result: 1

如果我删除了库,我会收到错误,说它无法找到库,因此某处存在问题。我必须说一个朋友给了我他的dll对他有用,但它对我不起作用。我得到了同样的错误。

有什么猜测? 另一个问题是如何调用这个本地方法// native int ENrunQ(long *); ?

所以这就是你的建议(主要是第二条评论):

我的Epanet类加载我的dll而不是epanet dll(第三方)。

public class Epanet {

   //Native method declaration
   native int  ENopen(String fileInput, String fileOutput, String optBinFileOut);
   native int  ENsaveinpfile(String file);
   native int  ENclose();
   native int  ENsolveH();
   native int  ENsaveH();
   native int  ENopenH();
   //native int  ENrunQ(long *);

   //Load the library
   static {
     System.loadLibrary("myDll");
   }
}

我的测试类不应加载它。实际上,它不应该加载Epanet类所做的任何事情。

public class NewClass {

     public static void main(String[] args) {

         System.out.println("started");
         new Epanet().ENopen("C:\\Red2.inp", "C:\\salida.txt", "");
         System.out.println("finished");
     }
}

然后我的包装器dll应如下所示:

#include "jni.h"
#include <stdio.h>
#include "myDll.h"
#include "epanet2.h"

JNIEXPORT jint JNICALL Java_Epanet_ENopen
  (JNIEnv *env, jobject obj, jstring fichIn, jstring fichOut, jstring fichBin){

       const char *CStringFichIn = (*env)->GetStringUTFChars(env,fichIn,NULL);
       const char *CStringFichOut =  (*env)->GetStringUTFChars(env,fichOut,NULL);
       const char *CStringFichBin = (*env)->GetStringUTFChars(env,fichBin,NULL);
       int result;

       result =  ENopen (CStringFichIn, CStringFichOut, CStringFichBin);

       (*env)->ReleaseStringUTFChars(env, fichIn, CStringFichIn);
       (*env)->ReleaseStringUTFChars(env, fichOut, CStringFichOut);
       (*env)->ReleaseStringUTFChars(env, fichBin, CStringFichBin);

       return result;
}

或更像这样:

#include "jni.h"
#include <stdio.h>
#include <windows.h>
#include "myDll.h"
#include "epanet2.h"

typedef int (* FPTR)(char *, char *, char*);

JNIEXPORT jint JNICALL Java_Epanet_ENopen
  (JNIEnv *env, jobject obj, jstring fichIn, jstring fichOut, jstring fichBin){

        HMODULE dllHandle = LoadLibrary("epanet2.dll");  // cargar librería 

        const char *CStringFichIn = (char *)(*env)->GetStringUTFChars(env,fichIn,NULL);
        const char *CStringFichOut = (char *) (*env)->GetStringUTFChars(env,fichOut,NULL);
        const char *CStringFichBin = (char *)(*env)->GetStringUTFChars(env,fichBin,NULL);
        int result;

        FPTR ENopen = (FPTR) GetProcAddress(dllHandle, "ENopen");

        result =  ENopen (CStringFichIn, CStringFichOut, CStringFichBin );

       (*env)->ReleaseStringUTFChars(env, fichIn, CStringFichIn);
       (*env)->ReleaseStringUTFChars(env, fichOut, CStringFichOut);
       (*env)->ReleaseStringUTFChars(env, fichBin, CStringFichBin);


        FreeLibrary(dllHandle);    // descargar librería
        return result;
}

另外,你知道怎么称呼这个功能吗?

  

native int ENrunQ(long *);

我不知道如何在mydll中获得长*因为字符串 - &gt; jstring或int - &gt; jint但长* - &gt;?或者int * - &gt;?

4 个答案:

答案 0 :(得分:0)

我的两分钱:

包装器DLL包含Epanet类中本机方法的实现,而不是您在测试代码中调用的本机方法(请注意堆栈跟踪中的类名)。我认为你应该使用new Epanet().ENopen( "C:\\Red2.inp", "C:\\salaida.txt", "" );代替。

另外,Epanet的静态初始化程序应该加载你的DLL,而不是包装的库(如果你的包装器是正确构建的,操作系统会处理它。)

答案 1 :(得分:0)

您已经为两个Java类提供了源代码,并且只提供了一个本机实现。这让我们更难理解。摆脱NewClass。

您希望您的Epanet java类在System.loadLibrary()调用中加载它的本机包装器,然后您的包装器DLL将自动加载epanet.dll。

在将long *传递给您的本机代码方面,您不能。创建java-c包装器类的技巧是你不能直接调用原始方法!您可以传入一个简单的长整数,但是对长整数所做的任何更改都将丢失。所以你可以将一个可变的java对象传递给你的包装器调用并更改它或者更简单地让本机方法改变一些Epanet类的状态。

答案 2 :(得分:0)

我建议您尝试Dependency Walker查看是否还有其他可能需要的DLL(例如,您可能缺少Microsoft C运行时)。

答案 3 :(得分:0)

http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html

  

我不知道如何在mydll中获得长*因为字符串 - &gt; jstring或int - &gt; jint但很长* - &gt; ???或者int * - &gt; ????   那个长* - &gt; jlong​​Array(和int * - &gt; jintArray)

例如: 在java本机方法声明中接受long [],在jni中,您将在该参数位置看到jlong​​Array。使用GetDoubleArrayElements()将jlong​​Array转换为jlong​​ *(请参阅doc的链接),jlong​​是64bit(http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html),您可以使用它。
对于boolean,int,java对象也是如此(请参阅变体的文档)....


首次更新前

Exception in thread "main" java.lang.UnsatisfiedLinkError: NewClass.epanet(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String)V 我猜你在编译和/或管理

时犯了错误
public class NewClass {
     private native void ENopen(String f1, String f2, String f3);

原因:错误应该是

java.lang.UnsatisfiedLinkError:NewClass。 ENopen (Ljava / lang / String; Ljava / lang / String; Ljava / lang / String)V

他们不是方法名称&#34; NewClass.epanet&#34;在你的来源(即使在更新后)。