你能不能使用 JNI 在从 java 调用的 c++ 函数中创建一个新的 JVM



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

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

现在,我想做的是将 A.dllC.jar 之间的调用添加为 C .jar 开发环境中测试套件的一部分。到目前为止,我已经创建了另一个.dll(D.dll),它镜像了 A.dll 中的所有函数,但使用 JNIEXPORT,并且只是直接调用 A.dll 中的相应函数。因此,在这种情况下,"通信流"将如下所示:

C.jar开发框架中的单元测试 -> (通过 JNI) -> D.dll -> A.dll -> (通过 JNI) -> C.jar

在这一点上,一个非常简单的函数调用,简单地在C中打印出一些东西.jar在整个链中工作;从单元测试调用一直到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等)调用。

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

最新更新