父进程意外地被子进程 cmd.exe 进程 运行 java jar - WINDOWS 7 问题 - C++

Parent process unexpectedly killed by child cmd.exe process running java jar - WINDOWS 7 PROBLEM - C++

项目信息: 我正在创建一个 C++ 控制台应用程序,它通过侦听端口 activity 来管理 Minecraft 服务器。当服务器端口被 ping 时,它会启动服务器,然后定期检查该端口上是否已建立连接。如果 none,服务器关闭,应用程序再次进入监听模式。

服务器停止时出现问题。不知何故,我的主控制台应用程序被子服务器进程杀死,我似乎无法找出如何以及为什么或任何解决方案。

我的控制台应用程序创建了一个新的 cmd.exe 子进程,它在启动服务器时运行 "java -jar server.jar" 命令。当停止服务器时,一个简单的 "stop" 消息被写入子进程的标准输入。一切正常,java 服务器停止。

然而,一旦子进程退出,控制台应用程序就会意外崩溃并出现 Windows "Program has stopped working." 对话框。 奇怪的是,我已经在我的编程笔记本电脑上测试了运行 Windows 10 的应用程序,它在发布和调试模式下运行都没有任何问题。然而,我的服务器机器是 运行 Windows 7,所以它似乎是 Windows 7 的问题。

现在没有我可以真正向您展示的代码,因为它是执行退出的 java 和 cmd.exe 子进程,我当然没有编写 server.jar 文件的代码。但我会附上控制台崩溃时的图像 link 只是为了好玩。

子进程没有单独的 window,它从父控制台应用程序继承句柄并写入父级的 STDOUT,因此来自子进程的消息显示在主应用程序的控制台中。

我已经尝试使用 CREATE_NEW_PROCESS_GROUP 标志启动子进程,但仍然崩溃。

我试过忽略 SIGINT 和 SIGTERM 信号,仍然崩溃。

我还验证了应用程序不会在服务器关闭调用(将停止消息写入服务器进程的标准输入)之后开始执行命令,所以它们不会是问题所在。

如果有人对可能出现的问题有任何提示或想法,我会洗耳恭听。谢谢!

Console application crashes, Windows "Program has stopped working." dialog is not shown on picture.

编辑:

好的,所以我创建了一个最小的可重现示例。这是所有需要的代码(对于 C++ 主函数):

//security attributes for pipes
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;

//handles for child standard input/output
HANDLE child_stdin_rd = NULL;
HANDLE child_stdin_wr = NULL;

if (!CreatePipe(&child_stdin_rd, &child_stdin_wr, &saAttr, 0))
    return -1;

if (!SetHandleInformation(child_stdin_wr, HANDLE_FLAG_INHERIT, 0))
    return -1;

STARTUPINFOW startupInfo;
ZeroMemory(&startupInfo, sizeof(STARTUPINFOW));
startupInfo.cb = sizeof(STARTUPINFOW);
startupInfo.hStdInput = child_stdin_rd;
startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
startupInfo.dwFlags |= STARTF_USESTDHANDLES;

PROCESS_INFORMATION processInfo;
ZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION));

//cmd.exe path
wstring exepath = L"c:\windows\system32\cmd.exe";

//cmd command to start server
wstring command = L"cmd.exe /c java -Xms1G -Xmx4G -jar server.jar nogui";
LPWSTR com = new wchar_t[command.size() + 1];
copy(command.begin(), command.end(), com);
com[command.size()] = 0;

if (!CreateProcessW(exepath.c_str(), com, 0, 0, TRUE, CREATE_NEW_PROCESS_GROUP, 0, 0, &startupInfo, &processInfo))
    return -1;

//sleep for 1 min, letting server start up
this_thread::sleep_for(chrono::minutes(1));

//command to stop server
string stopCmd = "stop\n[=14=]";
DWORD stopCmdByteSize = stopCmd.size() * sizeof(char);

if (!WriteFile(child_stdin_wr, stopCmd.c_str(), stopCmdByteSize, 0, 0))
    return -1;

CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
CloseHandle(child_stdin_wr);

要重现这个,你的机器需要 运行 Windows 7 并且在同一个文件夹,因为应用程序必须是 Minecraft server.jar 文件(撰写本文时版本为 1.15.2),您可以从 their website. 获得此外,服务器可能首先需要一些设置,运行 第一次通过双击 .jar 文件创建所有需要的服务器文件,您必须打开 "eula.txt" 并通过将 eula=false 更改为 [ 来接受 EULA =16=]。然后服务器应该可以运行了。

如前所述,我没有编写 server.jar 文件,因此不知道 java 服务器程序的完整行为。

使用 RCON 可能会更好,它是 Java 版本服务器中内置的协议,用于远程管理具有简单 TCP 数据包格式的服务器,而不是尝试将命令写入标准输入直接从服务器。

有关数据包格式的说明,请参阅 wiki.vg's page on RCON

发现错误!问题已解决!

啊,经过一番深思熟虑并再次阅读文档以验证代码是否正确,我找到了罪魁祸首。

在调用 WriteFile() 函数时,我忘记给它一个指向 DWORD 的指针,以便它可以更新函数写入的字节数。

所以下面的代码:

if (!WriteFile(child_stdin_wr, stopCmd.c_str(), stopCmdByteSize, 0, 0))
    return -1;

需要改为:

DWORD bytesWritten = 0;

if (!WriteFile(child_stdin_wr, stopCmd.c_str(), stopCmdByteSize, &bytesWritten, 0))
    return -1;

所以我猜这是一种未定义的行为,Windows 10 可以处理而 Windows 7 不能处理,导致程序崩溃..写这么长有点尴尬post 因为函数调用中的一个小错误,但是伙计们,你已经知道了!感谢那些提供提示的人! :)