从C ++执行Java的简单性

时间:2013-06-25 19:07:42

标签: java c++ visual-c++ user-interface java-native-interface

背景信息:我习惯用Java编程,我知道如何使用Eclipse和Visual Studio。

最终目标:创建一个GUI,最好是在Visual Studio中,它执行Java函数。

我希望从这个问题中完成什么:C ++中的一个按钮,单击该按钮,执行Java函数并将结果返回给C ++。 (可能是通过调用JVM)

我真的怀疑在这里发布这个问题,但我无法在任何地方找到“直截了当”的答案;因此,我希望有人可以为这个问题展示最简单的解决方案。

我目前考虑了以下数据结构:

  • 通过'常用'文件共享数据,例如.txt文件(但之后如何启动Java功能?)
  • 打开套接字(这个问题看起来太复杂了)
  • 通过服务器连接(太复杂)
  • 从C ++调用JVM然后执行Java文件(我认为这是最合理的方法,但这需要大量代码)

现在我知道JaceJNISWIG的存在,但我认为它们非常便于制作复杂的程序,而不是简单的界面。我不想制作一个复杂的程序因此我觉得学习他们所有的命令都很麻烦。

我还读过许多Stack Exchange问​​题,问的问题完全相同,但所有这些问题似乎都给出了非常复杂的答案。

所以这是我的问题:

从C ++执行(如果必要的话:预编译的)Java函数的绝对最简单方法是什么,其中C ++代码将一些参数传递给此Java函数

提前致谢。

1 个答案:

答案 0 :(得分:14)

  

从C ++调用JVM然后执行Java文件(我认为这是最合理的方式,但这需要大量代码)

是的,这绝对是最合理的方式。对于JNI和invocation API,它甚至没有那么多代码。

查找jvm.dll

您可以尝试硬编码Oracle JVM jvm.dll的路径或在程序文件夹中搜索名为jvm.dll的文件,但所有这些都显然非常糟糕。但是,显然有一个非常简单的解决方案:注册表。密钥HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment包含名为REG_SZ的{​​{1}}。您可以读取此密钥的值(当前为CurrentVersion)并打开具有该名称的子密钥(在此示例中为1.7)。然后,该密钥将包含一个名为HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\1.7的{​​{1}},这是REG_SZ的路径。不要担心RuntimeLib vs jvm.dll。如果您是64位窗口上的32位进程,并且该密钥包含32位Program files的路径,WOW64将自动将您的注册表查询重定向到Program files (x86)。代码:

HKLM\SOFTWARE\Wow6432Node

加载jvm.dll并获取CreateJavaVM函数

这部分非常简单,您只需使用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); }

LoadLibrary

创建JVM

现在您可以调用该功能:

GetProcAddress

恭喜!现在有一个JVM在你的进程内运行!您可能会在应用程序启动时启动JVM。除非你100%确定,你只能从刚创建JVM的线程调用Java代码,否则你可以丢弃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"); 指针,但你必须保留{{ 1}}指针。

获取JNI环境(可选)

现在您创建了JVM,您的应用程序已启动并运行,然后有人单击该按钮。现在您要调用Java代码。如果您100%确定您现在正处于上一步中创建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,如果不是,则执行以下操作:

env

调用Java代码

现在你就在那里,你想要调用那个Java代码,你甚至有jvm指针。您需要最简单的解决方案,因此这就是您调用静态方法的方法:

env

您可以使用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(); (命令行工具)来确定方法的签名。 env可以是任何基本类型(它必须与Java方法的返回类型匹配)。参数可以是任何原始类型,只要它们与Java方法的参数匹配。

结束

你有它:在Windows上从C ++调用Java代码的最简单方法(实际上只有前两个部分是特定于Windows的......)。哦,也是最有效的一个。螺丝数据库和文件。使用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>); 套接字是一种选择,但效率明显较低,可能不会比这更少。哇,这个答案比我想象的要长一点。希望它有所帮助。