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];
}
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 byjava.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 obtainJNIEnv*
for the existing VM, otherwise create a new VM.