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

后端 未结 6 1828
盖世英雄少女心
盖世英雄少女心 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: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.

提交回复
热议问题