読者です 読者をやめる 読者になる 読者になる

アーキテクチャをスマートに。

株式会社ネオジニア代表。ITアーキテクトとしてのお仕事や考えていることなどをたまに綴っています。(記事の内容は個人の見解に基づくものであり、所属組織を代表するものではありません)

世界初!Pure Java で DLL

Java ネタ プログラミング

今回はかなり独創的でトリッキーな内容です。

このような試みはおそらく世界初です!

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         // 予約済み
 )    

それで?

これが何の役に立つのかという疑問はとりあえずおいといて、興味のある方は一度試してみて下さい!
びっくりしますよ!


解説は次回

コードの流用、記事へのリンクはご自由に。自己責任でね。