JNIで呼び出したCの関数からJavaのメソッドをコールバックする

やりたいことは、


Javaからnative呼び出し
呼び出されたCの関数から、呼び出し元のJavaにあるメソッドを呼び出し




予想外に苦戦したので、メモしておきます。



MinGWのインストール



Cのビルドは、MinGWで行いました。
Symfoware Windows 7にMinGWをインストールする(mingw-get-inst使用)

また、以前JNIを作成したときの記事は以下の通り。
Symfoware MinGWで作成したDLLをJavaからJNI経由で呼び出す






Javaのサンプルプログラム



Java側のサンプルプログラムはこんな感じになりました。


package jnitest;

public class MainProccess {
    
    //ライブラリをロード
    static {
        System.loadLibrary("test");
    }
    
    public static void main(String[] args) throws InterruptedException {
        
        System.out.println("main start");

        MainProccess proccess = new MainProccess();
        for (int i = 0; i < 10; i++) {
            synchronized (proccess) {
                System.out.println("main:" + i);
                
                //C言語の関数呼び出し
                add10(proccess, i);
            }
            
            //特に意味はないが、実行結果をじっくり確認するため待つ
            Thread.sleep(500);
        }
        
        System.out.println("main end");
        
    }
    
    
    //JNI呼び出し
    protected static native void add10(MainProccess listener, int value);

    // C++から呼ばれるメソッド
    public synchronized void callback(int n) {
        System.out.println("callback:" + n);
    }
}




「callback」がC側から呼び出す予定のメソッドです。







Cのサンプルプログラム



Javaのプログラムを作成したら、ビルドしてclassファイルを作成します。
その後、javahコマンドでCのヘッダーファイルを作成します。


"C:\Program Files\Java\jdk1.6.0_24\bin\javah.exe" -classpath . jnitest.MainProccess





出力される「jnitest_MainProccess.h」の内容はこんな感じ。


/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class jnitest_MainProccess */

#ifndef _Included_jnitest_MainProccess
#define _Included_jnitest_MainProccess
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     jnitest_MainProccess
* Method:    add10
* Signature: (Ljnitest/MainProccess;I)V
*/
JNIEXPORT void JNICALL Java_jnitest_MainProccess_add10
(JNIEnv *, jclass, jobject, jint);

#ifdef __cplusplus
}
#endif
#endif







JNIで呼び出されるCプログラム「jnitest_MainProccess.c」はこんな感じ。


#include <jni.h>
#include "jnitest_MainProccess.h"

JNIEXPORT void JNICALL Java_jnitest_MainProccess_add10
    (JNIEnv *env, jclass cls, jobject obj, jint value) {
    
    //リスナーのlistenメソッドを取得する
    jmethodID func = (*env)->GetMethodID(env, cls, "callback", "(I)V");
    if(func==NULL) return;
    
    (*env)->CallVoidMethod(env, obj, func, value + 10);

}





dllにするため、jnitest_MainProccess.defを作成しておきます。


LIBRARY test.dll
EXPORTS
Java_jnitest_MainProccess_add10@16 @ 1






Makefileはこんな感じ。


CC=gcc
RM=del

CPPFLAGS= -I"C:\Program Files\Java\jdk1.6.0_24\include" -I"C:\Program Files\Java\jdk1.6.0_24\include\win32"

all: test.dll

test.dll: jnitest_MainProccess.o
    dllwrap.exe -k -def jnitest_MainProccess.def --driver-name gcc -o test.dll jnitest_MainProccess.o

jnitest_MainProccess.o: jnitest_MainProccess.c
    $(CC) $(CPPFLAGS) -c jnitest_MainProccess.c -o jnitest_MainProccess.o $(LDFLAGS)

clean:
    $(RM) *.o *.bak *~ core TAGS

distclean:
    $(RM) *.o *.bak *~ core TAGS
    $(RM) julius-simple




※実際は4スペースではなくタブ。



コマンドプロンプトで、mingw32-makeを実行し、dllを作成します。


c:\>mingw32-make
gcc -I"C:\Program Files\Java\jdk1.6.0_24\include" -I"C:\Program Files\Java\jdk1.
6.0_24\include\win32" -c jnitest_MainProccess.c -o jnitest_MainProccess.o
dllwrap.exe -k -def jnitest_MainProccess.def --driver-name gcc -o test.dll jnite
st_MainProccess.o



これで「test.dll」が出来上がります。





実行してみる




c:\>java -cp . -Djava.library.path=[test.dllのあるパス] jnitest.MainProccess
main start
main:0
callback:10
main:1
callback:11
main:2
callback:12
main:3
callback:13
main:4
callback:14
main:5
callback:15
main:6
callback:16
main:7
callback:17
main:8
callback:18
main:9
callback:19
main end




狙い通りの動作です。





【参考URL】
JNI JavaVMメモ(Hishidama's Java native interface JVM Memo)
関連記事

コメント

プロフィール

Author:symfo
blog形式だと探しにくいので、まとめサイト作成中です。
https://symfo.web.fc2.com/

PR

検索フォーム

月別アーカイブ