无法从CreateProcess()线程读取管道

时间:2017-01-18 00:10:51

标签: c++ windows

自从几天以来我一直坚持这一点,我疯了。 基本上我试图在一个线程中打开cmd.exe,并从父节点给它输入和读取输出。比如,在linux中分配一个tty,因为在Windows中没有这样的东西。我对linux系统有很好的理解,但对于windows不能说同样的事情。

所以,这里"我的"代码:

  #undef UNICODE


  #include <windows.h>
  #include <tchar.h>
  #include <stdio.h>
  #include <strsafe.h>


  //using namespace std;
  #define BUFFER_SIZE 99

  // handles for cmd thread pipes
  HANDLE cmd_in_rd = NULL;
  HANDLE cmd_in_wr = NULL;
  HANDLE cmd_out_rd = NULL;
  HANDLE cmd_out_wr = NULL;

  HANDLE cmd_thread_handle;


  void PrintError(char *text, int err) {
    DWORD retSize;
    LPTSTR pTemp = NULL;

    if (!err) return;

    retSize = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_ARGUMENT_ARRAY,
        NULL,
        err,
        LANG_NEUTRAL,
        (LPTSTR)&pTemp,
        0,
        NULL);


    if (pTemp) printf("%s: %s\n", text, pTemp);
    LocalFree((HLOCAL)pTemp);
    return;

  }


  int pipewrite(char *command) {
    DWORD dwRead, dwWritten;
    BOOL bSuccess = FALSE;
    SetLastError(0);
    WriteFile(cmd_in_wr, command, strlen(command), &dwWritten, NULL);
    bSuccess = GetLastError();
    PrintError("WriteToPipe", bSuccess);
    return (bSuccess == 0) || (bSuccess == ERROR_IO_PENDING);
  }

  int __stdcall cmd_thread(int arg) {
    // this function only prints when data is ready
    DWORD dwRead, dwWritten;
    CHAR chBuf[BUFFER_SIZE];
    BOOL bSuccess = FALSE;
    HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    int rf_ret, wf_ret;

    //CloseHandle(cmd_out_wr); makes readfile fail!!

    SetLastError(0);
    while (1) { // only executes once!!!!!!!
        (rf_ret = ReadFile(cmd_out_rd, chBuf, BUFFER_SIZE, &dwRead, NULL))
            &&
            (wf_ret = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL));
        printf("ReadFile returned: %d\nWriteFile returned: %d\n", rf_ret, wf_ret);
        bSuccess = GetLastError();
        PrintError("ReadingFromPipe", bSuccess);
    }

    bSuccess = GetLastError();
    return (bSuccess == 0) || (bSuccess == ERROR_IO_PENDING);
  }


  int main(void) {

    char buffer[BUFFER_SIZE];

    // init the pipes
    SECURITY_ATTRIBUTES cmd_sa;
    cmd_sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    cmd_sa.bInheritHandle = TRUE;
    cmd_sa.lpSecurityDescriptor = NULL;

    if (!CreatePipe(&cmd_out_rd, &cmd_out_wr, &cmd_sa, 0)) {
        printf("%s\n", "Error creating pipes");
        return 1;
    }

    if (!SetHandleInformation(cmd_out_rd, HANDLE_FLAG_INHERIT, 0)) {
        printf("%s\n", "Error setting handle infos");
        return 1;
    }

    if (!CreatePipe(&cmd_in_rd, &cmd_in_wr, &cmd_sa, 0)) {
        printf("%s\n", "Error creating pipes");
        return 1;
    }

    if (!SetHandleInformation(cmd_in_rd, HANDLE_FLAG_INHERIT, 0)) {
        printf("%s\n", "Error setting handle infos");
        return 1;
    }

    // create the cmd thread
    PROCESS_INFORMATION cmd_pi;
    STARTUPINFO cmd_si;
    ZeroMemory(&cmd_pi, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&cmd_si, sizeof(STARTUPINFO));
    cmd_si.cb = sizeof(STARTUPINFO);
    cmd_si.hStdError = cmd_out_wr;
    cmd_si.hStdOutput = cmd_out_wr;
    cmd_si.hStdInput = cmd_in_rd;
    cmd_si.dwFlags |= STARTF_USESTDHANDLES;

    TCHAR comm[] = TEXT("cmd.exe");
    BOOL th = CreateProcess(NULL,
        comm,
        NULL,
        NULL,
        TRUE, // handles are inherited
        0,
        NULL,
        NULL,
        &cmd_si,
        &cmd_pi);

    if (th) {
        CloseHandle(cmd_pi.hProcess);
        CloseHandle(cmd_pi.hThread);
    }

    cmd_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)cmd_thread, NULL, 0, NULL);

    // read commands from shell and send them to cmd
    ZeroMemory(&buffer, BUFFER_SIZE);
    while (1) {
        fgets(buffer, BUFFER_SIZE, stdin);
        if (!pipewrite(buffer)) break;
    }
    printf("Program terminated\n");

    return 0;
  }

实际上,出于测试目的,我从stackoverflow和MSDN上的另一个问题中复制了很多,因为我无法让它在我的主程序上工作。我不明白的事情是:

为什么cmd_thread中的while循环在启动时执行,然后在那里挂起等待世界末日?我试图在阅读之前从父级关闭管道out_write句柄,但这会使其他部分无效。

pipewrite()似乎工作,但我不能确定cmd.exe线程接收并处理输入...因为我没有输出:/ 我想过stracing / ltracing程序或运行它到调试器,但我知道没有工具... 奇怪的是original有效(我得到代码的那个)。我试图发现两者之间的差异,但即使我并排看待它们,它们似乎都做了完全相同的事情。

1 个答案:

答案 0 :(得分:0)

子进程一旦尝试从标准输入读取就会死亡,因为:

if (!SetHandleInformation(cmd_in_rd, HANDLE_FLAG_INHERIT, 0)) {

这应该是:

if (!SetHandleInformation(cmd_in_wr, HANDLE_FLAG_INHERIT, 0)) {

就像在原始代码中一样。

此外,您的错误处理在很大程度上是错误的;您没有始终检查错误,有时在没有发生错误时调用GetLastError()。 (这些问题也在原始代码中。)

您还需要重新拨打CloseHandle(cmd_out_wr);,否则您将无法判断孩子何时退出。

哦,顺便说一下,cmd.exe是一个进程,而不是一个帖子。