如何分离在 C++ 中执行的 java 应用程序的 stdin/stout/stderr 流
How can I separate the stdin/stout/stderr streams of a java application executed in C++
我正在为现有的 jarfile 创建一个 C++ 包装器。在本例中,我使用 Spigot Minecraft 服务器 jarfile 执行此操作。
当我执行应用程序时,我遇到了应用程序的输入和输出由 Java 应用程序控制的问题。这意味着当 java 应用程序成功终止时,C++ 应用程序也成功终止,这表明 stdin 的文件描述符正在关闭。
我查看了许多现有的 Whosebug 帖子,我看到的最接近实现此目的的方法是使用分叉进程,然后使用 pipe()
和管道传输文件描述符dup()
:
我目前正在重建代码以使其更具可移植性,并允许我向 C++ 代码添加额外的功能,但以下代码是我开始时使用的代码,也是我将要使用的代码用来测试这个。
#include <jni.h>
#include <iostream>
using namespace std;
int main () {
JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=/path/to/spigot.jar";
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
jint instance = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
delete options;
if (instance != JNI_OK) {
cin.get();
exit(EXIT_FAILURE);
}
cout << "JVM Version: ";
jint ver = env->GetVersion();
cout << ((ver>>16)&0x0f) << "." << (ver&0x0f) << endl;
jclass cls = env->FindClass("org/bukkit/craftbukkit/Main");
if (cls == nullptr) {
cout << "Error starting minecraft" << endl;
} else {
jmethodID mid = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");
if (mid == nullptr) {
cout << "Error: main not found" << endl;
} else {
jobjectArray arr = env->NewObjectArray(1,
env->FindClass("java/lang/String"),
env->NewStringUTF(""));
env->CallStaticVoidMethod(cls, mid, arr);
cout << "Started" << endl;
cout << endl;
}
}
cin.get();
jvm->DestroyJavaVM();
return 0;
}
理想情况下,我希望 java 应用程序 运行 的输入和输出位于 stdin、stdout 和 stderr 的一组不同的文件描述符上,而无需分叉它。
有没有办法指示 JVM,使用 c++ 中的 JNI 库来实现这个目标?
你可以打电话 System.setIn
and System.setOut
。
或者,直接调用 native implementations setIn0
和 setOut0
或复制它们的实现。
后者看起来像:
// Make a FileOutputStream
jclass os_cla = env->FindClass("java/io/FileOutputStream");
jmethodID os_init = env->GetMethodID(os_cla, "<init>", "(Ljava/lang/String;)V");
jobject os = env->NewObject(os_cla, os_init, env->NewStringUTF("output.txt"));
// Make a PrintStream
jclass ps_cla = env->FindClass("java/io/PrintStream");
jmethodID ps_init = env->GetMethodID(ps_cla, "<init>", "(Ljava/io/PrintStream;)V");
jobject ps = env->NewObject(ps_cla, ps_init, os);
// Reassign System.out
jclass system_cla = env->FindClass("java/lang/System");
jfieldID fid = env->GetStaticFieldID(system_cla, "out", "Ljava/io/PrintStream;");
env->SetStaticObjectField(system_cla, fid, ps);
我正在为现有的 jarfile 创建一个 C++ 包装器。在本例中,我使用 Spigot Minecraft 服务器 jarfile 执行此操作。
当我执行应用程序时,我遇到了应用程序的输入和输出由 Java 应用程序控制的问题。这意味着当 java 应用程序成功终止时,C++ 应用程序也成功终止,这表明 stdin 的文件描述符正在关闭。
我查看了许多现有的 Whosebug 帖子,我看到的最接近实现此目的的方法是使用分叉进程,然后使用 pipe()
和管道传输文件描述符dup()
:
我目前正在重建代码以使其更具可移植性,并允许我向 C++ 代码添加额外的功能,但以下代码是我开始时使用的代码,也是我将要使用的代码用来测试这个。
#include <jni.h>
#include <iostream>
using namespace std;
int main () {
JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=/path/to/spigot.jar";
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
jint instance = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
delete options;
if (instance != JNI_OK) {
cin.get();
exit(EXIT_FAILURE);
}
cout << "JVM Version: ";
jint ver = env->GetVersion();
cout << ((ver>>16)&0x0f) << "." << (ver&0x0f) << endl;
jclass cls = env->FindClass("org/bukkit/craftbukkit/Main");
if (cls == nullptr) {
cout << "Error starting minecraft" << endl;
} else {
jmethodID mid = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");
if (mid == nullptr) {
cout << "Error: main not found" << endl;
} else {
jobjectArray arr = env->NewObjectArray(1,
env->FindClass("java/lang/String"),
env->NewStringUTF(""));
env->CallStaticVoidMethod(cls, mid, arr);
cout << "Started" << endl;
cout << endl;
}
}
cin.get();
jvm->DestroyJavaVM();
return 0;
}
理想情况下,我希望 java 应用程序 运行 的输入和输出位于 stdin、stdout 和 stderr 的一组不同的文件描述符上,而无需分叉它。
有没有办法指示 JVM,使用 c++ 中的 JNI 库来实现这个目标?
你可以打电话 System.setIn
and System.setOut
。
或者,直接调用 native implementations setIn0
和 setOut0
或复制它们的实现。
后者看起来像:
// Make a FileOutputStream
jclass os_cla = env->FindClass("java/io/FileOutputStream");
jmethodID os_init = env->GetMethodID(os_cla, "<init>", "(Ljava/lang/String;)V");
jobject os = env->NewObject(os_cla, os_init, env->NewStringUTF("output.txt"));
// Make a PrintStream
jclass ps_cla = env->FindClass("java/io/PrintStream");
jmethodID ps_init = env->GetMethodID(ps_cla, "<init>", "(Ljava/io/PrintStream;)V");
jobject ps = env->NewObject(ps_cla, ps_init, os);
// Reassign System.out
jclass system_cla = env->FindClass("java/lang/System");
jfieldID fid = env->GetStaticFieldID(system_cla, "out", "Ljava/io/PrintStream;");
env->SetStaticObjectField(system_cla, fid, ps);