首页 文章

如何正确启动进程并转发stdin / stdout / stderr?

提问于
浏览
5

我正在使用 CreateProcess 启动一个交互式脚本解释器,并希望透明地将stdin / stdout / stderr转发给解释器 .

我的第一次尝试是设置传递给 CreateProcessSTARTUPINFO 结构

STARTUPINFOA si = { sizeof( si ) };
si.hStdError = ::GetStdHandle( STD_ERROR_HANDLE );
si.hStdOutput = ::GetStdHandle( STD_OUTPUT_HANDLE );
si.hStdInput = ::GetStdHandle( STD_INPUT_HANDLE );
si.dwFlags |= STARTF_USESTDHANDLES;

即我试图使脚本解释器进程使用与我的启动程序进程使用的读/写相同的句柄 . 这似乎不起作用(我甚至不确定那些标准句柄是否可以继承) .

基于Creating a Child Process with Redirected Input and Output示例的第二个想法是设置三个管道以转发写入任何管道的所有数据 . 由于我不知道如何等待数据写入多个文件( WaitForMultipleObjects 无法在管道上同步),我正在考虑有三个线程,每个线程在管道上执行阻塞 ReadFile 调用 .

我怀疑这可能有点矫枉过正,所以我想知道:有更简单的方法吗?我根本不需要对从脚本解释器传递的数据进行任何处理 .

作为旁注,在Linux上我使用 execvp 只是用脚本解释器进程替换当前进程,但在Windows上我需要启动脚本解释器,主线程处于挂起状态(这样我就可以进行一些字节码修补) - 所以即使自_execvp似乎可以在Windows上使用,我显然必须使用CreateProcess .

2 回答

  • 4

    为了等待多个文件或管道上的I / O,您需要对每个文件发出异步I / O请求,然后等待所述请求的完成 . 沿着这些方向的东西(未经测试):

    HANDLE file1, file2; // initialized somehow
    
    HANDLE events[2];
    events[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
    events[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
    
    OVERLAPPED overlapped1 = {0};
    overlapped1.hEvent = events[0];
    OVERLAPPED overlapped2 = {0};
    overlapped2.hEvent = events[1];
    
    ReadFile(file1, buffer1, size1, NULL, &overlapped1);
    ReadFile(file2, buffer2, size2, NULL, &overlapped2);
    
    WaitForMultipleObjects(2, events, FALSE, INFINITE);
    

    需要在循环中调用 ReadFileWaitForMultipleObjects . 检查 WaitForMultipleObjects 的返回值以了解哪个操作已完成,使用 GetOverlappedResult 来发现该操作的结果(是否成功,如果是,它检索了多少字节),处理数据,再次调用该句柄的 ReadFile 如果你想从中读更多,然后回到等待 . 这有点类似于Linux中由 select 驱动的非阻塞I / O循环 .

    更先进的技术是I/O completion ports . 这允许一个线程池处理大量异步I / O.常用于Web服务器等,可能对您的情况有些过分 .

  • 1

    如果确保不在CreateProcessdwFlags 参数中传递 CREATE_NO_WINDOW 参数,那么如OP所示Popuplating STARTUPINFO 就可以正常工作 .

相关问题