I am trying to execute a command line program and get the output using C++ on windows. I had implemented this initially with _popen()
but this cause the command prompt window to pop-up as mentioned here and here.
As such I implemented a mixture of this answer and this article but encountered an issue with ReadFile()
deadlocking. After more reading I added a call to PeakNamedPipe()
which in causes the read loop to execute infinity rather than deadlock (the opposite of what I want, I would rather wait for the exe to exit), which lead me to believe that I am just setting this up incorrectly.
The following code is slightly edited from the original (names and such)
std::wstring args = "cmdApplication.exe with some args";
HANDLE readHandle;
HANDLE writeOutHandle;
HANDLE writeErrHandle;
HANDLE writeHandle;
if (!CreatePipe(&readHandle, &writeHandle, NULL, 0)) {
return false;
}
DuplicateHandle(GetCurrentProcess(), writeHandle, GetCurrentProcess(), &writeOutHandle, 0, true, DUPLICATE_SAME_ACCESS);
DuplicateHandle(GetCurrentProcess(), writeHandle, GetCurrentProcess(), &writeErrHandle, 0, true, DUPLICATE_SAME_ACCESS);
PROCESS_INFORMATION processInfo;
STARTUPINFO startupInfo;
BOOL success = FALSE;
ZeroMemory( &processInfo, sizeof(PROCESS_INFORMATION) );
ZeroMemory( &startupInfo, sizeof(STARTUPINFO) );
startupInfo.cb = sizeof(STARTUPINFO);
startupInfo.hStdError = writeErrHandle;
startupInfo.hStdOutput = writeOutHandle;
startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
success = CreateProcess(NULL, (LPWSTR)args.c_str(), NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &startupInfo, &processInfo);
DWORD read;
CHAR buffer[4096];
while(true) {
success = ReadFile(writeOutHandle, buffer, 4096, &read, NULL);//return false, GetLastError() returns ERROR_BROKEN_PIPE
if( ! success || read == 0 ) break;
}
CloseHandle(readHandle);
//Deal with the data in buffer
Can I run a command line application like this or I should I run cmd.exe and use and input pipe to provide the args? Have I actually set up my pipes correctly?
Edit: After some back and forth in the comments, ReadFile()
no longer deadlocks (keeping the write handle was preventing the child from exiting fully) but unfortunately returns false with error 109: ERROR_BROKEN_PIPE most likely related to this article (see exert below).
If an anonymous pipe is being used and the write handle has been closed, when ReadFile attempts to read using the pipe's corresponding read handle, the function returns FALSE and GetLastError returns ERROR_BROKEN_PIPE.
Which begs the question, if I can't read while the pipe is open (due to ReadFile()
blocking) and I can't read when it exits (as I get an error saying the pipe has been ended), how do I read from the pipe?