2010-11-21 8 views
1

Je veux utiliser epanet.dll afin de l'appeler, je dois créer ma passerelle dll.Problème JNI: appeler en Java une DLL qui utilise une DLL tierce

J'ai créé la classe 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"); 
    } 
} 

Puis javah créé de fichier .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 

Ensuite, j'ai créé le fichier .c qui devrait appeler 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; 
} 

Et puis compilez. Visual C++ crée ma DLL. J'ai copié les deux DLLs dans system32. Ensuite, j'essaie d'utiliser ma 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"); 
    } 
} 

Je reçois cette erreur:

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 

Si j'ai supprimé les bibliothèques que je reçois des erreurs en disant qu'il ne peut pas trouver les bibliothèques donc il y a un problème quelque part. Je dois dire qu'un ami m'a donné sa DLL qui fonctionne pour lui mais ça ne marche pas pour moi. J'ai la même erreur.

Devinez? Une autre question est de savoir comment appeler cette méthode native // ​​native int ENrunQ (long *); ?

Donc, c'est ce que vous me proposez (surtout pour le second commentaire):

classe Mon Epanet charge ma dll et non la dll EPANET (tiers un).

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

Et ma classe de test ne devrait pas le charger. En fait, il ne devrait pas être chargé car la classe Epanet le fait.

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

Ensuite, mon dll wrapper devrait ressembler à ceci:

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

Ou plus comme ceci:

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

Aussi, savez-vous comment appeler cette fonction?

native int ENrunQ(long *);

Je ne sais pas comment faire obtenir de longues * à mydll parce que string -> jstring ou int -> JINT mais long * ->? ou int * ->?

+0

Vous avez vérifié que vous ne disposez pas d'un DLL défectueux dans votre dossier bin? – dacwe

+0

Pourquoi avez-vous deux appels 'loadLibrary (..)'? Vous avez seulement une interface! – dacwe

+0

Je ne sais pas comment vérifier s'il y a une DLL défectueuse. Tout fonctionne bien. – Alberto

Répondre

0

Mes deux cents:

La DLL wrapper contient les implémentations pour les méthodes natives dans votre classe Epanet, non pas pour la méthode native que vous appelez dans votre code de test (notez le nom de classe dans le stacktrace). Je pense que vous devriez utiliser new Epanet().ENopen("C:\\Red2.inp", "C:\\salaida.txt", ""); à la place.

En outre, l'initialiseur statique pour Epanet devrait charger votre DLL, pas la bibliothèque encapsulée (le système d'exploitation s'occupera de cela si votre encapsuleur a été construit correctement).

+0

J'ai changé l'appel à nouveau Epanet() .Enopen (......) et maintenant je reçois une erreur.C'est bien après une semaine de travail sur ceci: D – Alberto

+0

Le deuxième commentaire que vous dites je ne sais pas comment y remédier. ceci est limité car il n'y a pas de tutoriel/démo qui correspond à ma DLL – Alberto

0

Vous avez fourni la source pour deux classes Java et une seule des implémentations natives. Ce qui nous rend la tâche difficile à comprendre.Débarrassez-vous de NewClass.

Vous voulez que votre classe java Epanet charge son wrapper natif dans l'appel System.loadLibrary(), puis votre wrapper dll chargera automatiquement epanet.dll.

En termes de passage d'un long * dans votre code natif, vous ne pouvez pas. La compétence dans la création d'une classe wrapper java-c est que vous ne pouvez pas appeler directement les méthodes d'origine directement! Vous pouvez passer un long simple, mais tous les changements apportés à la longue seront perdus. Vous pouvez donc passer un objet Java mutable à votre appel wrapper et le changer ou plus simplement faire modifier la méthode native par un état de la classe Epanet.

+0

S'il vous plaît, vérifiez la nouvelle solution que j'ai posté – Alberto

+0

En ce qui concerne le deuxième commentaire, je crois que je vous comprends Ma classe Java passera à mon dll aussi longtemps que jlong. Ensuite, je crée dans mydll un long var, copiez-y le jlong ​​et j'appelle la méthode native avec & long. Comme vous le dites, quand ma dll revient, je vais perdre toute modification sur le long var. – Alberto

0

Je recommande d'essayer Dependency Walker pour voir si vous avez besoin d'autres DLL (par exemple, le runtime de Microsoft C est peut-être absent).

+0

T il y avait un problème quelque part. J'ai tout reconstruit à partir de zéro et ça marche. J'ai également vérifié les dépendances et ma DLL dépend de epanet.dll. – Alberto

0

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)

exemple: accepter une longue [] dans la déclaration de la méthode native java, dans JNI vous verrez jlongArray dans cette position d'argument. convertir jlongArray en jlong ​​* en utilisant GetDoubleArrayElements() (voir le lien pour doc) et jlong ​​est 64bit (http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html), vous pouvez l'utiliser.
même pour booléen, int, objet java (voir docs pour variations) ....


avant la première mise à jour Exception in thread "main" java.lang.UnsatisfiedLinkError: NewClass.epanet(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String)V je suppose que vous avez fait erreur quelque part dans la compilation et/ou la gestion

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

raison: l'erreur aurait dû être

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

leur est pas le nom de la méthode "NewClass.epanet" en vous source (même après la mise à jour).

Questions connexes