【问题标题】:JNI issue: calling in Java a Dll that uses a third party dllJNI 问题:在 Java 中调用使用第三方 dll 的 Dll
【发布时间】:2011-05-13 08:58:57
【问题描述】:

我想使用 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 中获取 long*,因为 string -> jstring 或 int -> jint 但 long* ->?或 int* ->?

【问题讨论】:

  • 您检查过您的 bin 文件夹中没有错误的 dll 吗?
  • 为什么你有两个loadLibrary(..) 电话?你只有一个界面!
  • 我不知道如何检查是否有错误的dll。一切正常。
  • 此外,一个加载库在我用来用 javah 生成 de .h 的类中。另一种是加载我创建的库进行测试。
  • 可怕的缩进 :( 无助于任何人理解代码。

标签: java dll java-native-interface


【解决方案1】:

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

我不知道如何在 mydll 中获取 long*,因为 string -> jstring 或 int -> jint 但 long* -> ???或 int* -> ???? 那 long* -> jlong​​Array (和 int* ->jintArray)

示例: 在 java 本地方法声明中接受 long[],在 jni 中,您将在该参数位置看到 jlong​​Array。使用 GetDoubleArrayElements() 将 jlong​​Array 转换为 jlong​​(请参阅文档链接)并且 jlong​​ 是 64 位(http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html),您可以使用它。
boolean, int, java object 相同(参见文档的变化)....


在您首次更新之前 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

它们不是您源中的方法名称“NewClass.epanet”(即使在更新之后)。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 某处出现问题。我已经从头开始重建了所有东西,并且可以正常工作。我也检查了依赖关系,我的 dll 依赖于 epanet.dll。
    【解决方案3】:

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

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

    就将 long* 传递到您的本机代码而言,您不能。创建java-c包装类的技巧是不能直接调用原来的方法!您可以传入一个简单的 long,但是对 long 所做的任何更改都将丢失。因此,您可以将一个可变的 java 对象传递给您的包装器调用并对其进行更改,或者更简单地让本机方法更改 Epanet 类的某些状态。

    【讨论】:

    • 请检查我发布的新解决方案。
    • 关于第二条评论,我想我理解你。我的 Java 类将传递给我的 dll,只要 jlong​​。然后,我在 mydll 中创建一个 long var,将 jlong​​ 复制到其中,然后使用 &long 调用本机方法。正如你所说,当我的 dll 返回时,我将丢失对 long var 的任何修改。
    【解决方案4】:

    我的两分钱:

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

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

    【讨论】:

    • 我已将调用更改为 new Epanet().ENopen ( ...... ),现在出现错误。经过一周的努力,这很棒:D
    • 你说的第二条评论我不知道怎么解决。我对此的了解有限,因为没有适合我的 dll 的教程/演示。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多