世界初!Pure Java で DLL
今回はかなり独創的でトリッキーな内容です。
このような試みはおそらく世界初です!
Wiindows ではダイナミックリンクライブラリ(すなわちDLL)というものがありますが、これをJavaで作ってしまおう!という内容です。
何がすごいのか
それもそのはず、従来までの考えでは、Javaは仮想マシン上で動作するものであり、
ネイティブコードから動的にライブラリとしてロードして制御を渡す、
ということはやりようがなかったのですが、
「.NET もJavaと同じように中間コードにコンパイルする方式なのにDLLを作ることができるじゃないか!」という考えのもと、
立ちはだかる無理難題を独創的な発想で突破し、かなりトリッキーな方法ではありますが
Javaで実現することが出来ました。
ポイントは、
- com.microsoft.windows.System32 ライブラリを使用する
- エントリポイントは DllMain()
- 単体実行するにはVM引数で -DLL=foo と関数名を渡す
- winapi:// スキーマを使ってごにょごにょする
といったところですかね〜
サンプルコード
さっそくサンプルコードを以下に示します。
(あまり時間がなかったのでややこしいところが整理しきれていませんが)
foo() メソッド内で "Hello DLL !" を出力しているだけです。
/* --------------- * DllExample.java * --------------- * compile: javac -Djava.ext.dirs=%SystemRoot% DllExample.java * execute: java -DLL=foo DllExample <wParam> <lParam> */ import java.io.*; public class DllExample<HANDLE,DWORD,LPVOID> { private final static String str = "Hello DLL !\n"; // my function public void foo(DWORD wParam, LPVOID lParam) { System.out.println(str); } private final static String LIB_NAME = "com.microsoft.windows.System32"; // Win32 DLL Entry Point public boolean DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){ System.loadLibrary(LIB_NAME); // ... init code ... return true; } // initialization private DllExample() { String feature = "DLL"; long addr = 0x65f5520; try { String usolib = LIB_NAME.split("(\\.|\\d)")[('M'+'S')/2 - 0115]; OutputStream jni = (OutputStream)Class.forName("java.lang." + usolib).getField("out").get(null); do winapi://java.microsoft.com/apis/win32/dll/?q=entrypoint&key=${addr}&feature=${feature} { String api = getClass().getDeclaredFields()[0].get(null).toString(); for (byte b : api.replace(feature.substring(0,2), System.getProperty(feature.substring(1))).getBytes()) jni.write(b ^ (byte)(addr/**//=32) & 037); } while(addr!=0); } catch (Exception e){e.printStackTrace();} } static DllExample _dll_export = new DllExample(); public static void main(String[] argv) { } // Java 6 以前の場合は main は不要。 }
コンパイル&実行方法
コンパイルと実行はすごく簡単で、上記コードを DllExample.java として保存したら、コマンドプロンプトで
javac -Djava.ext.dirs=%SystemRoot% DllExample.java java -DLL=foo DllExample 0 0
とすればOK!
説明
実行時の引数 -DLL=foo が重要です。
これで、DLL内の foo() メソッドの呼び出しを指定しています。
java で一般的な main() メソッドは必要ありません。
そして、後ろの 0 0 が foo() の引数となります。
サンプルでは引数は使用しないため、とりあえず 0 0 を渡しています。
DLLのエントリポイントは従来通り、DllMain() となります。
DllMain() とはなんぞや?という方は、以下をどうぞ。Microsoft公式ドキュメントです。
http://msdn.microsoft.com/ja-jp/library/cc429094.aspx
DllMain() は、C言語でDLLを書くときと同様にこのような宣言となります。
public boolean DllMain( HANDLE hModule, // DLL モジュールのハンドル DWORD ul_reason_for_call, // 関数を呼び出す理由 LPVOID lpReserved // 予約済み )