你可以在使用JNI从java调用的c ++函数中创建一个新的JVM吗?

所以我的设置是我有一个由我开发的.dll( A.dll ),它在原始应用程序中从外部进程调用,它基本上只是一个.exe文件,我没有源代码( B) .exe )。 A.dll的目的是与.jar文件通信,该文件也是由我开发的( C.jar )。 所以在应用程序中,“通信流程”如下所示

B.exe – > A.dll – > (通过JNI) – > C.jar

现在,我想要做的是在C.jar的开发环境中添加A.dllC.jar之间的调用作为我的测试套件的一部分。 到目前为止,我已经创建了另一个.dll( D.dll ),它反映了A.dll中的所有函数,但是使用了JNIEXPORT,只是直接调用A.dll中的相应函数。 所以这种情况下的“沟通流程”如下:

C.jar开发框架中的unit testing – >(通过JNI) – > D.dll – > A.dll – >(通过JNI) – > C.jar

在这一点上,一个非常简单的函数调用只是在C.jar中打印出来的东西, 通过整个链条起作用; 从unit testing调用到C.jar 。 但是当我在A.dll中调用函数时会出现问题,该函数使用CreateJavaVM()创建一个新的JVM,这会产生以下错误:

初始化VM时出错无法加载本机库:找不到指定的过程

所以基本上我想知道它是否真的可以这样做,或者只是在同一进程中已经有一个正在运行的JVM时调用CreateJavaVM()根本不可能? 我知道你不能在同一个进程中多次调用CreateJavaVM() ,但是在这种情况下它只被调用一次但是过程中已经存在JVM – 你甚至可以在同一个进程中运行多个JVM吗?

解:

感谢@ apangin的回答,下面的代码片段解决了我的问题:

jsize nVMs = 0; JavaVM** buffer; jni_GetCreatedJavaVMs = (GetCreatedJavaVMs) GetProcAddress(GetModuleHandle( TEXT("jvm.dll")), "JNI_GetCreatedJavaVMs"); if (jni_GetCreatedJavaVMs == NULL) { // stuff CreateJavaVM(&jvm, (void **) &env, &args); } else { jni_GetCreatedJavaVMs(NULL, 0, &nVMs); // 1. just get the required array length JavaVM** buffer = new JavaVM*[nVMs]; jni_GetCreatedJavaVMs(buffer, nVMs, &nVMs); // 2. get the data buffer[0]->GetEnv((void **) &env, jni_version); // 3. get environment jvm = buffer[0]; } 

当前的JNI规范明确规定不支持在单个进程创建多个VM ,这实际上是在HotSpot源代码中声明的 。

即使你的dll只调用一次JNI_CreateJavaVM ,也不意味着这是整个过程中的第一个调用。 实际上, JNI_CreateJavaVM首先由java.exe或IDE的另一个启动程序( idea.exeeclipse.exenetbeans.exe等) idea.exe

因此,不是盲目地创建Java VM,而是应该首先通过调用JNI_GetCreatedJavaVMs来检查当前进程中JVM是否已经存在。 如果函数返回非空数组,则使用GetEnv或AttachCurrentThread获取现有VM的JNIEnv* ,否则创建新VM。