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 ビットへのパスが含まれている場合、レジストリ クエリを自動的にリダイレクトします。コード:RuntimeLib
jvm.dll
Program files
Program files (x86)
HKLM\SOFTWARE\Wow6432Node
jvm.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 関数を取得する
この部分は非常に簡単です。 LoadLibrary
and を使用するだけ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
することもできますが、これは効率が大幅に低下し、おそらくこれよりも少ない作業ではありません。うわー、この答えは私が予想したよりも少し長いです。うまくいけば、それは役に立ちます。