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* ->?
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.
I recommend trying Dependency Walker to see if there are any other DLL's that you might need (for example, you may be missing the Microsoft C runtime).
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).
精彩评论