今まで JNI を作ったことがなかったので、ちょっと作ってみようと思いました。
で、 DLL のコンパイルには MinGW を使うことにしてやってみると、 Java から DLL を読み込むのに失敗する。
同じ物を VC++ でコンパイルしてみると何も問題なく動く。
調べてみると…
どうも、 VC++ だと “_<関数名>@<引数サイズ>” という名前でエクスポートされるのに対して、
MinGW だと “<関数名>@<引数サイズ>” というように関数名の前にアンダーバー(アンダースコア: “_”)が付かない!
(Stdcall and DLL tools of MSVC and MinGW には、他のコンパイラのエクスポート名のパターンが載ってる)
“@” の後ろの引数のサイズ(呼び出すのに必要となるスタックのサイズ?)は、コンパイラじゃないと分からないか、分かってもいちいち計算するのは面倒。
ということで、いったん、 MinGW のデフォルトのエクスポート名で .DEF ファイルだけ出力させて、
sed で加工して関数名の前にアンダースコアを付けるエイリアスとするようにした。
そのときの makefile が次の通り。
TARGET=HelloJni.dll CFLAGS=-mwindows -D_JNI_IMPLEMENTATION_ -I"/c/Program Files/java/jdk1.6.0_24/include" -I"/c/Program Files/java/jdk1.6.0_24/include/win32" LFLAGS=-mwindows -shared OBJS=HelloJni.o all: $(TARGET) $(TARGET): $(OBJS) dlltool --output-def HelloJni_tmp.def $(OBJS) sed -e 's/^\([[:space:]]\+\)\([[:alnum:]_]\+\@[[:digit:]]\+\)\(.*\)$$/\1_\2=\2\3/' HelloJni_tmp.def > HelloJni.def dllwrap --def HelloJni.def -mwindows -o $(TARGET) $(OBJS) HelloJni.o: HelloJni.c gcc $(CFLAGS) -c HelloJni.c clean: rm -f *.dll *.o *.def
dlltool にオブジェクトファイルを指定して編成される予定の DLL の DEF ファイルだけを出力できる。
その .DEF の関数毎に “<エイリアス>=<元の関数名>” の書式を利用して、アンダースコア付きの関数名に変換。
変換後の .DEF ファイルを dllwrap に指定して、アンダースコア付きエクスポート名の DLL に編成するという仕組み。
sed の正規表現パターンの “$” は二重(“$$”)にすること。
make がダブルクォーテーションに括られている事など無視して、$とそれに続く文字や記号をマクロと判断して展開しようとして消してしまうため、対make用のエスケープをしなければならない。