はじめに
JavaからC言語で書かれた処理を呼ぶ必要が出てきたため、調べたことをメモしておきます。
昔の知識でJNI(Java Native Interface)を使うものだと思っていたけれど、
最近ではJNA(Java Native Access)という便利なクラスライブラリがあって、
驚くほど簡単に呼び出せます。
JNAライブラリの入手
以下のWebサイトからダウンロードできます。
https://github.com/twall/jna#download
- jna.jar
サンプルコード1 (静的にマッピング)
C言語の標準ライブラリであるprintfを呼び出すサンプルです。(上記サイトを参考)
まず、呼び出したい関数をinterfaceで定義しておきます。
このインタフェースをNative#loadLibrary()メソッドにかけるとあら不思議、
ライブラリ関数を呼ぶインスタンスが生成されます。
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
public class HelloWorld {
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary) Native.loadLibrary(
Platform.isWindows() ? "msvcrt" : "c",
CLibrary.class);
void printf(String format, Object... args);
}
public static void main(String[] args) {
CLibrary.INSTANCE.printf("Hello, World\n");
}
}
サンプルコード2(動的にマッピング)
呼び出したいライブラリや関数がパラメータなどで渡され、動的に決まる場合はどうしたらいいの?
今回、実装したい処理はまさにこのケースだったので、あらかじめinterfaceを定義できないし、
javax.tools.JavaCompilerを使ってinterfaceを動的に生成して・・・という検証用コードを
思わず書いてしまったのですが、実はそんなに頑張らなくても簡単にできました。
import com.sun.jna.Function;
import com.sun.jna.Platform;
public class HelloWorld {
public static void main(String[] args) {
String libraryName = Platform.isWindows() ? "msvcrt" : "c";
String functionName = "printf";
// Function
Function function = Function.getFunction(
libraryName,
functionName,
Function.C_CONVENTION);
// Invoke
function.invoke(new Object[]{"Hello, world\n"});
}
}