Reputation: 33385
When using CreateProcess to run another program, what is the recommended way to capture the stdout? That is, to take whatever the second program was printing to stdout, and end up with it in an array where the first program can analyze it?
Both programs are straight Win32 programs written in C, no fancy stuff.
Upvotes: 5
Views: 21619
Reputation: 167
Microsoft example consider both program written from the same developer. In my case I was launching a generic program which output on stdout/stderr. I changed the Microsoft example in this way.
The following function run a generic externalProgram
with command line arguments
:
HANDLE m_hChildStd_OUT_Rd = NULL;
HANDLE m_hChildStd_OUT_Wr = NULL;
HANDLE m_hreadDataFromExtProgram = NULL;
HRESULT RunExternalProgram(std::string externalProgram, std::string arguments)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES saAttr;
ZeroMemory(&saAttr, sizeof(saAttr));
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&m_hChildStd_OUT_Rd, &m_hChildStd_OUT_Wr, &saAttr, 0))
{
// log error
return HRESULT_FROM_WIN32(GetLastError());
}
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(m_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
{
// log error
return HRESULT_FROM_WIN32(GetLastError());
}
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.hStdError = m_hChildStd_OUT_Wr;
si.hStdOutput = m_hChildStd_OUT_Wr;
si.dwFlags |= STARTF_USESTDHANDLES;
ZeroMemory(&pi, sizeof(pi));
std::string commandLine = extProgram + " " + arguments;
// Start the child process.
if (!CreateProcessA(NULL, // No module name (use command line)
(TCHAR*)commandLine.c_str(), // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
TRUE, // Set handle inheritance
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
return HRESULT_FROM_WIN32(GetLastError());
}
else
{
m_hreadDataFromExtProgram = CreateThread(0, 0, readDataFromExtProgram, NULL, 0, NULL);
}
return S_OK;
}
If CreateProcessA
succedeed, an external thread will be launched to asynchronousvly capture the output from the launched program. The code executed from the external thread is the following one:
DWORD __stdcall readDataFromExtProgram(void * argh)
{
DWORD dwRead;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
for (;;)
{
bSuccess = ReadFile(m_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if (!bSuccess || dwRead == 0) continue;
// Log chBuf
if (!bSuccess) break;
}
return 0;
}
I omitted any closing condition on all HANDLE
and threads involved in the code.
Upvotes: 10
Reputation: 312
The MSDN sample mentioned by Remy Lebeau is not working for me, it hangs forever in the WriteToPipe() call. To make it simpler I replaced the child process with "ping 127.0.0.1" and commented out the WriteToPipe() call. Now I am able to capture the output.
Upvotes: 2
Reputation: 101616
The short answer is to create an anonymous pipe, setting the hStdOut
/hStdErr
and dwFlag
members of the STARTUPINFO
structure accordingly, and have CreateProcess()
inherit the handle for the writing end of the pipe. Don't forget to close your writing handle of your pipe, then you can read from the reading handle of the pipe in a loop until it fails with an ERROR_BROKEN_PIPE
error.
MSDN provides a detailed example of this:
Creating a Child Process with Redirected Input and Output
You are not the first person to do this, there should be plenty of example code and duplicate questions here on StackOverflow.
Upvotes: 11