Mise en œuvre de JNI (Java Native Interface) à partir de Visual

publicité
STS IRIS
Jean-Claude CABIANCA
Mise en œuvre de JNI (Java Native Interface)
à partir de Visual Studio C++
I – INTRODUCTION
I.1 – Versions des logiciels
Microsoft Visual Studio 2008
Netbeans 6.9
I.2 – Généralités
Le JNI (Java Native Interface) est un framework qui permet au code Java s'exécutant à
l'intérieur de la JVM d'appeler et d'être appelé par des applications natives (c'est-à-dire des
programmes spécifiques au matériel et au système d'exploitation de la plate-forme concernée),
ou avec des bibliothèques logicielles basées sur d'autres langages (C, C++, assembleur, etc.).
II – MISE EN OEUVRE DE JNI
II.1 – Cahier des charges
On se propose de concevoir une dll nommée dllstat permettant de faire un calcul de valeur
moyenne sur un lot de 2 valeurs entières. Cette dll correspond à celle qui est fournie par le
constructeur.
Ensuite nous allons créer une nouvelle dll nommée dllcalc permettant de calculer l'erreur relative
et utilisant la fonction fournit par dllstat, ceci afin de pouvoir l'utiliser via JNI avec du code
Java.
Le fonctionnement peut être représenté par le diagramme suivant :
Visual Studio C++ et JNI
page 1/8
STS IRIS
Jean-Claude CABIANCA
II.2 – Génération et test d'une DLL avec Visual C++ (dllstat)
2.2.1. Génération de la dll
Les première étapes de la génération d'une DLL (ici dllstat.dll )sont similaires à celles d'une
application classique (Application Console). La solution (et donc le projet) s'appelle dllstat. Il
faut par contre choisir « DLL » dans la dernière boîte de dialogue de l'assistant à la création
d'application.
Deux fichiers .cpp apparaissent alors dans l'explorateur de solution :
- Le fichier dllmain.cpp est inséré automatiquement par Visual Studio afin de créer un point
d'entrée pour la DLL.
- Le fichier dllstat.cpp (même nom que le projet et que la DLL qui sera créée) permettra de
définir les fonctions de la DLL.
Il faut ensuite créer le fichier d'en-tête de la DLL (dllstat.h) :
Le contenu du fichier .h va par contre varier. En effet, il faut prototyper les fonctions en
employant les mots clés __declspec(dllimport) afin de signifier au programme source qui
voudra appeler la fonction qu'elle provient d'une DLL :
Visual Studio C++ et JNI
page 2/8
STS IRIS
Jean-Claude CABIANCA
La définition de la fonction CalculerMoyenne() dans dllstat.cpp nécessite aussi une adaptation.
Il faudra utiliser les mots clés __declspec(dllexport) pour signifier que les fonctions seront
«exportées» pour des programmes (modules logiciels) situés « hors » de la DLL (cette directive
permet ne pas avoir à créer de fichier .def utilisant la directive EXPORTS) :
Afin de faciliter l'utilisation de la DLL, la technique employée sera liée à l'utilisation d'une
bibliothèque d'importation.
Une bibliothèque d'importation (fichier .lib) n'est pas une librairie statique mais un fichier qui
permet à un programme d'appeler les fonctions d'une DLL de manière transparente, comme s'il
utilisait une bibliothèque statique.
Pour que cette bibliothèque d'importation soit créée lors de la génération du projet et de la DLL,
il faut s'assurer que dans les propriétés du projet (Propriétés de configuration > Editeur de
liens > Avancé) , le champ Bibliothèque d'importation soit renseigné :
Visual Studio C++ et JNI
page 3/8
STS IRIS
Jean-Claude CABIANCA
Le projet peut être maintenant généré comme vu précédemment. On trouve alors dans le
répertoire Release de la solution les fichiers dllstat.dll (DLL) et dllstat.lib (bibliothèque
d'importation) :
On dispose maintenant d'une solution comportant les fichiers dllstat.dll (DLL) et dllstat.lib
(bibliothèque d'importation) au même titre que celle qui est livrée par le fabricant d'une carte
d'entrées-sorties.
2.2.2. Test de la dll
Nous allons créer un nouveau projet nommer testdllstat afin de tester la dll crée précédemment.
Afin de pouvoir appeler les fonctions de la DLL tout comme s'il s'agissait d'une librairie statique,
il faut inclure au projet la librairie d'importation, en se positionnant sur l'onglet Fichiers de
ressources, de l'explorateur de ressource, il faut cliquer droit et sélectionner Ajouter > Élément
existant.
Il faut ensuite sélectionner la librairie d'importation (dllstat.lib) afin de l'associer aux Fichiers de
ressources.
Visual Studio C++ et JNI
page 4/8
STS IRIS
Jean-Claude CABIANCA
Nous avons le résultat suivant :
II.3 – JNI
2.3.1. Démarche
Pour mettre en œuvre JNI, il faut suivre les étapes suivantes :
–
–
–
–
Déclarer des méthodes natives (prototype Java) et charger les librairies natives;
Générer le header C/C++;
Implémenter le code natif;
Compiler et générer la librairie native.
2.3.2. Déclaration des méthodes natives
La première étape consiste donc à écrire le prototype Java des méthodes natives. Dans le code de
la classe TestJNI1.java qui suit, on retrouve une méthode correspondant à la méthode native à
appeler CalculerErreur().
Ce code peut être compilé normalement sans problème puisque le compilateur ne vérifie pas les
liens vers les méthodes natives (ne charge par la librairie dynamique), par contre l'exécution
génèrera une belle exception puisque les méthodes natives correspondantes n'existent pas encore.
Visual Studio C++ et JNI
page 5/8
STS IRIS
Jean-Claude CABIANCA
Remarque : Il est impératif de charger les librairies (présentes dans le dossier dll du projet Java)
pendant le chargement de la classe afin que les méthodes natives puissent être utilisées sans
problème. Pour cela il suffit d'ajouter un bloc static dans le corps de la classe avec l'instruction
System.loadLibrary.
2.3.3. Génération du header C++
Une fois cette classe compilée, il faut utiliser l'outil javah sur la classe générée et non sur le code
source. L'outil javah, fourni avec le JDK, permet de générer un fichier d'en-tête C/C++. Ce
dernier s'utilise comme la commande java et nécessite donc un nom de classe complet (c'est-àdire avec le package) :
javah -o D:\entete.h -classpath D:\sources\java\testjni\build\classes testjni.TestJNI1
entete.h : nom du fichier .h généré par javah
-classpath : option précisant le chemin vers la classe contenant les méthodes natives
../build/classes : chemin vers la classe
Visual Studio C++ et JNI
page 6/8
STS IRIS
Jean-Claude CABIANCA
testjni.TestJNI1 : nom du paquet.nom de la classe
Ce qui nous génèrera dans le cas présent un fichier nommé entete.h contenant le code suivant :
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class testjni_TestJNI1 */
#ifndef _Included_testjni_TestJNI1
#define _Included_testjni_TestJNI1
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: testjni_TestJNI1
* Method: CalculerErreur
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_testjni_TestJNI1_CalculerErreur
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
2.3.4. Implémentation du code natif
Il est maintenant nécessaire de coder les fonctions natives correspondant au prototype généré, ce
qui donne pour notre étude de cas le résultat suivant :
// dllcalc.cpp : définit les fonctions exportées pour l'application DLL.//
#include "stdafx.h"
#include "dllstat.h"
#include "entete.h"
JNIEXPORT jint JNICALL Java_testjni_TestJNI1_CalculerErreur (JNIEnv *, jobject, jint val1,
jint val2)
{
int erreur = (50 * ( val2 - val1 )) / CalculerMoyenne(val1, val2);
return (erreur);
}
Visual Studio C++ et JNI
page 7/8
STS IRIS
Jean-Claude CABIANCA
2.3.5. Compilation et génération de la librairie native
Il nous faut désormais compiler ce bout de code à l'aide de Visual C++. Pour cela il faut
spécifier au compilateur l'emplacement des headers natif de JNI qui se trouvent dans le
répertoire include du JDK.
Il faut donc configurer Visual C++, dans Propriétés de configuration > C/C++ > Général,
entrer les chemins des répertoires des include à ajouter dans le champ Autres répertoires
Include dans notre cas, C:\Program Files\Java\jdk1.6.0_20\include; C:\Program
Files\Java\jdk1.6.0_20\include\win32 :
2.3.6. Utilisation de la librairie native
Il suffit maintenant de copier les librairies (dll et obj) dans le dossier dll du projet puis exécuter
le projet.
On obtient le résultat suivant :
Vérification : erreur = (50*(20-10)) / ((20+10)/2) = 500/15 = 33,33
Visual Studio C++ et JNI
page 8/8
Téléchargement