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

后端 未结 6 1805
盖世英雄少女心
盖世英雄少女心 2020-12-09 12:02

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

I have been trying to create a child process executing c

相关标签:
6条回答
  • 2020-12-09 12:17

    I think you did everything right. But cmd.exe prints nothing or very little amount of data after start and your ReadFile blocks. 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. Either you can make read buffer smaller (16 bytes e.g.).

    0 讨论(0)
  • 2020-12-09 12:19

    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
    • two of them are the child's end of the pipe

     

    ╔══════════════════╗                ╔══════════════════╗
    ║  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
    • readable end of the child output pipe

    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);
    
    //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.
    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;
    

    The Ignorant Hacks - Waiting on the child process

    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
    • they try to MsgWait for child processes to end

    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. 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. As a result the child process will never terminate; you've deadlocked everything.

    The correct solution comes by simply reading from the pipe.

    • Once the child process terminates,
    • it will CloseHandle on its end of the pipes.
    • The next time you try to read from the pipe
    • you'll be told the pipe has been closed (ERROR_BROKEN_PIPE).
    • That's how you know the process is done and you have no more stuff to read.

    All without a dangerous MsgWaitForSingleObject, that are error-prone to use correctly, and causes the very bug you want to avoid.

    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);
    
    0 讨论(0)
  • 2020-12-09 12:22

    I too have same scenario. in my case from Lib, need to execute internal exe and read output. 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);
                }
            }
        }
    
    0 讨论(0)
  • 2020-12-09 12:33

    I tried Stephen Quan's answer out and got a segfault. 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;
    }
    
    0 讨论(0)
  • 2020-12-09 12:35

    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.

    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");
          }
       }  
    
    0 讨论(0)
  • 2020-12-09 12:37

    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:

    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:
      • 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)
      • everyting to do with hStdInPipeRead & hStdInPipeWrite can be omitted
      • setting si.hStdInput can be omitted
    • Replace hardcoded L"C:\\Windows\\System32\\cmd.exe" with reading the COMSPEC environment variable.
    • Replace LPWSTR with LPTSTR if we wish to compile for non-UNICODE.
    • 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.
    0 讨论(0)
提交回复
热议问题