【问题标题】:I/O redirection from child process using pipes - winapi使用管道从子进程进行 I/O 重定向 - winapi
【发布时间】:2015-06-23 10:35:05
【问题描述】:

我正在使用一个提供 api 的应用程序,以便更轻松地编写脚本。基本上,当您编写有效输入时,它会输出一个答案。我想使用该输出来发送更多输入,例如:

Input: <nodes>
Output: 1, 56, 23
Input <56>
Output: "Apple"

我想做的是一个程序,它写入目标进程 STDIN,然后从它的 STDOUT 读取输出。为此,我主要从那里获取代码:

Creating a Child Process with Redirected Input and Output (Windows) - MSDN

唯一的问题是,为了从子进程中读取,我首先需要关闭用于写入它的父进程的句柄,这意味着我不能使用子进程的输出来写更多东西。

这是从 msdn 获取的简化代码:

#include <Windows.h>
#include <string>

using std::string;

HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;

#define BUFSIZE 4096

void WriteToPipe(string msg); 
void ReadFromPipe(void); 

int main()
{
    /*
     * CREATE PIPES
    */

    SECURITY_ATTRIBUTES saAttr;

    // Set the bInheritHandle flag so pipe handles are inherited.
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle = TRUE;
    saAttr.lpSecurityDescriptor = NULL;

    // Create pipes for the child process's STDOUT and STDIN,
    // ensures both handle are not inherited
    CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0);
    SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0);

    CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0);
    SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0);

    /*
     * CREATE CHILD PROCESS
    */

    TCHAR szCmdline[]=TEXT("target.exe");
    STARTUPINFO siStartInfo;
    PROCESS_INFORMATION piProcInfo;

    // Set up members of the PROCESS_INFORMATION structure.
    ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );

    // Set up members of the STARTUPINFO structure.
    // This structure specifies the STDIN and STDOUT handles for redirection.
    ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
    siStartInfo.cb = sizeof(STARTUPINFO);
    siStartInfo.hStdError  = g_hChildStd_OUT_Wr;
    siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
    siStartInfo.hStdInput  = g_hChildStd_IN_Rd;
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

    CreateProcess(NULL, szCmdline, NULL, NULL, TRUE, 0,
                  NULL,  NULL, &siStartInfo, &piProcInfo);

    // Close handles to the child process and its primary thread.
    // Some applications might keep these handles to monitor the status
    // of the child process, for example. 
    CloseHandle(g_hChildStd_OUT_Wr);
    CloseHandle(g_hChildStd_IN_Rd);
    CloseHandle(piProcInfo.hProcess);
    CloseHandle(piProcInfo.hThread);

    /*
     * ACTUAL APPLICATION
    */

    WriteToPipe("<nodes>\n");

    // Need to close the handle before reading
    CloseHandle(g_hChildStd_IN_Wr); // PROBLEM HERE

    ReadFromPipe();

    WriteToPipe("<56>\n"); // I can't, as I have released the handle already

    system("pause");

    return 0;
}

void WriteToPipe(string msg)
{
    WriteFile(g_hChildStd_IN_Wr, msg.c_str(), msg.size(), NULL, NULL);
}

void ReadFromPipe(void)
{
    DWORD dwRead, dwWritten;
    CHAR chBuf[BUFSIZE];
    BOOL bSuccess = FALSE;
    HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    for (;;)
    {
        bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
        if( ! bSuccess || dwRead == 0 ) break;

        bSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL);
        if (! bSuccess ) break;
    }
}

问题出在这行:

CloseHandle(g_hChildStd_IN_Wr);

要么我把它留在那里,我读过一次后就无法写入我的子进程,或者我删除它,然后 ReadFromPipe 陷入死锁。

任何帮助将不胜感激,谢谢!

【问题讨论】:

  • "要从子进程读取,我首先需要关闭用于写入它的父进程的句柄,"不,你不需要,在某个地方有办法做到这一点。我做过一次,我只需要记住如何
  • 如果我删除“CloseHandle(g_hChildStd_IN_Wr);”行,程序会卡在“bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);”行的 ReadFromPipe 中。虽然我不确定它为什么会发生,但它确实......有什么解决方案吗?
  • 是的,这就是我刚刚尝试过的!但由于某种原因,“result = PeekNamedPipe(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, &dwAvailableBytes, &dwBytesLeft);”总是成功,但 dwRead、dwAvailableBytes 和 dwBytesLeft 总是设置为 0,无论是否有要读取的字节。我会更深入地研究它,也许我错过了什么......
  • 您可能在编写过程之前检查过。您必须不时检查一下。
  • 当然,我很笨!非常感谢您的帮助,感谢您的帮助!

标签: c++ windows io pipe child-process


【解决方案1】:

ReadFile(..., BUFSIZE) 表示“等到程序写入BUFSIZE 字节,或者关闭它的管道末端”。该程序仅写入少量字节,然后等待您未提供的更多输入。如果您关闭写管道,它知道没有更多输入并退出,此时您的 ReadFile 知道没有更多输入并返回。你需要找到一种方法来只读取管道中的字节数。

这里的神奇之处是PeekNamedPipe,它告诉你程序输出了多少数据,因此你可以在不阻塞的情况下读取多少。请注意,您必须不时地检查一下,看看程序自上次检查后是否写入了更多字节。

【讨论】:

  • 作为记录,第一句话不太正确。管道是一种特殊情况。每次在管道的另一侧完成写入操作时,ReadFile 都会退出。 (这在这种特定情况下没有多大帮助,但在某些情况下它可以大大简化事情。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多