C++ 用重定向stdin处理子进程中的kbhit

C++ 用重定向stdin处理子进程中的kbhit,c++,windows,winapi,C++,Windows,Winapi,我编写了一个程序,该程序使用windows API(CreateProcess、CreatePipe等)启动另一个进程并将其标准I/O重定向到管道 程序应该启动多个不同的控制台程序,并使用stdio与它们通信 在我尝试启动一个使用kbhit的程序并与之通信之前,这一切都运行得很好(我可以写入进程的stdin并使用管道读取进程) 为了简化它,我想用标准输入启动该程序: while(1) { if(kbhit()) { fgets(line, sizeof(line)

我编写了一个程序,该程序使用windows API(CreateProcess、CreatePipe等)启动另一个进程并将其标准I/O重定向到管道

程序应该启动多个不同的控制台程序,并使用stdio与它们通信

在我尝试启动一个使用kbhit的程序并与之通信之前,这一切都运行得很好(我可以写入进程的stdin并使用管道读取进程)

为了简化它,我想用标准输入启动该程序:

while(1)
{
    if(kbhit())
    {
        fgets(line, sizeof(line), stdin);
        //do something with line
    }
    Sleep(100);
 } 
结果是fgets永远不会被调用,因为kbhit不会返回true,即使我已经将stdin重定向到了管道中。我知道这一点,因为我已经调试到了另一个程序中。我尝试删除kbhit的调用,然后它确实起作用,但我无法更改该代码

是否有一种方法可以将某些内容发送到进程,以便子进程中的kbhit返回true

该功能检查控制台最近的击键。它从不检查stdin,而是打开CONIN$(控制台输入)并始终从这里读取

所以把stdin重定向到这里,什么都不给。如果我们和孩子共享同一个控制台,我们就可以使用它来完成这个任务。将字符串写入子对象的示例:

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;
        }
    }
}
void write_to_conin(PCWSTR msg)
{
如果(ULONG len=(ULONG)wcslen(msg))
{
if(输入\记录*lpBuffer=新输入\记录[len])
{
输入_记录*pir=lpBuffer;
ULONG n=len;
做
{
WCHAR UnicodeChar=*msg++;
单词wVirtualKeyCode=UnicodeChar;
DWORD dwControlKeyState=CAPSLOCK_ON;
如果((USHORT)(UnicodeChar-'a')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)映射VirtualKey(wVirtualKey代码,映射到VSC);
pir++->EventType=按键事件;
}而(--n);
HANDLE hcon=CreateFileW(L“CONIN$”,文件\通用\写入,文件\共享\有效\标志,0,打开\存在,0,0);
if(hcon!=无效的句柄值)
{ 
WriteConsoleInput(hcon、lpBuffer、len和n);
闭合手柄(hcon);
}
删除[]lpBuffer;
}
}
}

我想
kbhit
直接读取键盘,而不是通过stdin读取。为什么这里需要
kbhit
?这是一个吗?我没有制作的另一个程序使用kbhit,我无法更改它
kbhit()不是C++中的标准函数。为了了解如何与其他程序交互,需要读取编译器/库的文档,假设它是可能的。<代码> IKBHIT < /Cord>从控制台读取(<代码> CONIN $< /CKE>设备)。它忽略stdin。你对stdin所写的内容将被忽略。没有办法在那里写东西吗?你的解释很有道理。我曾尝试以各种方式使用你的代码,但我只是通过创建一个调用实际进程的包装进程来实现它。@JimmyT.,你必须连接到同一个控制台。如果你有GUI应用程序,你将无法使用它ed到任一调用
AttachConsole(子pid)
或者更简单地说,事先调用
alloconsole
让孩子继承你的控制台。使用Windows 10秋季更新中提供的新ConPTY系统,这类问题将更容易处理。ConPTY提供一个指向conhost.exe实例的I/O通道。目标受众是另一个控制台应用程序例如ConEmu,生成和使用UTF-8虚拟终端序列。但这对于与控制台应用程序交互的类似pexpect的库也同样有效。问题是我需要启动多个进程并与之通信,所以我不能让它们共享同一个控制台,这就是为什么需要使用为每个进程实例提供单独的控制台。@JimmyT。是的,我如何写答案-如果我们与子进程共享同一个控制台