.NETとJavaの相互運用

IKVM.NETをご存知でしょうか? 日記でも紹介したことがありますが、.NETランタイム上で動作するJavaVMです。以前見たときは、.NET上でJavaを動かしても何が嬉しいのかよく分かってませんでしたが、調べてみたところ結構面白いことが出来ることが分かりました。

※注意
IKVM.NETをダウンロード後、binディレクトリにPathを通し、また、開発ディレクトリに

これら2つのアセンブリをコピーしていることを前提に話を進めます。

・シナリオ1
あなたは.NETプログラマです。.NETに大変満足していますが、Javaの方が歴史があるため、多数の魅力的なライブラリがあることを羨ましく思っています。今日も、こんな魅力的なライブラリを見つけてしまい、欲しくてたまりません。

import java.io.*;

public class FindFile {
  public static void find(String dir) {
    findSub(new File(dir));
  }

  private static void findSub(File dir) {
    File[] files = dir.listFiles();
    for (int i = 0; i < files.length; ++i) {
      if (files[i].isDirectory())
        findSub(files[i]);
      else
        System.out.println(files[i].getName());
    }
  }
}

こんなもん、いらんとか言わないで。(^^; これをC#から呼び出してみましょう。上記ソースはコンパイルされ、FindFild.classになっているとします。

> ikvmc -target:library -reference:IKVM.GNU.Classpath.dll FileFind.class

このコマンドを実行するとjava bytecodeがcilへ変換されFileFind.dllが生成されます。FileFind.dllを呼び出すC#のコードを書きます。

public class Foo {
  public static void Main(string[] args) {
    if (args.Length > 0) {
      FindFile.find(args[0]);
    }
  }
}

これを、Foo.csとして保存してコンパイルします。

> mcs /r:FindFile.dll,IKVM.GNU.Classpath.dll Foo.cs

これで、Foo.exeが完成です。ikvmcで変換したアセンブリコンパイルするにはIKVM.GNU.Classpath.dllが必要になります。ちなみに、実行にはIKVM.Runtime.dllも必要なので注意です。で、

> Foo.exe c:\windows

とか実行すると、ディレクトリに存在するファイルが列挙されます。このように、.NET側からJavaを呼び出すことに成功しました。

・シナリオ2
Javaプログラマさんは面白くありません。.NET側から一方的にライブラリを利用されるだけです、腹いせに.NETライブラリを無駄に呼んでやろうとか黒い情念を燃やしています。で、yaneSDK4Csとかいうマイナーなライブラリに何故か目をつけました。どうやら、Javaから.NETを呼び出すには、ikvmstub.exeを使えば良いらしいです。

> ikvmstub mscorlib.dll

mscorlib.jarが生成され、.NETのコアライブラリは呼び出し放題です。

> ikvmstub IKVM.GNU.Classpath.dll

として、IKVM.GNU.Classpath.jarを作っておくと、.NET特有のboxing/unboxingのためのラッパークラスが使えます。準備が整ったので、いよいよyaneSDK4CS.dllを変換します。

> ikvmstub yaneSDK4CS.dll

では、変換したライブラリを呼び出してみます。

import ikvm.lang.*;
import cli.y4cs.sdl.*;
import cli.y4cs.aux.*;
import cli.y4cs.math.*;
import cli.y4cs.timer.*;
import cli.y4cs.draw.*;

public class Game {
  public static void main(String[] args) {
    Screen screen = new Screen();

    screen.beginScreenTest();
    screen.setVideoMode(640, 480, 0);
    screen.endScreenTest();

    Surface bg = new Surface();
    Surface sp = new Surface();

    bg.load("bg.bmp");
    sp.load("sp.bmp");

    Color4ub key = new Color4ub(
        CIL.box_ubyte((byte)0),
        CIL.box_ubyte((byte)0),
        CIL.box_ubyte((byte)255),
        CIL.box_ubyte((byte)192) );
    bg.blt(sp, 0, 0, key);

    Texture tx = new Texture();
    tx.setSurface(bg);

    FpsTimer fpstimer = new FpsTimer();
    fpstimer.setFps(60);

    while (GameFrame.pollEvent() == 0) {
      screen.clear();
      screen.blt(tx, 0, 0);
      screen.update();
      fpstimer.waitFrame();
    }
  }
}

ファイル名をGame.javaとして保存します。見てのとおり、変換したライブラリのパッケージはcli."C#での名前空間名"となります。では、こいつをコンパイルしましょう。

> javac -classpath mscorlib.jar;yaneSDK4Cs.jar;IKVM.GNU.Classpath.jar Game.java

これで、Game.classが出来ました。これで完成かと思いきや、実はこのまんま、JavaVMで実行することが出来ないのです。最初の例のように.NETアセンブリへ変換しなければなりません。

> ikvmc -reference:yaneSDK4Cs.dll:IKVM.GNU.Classpath.dll -target:exe Game.class

Javaプログラマとしては騙された気がしますが、一応、Javaでソースは書けたと無理矢理納得することに。(^^;

というわけで、.NET⇔Javaが出来ました。.NET←JavaがそのまんまJavaVMで実行できないのが残念ですけど、これは結構面白いんじゃないかと思います。