gpt4 book ai didi

c++ - 通过 Windows 10 C++ 中的控制台屏幕缓冲区重定向子进程标准输出

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:59:18 27 4
gpt4 key购买 nike

我正在尝试重定向 Windows 中子进程的 stdout。两者都是控制台程序。我没有子进程的源代码,所以我不能强制它刷新缓冲区。正如所讨论的herehere ,对于 printf 和类似的实现,C 运行时缓冲除控制台和打印机之外的所有内容。因此,解决方案显然是使用 CreateConsoleScreenBuffer 来创建控制台屏幕缓冲区。我正在使用 the approach from codeproject .

PROCESS_INFORMATION pi;
HANDLE hConsole;
const COORD origin = { 0, 0 };

// Default security descriptor. Set inheritable.
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE; // So the child can use it

// Create and initialize screen buffer
hConsole = CreateConsoleScreenBuffer(
GENERIC_READ | GENERIC_WRITE, // Desired access
FILE_SHARE_WRITE | FILE_SHARE_READ, // share mode to child processes
&sa, // SECURITY_ATTRIBUTES
CONSOLE_TEXTMODE_BUFFER, // Must be this.
NULL // Reserved. Must be NULL
);
DWORD dwDummy;
FillConsoleOutputCharacter(hConsole, '\0', MAXLONG, origin, &dwDummy)

现在我将 child 的标准输出定向到控制台并启动进程

STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_FORCEOFFFEEDBACK | STARTF_USESTDHANDLES; // first one prevents cursor from showing loading.
si.hStdOutput = hConsole;

//...
// Get the command line and environmental block
//...

if (! CreateProcess(
NULL, // module name.
(char*)command_line.c_str(), // command line
NULL, // process SECURITY_ATTRIBUTES
NULL, // thread SECURITY_ATTRIBUTES
TRUE, // inherit handles
NULL, // creation flags
enviros, // environmentBlock (enviros=NULL for testing)
cDir, // working directory
&si, // STARTUP_INFO object
&pi // PROCESSINFO
) ){
auto test = GetLastError();
CloseHandle(hConsole);
return false;
}
CloseHandle(pi.hThread);

然后,在循环中,我可以使用 ReadConsoleOutputCharacter 获取输出,如代码项目链接所示。看起来像

//... some initialization

GetConsoleScreenBufferInfo(hConsole, &csbi);
DWORD count = (csbi.dwCursorPosition.Y - lastpos.Y)*lineWidth + csbi.dwCursorPosition.X - lastpos.X;
LPTSTR buffer = (LPTSTR)LocalAlloc(0, count * sizeof(TCHAR));
ReadConsoleOutputCharacter(hConsole, buffer, count, lastpos, &count);
DWORD dwDummy;
FillConsoleOutputCharacter(hConsole, '\0', count, lastpos, &dwDummy);

//... Now move the cursor and grab the data from `buffer`

在 Windows 7/8.1 上,这适用于所有程序。 在 Windows 10 上,某些程序似乎绕过提供的句柄并直接打印到父控制台,这使我无法根据需要获取输出。

我还有一条线索。如果我强制进程创建一个新的控制台窗口,即

CreateProcess(NULL, (char*)command_line.c_str(), NULL, NULL, TRUE, CREATE_NEW_CONSOLE, enviros, cDir, &si, &pi)

但仍然重定向STARTUPINFO对象中的句柄,新的控制台将显示一行The system cannot write to the specified device,这恰好是 the MSDN docs 中 Windows 错误代码 ERROR_WRITE_FAULT = 29 的确切措辞.这只发生在那些没有按预期工作的程序上。其他程序,新控制台是空白的,它们仍然按预期运行。

我首先想到的是权限问题,但我对相关可执行文件的目录拥有广泛的权限。

我尝试过的事情

  • 另一台装有 Windows 10 的计算机
  • 不同版本的 MS Visual C++ 运行时
  • CreateProcess 中显式设置工作目录
  • 将 super 许可 DACL 添加到传递给 CreateProcessSECURITY_ATTRIBUTES 对象
  • 将 super 许可 DACL 添加到传递给 CreateConsoleScreenBufferSECURITY_ATTRIBUTES 对象
  • 将所有内容移动到我的 Windows 用户目录下新创建的目录
  • 以管理员身份运行

更深层次

感谢@PaulSanders 的建议。

为了帮助任何可能想要提供帮助的人,我提供了 a modified version of the RTConsole code from the codeproject page .它应该在 Visual Studio 中编译,只需要一个重定向,我认为。在第 135 行附近,我在采用预期路径的输出前面添加了一个小字符串。为了方便起见,我也在那里提供了一个预编译版本。

一个不起作用的软件示例是 EWBF miner .要使用我上面提供的代码进行快速测试,您可以运行

RTConsole2.exe "path\to\ewbf.exe" --help

您会看到输出中不存在前置标志。

另一方面,ccminer ,您将在运行时获得预期的行为

RTConsole2.exe "path\to\ccminer.exe" --help

最佳答案

Windows 10 中的新控制台实现有一个错误,其中高级 WriteConsoleWriteFile 到非事件屏幕缓冲区,而不是总是写入事件屏幕缓冲区.低级 WriteConsoleOutput[Character] 工作正常。使用旧版控制台也可以。您可以在属性对话框中启用旧版控制台。


请注意,如果由于 CREATE_NEW_CONSOLE 标志而分配新控制台,则进程无法在父控制台中为屏幕缓冲区使用继承的句柄。尝试写入屏幕缓冲区文件将失败,因为它未绑定(bind)到调用者的附加控制台(即 conhost.exe 的实例)。

绑定(bind)的控制台文件包括“CON”、“CONIN$”、“CONOUT$”和来自 CreateConsoleScreenBuffer 的新屏幕缓冲区。还有未绑定(bind)的输入和输出控制台文件,在分配新控制台时将其设置为标准句柄(例如通过 AllocConsole())。这些句柄访问任何连接的控制台 [*] 的输入缓冲区和事件屏幕缓冲区。请注意,一个进程可以拥有绑定(bind)到多个控制台的控制台文件的句柄,并且可以使用 AttachConsole 在控制台之间切换。

另请注意,某些程序会打开“CONOUT$”而不是写入StandardOutputStandardError 句柄,尤其是当它们需要控制台而不是标准句柄时是(例如管道或磁盘文件)。在这种情况下,在 STARTUPINFO 中设置 hStdOutput 是不够的。您必须通过 SetConsoleActiveScreenBuffer 暂时激活新的屏幕缓冲区。这不会影响调用者的标准句柄。它在连接的控制台中设置事件屏幕缓冲区,这就是“CONOUT$”打开的内容。子进程退出后,或者您知道子进程已经打开并写入新的屏幕缓冲区后,可以恢复之前的屏幕缓冲区。


[*] 在 Windows 8+ 中,这些控制台文件由 condrv.sys 设备驱动程序实现,并在“\Device\ConDrv”上打开。它们分别命名为“Console”、“CurrentIn”、“CurrentOut”、“ScreenBuffer”、“Input”和“Output”。控制台连接本身的文件名是“Connect”。在内部,后者作为进程 ConsoleHandle 打开,它在某些情况下由控制台 API 隐式使用(例如 GetConsoleWindowGetConsoleCPGetConsoleTitle).

关于c++ - 通过 Windows 10 C++ 中的控制台屏幕缓冲区重定向子进程标准输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51234926/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com