Can you create a new JVM in a c++ function called

2019-02-20 05:19发布

So my setup is that I have a .dll which is developed by me (A.dll) which in the original application is called from an external process which is basically just a .exe file that I do not have the source code for (B.exe). The purpose of A.dll is to communicate with a .jar file, which is also developed by me (C.jar). So in the application, the "communication flows" as shown below

B.exe -> A.dll -> (through JNI) -> C.jar

Now, what i want to do is to add the calls going between A.dll and C.jar as a part of my test suite in the development environment for C.jar. What I have so far is that I have created another .dll (D.dll) which mirrors all functions in A.dll, but with JNIEXPORT, and simply makes direct call to the respective function in A.dll. So the "communication flow" in this situation will be as follows:

Unit test in C.jar development framework -> (through JNI) -> D.dll -> A.dll -> (through JNI) -> C.jar

At this point, a very simple function call that simply prints out something in C.jar works through the whole chain; all the way from the unit test call and into C.jar. The problem however arises when i call the function in A.dll which creates a new JVM using CreateJavaVM(), which produces the following error:

Error occurred during initialization of VM Unable to load native library: The specified procedure could not be found

So basically I'm wondering if it is actually possible to do this, or is it just simply impossible to call CreateJavaVM() when there is already a running JVM in the same process? I know that you can't call CreateJavaVM() several times within the same process, but in this situation it is only called once but a JVM already exists in the process - can you even have several JVMs running in the same process?

SOLUTION:

Thanks to @apangin's answer the code snippet below solved my problem:

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];
}

标签: java c++ c dll jvm
1条回答
不美不萌又怎样
2楼-- · 2019-02-20 06:08

Current JNI specification explicitly states that creation of multiple VMs in a single process is not supported, and this is actually asserted in HotSpot source code.

Even if your dll calls JNI_CreateJavaVM only once, it does not mean that this is the very first call within the whole process. In fact, JNI_CreateJavaVM is first called by java.exe or by another launcher of your IDE (idea.exe, eclipse.exe, netbeans.exe etc).

Therefore, instead of creating Java VM blindly, A.dll should check first if JVM already exists in current process by calling JNI_GetCreatedJavaVMs. If the function returns nonempty array, then use GetEnv or AttachCurrentThread to obtain JNIEnv* for the existing VM, otherwise create a new VM.

查看更多
登录 后发表回答