从C++执行Java的简单性

Jea*_*aul 7 c++ java java-native-interface user-interface visual-c++

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

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

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

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

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

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

现在我知道的存在Jace,JNI以及SWIG,但我认为他们是制造复杂的程序,而不是简单的接口非常方便.我不想制作一个复杂的程序,因此我觉得学习他们所有的命令都很麻烦.

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

所以这是我的问题:

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

提前致谢.

mai*_*n-- 14

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

是的,这绝对是最合理的方式.使用JNI和调用API,它甚至没有那么多代码.

找到jvm.dll

您可以尝试硬编码Oracle JVM的路径jvm.dll或搜索jvm.dll程序文件夹中调用的文件,但所有这些都显然非常糟糕.但是,显然有一个非常简单的解决方案:注册表.密钥HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment包含一个REG_SZ被叫CurrentVersion.您可以读取此键的值(当前它是1.7)并打开具有该名称的子键(HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\1.7在此示例中).然后该密钥将包含一个REG_SZ被调用RuntimeLib的路径jvm.dll.不要担心Program filesVS Program files (x86).HKLM\SOFTWARE\Wow6432Node如果您是64位Windows上的32位进程并且该键包含32位的路径,WOW64将自动将您的注册表查询重定向到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);
}
Run Code Online (Sandbox Code Playgroud)

加载jvm.dll并获取CreateJavaVM函数

这部分非常简单,您只需使用LoadLibraryGetProcAddress:

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");
Run Code Online (Sandbox Code Playgroud)

创建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
Run Code Online (Sandbox Code Playgroud)

恭喜!现在有一个JVM在你的进程内运行!您可能会在应用程序启动时启动JVM.除非您100%确定只从刚创建JVM的线程调用Java代码,否则可以丢弃env指针,但必须保留jvm指针.

获得JNI环境(可选)

所以现在你创建了JVM,你的应用程序启动并运行,然后有人点击了那个按钮.现在您要调用Java代码.如果您100%确定您现在正处于上一步中创建JVM的线程上并且仍然有env指针,那么您可以跳过此步骤.否则,请快速检查当前线程是否已连接到JVM,如果不是,请将其附加:

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();
Run Code Online (Sandbox Code Playgroud)

调用Java代码

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

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>);
Run Code Online (Sandbox Code Playgroud)

您可以使用javap -s(命令行工具)来确定方法的签名.<type>可以是任何基本类型(它必须与Java方法的返回类型匹配).参数可以是任何原始类型,只要它们与Java方法的参数匹配即可.

结束

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