有关该项目的信息:我正在创建一个C ++控制台应用程序,该应用程序通过侦听端口活动来管理Minecraft服务器。 ping服务器端口后,它将启动服务器,然后定期检查该端口上是否已建立连接。如果不存在,则服务器将关闭,并且应用程序再次进入侦听模式。
服务器停止时出现问题。不知何故,我的主控制台应用程序被子服务器进程杀死了,我似乎找不到如何,为什么或任何解决方案。
我的控制台应用程序创建一个新的cmd.exe子进程,该子进程在启动服务器时运行“ java -jar server.jar”命令。停止服务器时,将简单的“停止”消息写入子进程的标准输入。一切正常,java服务器停止。
但是子进程退出后,控制台应用程序意外崩溃,并且Windows“程序已停止运行。”对话框出现。 奇怪的是,我已经在运行Windows 10的编程笔记本电脑上测试了该应用程序,并且该应用程序在发行和调试模式下都没有出现任何问题。我的服务器计算机运行的是Windows 7,因此似乎是Windows 7的问题。
现在没有真正的代码可以显示给您,因为它是执行出口的java和cmd.exe子进程,我当然没有对server.jar文件进行编码。但是当它崩溃时,我会附加一个控制台的图像链接,只是为了好玩。
子进程没有单独的窗口,它从父控制台应用程序继承了句柄,并写入父进程的STDOUT,因此来自子进程的消息显示在主应用程序的控制台中。
我尝试使用CREATE_NEW_PROCESS_GROUP标志启动子进程,仍然崩溃。
我尝试忽略SIGINT和SIGTERM信号,仍然崩溃。
我还验证了应用程序不会在服务器关闭调用之后开始执行命令(将停止消息写入服务器进程的stdin),因此它们不会成为问题。
[如果有人对可能出现的问题有任何提示或想法,我将耳熟能详。谢谢!
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\0";
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=true
接受EULA。然后服务器就可以了。
如前所述,我没有编写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无法处理,这是一种不确定的行为,导致程序崩溃。.由于函数调用中的一个小错误,写这篇冗长的帖子有点尴尬,但是那里有你们!感谢那些给小费的人! :)