【问题标题】:Simplicity for executing Java from C++从 C++ 执行 Java 的简单性
【发布时间】:2013-06-25 19:07:42
【问题描述】:

背景资料:我习惯用 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 函数

提前致谢。

【问题讨论】:

  • SO 中的许多问题都解决了第二个问题。这个问题链接到其中一些:stackoverflow.com/questions/2011664/…。 (就我个人而言,我使用 exe4j。)
  • 是的,我也读过这些问题。但这将使我的 Java 函数可执行,而不是呈现一个“好”(Visual C++ 好)接口。
  • “与 Visual C++ 类似的界面”——你的意思是像 SWT 那样吗?
  • 根据System::Object^ 判断,您说的是c++/cli(不是c++),对吧?
  • stackoverflow.com/questions/16689/… - 也许这有帮助? jni4net 库?

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


【解决方案1】:

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

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

查找 jvm.dll

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

#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);
}

加载 jvm.dll 并获取 CreateJavaVM 函数

这部分非常简单,你只需使用LoadLibrary 和GetProcAddress:

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");

创建 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 在您的进程中运行!您可能会在应用程序启动时启动 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();

调用 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>);

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

结束

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

【讨论】:

  • 这个答案太棒了!您完全回答了我的问题,并清楚地阐述了每一步!非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-27
  • 1970-01-01
  • 2019-03-12
  • 2015-08-08
  • 2014-12-09
  • 2016-05-24
相关资源
最近更新 更多