JNA : Echange de données entre JAVA et un langage Natif

  • Sharebar

Ceux qui utilisent la librairie JNI (Java Native Interface) pour la communication et l’échange de données entre JAVA et du C/C++ (ou Python) savent que la mise au point est souvent très ardue. En effet, la première étape consiste à générer (à l’aide de javah) la signature des fonctions C correspondantes aux méthodes déclarées “native” dans les classes JAVA. Dans le corps de ces fonctions C, la manipulation des données n’est possible que par l’utilisation de fonctions JNI spécialisées aussi bien pour la conversion des données (JAVA vers C et C vers JAVA) que pour le passage en paramètre ou de valeur de retour dans le cas d’un appel à une méthode JAVA ou encore lorsqu’il faut libérer la mémoire allouée lors de la manipulation de chaîne de caractères.

Tout ceci est grandement simplifié avec l’utilisation de la librairie JNA (Java Native Access).
Elle repose entre autres sur la correspondance entre alter-ego JAVA et C/C++ (des structures en C/C++ et des classes en JAVA).
La contrainte principale étant que les noms des structures C et de leurs équivalents JAVA ainsi que les noms et l’ordre de déclaration de leurs attributs soient les mêmes. Il n’est plus nécessaire d’utiliser le mot clef “native” au sein de classe Java ni de générer des fonctions C à l’aide de javah. Il faut créer des interfaces JAVA qui déclarent les méthodes qui serviront de pont entre JAVA et C. Il faut juste respecter la correspondance JNA des types des paramètres ou des valeurs de retour entre JAVA et C.

La correspondance entre les méthodes des interfaces et les fonctions C correspondantes sont établies dynamiquement au runtime. Une méthode de la librairie JNA prend le nom de la DLL et le nom de l’interface pour créer un objet (qui implémente l’interface) qui permettra de communiquer entre Java et C/C++. Le seul inconvénient par rapport au JNI est qu’il n’est pas possible de solliciter une méthode JAVA depuis le C/C++ directement mais il est possible néanmoins d’appeler un callback déclaré dans le code JAVA.

Exemple:
Coté C/C++
MyStruct.h
struct MyStruct {
int i;
float f;
char* s;
MyStruct() :i(0), f(0), s(NULL) {}
~MyStruct() {
if (s != NULL) {
delete s;
s = NULL;
}
}
};

MyDeclaration.h (implémentation dans MyDeclaration.cpp par exemple)
__declspec(dllexport) void MyMethod(MyStruct* mystruct);
__declspec(dllexport) MyStruct* GetMyStruct(const char* cs, int len);

Coté JAVA
MyStruct.java
public class MyStruct extends Structure {
public int i;
public float f;
public String s;
public MyStruct() {
i = 0;
f = 0;
s = null;
}
}

MyDeclarationAPI.java
public interface MyDeclarationAPI extends Library {
public void MyMethod(MyStruct mystruct);
public MyStruct GetMyStruct(String s, int slen);
}

Quelque part dans une classe Java:
// chargement de la dll et de l’instanciation de l’API
MyDeclarationAPI api = (MyDeclarationAPI) Native.loadLibrary(“myDll.dll”, MyDeclarationAPI.class);

// Appel au C/C++
MyStruct mystruct = new MyStruct();
mystruct.i = 5;
mystruct.f = -0.123;
mystruct.s = “Une chaine”;
api.MyMethod(mystruct);

String s = “Une autre chaine”;
MyStruct mystr = api.GetMyStruct(s, s.length);

Notez bien les similitudes entre les déclarations en C/C++ et leurs alter-ego en JAVA, notamment les noms et les ordres de déclaration des attributs.

Pour en savoir plus: https://github.com/twall/jna

This entry was posted in JNA, Java. Bookmark the permalink.

Leave a Reply