简体   繁体   中英

JNI issue: calling in Java a Dll that uses a third party dll

I want to use epanet.dll so in order to call it I have to create my bridge dll.

I created the Java class

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");
   }
}

Then javah created de .h file

/* 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

Then I created the .c file that should call epanet2 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 =  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;
}

And then compile. Visual C++ creates my dll. I copied both dlls in system32. Then I try to use my 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");
     }
}

I get this error:

    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

If I removed the libraries I get errors saying it can't find the libraries so there is a problem somewhere. I must say that a friend gave me his dll that works for him but it doesn't work for me. I get the same error.

Any guess? Another question is how can a call this native method //native int ENrunQ(long *); ?

So this is what you suggest me (mainly for the second comment):

My Epanet class loads my dll and not the epanet dll ( third party one ).

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");
   }
}

And my test class shouldn't load it. Actually, it shouldn't load any beacuse the Epanet class does it.

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");
     }
}

Then my wrapper dll should look like this:

#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;
}

Or more like this:

#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;
}

Also, do you know how to call this function?

native int ENrunQ(long *);

I don't know how do get long* in mydll because string -> jstring or int -> jint but long* ->? or int* ->?

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

I don't know how do get long* in mydll because string -> jstring or int -> jint but long* -> ??? or int* -> ???? that long* -> jlongArray (and int* ->jintArray)

example: accept a long[] in java native method declaration, in jni you will see jlongArray in that argument position. convert jlongArray to jlong* using GetDoubleArrayElements() (see the link for doc) and jlong is 64bit ( http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html ), you can use it.
same for boolean, int, java object (see docs for variations) ....


before you first update Exception in thread "main" java.lang.UnsatisfiedLinkError: NewClass.epanet(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String)V i guess you have made mistake somewhere in compiling and/or managing

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

reason: the error should have been

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

their is not method name "NewClass.epanet" in you source (even after update).

My two cents:

The wrapper DLL contains the implementations for the native methods in your Epanet class, not for the native method you're calling in your test code (note the class name in the stacktrace). I think you should be using new Epanet().ENopen( "C:\\\\Red2.inp", "C:\\\\salaida.txt", "" ); instead.

Also, the static initializer for Epanet should be loading your DLL, not the wrapped library (the OS will take care of that if your wrapper was built correctly).

You've provided the source for two Java classes and only one of the native implementations. Which does make it harder for us to understand. Get rid of NewClass.

You want your Epanet java class to load it's native wrapper in the System.loadLibrary() call and then your wrapper dll will automatically load the epanet.dll.

In terms of passing a long* into your native code, you can't. The skill in creating a java-c wrapper class is that you can't just call the original methods directly! You can pass in a simple long, but then any changes made to the long will be lost. So you can either pass in a mutable java object to your wrapper call and change it or more simply have the native method alter some state of the Epanet class.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM