ブログトップ 記事一覧 ログイン 無料ブログ開設

DO☆KA☆TA ~information technology~ このページをアンテナに追加 RSSフィード Twitter

2009-12-02

泥臭くJNAにチャレンジ

| 泥臭くJNAにチャレンジを含むブックマーク 泥臭くJNAにチャレンジのブックマークコメント

前から気になってたJNA*1をやってみました。以前、JNI*2で泥臭くHellWorld的なものをやりましたが、泥臭過ぎてオエッとなりました。で、気持ち的にやっと泥が取れたところでJNAに取り組んでみます。

一応、泥臭くJNIにチャレンジ(2)でやったような感じでJNAをやってみます。

コマンドラインベースで泥臭いですが以前のJNIのエントリの流れでやればJNAとの比較がしやすいかなと思いました。

実施環境など

環境

JNAは、jna.jarというライブラリが必要なのでここからダウンロードします。JNAは、JVM 1.4以降が対象です。

前提
  • MinGWへの環境パスは通しています。インストール含めてこちらを参考にしました。セットアップしてみよう
  • Javaへの環境パスはとおしています。私の環境では、C:\Program Files\Java\jdk1.6.0_11\binです。
  • C:\jnaというフォルダで作業します。
  • C:\jnaにjna.jarを配備しています。

全体手順

  • javaでJNAコードを書く
  • javacでコンパイル
  • Cのヘッダファイルを書く
  • Cのコードを書く
  • gccdll作成
  • javaコマンドで実行

DLLから作った方が個人的に分かりやすいと思ったんですがJNIとの比較の為、敢えてjavaからの実装にしました。

javaでJNAコードを書く

JNIで書いたのと同じ足し算と引き算をするだけの簡単な実装です。JNAでは以下のようになります。

JnaEx.java

import com.sun.jna.Library;
import com.sun.jna.Native;

/**
 * JNAの実験クラスです。
 * @author ujiujise
 */
public class JnaEx {
    /**
     * DLLに対してのインターフェースを定義します。
     * インターフェース名は任意です。
     * JNAのcom.sun.jna.Libraryを継承する必要があります。
     * 使用したいDLLの関数定義をJava用としてメソッド定義します。
     */
    public interface JnaExDLL extends Library {
        /** 
         * DLLのインスタンスです。これを通してDLLをアクセスします。
         * 物理ファイルのDLLへ実行パスが通るようにしておく必要があります。
         * 第1引数がDLLファイル名になります。拡張子の記述があっても動きます。
         * 絶対パスを指定してもOKです。こんな感じ→Native.loadLibrary("C:/jna/JnaEx", JnaExDLL.class);
         */
        JnaExDLL INSTANCE = (JnaExDLL) Native.loadLibrary("JnaEx", JnaExDLL.class);
        /**
         * Cに足し算させます。
         * @param left
         * @param right
         * @return 足し算した結果
         */
        int add(int left, int right);
        /**
         * Cに引き算させます。
         * @param left
         * @param right
         * @return 引き算した結果
         */
        int sub(int left, int right);
    }
    
    public static void main(String[] args) {
        JnaExDLL dll = JnaExDLL.INSTANCE;
        // 1 + 2
        int addResult = dll.add(1, 2);
        // 3 - 2
        int subResult = dll.sub(3, 2);
        System.out.println("addの結果>"+ addResult + ":subの結果>" + subResult);
    }
}

javacでコンパイル

javacコマンドを実行します。

c:\jna>javac -cp jna.jar JnaEx.java

各クラスファイルができました。

C:\jna>dir /b /o:d
jna.jar
JnaEx.java
JnaEx$JnaExDLL.class
JnaEx.class

javaとしてのビルド作業はこれで終わりです。

Cのヘッダファイルを書く

JNIではjavahでヘッダファイルを生成してましたが、JNAでは純粋なCとしてのヘッダファイルを自力で書きます。

jnaex.h

#ifndef JnaEx_DLL
#define JnaEx_DLL
#ifdef __cplusplus
extern "C" {
#endif
int add(int left, int right);

int sub(int left, int right);
#ifdef __cplusplus
}
#endif
#endif

Cのコードを書く

JNIではJNI用の独自の型などを知っておく必要がありましたが、JNAは純粋なCプログラムが書けます。

JnaEx.c

#include "jnaex.h"

int add(int left, int right) {
    return left + right;
}

int sub(int left, int right) {
    return left - right;
}

gccdll作成

DLLを作ります。

JnaEx.dll

C:\jna>gcc -Wall -shared JnaEx.c -o JnaEx.dll

普通にDLLを作れるので、JNIみたいに長ったらしくて複雑ではありません。

C:\jna>dir /b /o:d
jna.jar
JnaEx.java
JnaEx$JnaExDLL.class
JnaEx.class
jnaex.h
JnaEx.c
JnaEx.dll

javaコマンドで実行

javaコマンドを実行してみると

C:\jna>java -cp c:\jna\;jna.jar JnaEx
addの結果>3:subの結果>1

動きました。

感想

一言で言うと、JNIに比べJNAは手軽に動かせます。楽でした。楽は楽なんですが、本エントリの内容は全然JNAについて突っ込んだ内容ではありません。*3Cとjavaの型の差を把握し、Cの型に対応するjavaの型*4javaの型だけでは吸収できないJNAのクラス*5を押さえないと本格的に使えません。

JNIに対しては相変わらずやる気がないですが、JNAは手軽なのでもう少し突っ込んで色々遊んでみようと思います。

話は変わりますが、JNAと同じく手軽にDLLやSOにアクセスするJ/Invokeというのがあります。アノテーションでDLL、SOに紐づけるのが特徴的です。但し、お金が必要なライセンスです orz

具体的な実装はyone098さんのところが参考になります。J/Invoke - 1.0 Released - よねのはてな

他にも色々あるようです。teematsuさんのところでピックアップされております。意外に多いものですね。JavaとWindowsネイティブを連携させたい - プログラミングメモ

おまけ1(Eclipseで手軽に動かす)

いくらJNAが手軽といえど、本エントリのように全部コマンドラインベースでやるのは正直手軽とは言えないと思うのでEclipseで簡単に実行する方法を書いておきます。

Windowsの話になってしまいますが、有名なuser32.dllやkernel32.dllなどを呼び出すのであれば、jna.jarビルドパスを通すだけでOKです。

本エントリのように自作のDLLを使うのであれば、一つの方法としてソース・フォルダーを作ってその中にDLLを入れればOKです。

こんな感じ。

f:id:ujiujise:20091202175310j:image

但し、上記の例だとloadLibraryをするときに以下のようにパスを指定しないといけません。

Native.loadLibrary("dll/JnaEx", JnaExDLL.class);

おまけ2(参考になると思うサイト)

Java Native Access - Wikipedia
JNAの概要を知るにはまずここです。
jna
JNAのサイトです。英語ですが、Wikipediaよりは色々詳しく載っています。
JNA API Documentation
APIドキュメントです。
JNAなるもの - こげつきません
user32.dllの使い方が参考になります。
JNA samples - ping, gethostbyname
お馴染みのpingをJNAを使用して実装されています。
Javaの応用:Felicaカードリーダ - 亜大用授業資料サイト
恐らく実務で使う時もこんなソースコードの雰囲気になるのかなぁといった印象です。
jnaerator - Project Hosting on Google Code
JNAeratorというジェネレートツールです。JNAで使用したいDLLとその関数定義を指定するとcom.sun.jna.Libraryを継承するインターフェースを自動生成してくれます。
mono’s Tech Blog ≫ JNIよりずっとお手軽なJNA(Java Native Access)
JNAeratorの実例が記載されています。

*1Java Native Access

*2Java Native Interface

*3:int型しか使っていない。int型はjavaもcも同じように扱える

*4:プリミティブ、Stringクラス

*5:Pointerクラス、NativeLongクラス、Structureクラス等