8

背景情報: 私は Java でのプログラミングに慣れており、Eclipse と Visual Studio の使い方を知っています。

最終的な目的: できれば Visual Studio で、Java 関数を実行する GUI を作成すること。

この質問から私が達成したいこと:クリックするとJava関数を実行し、結果をC++に返すC++のボタン。(おそらく JVM を呼び出すことによって)

現在、次のデータ構造を検討しています。

  • .txt ファイルなどの「共通」ファイルを介してデータを共有する (しかし、Java 関数を開始するにはどうすればよいでしょうか?)
  • ソケットを開く (この問題には複雑すぎるようです)
  • サーバー経由の接続 (複雑すぎる)
  • C++ から JVM を呼び出し、Java ファイルを実行します (これが最も合理的な方法だと思いますが、多くのコードが必要です)。

の存在を知りましたがJace、簡単なインターフェイスではなく、複雑なプログラムを作成するのに非常に便利だJNISWIG思います。私は複雑なプログラムを作りたくないので、それらのコマンドをすべて学習するのはかなり面倒だと感じています。

また、まったく同じことを尋ねる多くの Stack Exchange の質問を読みましたが、それらはすべて非常に複雑な回答をしているようです。

だからここに私の質問があります:

C++コードがこのJava関数にいくつかの引数を渡すC++から(必要に応じてプリコンパイルされた)Java関数を実行する最も簡単な方法は何ですか?

前もって感謝します。

4

1 に答える 1

16

C++ から JVM を呼び出し、Java ファイルを実行します (これが最も合理的な方法だと思いますが、多くのコードが必要です)。

はい、間違いなく最も合理的な方法です。また、JNI と呼び出し APIを使用すると、コードはそれほど多くありません。

jvm.dll の検索

Oracle JVM へのパスをハードコーディングしjvm.dllたり、programs フォルダーで呼び出されたファイルを検索したりすることもできjvm.dllますが、これらは明らかに非常にハックです。ただし、かなり簡単な解決策があるようです: レジストリです。キーHKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environmentには、REG_SZ呼び出されたCurrentVersion. このキーの値 (現在は1.7) を読み取り、その名前の子キーを開くことができます (HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\1.7この例では)。そのキーには、へのパスであるREG_SZ呼び出されたが含まれます。vsについて心配する必要はありません。WOW64 は、64 ビット Windows 上の 32 ビット プロセスであり、そのキーに 32 ビットへのパスが含まれている場合、レジストリ クエリを自動的にリダイレクトします。コード:RuntimeLibjvm.dllProgram filesProgram files (x86)HKLM\SOFTWARE\Wow6432Nodejvm.dll

#include <Windows.h>
#include <jni.h> // C:\Program Files\Java\jdk1.7.0_10\include\jni.h

// ...

DWORD retval;
// fetch jvm.dll path from registry
HKEY jKey;
if (retval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\JavaSoft\\Java Runtime Environment"), 0, KEY_READ, &jKey))
{
    RegCloseKey(jKey);
    // assuming you're using C++/CLI
    throw gcnew System::ComponentModel::Win32Exception(retval);
}

TCHAR versionString[16]; // version numbers shouldn't be longer than 16 chars
DWORD bufsize = 16 * sizeof(TCHAR);
if (retval = RegGetValue(jKey, NULL, TEXT("CurrentVersion"), RRF_RT_REG_SZ, NULL, versionString, &bufsize))
{
    RegCloseKey(jKey);
    // assuming you're using C++/CLI
    throw gcnew System::ComponentModel::Win32Exception(retval);
}

TCHAR* dllpath = new TCHAR[512];
bufsize = 512 * sizeof(TCHAR);
retval = RegGetValue(jKey, versionString, TEXT("RuntimeLib"), RRF_RT_REG_SZ, NULL, dllpath, &bufsize)
RegCloseKey(jKey);
if (retval)
{
    delete[] dllpath;
    // assuming you're using C++/CLI
    throw gcnew System::ComponentModel::Win32Exception(retval);
}

jvm.dll をロードして CreateJavaVM 関数を取得する

この部分は非常に簡単です。 LoadLibraryand を使用するだけGetProcAddressです。

HMODULE jniModule = LoadLibrary(dllpath);
delete[] dllpath;
if (jniModule == NULL)
    throw gcnew System::ComponentModel::Win32Exception();
typedef int (JNICALL * JNI_CreateJavaVM)(JavaVM** jvm, JNIEnv** env, JavaVMInitArgs* initargs);
JNI_CreateJavaVM createJavaVM = (JNI_CreateJavaVM)GetProcAddress(jniModule, "JNI_CreateJavaVM");

JVM の作成

これで、その関数を呼び出すことができます:

JavaVMInitArgs initArgs;
initArgs.version = JNI_VERSION_1_6;
initArgs.nOptions = 0;
JavaVM* jvm;
JNIEnv* env;
if ((retval = createJavaVM(&jvm, &env, &initArgs)) != JNI_OK)
    throw gcnew System::Exception(); // beyond the scope of this answer

おめでとう!プロセス内で JVM が実行されるようになりました。おそらく、アプリケーションの起動時に JVM を起動するでしょう。JVM を作成したばかりのスレッドからのみ Java コードを呼び出すことが 100%確実envでない限り、ポインターを破棄できますが、ポインターは保持する必要がありjvmます。

JNI 環境の取得 (オプション)

これで JVM が作成され、アプリケーションが起動して実行され、誰かがそのボタンをクリックします。次に、Java コードを呼び出します。前の手順で JVM を作成したスレッドに現在いることが 100%確実envで、ポインタがまだ残っている場合は、これをスキップできます。それ以外の場合は、現在のスレッドが JVM にアタッチされているかどうかを簡単に確認し、そうでない場合はアタッチします。

JNIEnv* env;
bool mustDetach = false;
jint retval = jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
if (retval == JNI_EDETACHED)
{
    JavaVMAttachArgs args;
    args.version = JNI_VERSION_1_6;
    args.name = NULL;
    args.group = NULL;
    retval = jvm->AttachCurrentThread(&env, &args);
    mustDetach = true; // to clean up afterwards
}
if (retval != JNI_OK)
    throw gcnew System::Exception(); // should never happen
invokeJavaCode(env); // next step
if (mustDetach)
    jvm->DetachCurrentThread();

Java コードの呼び出し

envこれで、Java コードを呼び出す必要があり、ポインターも取得できます。最も簡単な解決策が必要なので、静的メソッドを呼び出す方法は次のとおりです。

jclass clazz = env->FindClass("com/myself/MyClass");
if (clazz == NULL)
    throw gcnew System::Exception();
jmethodID mid = env->GetStaticMethodID(clazz, "myStaticMethod", "<signature>");
if (mid == NULL)
    throw gcnew System::Exception();
<type> returnedValue = env->CallStatic<type>Method(clazz, mid, <arguments>);

javap -s(コマンド ライン ツール) を使用して、メソッドのシグネチャを確認できます。<type>任意のプリミティブ型にすることができます (Java メソッドの戻り値の型と一致する必要があります)。引数は、Java メソッドの引数と一致する限り、任意のプリミティブ型にすることができます。

終わり

Windows で C++ から Java コードを呼び出す最も簡単な方法 (実際には、最初の 2 つの部分だけが Windows 固有です...)。ああ、そして最も効率的なものでもあります。データベースとファイルをねじ込みます。ソケットを使用127.0.0.1することもできますが、これは効率が大幅に低下し、おそらくこれよりも少ない作業ではありません。うわー、この答えは私が予想したよりも少し長いです。うまくいけば、それは役に立ちます。

于 2013-06-27T17:49:38.827 に答える