MingWとEclipse CDTでWin32プログラムでJNIのランチャー作成

投稿者: | 2015/12/16 水曜日

XP sp3で挑戦
まずMingWをインストールする。
01mingdown

デフォルトのフォルダへ
02minginst
msysは今回は使わないと思うけどとりあえずインストールgcc-g++は必須のはず。
3minginst2

次にjavaをインストール。
clipshot 2015-12-15 07-16-41
警告はスルー。8.66をインストールした。PATH関係でエラーが出るかもしれないのでいったんログオフ。

次にeclipse CDTのインストール、eclipse.orgへ行ってCDTをダウンロード、ここではeclipse-cpp-mars-1-win32.zipをダウンロードしてC:\に解凍。

起動しようとするとすぐ終了してしまうので、コマンドラインからeclipsec.exeで起動してみる。
clipshot 2015-12-15 07-24-39
メモリーが足りない?eclipse.iniの最後を以下のように書き換えて起動。

-Dosgi.requiredJavaVersion=1.7
-Xms256m
-Xmx640m

起動できた。
clipshot 2015-12-15 07-27-51
とりあえずHello World C++というのを作ってみる
clipshot 2015-12-15 07-32-37
メニューから{Project][Build All]

07:34:17 **** Rebuild of configuration Debug for project HelloWorld ****
Info: Internal Builder is used for build
g++ -O0 -g3 -Wall -c -fmessage-length=0 -o "src\\HelloWorld.o" "..\\src\\HelloWorld.cpp" 
g++ -o HelloWorld.exe "src\\HelloWorld.o" 
 
07:34:22 Build Finished (took 5s.656ms)

ビルドできた。次に[Run][Run Configuration]で以下のように設定。というか自動で作られる。
clipshot 2015-12-15 07-36-21
[Run][Run]を実行。しかし!!!Hello World!!!が表示されないので、コマンドラインから起動してみると以下のエラーがでた。
clipshot 2015-12-15 07-39-41

リンカオプションに-static-libgcc -static-libstdc++をつけて解決
clipshot 2015-12-15 20-35-04
clipshot 2015-12-15 20-36-28
コンソールを出さないWindowsプログラムを作るため、Makefileプロジェクトを作った。
clipshot 2015-12-16 03-06-44

ソースファイル

#include <windows.h>
 
extern "C" {
 
int WINAPI WinMain(
  HINSTANCE hInstance,
  HINSTANCE hPrevInstance,
  LPSTR lpCmdLine,
  int nShowCmd)
{
	MessageBox(NULL, "Hello", "World", 0);
	return 0;
}
 
}

メイクファイル

CXXFLAGS =	-O2 -g -Wall -fmessage-length=0 -mwindows
 
OBJS =		main.o
 
LIBS =
 
TARGET =	main.exe
 
$(TARGET):	$(OBJS)
	$(CXX) -static-libgcc -static-libstdc++ -o $(TARGET) $(OBJS) $(LIBS)
 
all:	$(TARGET)
 
clean:
	rm -f $(OBJS) $(TARGET)

フラグの指定の仕方が強引だがLDFLAGSが効かないみたいなので今はこれで。

実行
clipshot 2015-12-16 03-22-35

これでwin32アプリの準備はできたので、JNIを利用してjavaのランチャーを作る。jni.hがJDKに入ってるので仕方なくJDK7をインストール。
mymain()を作成。http://homepage2.nifty.com/igat/igapyon/diary/2006/ig061108.html からコピペ

#include "jni.h"
 
 
int mymain()
{
    JNIEnv *jnienv;
    JavaVM *javavm;
    JavaVMInitArgs vm_args;
 
    JavaVMOption options[1];
    options[0].optionString = "-Djava.class.path=.";
    vm_args.version = JNI_VERSION_1_2;
    vm_args.options = options;
    vm_args.nOptions = 1;
    vm_args.ignoreUnrecognized = true;
 
    int result = JNI_CreateJavaVM(&javavm, (void **)&jnienv, &vm_args);
 
    if (result != 0) {
        return 1;
    }
 
    jclass cls = jnienv->FindClass("MyClass");
    if (cls == 0) {
        return 1;
    }
 
    jmethodID mid = jnienv->GetStaticMethodID(cls, "process", "()Ljava/lang/String;");
    if (mid == 0) {
        return 1;
    }
 
    jobject objResult = jnienv->CallStaticObjectMethod(cls, mid);
 
    jthrowable throwResult = jnienv->ExceptionOccurred();
    if (throwResult != NULL) {
        jnienv->ExceptionDescribe();
        jnienv->ExceptionClear();
        return 1;
    }
 
    if (objResult == NULL) {
    } else {
        jstring strResult = (jstring) objResult;
        printf("[%s]\n", jnienv->GetStringUTFChars(strResult, NULL));
    }
 
    result = javavm->DestroyJavaVM();
    if (result != 0) {
        return 1;
    }
 
    return 0;
}

メイクファイル修正

CXXFLAGS =	-O2 -g -Wall -fmessage-length=0 -mwindows -I"C:\Program Files\Java\jdk1.7.0_80\include" -I"C:\Program Files\Java\jdk1.7.0_80\include\win32"
 
OBJS = main.o mymain.o
 
LIBS = 
LDLIBS = -ljvm
TARGET =	main.exe
 
$(TARGET):	$(OBJS)
	$(CXX) -static-libgcc -static-libstdc++ -L"C:\Program Files\Java\jdk1.7.0_80\lib" -ljvm -o $(TARGET) $(OBJS) $(LIBS)
 
all:	$(TARGET)
 
clean:
	rm -f $(OBJS) $(TARGET)

ビルドするとエラー

undefined reference to `_imp__JNI_CreateJavaVM@12'

jvm.libがこの関数をエクスポートしてないみたいなので、dllから直接よぶ。今回はjvm.dllの位置は固定でやる。

#include <windows.h>
#include "jni.h"
 
 
typedef jint (JNICALL *FNJNI_CreateJavaVM)(JavaVM **pvm, void **penv, void *args);
 
int mymain()
{
    JNIEnv *jnienv;
    JavaVM *javavm;
    JavaVMInitArgs vm_args;
 
    JavaVMOption options[1];
    options[0].optionString = "-Djava.class.path=.";
    vm_args.version = JNI_VERSION_1_2;
    vm_args.options = options;
    vm_args.nOptions = 1;
    vm_args.ignoreUnrecognized = true;
 
    HMODULE h = LoadLibrary("C:\\Program Files\\Java\\jre1.8.0_66\\bin\\client\\jvm.dll");
    if(!h)
    	return 1;
    FNJNI_CreateJavaVM fn = (FNJNI_CreateJavaVM)GetProcAddress(h, "JNI_CreateJavaVM");
    if(!fn)
    	return 1;
 
    int result = fn(&javavm, (void **)&jnienv, &vm_args);
 
    if (result != 0) {
        return 1;
    }
 
    jclass cls = jnienv->FindClass("MyClass");
    if (cls == 0) {
        return 1;
    }
 
    jmethodID mid = jnienv->GetStaticMethodID(cls, "process", "()Ljava/lang/String;");
    if (mid == 0) {
        return 1;
    }
 
    jobject objResult = jnienv->CallStaticObjectMethod(cls, mid);
 
    jthrowable throwResult = jnienv->ExceptionOccurred();
    if (throwResult != NULL) {
        jnienv->ExceptionDescribe();
        jnienv->ExceptionClear();
        return 1;
    }
 
    if (objResult == NULL) {
    } else {
        jstring strResult = (jstring) objResult;
        printf("[%s]\n", jnienv->GetStringUTFChars(strResult, NULL));
    }
 
    result = javavm->DestroyJavaVM();
    if (result != 0) {
        return 1;
    }
 
    return 0;
}

これでVMは作れたのでパラメータを調節すれば動くだろう。
CDTはいろいろくせがあって慣れないと使いづらい。

MingWとEclipse CDTでWin32プログラムでJNIのランチャー作成」への2件のフィードバック

  1. trueff 投稿作成者

    jvm.libからCreateVMがはずされたのはちゃんと動かないからだと思う。やってみるとCRT関連と思われるエラーが出る。java8の場合vc10のCRTかCRTなしだと動くようだ。でも64ビットはこれでも動かない。eclipseも同じことをしていると思われるのでソースを見てみよう。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です