JNA Java Native Access
Willemers Informatik-Ecke

Mit JNA können in C geschriebene dynamische Bibliotheken von Java aus aufgerufen werden. JNA kapselt die JNI (Java Native Interface und vereinfacht ihren Gebrauch. Prinzipiell besteht der Unterschied darin, dass JNI die Datenkonversion auf der C-Ebene durchführt, während JNA dies auf Java-Seite übernimmt.

Download der JNA JAR-Datei

Für die Arbeit mit der JNA muss eine JAR eingebunden werden, die auf der Website https://github.com/twall/jna/#download heruntergeladen werden kann.

Im Listing müssen die entsprechenden Klassen importiert werden.

// Das Interface stellt C-Funktionen am Beispiel der Standardfunktion puts zur Verfügung
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

public interface MeinInterface extends Library {
    // Windows will wieder Sonderwurst beim Namen der Standardbibliothek
    String libName = Platform.isWindows() ? "msvcrt" : "c";
    MeinInterface INSTANCE = (MeinInterface) Native.loadLibrary(libName, MeinInterface.class);
    // Es folgen die aufrufbaren C-Funktionen der Bibliothek 
    void puts(String s);
}
Dieses Interface wird im folgenden Beispiel aufgerufen.
// Die Funktion puts wird gerufen.
public class MeinCaller {
    public static void main(String[] args) {
        MeinInterface.INSTANCE.puts("Hello, World");
    }
}

Einbinden der JAR-Datei

Kommandozeile

Bei der Übersetzung muss die jar-Datei für JNA eingebunden werden. Bei den IDEs wird die jar-Datei als Bibliothek dem Projekt hinzugefügt. Bei Übersetzung über den Befehl javac muss diese per Option -classpath hinzugefügt werden.
javac -classpath jna-4.1.0.jar MeinInterface.java 
javac -classpath .:jna-4.1.0.jar MeinCaller.java 
Ausgeführt wird mit der Option -cp
java -cp .:jna-4.1.0.jar MeinCaller 
Unter Windows muss als Trennzeichen ein Semikolon statt eines Doppelpunkts angegeben werden.

NetBeans

Bei NetBeans wird die JAR-Datei dem Projekt hinzugefügt. Dazu klickt man das Projekt mit der rechten Maustaste an und folgt den Menüeinträgen bzw. Buttons:
Project-Properties | Libraries | Add JAR/Folder
Dann fügt man die heruntergeladene jna.jar ein. Es beginnt automatisch das Background Scanning des Projekts.

Die eigene C/C++-Bibliothek

Nun soll eine eigene C/C++-Bibliothek aufgerufen werden. Spätestens wenn auch von C/C++ aus Java-Methoden aufgerufen werden sollen, ist ein Umstieg zu
JNI (Java Native Interface erforderlich.

Um eine eigene C-Funktion aufzurufen, muss diese als dynamische Bibliothek vorliegen. Prinzipiell kann die Bibliothek auch in C++ geschrieben sein, nur die Schnittstelle muss C sein, weil sowohl UNIX/Linux als auch Windows nur C-Schnittstellen bei dynamischen Bibliotheken verwenden.

Die Vorgehensweise wird anhand der einfacheren Variante unter Linux gezeigt. Die Windows-Version wird an anderer Stelle im Zusammenhang mit dynamischen Bibliotheken unter C++ ausgeführt.

Wir programmieren eine Bibliothek libhello. Unter Linux muss eine dynamische Library immer mit lib anfangen. Beim Einbinden des Bibliothek wird aber nur noch der Name hinter lib genannt, im Beispiel also hello.

#include 
using namespace std;

extern "C" { // damit der Zugriff von JNA klappt
    void sagHallo()
    {
        cout << "Hallo" << endl;
    }
}
Man sieht hier deutlich, dass es sich um C++-Code handelt. Nur die externe Schnittstelle wird als "C" markiert. Übersetzt wird die Bibliothek so:
cc -c libhello.cpp
cc -shared -o libhello.so libhello.o
Die fertige Bibliothek wird mit root-Rechten in ein Verzeichnis geschoben, in dem dynamische Bibliotheken gesucht werden. Das ist beispielsweise /usr/lib.
sudo cp libhello.so /usr/lib
Das Interface muss auf die Bibliothek hello und die Funktion sagHallo angepasst werden.
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

public interface LibHelloInterface extends Library {
    String libName = "hello";
    LibHelloInterface INSTANCE = (LibHelloInterface) Native.loadLibrary(libName, LibHelloInterface.class);
    // Es folgen die aufrufbaren C-Funktionen der Bibliothek 
    void sagHallo();
}
Der Aufrufer ruft sagHello über das Interface LibHelloInterface.
public class LibHelloCaller {
    public static void main(String[] args) {
        LibHelloInterface.INSTANCE.sagHallo();
    }
}
Beides muss übersetzt werden und kann dann gestartet werden.
javac -classpath jna-4.1.0.jar LibHelloInterface.java
javac -classpath .:jna-4.1.0.jar LibHelloCaller.java 
java -cp .:jna-4.1.0.jar LibHelloCaller