I'm writing an integration test driver for a command-line executable. I control both the driver and the executable, so I can make guarantees about their behaviour- for example, the executable never reads from stdin, it just takes command-line arguments, does its thing, and then writes output to a file and stdout.
I wish to capture both the exit code and the stdout of the process for verification.
Here's the code that I'm using:
#include <Windows.h>
class Pipe {
HANDLE ReadHandle;
HANDLE writehandle;
public:
Pipe() {
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
CreatePipe(&ReadHandle, &writehandle, &saAttr, 0);
}
HANDLE WriteHandle() {
return writehandle;
}
std::string Contents() {
CloseHandle(writehandle);
DWORD dwRead;
CHAR chBuf[1024];
BOOL bSuccess = FALSE;
std::string result;
for (;;)
{
bSuccess = ReadFile(ReadHandle, chBuf, 1024, &dwRead, NULL);
if (!bSuccess) break;
result += std::string(chBuf, chBuf + dwRead);
if (dwRead < 1024)
break;
}
return result;
}
~Pipe() {
CloseHandle(ReadHandle);
}
};
Wide::Driver::ProcessResult Wide::Driver::StartAndWaitForProcess(std::string name, std::vector<std::string> args, Util::optional<unsigned> timeout)
{
ProcessResult result;
Pipe stdoutpipe;
PROCESS_INFORMATION info = { 0 };
STARTUPINFO startinfo = { sizeof(STARTUPINFO) };
std::string final_args = name;
for (auto arg : args)
final_args += " " + arg;
startinfo.hStdOutput = stdoutpipe.WriteHandle();
startinfo.hStdError = INVALID_HANDLE_VALUE;
startinfo.hStdInput = INVALID_HANDLE_VALUE;
startinfo.dwFlags |= STARTF_USESTDHANDLES;
auto proc = CreateProcess(
name.c_str(),
&final_args[0],
nullptr,
nullptr,
TRUE,
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
nullptr,
nullptr,
&startinfo,
&info
);
if (!proc) {
DWORD dw = GetLastError();
const char* message;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&message, 0, nullptr);
std::string err = message;
LocalFree((void*)message);
throw std::runtime_error(err);
}
if (timeout == 0)
timeout = INFINITE;
result.std_out = stdoutpipe.Contents();
if (WaitForSingleObject(info.hProcess, timeout ? *timeout : INFINITE) == WAIT_TIMEOUT)
TerminateProcess(info.hProcess, 1);
DWORD exit_code;
GetExitCodeProcess(info.hProcess, &exit_code);
CloseHandle(info.hProcess);
CloseHandle(info.hThread);
result.exitcode = exit_code;
if (exit_code != 0)
return result;
return result;
}
I've got 259 integration tests that I run in this way. Some take longer than others. When I run the suite, about 1-3 will fail - different ones each time. I've looked at the outcome in the debugger, and the stdout is cut off half way through. If I don't try to capture the stdout, all the tests succeed every time, so I know that it's based around the stdout capture.
The timeout is specified, but it's a very generous 60 seconds- much longer than the tests take to run ordinarily. I spawn a new process for each test.
How can I capture stdout in a more reliable way, without getting random failures?
As a final note, it takes a long time to run the suite to capture the failure in the debugger, so it may take a while to service any requests for further information.
I have a theory about this, but I'm not wholly certain. The key is in the loop condition for reading the stdout of the process.
std::string result;
for (;;)
{
bSuccess = ReadFile(ReadHandle, chBuf, 1024, &dwRead, NULL);
if (!bSuccess) break;
result += std::string(chBuf, chBuf + dwRead);
if (dwRead < 1024)
break;
}
return result;
There's actually an implicit assumption here. ReadFile is a blocking API, so we would assume that it keeps blocking until it has the data we asked for or input has ended. But I hypothesize that in fact, ReadFile may return before it has a block as big as we asked for, even if the pipe has not terminated. This would cause the input reading loop to terminate.
Since the parent is no longer reading stdout, the child trying to write stdout may block waiting for somebody to clear the buffer- effectively a deadlock since nobody will. Therefore, the timeout will trigger and terminate the process, recording a failure.
The MSDN docs say this:
The ReadFile function returns when one of the following conditions occur:
The number of bytes requested is read.
A write operation completes on the write end of the pipe.
An asynchronous handle is being used and the read is occurring asynchronously.
An error occurs.
It does not say that it will return when a write operation completes and the number of bytes requested is available. In fact, it makes no comment about the write operation making as many bytes as you requested available. So effectively, it behaves semi-asynchronously, even when called synchronously.
I have rewritten the loop as follows:
std::string result;
for (;;)
{
bSuccess = ReadFile(ReadHandle, chBuf, 1024, &dwRead, NULL);
if (!bSuccess || dwRead == 0) break;
result += std::string(chBuf, chBuf + dwRead);
}
return result;
So far I have been unable to reproduce the failures with this loop (and the tests complete noticably faster).
Related
Hello I am trying to make a front end GUI for cmd.exe so I can make it wider but I got stuck.
I try to design an API like this
char* Directory = WriteCommand("dir");
printf("- %s\n", Directory);
and the output look exactly like it would in a cmd window, except I have it in a string, so it would be
DATE TIME FILESIZE FILENAME
etc etc etc
and then I can issue
char* Up = WriteCommand ("cd ..");
and it will give me the above directory listing. So I want a terminal control through using pipes to read and write.
I have tried many things based on this MSDN sample code - https://msdn.microsoft.com/en-us/library/ms682499.aspx
But I think this code is only good to issue one command, and read one response, because right after it deadlocks as described here - https://blogs.msdn.microsoft.com/oldnewthing/20110707-00/?p=10223
I see several other questions here, like this one with similar problems - How to read output from cmd.exe using CreateProcess() and CreatePipe() but no solutions posted work for me.
So here is my code.
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#define BUFSIZE 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;
HANDLE g_hInputFile = NULL;
void CreateChildProcess(void);
void WriteToPipe(char* Arg1);
void ReadFromPipe(void);
void ErrorExit(PTSTR);
int _tmain(int argc, TCHAR *argv[])
{
SECURITY_ATTRIBUTES saAttr;
printf("\n->Start of parent execution.\n");
// 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.
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
ErrorExit(TEXT("StdoutRd CreatePipe"));
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
ErrorExit(TEXT("Stdout SetHandleInformation"));
// Create a pipe for the child process's STDIN.
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit(TEXT("Stdin CreatePipe"));
// Ensure the write handle to the pipe for STDIN is not inherited.
if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
ErrorExit(TEXT("Stdin SetHandleInformation"));
// Create the child process.
CreateChildProcess();
// Get a handle to an input file for the parent.
// This example assumes a plain text file and uses string output to verify data flow.
/*if (argc == 1)
ErrorExit(TEXT("Please specify an input file.\n"));
g_hInputFile = CreateFile(
argv[1],
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL);
if (g_hInputFile == INVALID_HANDLE_VALUE)
ErrorExit(TEXT("CreateFile"));*/
// Write to the pipe that is the standard input for a child process.
// Data is written to the pipe's buffers, so it is not necessary to wait
// until the child process is running before writing data.
// Read from pipe that is the standard output for child process.
ReadFromPipe();
WriteToPipe("ipconfig");
// THIS IS WHERE DEADLOCK OCCURS, FROM HERE
// PROGRAM BECOMES UNRESPONSIVE - HOW TO FIX THIS?
ReadFromPipe();
printf("\n->End of parent execution.\n");
// The remaining open handles are cleaned up when this process terminates.
// To avoid resource leaks in a larger application, close handles explicitly.
return 0;
}
void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
TCHAR szCmdline[] = TEXT("cmd.exe /k");
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
// 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.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
"cmd.exe", // 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
// If an error occurs, exit the application.
if (!bSuccess)
ErrorExit(TEXT("CreateProcess"));
else
{
// Close handles to the child process and its primary thread.
// Some applications might keep these handles to monitor the status
// of the child process, for example.
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
}
}
void WriteToPipe(char* Command)
// Read from a file and write its contents to the pipe for the child's STDIN.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
bSuccess = WriteFile(g_hChildStd_IN_Wr, Command, strlen(Command), &dwWritten, NULL);
if (bSuccess == FALSE)
printf("write fail\n");
printf("written = %i\n", dwWritten);
//for (;;)
//{
//bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
//if (!bSuccess || dwRead == 0) break;
//bSuccess = WriteFile(g_hChildStd_IN_Wr, Command, strlen(Command), &dwWritten, NULL);
//if (bSuccess == FALSE)
//printf("write fail\n");
//printf("written = %i\n", dwWritten);
//}
// Close the pipe handle so the child process stops reading.
//if (!CloseHandle(g_hChildStd_IN_Wr))
//ErrorExit(TEXT("StdInWr CloseHandle"));
}
void ReadFromPipe(void)
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
int i;
for (i = 0; i < 4; i++)
{
/*DWORD dwAvail = 0;
if (!PeekNamedPipe(g_hChildStd_OUT_Rd, NULL, 0, NULL, &dwAvail, NULL)) {
// error, the child process might have ended
break;
}
if (!dwAvail) {
// no data available in the pipe
break;
}*/
bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if (!bSuccess || dwRead == 0) break;
/*bSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL);
if (!bSuccess) break;*/
chBuf[dwRead] = '\0';
printf("%i - %s\n", i, chBuf);
}
printf("done\n");
}
I issue the initial "cmd.exe" command which gives me the start of the command prompt. I now want to issue "ipconfig" (or any other command) to get networking info. The program deadlocks and becomes unresponsive. I can no longer read output of child process. How can I fix this? Thanks for your help.
the most power and effective solution for avoid any deadlocks - use asynchronous io. never wait for IO (read,write,ioctl) complete in place, but handle this in callbacks.
also note about use pipes for redirect output - very common errancy that we need use different handles for STDIN and STDOUT and need create 2 different pipes pair - one for STDIN and another for STDOUT. this is false. we can use single pipe handle for both STDIN and STDOUT (and STDERROR).
we need create server pipe handle by using CreateNamedPipeW with
PIPE_ACCESS_DUPLEX|FILE_READ_DATA|FILE_WRITE_DATA|FILE_FLAG_OVERLAPPED
flags. by using PIPE_ACCESS_DUPLEX we create bi-directional pipe,
as result both server and client processes can read from and write
to the pipe. and FILE_FLAG_OVERLAPPED give to as asynchronous
mode. also we not make this handle inheritable, so not need call
SetHandleInformation on it
client handle we create by CreateFileW also with
FILE_GENERIC_READ|FILE_GENERIC_WRITE access - this give ability
assign it both to stdin and stdout. because clients (like
cmd.exe) usually assume synchronous io - we not use
FILE_FLAG_OVERLAPPED here. also by using lpSecurityAttributes we
just make this handle inheritable.
we need bind server handle to some IOCP, for callback called when io
is ended. here we have 3 variants - use
BindIoCompletionCallback - the most simply way or use
CreateThreadpoolIo. also we can create IOCP yourself and own
thread pool, but for redirect child process output, this way usually
not need.
after we create child process - we need close client pipe handle
(which we duplicate to child) and just call ReadFile on our pipe
handle. when this ReadFile complete - we need again call
ReadFile from callback and so on - until we not got error from
ReadFile in completion (usually ERROR_BROKEN_PIPE). so we need
all time have active read request from pipe, until disconnect.
and we free call WriteFile at any time and any place - this never
cause deadlock, because we use asynchronous io.
some time (very very rarely) if we need complex processing on read
data(based on previous results and state) and this much more easy
handle in plain procedure but not in callbacks, we can create fiber
for this task (CreateFiber) and from working thread callback,
when read complete - first call ConvertThreadToFiber (if we
call this more than once for same working thread - will be error
ERROR_ALREADY_FIBER on second and next calls, but this is ok. but
all this work begin from vista only. on xp error here). remember
current fiber, to where need retirn (GetCurrentFiber()) and
call SwitchToFiber (with our dedicated for read fiber)- where
we can handle read result and after this return back by call
SwitchToFiber (with fiber for worked thread). but all this
really can be need in in very rare and specific scenarios. usually
handle all is callbacks with state in object related to pipe handle - more than enough.
simply example with cmd
#define _XP_SUPPORT_
struct IO_COUNT
{
HANDLE _hFile;
HANDLE _hEvent;
LONG _dwIoCount;
IO_COUNT()
{
_dwIoCount = 1;
_hEvent = 0;
}
~IO_COUNT()
{
if (_hEvent)
{
CloseHandle(_hEvent);
}
}
ULONG Create(HANDLE hFile);
void BeginIo()
{
InterlockedIncrement(&_dwIoCount);
}
void EndIo()
{
if (!InterlockedDecrement(&_dwIoCount))
{
SetEvent(_hEvent);
}
}
void Wait()
{
WaitForSingleObject(_hEvent, INFINITE);
}
};
struct U_IRP : OVERLAPPED
{
enum { read, write };
IO_COUNT* _pIoObject;
ULONG _code;
LONG _dwRef;
char _buffer[256];
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef)) delete this;
}
U_IRP(IO_COUNT* pIoObject) : _pIoObject(pIoObject)
{
_dwRef = 1;
pIoObject->BeginIo();
RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
}
~U_IRP()
{
_pIoObject->EndIo();
}
ULONG CheckIoResult(BOOL fOk)
{
if (fOk)
{
#ifndef _XP_SUPPORT_
OnIoComplete(NOERROR, InternalHigh);
#endif
return NOERROR;
}
ULONG dwErrorCode = GetLastError();
if (dwErrorCode != ERROR_IO_PENDING)
{
OnIoComplete(dwErrorCode, 0);
}
return dwErrorCode;
}
ULONG Read()
{
_code = read;
AddRef();
return CheckIoResult(ReadFile(_pIoObject->_hFile, _buffer, sizeof(_buffer), 0, this));
}
ULONG Write(const void* pvBuffer, ULONG cbBuffer)
{
_code = write;
AddRef();
return CheckIoResult(WriteFile(_pIoObject->_hFile, pvBuffer, cbBuffer, 0, this));
}
VOID OnIoComplete(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered)
{
switch (_code)
{
case read:
if (dwErrorCode == NOERROR)
{
if (dwNumberOfBytesTransfered)
{
if (int cchWideChar = MultiByteToWideChar(CP_OEMCP, 0, _buffer, (ULONG)dwNumberOfBytesTransfered, 0, 0))
{
PWSTR wz = (PWSTR)alloca(cchWideChar * sizeof(WCHAR));
if (MultiByteToWideChar(CP_OEMCP, 0, _buffer, (ULONG)dwNumberOfBytesTransfered, wz, cchWideChar))
{
if (int cbMultiByte = WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, 0, 0, 0, 0))
{
PSTR sz = (PSTR)alloca(cbMultiByte);
if (WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, sz, cbMultiByte, 0, 0))
{
DbgPrint("%.*s", cbMultiByte, sz);
}
}
}
}
}
Read();
}
break;
case write:
break;
default:
__debugbreak();
}
Release();
if (dwErrorCode)
{
DbgPrint("[%u]: error=%u\n", _code, dwErrorCode);
}
}
static VOID WINAPI _OnIoComplete(
DWORD dwErrorCode,
DWORD_PTR dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped
)
{
static_cast<U_IRP*>(lpOverlapped)->OnIoComplete(RtlNtStatusToDosError(dwErrorCode), dwNumberOfBytesTransfered);
}
};
ULONG IO_COUNT::Create(HANDLE hFile)
{
_hFile = hFile;
// error in declaration LPOVERLAPPED_COMPLETION_ROUTINE :
// second parameter must be DWORD_PTR but not DWORD
return BindIoCompletionCallback(hFile, (LPOVERLAPPED_COMPLETION_ROUTINE)U_IRP::_OnIoComplete, 0) &&
#ifndef _XP_SUPPORT_
SetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) &&
#endif
(_hEvent = CreateEvent(0, TRUE, FALSE, 0)) ? NOERROR : GetLastError();
}
void ChildTest()
{
static const WCHAR name[] = L"\\\\?\\pipe\\somename";
HANDLE hFile = CreateNamedPipeW(name,
PIPE_ACCESS_DUPLEX|FILE_READ_DATA|FILE_WRITE_DATA|FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, 1, 0, 0, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
IO_COUNT obj;
if (obj.Create(hFile) == NOERROR)
{
BOOL fOk = FALSE;
SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi;
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdError = CreateFileW(name, FILE_GENERIC_READ|FILE_GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, 0);
if (si.hStdError != INVALID_HANDLE_VALUE)
{
si.hStdInput = si.hStdOutput = si.hStdError;
WCHAR ApplicationName[MAX_PATH];
if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
{
if (CreateProcessW(ApplicationName, 0, 0, 0, TRUE, 0, 0, 0, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
fOk = TRUE;
}
}
CloseHandle(si.hStdError);
}
if (fOk)
{
STATIC_ASTRING(help_and_exit, "help\r\nexit\r\n");
U_IRP* p;
if (p = new U_IRP(&obj))
{
p->Read();
p->Release();
}
obj.EndIo();
//++ simulate user commands
static PCSTR commands[] = { "help\r\n", "ver\r\n", "dir\r\n", "exit\r\n" };
ULONG n = RTL_NUMBER_OF(commands);
PCSTR* psz = commands;
do
{
if (MessageBoxW(0,0, L"force close ?", MB_YESNO) == IDYES)
{
DisconnectNamedPipe(hFile);
break;
}
if (p = new U_IRP(&obj))
{
PCSTR command = *psz++;
p->Write(command, (ULONG)strlen(command) * sizeof(CHAR));
p->Release();
}
} while (--n);
//--
obj.Wait();
}
}
CloseHandle(hFile);
}
}
I know is it a bit old so you probably won't need this answer anymore. But for those who came to StackOverflow for a solution for the same problem, I faced the same problem when building a similar project and I found a solution.
Basically, just add "\n" newline character to the end of the command. This is needed to simulate the "ENTER" button is pressed. Otherwise, WriteFile() works but ReadFile() is still waiting because the command was never executed in child process cmd.exe hence there is nothing for ReadFile() to read, causing it to hang there.
So the modified code is (I didn't test run the following code but is just modified based on the example the original author posted):
void WriteToPipe(char* Command)
// Read from a file and write its contents to the pipe for the child's STDIN.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
// Fix for the issue
strcat_s(command, strlen(command) + 1, "\n", 1);
bSuccess = WriteFile(g_hChildStd_IN_Wr, Command, strlen(Command), &dwWritten, NULL);
if (bSuccess == FALSE)
printf("write fail\n");
printf("written = %i\n", dwWritten);
//for (;;)
//{
//bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
//if (!bSuccess || dwRead == 0) break;
//bSuccess = WriteFile(g_hChildStd_IN_Wr, Command, strlen(Command), &dwWritten, NULL);
//if (bSuccess == FALSE)
//printf("write fail\n");
//printf("written = %i\n", dwWritten);
//}
// Close the pipe handle so the child process stops reading.
//if (!CloseHandle(g_hChildStd_IN_Wr))
//ErrorExit(TEXT("StdInWr CloseHandle"));
}
I'm stuck on this since days, i'm goind crazy.
Basically i'm trying to open cmd.exe in a thread and give it input and read output from it, from the parent. Like, assigning a tty in linux, since there's no such thing in windows. I have a good understanding of linux systems but can't say the same thing about windows.
So, here's "my" code:
#undef UNICODE
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
//using namespace std;
#define BUFFER_SIZE 99
// handles for cmd thread pipes
HANDLE cmd_in_rd = NULL;
HANDLE cmd_in_wr = NULL;
HANDLE cmd_out_rd = NULL;
HANDLE cmd_out_wr = NULL;
HANDLE cmd_thread_handle;
void PrintError(char *text, int err) {
DWORD retSize;
LPTSTR pTemp = NULL;
if (!err) return;
retSize = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
NULL,
err,
LANG_NEUTRAL,
(LPTSTR)&pTemp,
0,
NULL);
if (pTemp) printf("%s: %s\n", text, pTemp);
LocalFree((HLOCAL)pTemp);
return;
}
int pipewrite(char *command) {
DWORD dwRead, dwWritten;
BOOL bSuccess = FALSE;
SetLastError(0);
WriteFile(cmd_in_wr, command, strlen(command), &dwWritten, NULL);
bSuccess = GetLastError();
PrintError("WriteToPipe", bSuccess);
return (bSuccess == 0) || (bSuccess == ERROR_IO_PENDING);
}
int __stdcall cmd_thread(int arg) {
// this function only prints when data is ready
DWORD dwRead, dwWritten;
CHAR chBuf[BUFFER_SIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
int rf_ret, wf_ret;
//CloseHandle(cmd_out_wr); makes readfile fail!!
SetLastError(0);
while (1) { // only executes once!!!!!!!
(rf_ret = ReadFile(cmd_out_rd, chBuf, BUFFER_SIZE, &dwRead, NULL))
&&
(wf_ret = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL));
printf("ReadFile returned: %d\nWriteFile returned: %d\n", rf_ret, wf_ret);
bSuccess = GetLastError();
PrintError("ReadingFromPipe", bSuccess);
}
bSuccess = GetLastError();
return (bSuccess == 0) || (bSuccess == ERROR_IO_PENDING);
}
int main(void) {
char buffer[BUFFER_SIZE];
// init the pipes
SECURITY_ATTRIBUTES cmd_sa;
cmd_sa.nLength = sizeof(SECURITY_ATTRIBUTES);
cmd_sa.bInheritHandle = TRUE;
cmd_sa.lpSecurityDescriptor = NULL;
if (!CreatePipe(&cmd_out_rd, &cmd_out_wr, &cmd_sa, 0)) {
printf("%s\n", "Error creating pipes");
return 1;
}
if (!SetHandleInformation(cmd_out_rd, HANDLE_FLAG_INHERIT, 0)) {
printf("%s\n", "Error setting handle infos");
return 1;
}
if (!CreatePipe(&cmd_in_rd, &cmd_in_wr, &cmd_sa, 0)) {
printf("%s\n", "Error creating pipes");
return 1;
}
if (!SetHandleInformation(cmd_in_rd, HANDLE_FLAG_INHERIT, 0)) {
printf("%s\n", "Error setting handle infos");
return 1;
}
// create the cmd thread
PROCESS_INFORMATION cmd_pi;
STARTUPINFO cmd_si;
ZeroMemory(&cmd_pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&cmd_si, sizeof(STARTUPINFO));
cmd_si.cb = sizeof(STARTUPINFO);
cmd_si.hStdError = cmd_out_wr;
cmd_si.hStdOutput = cmd_out_wr;
cmd_si.hStdInput = cmd_in_rd;
cmd_si.dwFlags |= STARTF_USESTDHANDLES;
TCHAR comm[] = TEXT("cmd.exe");
BOOL th = CreateProcess(NULL,
comm,
NULL,
NULL,
TRUE, // handles are inherited
0,
NULL,
NULL,
&cmd_si,
&cmd_pi);
if (th) {
CloseHandle(cmd_pi.hProcess);
CloseHandle(cmd_pi.hThread);
}
cmd_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)cmd_thread, NULL, 0, NULL);
// read commands from shell and send them to cmd
ZeroMemory(&buffer, BUFFER_SIZE);
while (1) {
fgets(buffer, BUFFER_SIZE, stdin);
if (!pipewrite(buffer)) break;
}
printf("Program terminated\n");
return 0;
}
I actually, for testing purposes, copied a lot from another question on stackoverflow and from MSDN since i couldn't get it to work on my main program. The things I don't understand are:
Why the while loop inside cmd_thread gets executed at startup and then hangs there waiting for the end of the world? I tried to close the pipe out_write handle from the parent before reading, but that makes other parts not working.
pipewrite() seems to work, but I can't be sure that the cmd.exe thread receives and works the input... Since i get no output :/
I thought about stracing/ltracing the program or running it into a debugger, but I know no tool for that...
The strange thing is that the original works (the one from where i got the code). I tried to spot the difference between the two, but even when I look to them side by side, they seem to do the exact same things.
The child process is dying as soon as it attempts to read from standard input, because:
if (!SetHandleInformation(cmd_in_rd, HANDLE_FLAG_INHERIT, 0)) {
This should have been:
if (!SetHandleInformation(cmd_in_wr, HANDLE_FLAG_INHERIT, 0)) {
like in the original code.
Also, your error handling is largely incorrect; you don't consistently check for errors and you sometimes call GetLastError() when no error has occurred. (Those problems are in the original code too.)
You also need to put the call to CloseHandle(cmd_out_wr); back in because otherwise you won't be able to tell when the child exits.
Oh, and incidentally, cmd.exe is a process, not a thread.
I want to get the live output of a running process. Therefore I have created in ExecuteCommand() pipes for stdout, stdin and stderr. Afterwards I have started a process and send a QTimer::singleShot() executing CheckOutput() one second later. Here the Code:
bool CommandExecutor_C::ExecuteCommand(QString &aCommand)
{
aCommand.push_front(std::getenv("ComSpec") + QString(" /c "));
mCommand = aCommand;
LOGINFO(FB_TDI,"Launch command: " + aCommand.toStdString());
SECURITY_ATTRIBUTES secattr;
ZeroMemory(&secattr,sizeof(secattr));
secattr.nLength = sizeof(secattr);
secattr.bInheritHandle = TRUE;
//in/out/err Handle
HANDLE StdInHandleRd, StdInHandleWr;//[0] read -> [1] write
HANDLE StdOutHandleRd, StdOutHandleWr;
HANDLE StdErrHandleRd, StdErrHandleWr;
//creating pipes to have access to handle contents
CreatePipe(&StdInHandleRd , &StdInHandleWr , &secattr, 4096);
CreatePipe(&StdOutHandleRd, &StdOutHandleWr , &secattr, 4096);
CreatePipe(&StdErrHandleRd, &StdErrHandleWr , &secattr, 4096);
//information for process
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdInput = StdInHandleRd; /**< read handle/pipe */
si.hStdOutput = StdOutHandleWr; /**< write handle/pipe */
si.hStdError = StdErrHandleWr; /**< write handle/pipe */
mHandles.StdOutHandle = StdOutHandleRd;
mHandles.StdErrHandle = StdErrHandleRd;
LPWSTR cmdTmp = reinterpret_cast<LPWSTR>(aCommand.data());
//creates process
bool res = CreateProcess(NULL,
cmdTmp, //command casted to LPWSTR
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
NORMAL_PRIORITY_CLASS|CREATE_NO_WINDOW,
NULL, // use parent's environment
NULL, // use parent's current directory
&si, // STARTUPINFO pointer
&pi); // receives PROCESS_INFORMATION
if(!res) //not successfully created
{
LOGERROR(FB_TDI,"Failed to create Process for cmd: " + aCommand.toStdString() + boost::lexical_cast<std::string>(GetLastError()));
CloseHandle(StdInHandleRd);
CloseHandle(StdInHandleWr);
CloseHandle(StdOutHandleRd);
CloseHandle(StdOutHandleWr);
CloseHandle(StdErrHandleRd);
CloseHandle(StdErrHandleWr);
return false; //failed
}
else
{
DWORD bytesWritten;
//wait for 1 second
QTimer::singleShot(1000, this, SLOT(CheckOutput()));
}
return true;
}
void CommandExecutor_C::CheckOutput()
{
QString StdOut;
QString StdErr;
//Logs Process output
LogProcessOutput(mHandles.StdOutHandle, StdOut);
//Logs Process error
LogProcessOutput(mHandles.StdErrHandle, StdErr);
mProcessStatus = CheckTdiAutomationInterface(StdOut.toStdString(), StdErr.toStdString());
if(mProcessStatus == AI_UNKNOWN)
{
//check output again 1 second later
QTimer::singleShot(1000, this, SLOT(CheckOutput()));
}
else
{
OnTdiActive(mProcessStatus);
}
}
Hint::The function CheckTdiAutomationInterface() does just compare a given string with some other defined strings and returns afterwards the Process Status.
But the problem seems to appear in LogProcessOutput(). The function gets called but PeekNamedPipe() does always return 0 bytes available. Only if the process has been finished bytes are available. Therefore it seems like the process output gets buffered and send to stdout only if the process has finished. Here the Code:
void CommandExecutor_C::LogProcessOutput(HANDLE &aReadOutHandle, QString &aProcessOutput)
{
//Copy data from pipe
DWORD bytesAvailable = 0;
if(!PeekNamedPipe(aReadOutHandle,NULL,0,NULL,&bytesAvailable,NULL))
{
LOGERROR(FB_TDI,"Failed to call PeekNamedPipe to print process output");
}
if(bytesAvailable)
{
DWORD dwRead;
char chBuf[BUFSIZ];
bool bSuccess = FALSE;
std::string out;
do
{
//blocks process and waits for more data, this cause errors because no more data is produced
//thats why PeekNamedPipe should look before if data is available
bSuccess=::ReadFile( aReadOutHandle, chBuf, BUFSIZ, &dwRead, NULL);
std::string s(chBuf, dwRead);
out += s;
//1. when the last read was successful
//2. when there is more data available then the bufsize size
//3. when the read bytes are not less then the BUFSIZE > would mean no more data..
}
while( bSuccess && (bytesAvailable>BUFSIZ) && !(dwRead<BUFSIZ) );
LOGINFO(FB_TDI,"OUTHANDLE:\n"+out);
aProcessOutput = QString::fromStdString(out);
}
else
{
aProcessOutput = "";
}
}
Edit: I want to get the Output live while the Process is running. If the Process has been finished everything works fine and the output can be read out.
I have a project which is a compiler. I have an integration test suite for my compiler that compiles the sample source into an object, invokes ld to link it, then invokes the executable and checks the result. Each of these three steps is a fresh process separate from the test driver.
Unfortunately I am seeing random test failures because for some reason, when I come to link, the previous test has not finished running, even though I explicitly wait on termination of each process before beginning the next step. Thus ld fails as it cannot write out the new executable.
I could fix this problem by running each test in a new directory or giving the temp files unique names, but I don't want to do that since this approach should work and I would just be papering over the problem that I can't wait on a process to terminate properly.
Here's my code for starting and waiting for the process:
#include <Windows.h>
#include <iostream>
#include <thread>
class Pipe {
HANDLE ReadHandle;
HANDLE writehandle;
public:
Pipe() {
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
CreatePipe(&ReadHandle, &writehandle, &saAttr, 0);
}
HANDLE WriteHandle() {
return writehandle;
}
std::string Contents() {
CloseHandle(writehandle);
DWORD dwRead;
CHAR chBuf[1024];
BOOL bSuccess = FALSE;
std::string result;
for (;;)
{
bSuccess = ReadFile(ReadHandle, chBuf, 1024, &dwRead, NULL);
if (!bSuccess || dwRead == 0) break;
result += std::string(chBuf, chBuf + dwRead);
}
return result;
}
~Pipe() {
CloseHandle(ReadHandle);
}
};
Wide::Driver::ProcessResult Wide::Driver::StartAndWaitForProcess(std::string name, std::vector<std::string> args, Util::optional<unsigned> timeout)
{
auto throw_last_err = [] {
DWORD dw = GetLastError();
const char* message;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&message, 0, nullptr);
std::string err = message;
LocalFree((void*)message);
throw std::runtime_error(err);
};
ProcessResult result;
Pipe stdoutpipe;
Pipe stderrpipe;
PROCESS_INFORMATION info = { 0 };
STARTUPINFO startinfo = { sizeof(STARTUPINFO) };
std::string final_args = name;
for (auto arg : args)
final_args += " " + arg;
startinfo.hStdOutput = stdoutpipe.WriteHandle();
startinfo.hStdError = stderrpipe.WriteHandle();
startinfo.hStdInput = INVALID_HANDLE_VALUE;
startinfo.dwFlags |= STARTF_USESTDHANDLES;
auto proc = CreateProcess(
name.c_str(),
&final_args[0],
nullptr,
nullptr,
TRUE,
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
nullptr,
nullptr,
&startinfo,
&info
);
if (!proc) {
throw_last_err();
}
if (timeout == 0)
timeout = INFINITE;
std::thread writethread([&] {
result.std_out = stdoutpipe.Contents();
});
std::thread errthread([&] {
result.std_err = stderrpipe.Contents();
});
auto waiterr = WaitForSingleObject(info.hProcess, timeout ? *timeout : INFINITE);
if (waiterr == WAIT_TIMEOUT) {
TerminateProcess(info.hProcess, 1);
waiterr = WaitForSingleObject(info.hProcess, timeout ? *timeout : INFINITE);
if (waiterr != WAIT_OBJECT_0) {
throw_last_err();
}
} else if (waiterr != WAIT_OBJECT_0) {
throw_last_err();
}
writethread.join();
errthread.join();
DWORD exit_code;
GetExitCodeProcess(info.hProcess, &exit_code);
CloseHandle(info.hProcess);
CloseHandle(info.hThread);
result.exitcode = exit_code;
if (exit_code != 0)
return result;
return result;
}
throw_last_err() is never called, so everything went fine, supposedly.
Why can I not wait on the process?
According to the comments, various Windows components like Search can hold locks on random files. This implies to me that in general, I can't assume that the file won't be locked and that therefore I should not assume that I can re-use it immediately.
Therefore I've decided that I will simply not re-use the intermediate files.
I've a simply process redirection routine in Win32. The problem here is that, if I put a Sleep between reads from the child process stdout, as soon as the process terminates while I sleep, I simply miss the last bytes from the pipe that outputs a ERROR_BROKEN_PIPE. It seems that , as soon as the child process terminates, it's pipes and associated handles are closed and anything pending discarded. The only solution seems to ReadFile from the pipe as fast as possible, but this is more than a problem for me due to the design of the software.
int _tmain(int argc, _TCHAR* argv[])
{
BOOL bSuccess;
WCHAR szCmdLine[MAX_PATH];
char chBuf[BUFSIZE];
DWORD dwRead;
HANDLE g_hChildStd_OUT_Rd2 = NULL;
HANDLE g_hChildStd_OUT_Wr2 = NULL;
SECURITY_ATTRIBUTES saAttr2;
STARTUPINFO si2;
PROCESS_INFORMATION pi2;
ZeroMemory( &si2, sizeof(si2) );
si2.cb = sizeof(si2);
ZeroMemory( &pi2, sizeof(pi2) );
//create pipe
saAttr2.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr2.bInheritHandle = TRUE;
saAttr2.lpSecurityDescriptor = NULL;
assert(CreatePipe(&g_hChildStd_OUT_Rd2, &g_hChildStd_OUT_Wr2, &saAttr2, 0));
//create child process
bSuccess = FALSE;
memset(szCmdLine, 0, MAX_PATH);
wsprintf(szCmdLine, L"c:\\myprocess.exe");
ZeroMemory( &pi2, sizeof(PROCESS_INFORMATION) );
ZeroMemory( &si2, sizeof(STARTUPINFO) );
si2.cb = sizeof(STARTUPINFO);
si2.hStdOutput = g_hChildStd_OUT_Wr2;
si2.hStdError = g_hChildStd_OUT_Wr2; // also add the pipe as stderr!
si2.dwFlags |= STARTF_USESTDHANDLES;
assert(CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si2, &pi2));
//read from pipe
CloseHandle(g_hChildStd_OUT_Wr2);
memset(chBuf, 0, BUFSIZE);
for (;;)
{
DWORD dwRead = 0;
DWORD bytes = 0;
if (!PeekNamedPipe(g_hChildStd_OUT_Rd2,NULL,NULL,NULL,&bytes,NULL)) {
//printf("Peek named pipe failed!");
break;
}
if (bytes != 0) {
if (!ReadFile( g_hChildStd_OUT_Rd2, chBuf, BUFSIZE, &dwRead, NULL))
{
printf("EOF!!\n");
break;
}
else {
chBuf[dwRead] = 0;
printf("%s", chBuf);
}
} else {
Sleep(5000);
}
}
while(1) {
Sleep(1000);
printf("Lopp!\n");
}
return 0;
}
Any hint ? Is there a way to keep the process on hold, like it happens in POSIX, until its pipes are read ?
Thanks!
I think the problem is not there.
You do have some problems in your code. Many are minors : you initialize twice si2 and pi2 (harmless), you have an endless loop so your prog will never end (I assume you Ctrl-C ...), you are mixing _tmain and WCHAR (should be either wmain and WCHAR or _tmain and TCHAR) but if you do an unicode build it is fine.
One is more serious : chBuf has size BUFSIZE, you read upto BUFSIZE chars in it (fine till here). But if you do read BUFSIZE chars, dwRead is BUFSIZE and on next line you have a buffer overflow
chBuf[dwRead] = 0; // buffer overflow if BUFSIZE chars have been read !
I only fixed that with :
if (!ReadFile( g_hChildStd_OUT_Rd2, chBuf, BUFSIZE - 1, &dwRead, NULL))
and the program works correctly.
BTW : the child process is knows by the system (even if terminated) until all handles to it have been closed ... and you have a handle to it in pi2.hProcess.
Of course I could not use c:\\myprocess.exe and tested with cmd /c dir. So if the above fix is not enough try to use same child as I did because the problem could come from myprocess.exe that is started with a NULL hStdInput
Ok for reference only, I would like to share my experience and how I solved the problem.
IT may be possible that there's something wrong in the child process I spawn, but from what I see, if I call Read in non-blocking mode, so only after a PeekNamedPipe, there's nothing that prevent the process from being deallocated, even I keep a reference to the pipe.
I've solved launching another thread that does blocking Read on the pipe descriptor, and I'm longer loosing the last bytes..