使用重定向的标准输入处理子进程中的 kbhit
Posted
技术标签:
【中文标题】使用重定向的标准输入处理子进程中的 kbhit【英文标题】:Dealing with kbhit in child process with redirected stdin 【发布时间】:2018-09-04 13:41:11 【问题描述】:我编写了一个程序,它启动另一个进程并使用 Windows API(CreateProcess、CreatePipe 等)将其标准 I/O 重定向到管道
程序应该启动多个不同的控制台程序并使用 stdio 与它们通信。
这一切都运行良好(我可以写入进程的标准输入并使用管道从进程中读取),直到我尝试启动并与使用 kbhit 的程序通信。
为了简化我想启动的程序对标准输入的作用:
while(1)
if(kbhit())
fgets(line, sizeof(line), stdin);
//do something with line
Sleep(100);
结果是 fgets 永远不会被调用,因为 kbhit 不会返回 true,即使我已写入已将 stdin 重定向到的管道。我知道,因为我已经调试到另一个程序。我试图删除 kbhit 的调用,然后它确实有效,但我无法更改该代码。
有没有办法向进程发送一些东西,以便子进程中的 kbhit 返回 true?
【问题讨论】:
我想kbhit
直接读取键盘而不是通过标准输入读取。为什么在这里需要kbhit
?这是XY Problem吗?
我没有制作的另一个程序使用 kbhit,我无法更改它
kbhit()
不是 C++ 中的标准函数。您需要阅读用于构建“其他程序”的编译器/库的文档,以便了解如何与之交互 - 假设这是可能的。
_kbhit
从控制台读取(CONIN$
设备)。它忽略标准输入。您写入标准输入的内容将被忽略
有没有办法在那里写东西?
【参考方案1】:
_kbhit
函数检查 控制台 是否有最近的击键。它从不检查标准输入,而是打开CONIN$
(控制台输入)并始终从这里读取。
所以在这里重定向标准输入什么都没有。如果我们与孩子共享同一个控制台,我们可以使用WriteConsoleInput
来完成此任务。将字符串写入孩子的示例:
void write_to_conin(PCWSTR msg)
if (ULONG len = (ULONG)wcslen(msg))
if (INPUT_RECORD* lpBuffer = new INPUT_RECORD[len])
INPUT_RECORD* pir = lpBuffer;
ULONG n = len;
do
WCHAR UnicodeChar = *msg++;
WORD wVirtualKeyCode = UnicodeChar;
DWORD dwControlKeyState = CAPSLOCK_ON;
if ((USHORT)(UnicodeChar - 'a') <= (USHORT)('z' - 'a'))
dwControlKeyState = 0;
wVirtualKeyCode &= ~0x20;
pir->Event.KeyEvent.bKeyDown = TRUE;
pir->Event.KeyEvent.dwControlKeyState = dwControlKeyState;
pir->Event.KeyEvent.wRepeatCount = 1;
pir->Event.KeyEvent.uChar.UnicodeChar = UnicodeChar;
pir->Event.KeyEvent.wVirtualKeyCode = wVirtualKeyCode;
pir->Event.KeyEvent.wVirtualScanCode = (WORD)MapVirtualKey(wVirtualKeyCode, MAPVK_VK_TO_VSC);
pir++->EventType = KEY_EVENT;
while (--n);
HANDLE hcon = CreateFileW(L"CONIN$", FILE_GENERIC_WRITE, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hcon != INVALID_HANDLE_VALUE)
WriteConsoleInput(hcon, lpBuffer, len, &n);
CloseHandle(hcon);
delete [] lpBuffer;
【讨论】:
你的解释很有道理。我尝试以各种方式使用您的代码,但我只能通过创建一个调用实际进程的包装进程来使其工作。 @JimmyT.,您必须连接到同一个控制台。如果您有一个 GUI 应用程序,您需要调用AttachConsole(child_pid)
,或者更简单地说,事先调用 AllocConsole
让孩子继承您的控制台。
使用新的 ConPTY 系统将更容易处理此类问题,该系统将在 Windows 10 的秋季更新中提供。ConPTY 为 conhost.exe 实例提供 I/O 通道。目标受众是替代控制台应用程序,例如 ConEmu,生成和使用 UTF-8 虚拟终端序列。但这对于与控制台应用程序交互的类似 pexpect 的库同样适用。
问题是我需要启动多个进程并与之通信,所以我不能让它们都共享同一个控制台,这就是为什么需要为每个进程实例提供单独控制台的包装进程.
@JimmyT。是的,我如何回答 - 如果我们与孩子共享同一个控制台以上是关于使用重定向的标准输入处理子进程中的 kbhit的主要内容,如果未能解决你的问题,请参考以下文章