xel*_*ela 8 c# windows console-output output-redirect
我正在为第三方控制台应用程序编写一个 GUI,我希望它捕获控制台窗口的输出并将其添加到 GUI 中的文本框。问题是,当我尝试重定向目标进程的输出流时,目标进程崩溃并显示以下错误消息:
CTextConsoleWin32::GetLine: !GetNumberOfConsoleInputEvents
触发此错误的代码如下:
// Called once after the application has initialized.
private void StartServer()
{
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = srcdsExeFile;
processStartInfo.UseShellExecute = false;
processStartInfo.CreateNoWindow = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardInput = true;
serverProcess = Process.Start(processStartInfo);
serverProcess.EnableRaisingEvents = true;
serverProcess.Exited += new EventHandler(Server_Exited);
serverProcess.OutputDataReceived += ServerProcess_OutputDataReceived;
serverProcess.ErrorDataReceived += ServerProcess_ErrorDataReceived;
serverProcess.BeginOutputReadLine();
serverProcess.BeginErrorReadLine();
}
private void ServerProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Console.Output.WriteLine("\n\nServer Error: " + e.Data + "\n\n");
}
private void ServerProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.Output.WriteLine(e.Data);
}
Run Code Online (Sandbox Code Playgroud)
当外部应用程序进行初始化时,上面的代码会运行一分钟左右,但在初始化过程中的特定点后崩溃。
经过一些研究后发现,第三方控制台应用程序依赖于输出流作为控制台,这就是当我尝试将其重定向到非控制台时它崩溃的原因。但是,尝试访问输出流而不重定向它会导致另一个错误,提示我必须先重定向它。
这让我想到了我的实际问题:
是否可以在不重定向输出流的情况下读取控制台应用程序的输出?
所以在过去的几年里这个问题已经被问过好几次了。
我刚刚遇到了同样的问题并用 C++ 解决了它,但相同的技术应该适用于任何其他编程语言,因为这是 WinAPI 特定的问题。我已经为任何希望使用 CreateProcess 创建 srcds 服务器并在 Windows 上重定向输入和输出的人描述了一个解决方案。
这个 github 存储库汇总了控制台句柄和标准句柄如何在窗口内协同工作。 https://github.com/rprichard/win32-console-docs
还有https://learn.microsoft.com/en-us/windows/console/creation-of-a-console的微软文档
我强烈建议阅读这篇文章,因为这使得 srcds 在重定向标准输入时失败的原因变得非常明显。
a) Windows 控制台句柄不等于标准输入和输出句柄。
b) 在 Windows 中,无法重定向控制台句柄。
c)GetNumberOfConsoleInputEvents需要一个有效的控制台句柄,其输入句柄不是文件、管道。它必须是一个 ConsoleHandle!
由于没有人真正了解GetNumberOfConsoleInputEvents失败的原因,而在阅读文档后应该很明显。
https://learn.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents
它明确指出:
hConsoleInput [输入]
控制台输入缓冲区的句柄。该句柄必须具有 GENERIC_READ 访问权限。有关详细信息,请参阅控制台缓冲区安全和访问权限。
但这里 https://github.com/rprichard/win32-console-docs#allocconsole-attachconsole-modern 解释说,当您重定向管道时,它几乎会破坏控制台输入缓冲区。因此,您必须实际使用控制台输入缓冲区,而不是使用 StdHandles。
幸运的是,WinAPI为我们提供了多个选项来访问现有进程的 std 句柄。这非常棘手并且没有很好的记录!您可以连接到控制台并获取 STDHandles。复制它们并做任何你喜欢的事情。请注意,这AttachConsole(ProcessId)要求您当前进程没有附加控制台,因此您必须调用FreeConsole();
下面是如何使用 WinAPI 将单个字母发送到另一个应用程序的控制台的代码。您还可以使用 GetStdHandle 随时获取控制台句柄并写入。
int ProcessId = GetProcessId(ProcessInfo.hProcess);
if (ProcessId <= 0)
{
printf("Process terminated.\n");
break;
}
printf("Process Id: %d\n", ProcessId);
FreeConsole();
if (!AttachConsole(ProcessId))
{
printf("Attach failed with error: %d\n", GetLastError());
exit(1);
}
INPUT_RECORD ir[2];
ir[0].EventType = KEY_EVENT;
ir[0].Event.KeyEvent.bKeyDown = TRUE;
ir[0].Event.KeyEvent.dwControlKeyState = 0;
ir[0].Event.KeyEvent.uChar.UnicodeChar = 'u';
ir[0].Event.KeyEvent.wRepeatCount = 1;
ir[0].Event.KeyEvent.wVirtualKeyCode = 'U';
ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey('U', MAPVK_VK_TO_VSC);
ir[1].EventType = KEY_EVENT;
ir[1].Event.KeyEvent.bKeyDown = FALSE;
ir[1].Event.KeyEvent.dwControlKeyState = 0;
ir[1].Event.KeyEvent.uChar.UnicodeChar = 'u';
ir[1].Event.KeyEvent.wRepeatCount = 1;
ir[1].Event.KeyEvent.wVirtualKeyCode = 'U';
ir[1].Event.KeyEvent.wVirtualScanCode = MapVirtualKey('U', MAPVK_VK_TO_VSC);
DWORD dwTmp = 0;
WriteConsoleInputA(GetStdHandle(STD_INPUT_HANDLE), ir, 2, &dwTmp);
FreeConsole();
if (!AttachConsole(ATTACH_PARENT_PROCESS))
{
printf("Attach failed with error: %d\n", GetLastError());
exit(1);
}
Run Code Online (Sandbox Code Playgroud)
因此,解决方案就是通过附加到 SRCDS 进程的控制台来写入控制台输入缓冲区。只需致电AttachConsole和FreeConsole。和WriteConsoleInput。
要读取输出,您只需调用ReadConsoleOutput
如需进一步阅读,请访问文档:
获取控制台输入事件数
https://learn.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents
连接控制台
https://learn.microsoft.com/en-us/windows/console/attachconsole
自由控制台
https://learn.microsoft.com/en-us/windows/console/freeconsole
写入控制台输入
https://learn.microsoft.com/en-us/windows/console/writeconsoleinput
读取控制台输出
https://learn.microsoft.com/en-us/windows/console/readconsoleoutput