简体   繁体   English

如何使用 CreateProcess() 和 CreatePipe() 从 cmd.exe 读取输出

[英]How to read output from cmd.exe using CreateProcess() and CreatePipe()

How to read output from cmd.exe using CreateProcess() and CreatePipe()如何使用 CreateProcess() 和 CreatePipe() 从 cmd.exe 读取输出

I have been trying to create a child process executing cmd.exe with a command-line designating /K dir .我一直在尝试使用指定/K dir的命令行创建一个执行cmd.exe的子进程。 The purpose is to read the output from the command back into the parent process using pipes.目的是使用管道将命令的输出读回父进程。

I've already got CreateProcess() working, however the step involving pipes are causing me trouble.我已经让CreateProcess()工作了,但是涉及管道的步骤给我带来了麻烦。 Using pipes, the new console window is not displaying (like it was before), and the parent process is stuck in the call to ReadFile() .使用管道,新的控制台窗口不会显示(就像以前一样),并且父进程卡在对ReadFile()的调用中。

Does anyone have an idea of what I'm doing wrong?有谁知道我做错了什么?

#include <Windows.h>
#include <stdio.h>
#include <tchar.h>

#define BUFFSZ 4096

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

int wmain(int argc, wchar_t* argv[]) 
{
    int result;
    wchar_t aCmd[BUFFSZ] = TEXT("/K dir"); // CMD /?
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    SECURITY_ATTRIBUTES sa;

    printf("Starting...\n");

    ZeroMemory(&si, sizeof(STARTUPINFO));
    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));

    // Create one-way pipe for child process STDOUT
    if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0)) {
        printf("CreatePipe() error: %ld\n", GetLastError());
    }

    // Ensure read handle to pipe for STDOUT is not inherited
    if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) {
        printf("SetHandleInformation() error: %ld\n", GetLastError());
    }

    // Create one-way pipe for child process STDIN
    if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &sa, 0)) {
        printf("CreatePipe() error: %ld\n", GetLastError());
    }

    // Ensure write handle to pipe for STDIN is not inherited
    if (!SetHandleInformation(g_hChildStd_IN_Rd, HANDLE_FLAG_INHERIT, 0)) {
        printf("SetHandleInformation() error: %ld\n", GetLastError());
    }

    si.cb = sizeof(STARTUPINFO);
    si.hStdError = g_hChildStd_OUT_Wr;
    si.hStdOutput = g_hChildStd_OUT_Wr;
    si.hStdInput = g_hChildStd_IN_Rd;
    si.dwFlags |= STARTF_USESTDHANDLES;

    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    // Pipe handles are inherited
    sa.bInheritHandle = true;

    // Creates a child process
    result = CreateProcess(
        TEXT("C:\\Windows\\System32\\cmd.exe"),     // Module
        aCmd,                                       // Command-line
        NULL,                                       // Process security attributes
        NULL,                                       // Primary thread security attributes
        true,                                       // Handles are inherited
        CREATE_NEW_CONSOLE,                         // Creation flags
        NULL,                                       // Environment (use parent)
        NULL,                                       // Current directory (use parent)
        &si,                                        // STARTUPINFO pointer
        &pi                                         // PROCESS_INFORMATION pointer
        );

    if (result) {
        printf("Child process has been created...\n");
    }
    else {
        printf("Child process could not be created\n");
    }

    bool bStatus;
    CHAR aBuf[BUFFSZ + 1];
    DWORD dwRead;
    DWORD dwWrite;
    // GetStdHandle(STD_OUTPUT_HANDLE)

    while (true) {
        bStatus = ReadFile(g_hChildStd_OUT_Rd, aBuf, sizeof(aBuf), &dwRead, NULL);
        if (!bStatus || dwRead == 0) {
            break;
        }
        aBuf[dwRead] = '\0';
        printf("%s\n", aBuf);
    }

        // Wait until child process exits
        WaitForSingleObject(pi.hProcess, INFINITE);

        // Close process and thread handles
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);

        printf("Stopping...\n");

        return 0;
    }

The subtle way out of your problem is to make sure you close the ends of the pipe you don't need.解决问题的巧妙方法是确保关闭不需要的管道末端。

Your parent process has four handles:你的父进程有四个句柄:

  • two of them are your ends of the pipe其中两个是管道的末端
    • g_hChildStd_IN_Wr
    • g_hChildStd_OUT_Rd
  • two of them are the child's end of the pipe其中两个是孩子的管道末端
    • g_hChildStd_IN_Rd
    • g_hChildStd_OUT_Wr

╔══════════════════╗                ╔══════════════════╗
║  Parent Process  ║                ║  Child Process   ║
╠══════════════════╣                ╠══════════════════╣
║                  ║                ║                  ║
║ g_hChildStd_IN_Wr╟───────────────>║g_hChildStd_IN_Rd ║ 
║                  ║                ║                  ║ 
║g_hChildStd_OUT_Rd║<───────────────╢g_hChildStd_OUT_Wr║
║                  ║                ║                  ║
╚══════════════════╝                ╚══════════════════╝

Your parent process only needs one end of each pipe:你的父进程只需要每根管子的一端

  • writable end of the child input pipe: g_hChildStd_IN_Wr子输入管道的可写端: g_hChildStd_IN_Wr
  • readable end of the child output pipe: g_hChildStd_OUT_Rd子输出管道的可读端: g_hChildStd_OUT_Rd

Once you've launched your child process: be sure to close those ends of the pipe you no longer need:启动子进程后:确保关闭不再需要的管道末端:

  • CloseHandle(g_hChildStd_IN_Rd)
  • CloseHandle(g_hChildStd_OUT_Wr)

Leaving:离开:

╔══════════════════╗                ╔══════════════════╗
║  Parent Process  ║                ║  Child Process   ║
╠══════════════════╣                ╠══════════════════╣
║                  ║                ║                  ║
║ g_hChildStd_IN_Wr╟───────────────>║                  ║ 
║                  ║                ║                  ║ 
║g_hChildStd_OUT_Rd║<───────────────╢                  ║
║                  ║                ║                  ║
╚══════════════════╝                ╚══════════════════╝

Or more fully:或者更完整:

STARTUP_INFO si;
PROCESS_INFO pi;
result = CreateProcess(..., ref si, ref pi);

//Bonus chatter: A common bug among a lot of programmers: 
// they don't realize they are required to call CloseHandle 
// on the two handles placed in PROCESS_INFO.
// That's why you should call ShellExecute - it closes them for you.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);

/*
   We've given the console app the writable end of the pipe during CreateProcess; we don't need it anymore.
   We do keep the handle for the *readable* end of the pipe; as we still need to read from it.
   The other reason to close the writable-end handle now is so that there's only one out-standing reference to the writeable end: held by the child process.
   When the child processes closes, it will close the pipe, and 
   your call to ReadFile will fail with error code:
      109 (The pipe has been ended).

   That's how we'll know the console app is done. (no need to wait on process handles with buggy infinite waits)
*/
CloseHandle(g_hChildStd_OUT_Wr);
g_hChildStd_OUT_Wr = 0;
CloseHandle(g_hChildStd_IN_Rd);
g_hChildStd_OUT_Wr = 0;

Waiting on the Child Process (aka the deadlock waiting to happen)等待子进程(也就是等待发生的死锁)

The common problem with most solutions is that people try to wait on a process handle.大多数解决方案的常见问题是人们试图等待进程句柄。

  • they create event objects他们创建事件对象
  • they try to MsgWait for events to be signaled他们尝试使用 MsgWait 来通知事件
  • they try to MsgWait for child processes to end他们尝试 MsgWait 等待子进程结束

That's wrong.那是错误的。 That's all wrong .那都是错的

There are many problems with these ideas;这些想法有很多问题; the main one being:主要是:

  • if you try to wait for the child the terminate如果您尝试等待孩子终止
  • the child will never be able to terminate孩子永远无法终止

If the child is trying to send you output through the pipe, and you're INFINITE waiting, you're not emptying your end of the pipe.如果孩子试图通过管道向您发送输出,而您正在INFINITE等待,那么您并没有清空管道的末端。 Eventually the pipe the child is writing to becomes full.最终孩子正在写的管道变满。 When the child tries to write to a pipe that is full, its WriteFile call waits for the pipe to have some room.当子进程尝试写入已满的管道时,它的WriteFile调用会等待管道有一些空间。

As a result the child process will never terminate;结果进程永远不会终止; you've deadlocked everything.你让一切陷入僵局。

The Right Approach - let the client do it's thing正确的方法 - 让客户做它的事

The correct solution comes by simply reading from the pipe .正确的解决方案来自简单地从管道中读取

  • Once the child process terminates,一旦子进程终止,
  • it will CloseHandle on its end of the pipes.它将CloseHandle管子上的尾声
  • The next time you try to read from the pipe下次尝试从管道读取时
  • you'll be told the pipe has been closed ( ERROR_BROKEN_PIPE ).您会被告知管道已关闭( ERROR_BROKEN_PIPE )。
  • That's how you know the process is done and you have no more stuff to read.这就是您如何知道该过程已完成并且您没有更多内容可阅读的方式。

String outputText = "";

//Read will return when the buffer is full, or if the pipe on the other end has been broken
while (ReadFile(stdOutRead, aBuf, Length(aBuf), &bytesRead, null)
   outputText = outputText + Copy(aBuf, 1, bytesRead);

//ReadFile will either tell us that the pipe has closed, or give us an error
DWORD le = GetLastError;

//And finally cleanup
CloseHandle(g_hChildStd_IN_Wr);
CloseHandle(g_hChildStd_OUT_Rd);

if (le != ERROR_BROKEN_PIPE) //"The pipe has been ended."
   RaiseLastOSError(le);

All without a dangerous MsgWaitForSingleObject - which is error-prone, difficult to use correctly, and causes the very bug you want to avoid.所有这些都没有危险的MsgWaitForSingleObject - 它容易出错,难以正确使用,并且会导致您想要避免的错误。

Ian Boyd's answer had this gem: Once you've launched your child process: be sure to close those ends of the pipe you no longer need.伊恩博伊德的回答有一个宝石:一旦你启动了你的子进程:一定要关闭你不再需要的管道末端。

I've produced another version of the CreatePipe + CreateProcess solution which, I hope, is more clear:我已经制作了另一个版本的CreatePipe + CreateProcess解决方案,我希望它更清晰:

int main()
{
    BOOL ok = TRUE;
    HANDLE hStdInPipeRead = NULL;
    HANDLE hStdInPipeWrite = NULL;
    HANDLE hStdOutPipeRead = NULL;
    HANDLE hStdOutPipeWrite = NULL;

    // Create two pipes.
    SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
    ok = CreatePipe(&hStdInPipeRead, &hStdInPipeWrite, &sa, 0);
    if (ok == FALSE) return -1;
    ok = CreatePipe(&hStdOutPipeRead, &hStdOutPipeWrite, &sa, 0);
    if (ok == FALSE) return -1;

    // Create the process.
    STARTUPINFO si = { };
    si.cb = sizeof(STARTUPINFO);
    si.dwFlags = STARTF_USESTDHANDLES;
    si.hStdError = hStdOutPipeWrite;
    si.hStdOutput = hStdOutPipeWrite;
    si.hStdInput = hStdInPipeRead;
    PROCESS_INFORMATION pi = { };
    LPCWSTR lpApplicationName = L"C:\\Windows\\System32\\cmd.exe";
    LPWSTR lpCommandLine = (LPWSTR)L"C:\\Windows\\System32\\cmd.exe /c dir";
    LPSECURITY_ATTRIBUTES lpProcessAttributes = NULL;
    LPSECURITY_ATTRIBUTES lpThreadAttribute = NULL;
    BOOL bInheritHandles = TRUE;
    DWORD dwCreationFlags = 0;
    LPVOID lpEnvironment = NULL;
    LPCWSTR lpCurrentDirectory = NULL;
    ok = CreateProcess(
        lpApplicationName,
        lpCommandLine,
        lpProcessAttributes,
        lpThreadAttribute,
        bInheritHandles,
        dwCreationFlags,
        lpEnvironment,
        lpCurrentDirectory,
        &si,
        &pi);
    if (ok == FALSE) return -1;

    // Close pipes we do not need.
    CloseHandle(hStdOutPipeWrite);
    CloseHandle(hStdInPipeRead);

    // The main loop for reading output from the DIR command.
    char buf[1024 + 1] = { };
    DWORD dwRead = 0;
    DWORD dwAvail = 0;
    ok = ReadFile(hStdOutPipeRead, buf, 1024, &dwRead, NULL);
    while (ok == TRUE)
    {
        buf[dwRead] = '\0';
        OutputDebugStringA(buf);
        puts(buf);
        ok = ReadFile(hStdOutPipeRead, buf, 1024, &dwRead, NULL);
    }

    // Clean up and exit.
    CloseHandle(hStdOutPipeRead);
    CloseHandle(hStdInPipeWrite);
    DWORD dwExitCode = 0;
    GetExitCodeProcess(pi.hProcess, &dwExitCode);
    return dwExitCode;
}

Some notes:一些注意事项:

  • The pipe for StdIn is not really required: StdIn 的管道并不是真正需要的:
    • This is because the DIR command doesn't require user input (but, I left it in the code, since it is a good template for running other commands)这是因为DIR命令不需要用户输入(但是,我把它留在了代码中,因为它是运行其他命令的好模板)
    • everyting to do with hStdInPipeRead & hStdInPipeWrite can be omittedhStdInPipeReadhStdInPipeWrite都可以省略
    • setting si.hStdInput can be omitted设置si.hStdInput可以省略
  • Replace hardcoded L"C:\\\\Windows\\\\System32\\\\cmd.exe" with reading the COMSPEC environment variable.将硬编码的L"C:\\\\Windows\\\\System32\\\\cmd.exe"替换为读取COMSPEC环境变量。
  • Replace LPWSTR with LPTSTR if we wish to compile for non-UNICODE.如果我们希望针对非 UNICODE 进行编译,请将 LPWSTR 替换为 LPTSTR。
  • Replace cmd.exe /k DIR with cmd.exe /c DIR since when the DIR command finishes we don't really want the cmd.exe to stick around.cmd.exe /k DIR替换为cmd.exe /c DIR因为当DIR命令完成时,我们真的不希望cmd.exe继续存在。

I too have same scenario.我也有同样的场景。 in my case from Lib, need to execute internal exe and read output.在我的情况下,来自 Lib,需要执行内部 exe 并读取输出。 The following works without any issues.以下工作没有任何问题。

  void executeCMDInNewProcessAndReadOutput(LPSTR lpCommandLine)
    {
        STARTUPINFO si;
        SECURITY_ATTRIBUTES sa;
        PROCESS_INFORMATION pi;
        HANDLE g_hChildStd_IN_Rd, g_hChildStd_OUT_Wr, g_hChildStd_OUT_Rd, g_hChildStd_IN_Wr;  //pipe handles
        char buf[1024];           //i/o buffer

        sa.nLength = sizeof(SECURITY_ATTRIBUTES);
        sa.bInheritHandle = TRUE;
        sa.lpSecurityDescriptor = NULL;

        if (CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &sa, 0))   //create stdin pipe
        {
            if (CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0))  //create stdout pipe
            {

                //set startupinfo for the spawned process
                /*The dwFlags member tells CreateProcess how to make the process.
                STARTF_USESTDHANDLES: validates the hStd* members.
                STARTF_USESHOWWINDOW: validates the wShowWindow member*/
                GetStartupInfo(&si);

                si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
                si.wShowWindow = SW_HIDE;
                //set the new handles for the child process
                si.hStdOutput = g_hChildStd_OUT_Wr;
                si.hStdError = g_hChildStd_OUT_Wr;
                si.hStdInput = g_hChildStd_IN_Rd;

                //spawn the child process
                if (CreateProcess(NULL, lpCommandLine, NULL, NULL, TRUE, CREATE_NEW_CONSOLE,
                    NULL, NULL, &si, &pi))
                {
                    unsigned long bread;   //bytes read
                    unsigned long avail;   //bytes available
                    memset(buf, 0, sizeof(buf));

                    for (;;)
                    {
                        PeekNamedPipe(g_hChildStd_OUT_Rd, buf, 1023, &bread, &avail, NULL);
                        //check to see if there is any data to read from stdout
                        if (bread != 0)
                        {
                            if (ReadFile(g_hChildStd_OUT_Rd, buf, 1023, &bread, NULL))
                            {
                                break;
                            }
                        }
                    }

                    //clean up all handles
                    CloseHandle(pi.hThread);
                    CloseHandle(pi.hProcess);
                    CloseHandle(g_hChildStd_IN_Rd);
                    CloseHandle(g_hChildStd_OUT_Wr);
                    CloseHandle(g_hChildStd_OUT_Rd);
                    CloseHandle(g_hChildStd_IN_Wr);
                }
                else
                {
                    CloseHandle(g_hChildStd_IN_Rd);
                    CloseHandle(g_hChildStd_OUT_Wr);
                    CloseHandle(g_hChildStd_OUT_Rd);
                    CloseHandle(g_hChildStd_IN_Wr);
                }
            }
            else
            {
                CloseHandle(g_hChildStd_IN_Rd);
                CloseHandle(g_hChildStd_IN_Wr);
            }
        }
    }

Here is an example (taken from a larger program) of a thread that does what you are looking for.这是一个执行您正在寻找的线程的示例(取自一个更大的程序)。 It creates pipes for stdout and stderr for the process it creates then goes into a loop reading those pipes until the program finishes.它为它创建的进程创建 stdout 和 stderr 管道,然后进入循环读取这些管道,直到程序完成。

DWORD WINAPI ThreadProc(LPVOID lpParameter)
   {
#define EVENT_NAME "Global\\RunnerEvt"

   HANDLE hev;
   SECURITY_ATTRIBUTES psa;
   InitSAPtr(&psa);
   DWORD waitRc;
   DWORD bytesRead;
   int manual_triggered = 1;

   hev = CreateEvent(&psa, FALSE, FALSE, EVENT_NAME);

   // Create pipes we'll read

      for(;;)
      {

      if (manual_triggered)
         {
         waitRc = WAIT_OBJECT_0;
         manual_triggered = 0;
         }
      else
         {
         waitRc = WaitForSingleObject(hev, 500);
         }

      if (waitRc == WAIT_OBJECT_0)
         {
         `logprint`f(LOG_DBG, "Received command to run process\n");

         CreateChildOutFile();

         stdOutEvt = CreateEvent(&psa, TRUE, FALSE, 0);
         stdOutOvl.hEvent = stdOutEvt;

         stdErrEvt = CreateEvent(&psa, TRUE, FALSE, 0);
         stdErrOvl.hEvent = stdErrEvt;

         gStdOutReadHand =  CreateNamedPipe(STD_OUT_PIPE_NAME, PIPE_ACCESS_DUPLEX + FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE + PIPE_READMODE_BYTE,
            PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &psa);
         if (gStdOutReadHand == INVALID_HANDLE_VALUE)
            {
            log(LOG_DBG, "Error %d on create STDOUT pipe\n", GetLastError());
            }

         gStdErrReadHand =  CreateNamedPipe(STD_ERR_PIPE_NAME, PIPE_ACCESS_DUPLEX + FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE + PIPE_READMODE_BYTE,
            PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &psa);
         if (gStdErrReadHand == INVALID_HANDLE_VALUE)
            {
            log(LOG_DBG, "Error %d on create STDERR pipe\n", GetLastError());
            }

         runProcess();

         log(LOG_DBG, "After runProcess, new PID is %d/%x\n", piProcInfo.dwProcessId, piProcInfo.dwProcessId);

         if (piProcInfo.dwProcessId == 0)
            {
            log(LOG_DBG, "runProcess failed, closing child STDIN/STDERR\n");
            closeChildPipes();

#define FAIL_MSG "Child process failed to start\n"
            writeChildOutFile(FAIL_MSG, strlen(FAIL_MSG) );

            CloseHandle(hChildOut);
            }
         else
            {
            log(LOG_DBG, "Child process created, setting up for redir/restart/termination\n");

            issueRead(gStdOutReadHand, &stdOutOvl, stdOutBuff, &stdOutBytesAvail);
            //log(LOG_DBG, "After read set on STDOUT\n");

            issueRead(gStdErrReadHand, &stdErrOvl, stdErrBuff, &stdErrBytesAvail);
            //log(LOG_DBG, "After read set on STDERR\n");

            HANDLE harr[4];

            for(;;)
               {
               harr[0] = hev;
               harr[1] = piProcInfo.hProcess;
               harr[2] = stdOutEvt;
               harr[3] = stdErrEvt;

               DWORD waitRc2 = WaitForMultipleObjects(4, harr, FALSE, 500);

               #if 0
               if (waitRc2 == -1)
                  {
                  log(LOG_DBG, "Wait error %d\n", GetLastError());
                  Sleep(500);
                  }

               log(LOG_DBG, "waitRc2 %d\n", waitRc2);
               #endif


               if ((waitRc2 - WAIT_OBJECT_0) == 0)
                  {
                  log(LOG_DBG, "Woke up because another trigger command was received\n");
                  #define NEW_CMD_MSG "Child process is being terminated because new trigger received\n"

                  writeChildOutFile(NEW_CMD_MSG, strlen(NEW_CMD_MSG));

                  terminateChild();
                  CloseHandle(hChildOut);
                  manual_triggered = 1;
                  break;
                  }
               else if ((waitRc2 - WAIT_OBJECT_0) == 1)
                  {
                  //log(LOG_DBG, "Woke up because child has terminated\n");
                  closeChildPipes();
                  #define NORM_MSG "Normal child process termination\n"
                  writeChildOutFile(NORM_MSG, strlen(NORM_MSG));
                  CloseHandle(hChildOut);
                  break;
                  }
               else if ((waitRc2 - WAIT_OBJECT_0) == 2)
                  {
                  //log(LOG_DBG, "Woke up because child has stdout\n");
                  if (GetOverlappedResult(gStdOutReadHand, &stdOutOvl, &bytesRead, TRUE))
                     {
                     writeChildOutFile(stdOutBuff, bytesRead);
                     ResetEvent(stdOutEvt);
                     issueRead(gStdOutReadHand, &stdOutOvl, stdOutBuff, &stdOutBytesAvail);
                     }

                  }
               else if ((waitRc2 - WAIT_OBJECT_0) == 3)
                  {
                  //log(LOG_DBG, "Woke up because child has stderr\n");

                  if (GetOverlappedResult(gStdErrReadHand, &stdErrOvl, &bytesRead, TRUE))
                     {
                     writeChildOutFile(stdErrBuff, bytesRead);
                     ResetEvent(stdErrEvt);
                     issueRead(gStdErrReadHand, &stdErrOvl, stdErrBuff, &stdErrBytesAvail);
                     }
                  }
               else
                  {
                  if (gShuttingDown)
                     {
                     log(LOG_DBG, "Woke with active child and service is terminating\n");

#define SHUTDOWN_MSG "Child process is being terminated because the service is shutting down\n"

                     writeChildOutFile(SHUTDOWN_MSG, strlen(SHUTDOWN_MSG));
                     terminateChild();
                     CloseHandle(hChildOut);
                     break;
                     }
                  }

               if (gShuttingDown)
                  {
                  break;
                  }

               }
            }
         }
      else if (gShuttingDown)
         {
         break;
         }

      CloseHandle(gStdOutReadHand);
      CloseHandle(gStdErrReadHand);

      }

   return 0;
   }

void writeChildOutFile(char *msg, int len)
   {
   DWORD bytesWritten;
   WriteFile(hChildOut, msg, len, &bytesWritten, 0);
   }


void terminateChild(void)
   {
   if (piProcInfo.dwProcessId != 0)
      {
      TerminateProcess(piProcInfo.hProcess, -1);
      CloseHandle(piProcInfo.hThread);
      CloseHandle(piProcInfo.hProcess);
      closeChildPipes();
      }
   }

void closeChildPipes(void)
   {
   CloseHandle(g_hChildStd_OUT_Wr);
   CloseHandle(g_hChildStd_ERR_Wr);
   }

void runProcess(void)
   {
   SECURITY_ATTRIBUTES saAttr;

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

   // Create a pipe for the child process's STDOUT.
   TCHAR szCmdline[]=TEXT("cmd.exe /C C:\\temp\\RunnerService.bat");
   STARTUPINFO siStartInfo;
   BOOL bSuccess = FALSE;

// Set up members of the PROCESS_INFORMATION structure.

   ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );

   g_hChildStd_OUT_Wr = CreateFile (STD_OUT_PIPE_NAME,
                FILE_WRITE_DATA,
                0,
                &saAttr,
                OPEN_EXISTING,
                0,
                NULL);

   if (g_hChildStd_OUT_Wr == INVALID_HANDLE_VALUE)
      {
      log(LOG_DBG, "Error creating child proc stdout file %d\n", GetLastError());
      }


   g_hChildStd_ERR_Wr = CreateFile (STD_ERR_PIPE_NAME,
                FILE_WRITE_DATA,
                0,
                &saAttr,
                OPEN_EXISTING,
                0,
                NULL);

   if (g_hChildStd_ERR_Wr == INVALID_HANDLE_VALUE)
      {
      log(LOG_DBG, "Error creating child proc stderr file %d\n", GetLastError());
      }


// 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.hStdOutput = g_hChildStd_OUT_Wr;
   siStartInfo.hStdError = g_hChildStd_ERR_Wr;
   siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

// Create the child process.

   bSuccess = CreateProcess(NULL,
      szCmdline,     // command line
      NULL,          // process security attributes
      NULL,          // primary thread security attributes
      TRUE,          // handles are inherited
      0,             // creation flags
      NULL,          // use parent's environment
      NULL,          // use parent's current directory
      &siStartInfo,  // STARTUPINFO pointer
      &piProcInfo);  // receives PROCESS_INFORMATION

   }


void CreateChildOutFile(void)
   {
   SYSTEMTIME st;
   SECURITY_ATTRIBUTES sa;
   char fName[_MAX_PATH];

   InitSAPtr(&sa);

   GetLocalTime(&st);

   sprintf(fName, "C:\\TEMP\\runsvcchild_%02d_%02d_%02d_%04d.out", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);

   hChildOut = CreateFile(fName, GENERIC_WRITE, FILE_SHARE_READ, &sa,  CREATE_ALWAYS,  FILE_ATTRIBUTE_NORMAL, 0);
   }

void issueRead(HANDLE hFile, OVERLAPPED *overLapped, char *buf, DWORD *dwRead)
   {
   //log(LOG_DBG, "Start of issueRead, hfile %08x, ovl is %08x\n", hFile, overLapped);
   BOOL brc = ReadFile(hFile, buf, 4096, dwRead, overLapped);
   if (!brc)
      {
      DWORD dwle = GetLastError();
      if (dwle != ERROR_IO_PENDING)
         {
         log(LOG_DBG, "Error %d on ReadFile\n", dwle);
         }
      }
   else
      {
      // log(LOG_DBG, "Read issued\n");
      }
   }  

I think you did everything right.我认为你做的一切都是正确的。 But cmd.exe prints nothing or very little amount of data after start and your ReadFile blocks.但是 cmd.exe 在启动和您的 ReadFile 块后不打印任何数据或打印很少量的数据。 If you move your cycle如果你移动你的周期

while (true) {
    bStatus = ReadFile(g_hChildStd_OUT_Rd, aBuf, sizeof(aBuf), &dwRead, NULL);
    if (!bStatus || dwRead == 0) {
        break;
    }
    aBuf[dwRead] = '\0';
    printf("%s\n", aBuf);
}

into background thread and run other cycle which will read your input and send it to cmd.exe, I think you can see any effect.进入后台线程并运行其他循环,它将读取您的输入并将其发送到 cmd.exe,我想您可以看到任何效果。 Either you can make read buffer smaller (16 bytes eg).您可以将读取缓冲区变小(例如 16 字节)。

I tried Stephen Quan's answer out and got a segfault.我尝试了 Stephen Quan 的答案并得到了一个段错误。 Perhaps someone with more experience might know why that is.也许有更多经验的人可能知道这是为什么。 At any rate, this should be a more correct example of what he was trying to do:无论如何,这应该是他试图做的更正确的例子:

#include <windows.h>
#include <cstddef>
#include <string>
#include <vector>
#include <cwchar>

using std::string;
using std::wstring;
using std::vector;
using std::size_t;

static inline wstring widen(string str) {
  size_t wchar_count = str.size() + 1;
  vector<wchar_t> buf(wchar_count);
  return wstring{ buf.data(), (size_t)MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, buf.data(), (int)wchar_count) };
}

static inline string narrow(wstring wstr) {
  int nbytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), NULL, 0, NULL, NULL);
  vector<char> buf(nbytes);
  return string{ buf.data(), (size_t)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), buf.data(), nbytes, NULL, NULL) };
}

string evaluate_shell(string command) {
  string output;
  wstring wstr_command = widen(command);
  wchar_t cwstr_command[32768];
  wcsncpy(cwstr_command, wstr_command.c_str(), 32768);
  BOOL ok = TRUE;
  HANDLE hStdInPipeRead = NULL;
  HANDLE hStdInPipeWrite = NULL;
  HANDLE hStdOutPipeRead = NULL;
  HANDLE hStdOutPipeWrite = NULL;
  SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
  ok = CreatePipe(&hStdInPipeRead, &hStdInPipeWrite, &sa, 0);
  if (ok == FALSE) return "";
  ok = CreatePipe(&hStdOutPipeRead, &hStdOutPipeWrite, &sa, 0);
  if (ok == FALSE) return "";
  STARTUPINFOW si = { };
  si.cb = sizeof(STARTUPINFOW);
  si.dwFlags = STARTF_USESTDHANDLES;
  si.hStdError = hStdOutPipeWrite;
  si.hStdOutput = hStdOutPipeWrite;
  si.hStdInput = hStdInPipeRead;
  PROCESS_INFORMATION pi = { };
  if (CreateProcessW(NULL, cwstr_command, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
    while (WaitForSingleObject(pi.hProcess, 5) == WAIT_TIMEOUT) {
      MSG msg;
      if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
    }
    CloseHandle(hStdOutPipeWrite);
    CloseHandle(hStdInPipeRead);
    char buffer[4096] = { };
    DWORD dwRead = 0;
    ok = ReadFile(hStdOutPipeRead, buffer, 4095, &dwRead, NULL);
    while (ok == TRUE) {
      buffer[dwRead] = 0;
      ok = ReadFile(hStdOutPipeRead, buffer, 4095, &dwRead, NULL);
    }
    CloseHandle(hStdOutPipeRead);
    CloseHandle(hStdInPipeWrite);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    output = narrow(widen(buffer));
    while (output.back() == '\r' || output.back() == '\n')
      output.pop_back();
  }
  return output;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM