How do you write to a second console? - c++

I had the idea of using a second console in my programs for the purpose of logging programming activity. I looked around on msdn for related functions/examples and tried to put together a simple program to do so:
//function in parent that communicates with child
void writeToChild()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
HANDLE inWrite, inRead,
outWrite, outRead;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&inRead, &inWrite, &saAttr, 0))
{
exit(0xbad);
}
if ( ! SetHandleInformation(inRead, HANDLE_FLAG_INHERIT, 0) )
{
exit(0xbad);
}
if (!CreatePipe(&outRead, &outWrite, &saAttr, 0))
{
exit(0xbad);
}
if ( ! SetHandleInformation(outRead, HANDLE_FLAG_INHERIT, 0) )
{
exit(0xbad);
}
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
si.lpTitle = "Log";
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdOutput = outWrite;
si.hStdError = outWrite;
si.hStdInput = inRead;
ZeroMemory( &pi, sizeof(pi) );
// Start the child process.
if( !CreateProcess( NULL,
"Logger",
NULL,
NULL,
TRUE,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&si,
&pi )
)
{
printf( "CreateProcess failed (%d).\n", GetLastError() );
exit(0xbad);
}
unsigned long numWritten = 0;
WriteFile(inWrite, "Test", 4, &numWritten, NULL);
cout<<"wrote "<<numWritten<<" characters"<<endl;
// Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}
//Logger
#include <windows.h>
#define BUFSIZE 4096
int main()
{
HANDLE stdin, stdout;
stdin = GetStdHandle(STD_INPUT_HANDLE);
stdout = GetStdHandle(STD_OUTPUT_HANDLE);
char buffer[BUFSIZE];
unsigned long numRead;
while (true)
{
ReadFile(stdin, buffer, BUFSIZE, &numRead, NULL);
if (numRead > 0)
WriteFile(stdout, buffer, numRead, NULL, NULL);
}
return 0;
}
The problem is that while the parent displays that 4 characters were written, nothing appears on the child console. I'm not sure how to go about debugging this because I have next to no experience with windows programming.

I am not quite sure where you are going with the separate process in your code snippet. However, you can't have more than one console associated with a single process in Windows, although you can have multiple screen buffers associated with a single console and toggle between them (see SetConsoleActiveScreenBuffer and go from there), but you'll have to implement a user-interface for that toggling; it's not a built-in thing.
If the screen buffers do not work for you, you could have a second logger process that you communicate with via pipes or loopback sockets or some other IPC method, though, e.g.:
You can use syslog, which is a common logging facility used by various applications and hardware.
You can also write to a log file and use a program to watch the file, e.g. the Windows equivalent of tail -f. This has the bonus of storing the logged data in a file for easy review later.
You could have your application act as a TCP server, telnet to it, and dump log messages via telnet.
Note that for any of the above options, as a convenience to your user, you can have your application start the second log watching process separately (with just a simple call to ShellExecute, don't go overboard).
Other options include:
You could use the Windows event log, although it can be a bit cumbersome.
You could create a separate GUI window with a text field or a list in it that displays log messages.
If your program is a GUI application that doesn't have its own console (from your description, this does not seem to be the case, but just for completeness) you can create a single console for it with AllocConsole and friends (but note that closing that console while it is still attached will also terminate your application).
There are many choices here. But you can't have two consoles for the same process.

Here is a summary of how I've managed to do it:
//parent process
YourString pipeName = format(L"\\\\.\\pipe\\%s", pName);
HANDLE hPipe = CreateNamedPipe(pipeName, /*dwOpenMode*/PIPE_ACCESS_OUTBOUND,
/*dwPipeMode*/PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS,
/*nMaxInstances*/PIPE_UNLIMITED_INSTANCES,
/*nOutBufferSize*/10000, /*nInBufferSize*/10000, /*nDefaultTimeOut*/50,
/*lpSecurityAttributes*/NULL);
//[check return value]
PROCESS_INFORMATION pi = {};
STARTUPINFOW si = {};
YourString commandLine = format(L"\"%s\" \"%s\"", childProcessExeFileName, pipeName);
HANDLE hProcess = CreateProcess(
/*lpApplicationName*/NULL, /*lpCommandLine*/commandLine,
/*lpProcessAttributes*/NULL, /*lpThreadAttributes*/NULL,
/*bInheritHandles*/TRUE, /*dwCreationFlags*/CREATE_NEW_CONSOLE,
/*lpEnvironment*/NULL, /*lpCurrentDirectory*/NULL,
&si, &pi);
//[check return value]
lpBuffer = ...
nNumberOfBytesToWrite = len(lpBuffer);
WriteFile(hPipe, lpBuffer, nNumberOfBytesToWrite, /*lpNumberOfBytesWritten*/NULL, /*lpOverlapped*/NULL);
//child process
int main(int argc, char ** argv) {
HANDLE hPipe = CreateFile(argv[1], GENERIC_READ, 0,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
//[check return value]
ULONG PID;
BOOL ok = GetNamedPipeServerProcessId(hPipe, &PID);
//[check return value]
HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, PID);
//[check return value]
char buffer[1000];
DWORD nByteRead;
while (WaitForSingleObject(hProcess, 0) == WAIT_TIMEOUT) {
if (hPipe) {
ok = ReadFile(hPipe, buffer,
sizeof(buffer) - 1, &nByteRead, NULL);
buffer[nByteRead] = 0;
if (!ok) {
if (GetLastError() != ERROR_BROKEN_PIPE) ERROR();
hPipe = NULL;
}
else printf("%s", buffer);
}
else Sleep(1000);
}
return 1;
}
If you are interested I could send you a class that uses this, with a simple syntax:
WinConsole secondConsole;
secondConsole.Printf("Hello %s\n", "World");
This let you have as many consoles as you want.

Related

Communicating with PuTTY via pipe

I want to capture PuTTY's STDIN and STDOUT pipes. So I open putty.exe via CreateProcess with passing created pipes but I can't read anything from pipes. My code works when I run a bat file.
How can I accomplish this?
Thanks
//This code is from MSDN and I edited it a little
#include <QCoreApplication>
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#pragma comment(lib, "user32.lib")
#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(void);
void ReadFromPipe(void);
void ErrorExit(PTSTR);
int main(int argc, char *argv[])
{
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.
if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )
ErrorExit((PTSTR)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((PTSTR)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((PTSTR)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((PTSTR)TEXT("Stdin SetHandleInformation"));
// Create the child process: putty
CreateChildProcess();
// read from pipe and printf incoming data
ReadFromPipe();
}
// 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.
void ReadFromPipe(void)
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
for (;;)
{
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead == 0 )
break;
printf("received hex:\n");
for (int i = 0; i < dwRead; i++)
{
printf("%02X", chBuf[i]);
}
printf("\nactual string: %s\n",chBuf);
}
}
void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
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(L"E:\\putty.exe",
NULL, // 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((PTSTR)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);
}
}
// Format a readable error message, display a message box,
// and exit from the application.
void ErrorExit(PTSTR lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
PuTTY is a GUI application. You cannot communicate with it using input/output redirection.
Use plink.exe from PuTTY package. It's a console application with the same set of command-line arguments and features as PuTTY.
Though even better, using some C++ SSH library instead.
I found out that PuTTY doesn't have pipe for this purpose and since I didn't want to use plink, I modified PuTTY's source code and embedded pipe in it. Now it works as I want.

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

How to read output from cmd.exe using CreateProcess() and CreatePipe()
I have been trying to create a child process executing cmd.exe with a command-line designating /K dir. The purpose is to read the output from the command back into the parent process using pipes.
I've already got CreateProcess() working, however the step involving pipes are causing me trouble. Using pipes, the new console window is not displaying (like it was before), and the parent process is stuck in the call to ReadFile().
Does anyone have an idea of what I'm doing wrong?
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#define BUFFSZ 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;
int wmain(int argc, wchar_t* argv[])
{
int result;
wchar_t aCmd[BUFFSZ] = TEXT("/K dir"); // CMD /?
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
printf("Starting...\n");
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
// Create one-way pipe for child process STDOUT
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0)) {
printf("CreatePipe() error: %ld\n", GetLastError());
}
// Ensure read handle to pipe for STDOUT is not inherited
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) {
printf("SetHandleInformation() error: %ld\n", GetLastError());
}
// Create one-way pipe for child process STDIN
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &sa, 0)) {
printf("CreatePipe() error: %ld\n", GetLastError());
}
// Ensure write handle to pipe for STDIN is not inherited
if (!SetHandleInformation(g_hChildStd_IN_Rd, HANDLE_FLAG_INHERIT, 0)) {
printf("SetHandleInformation() error: %ld\n", GetLastError());
}
si.cb = sizeof(STARTUPINFO);
si.hStdError = g_hChildStd_OUT_Wr;
si.hStdOutput = g_hChildStd_OUT_Wr;
si.hStdInput = g_hChildStd_IN_Rd;
si.dwFlags |= STARTF_USESTDHANDLES;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
// Pipe handles are inherited
sa.bInheritHandle = true;
// Creates a child process
result = CreateProcess(
TEXT("C:\\Windows\\System32\\cmd.exe"), // Module
aCmd, // Command-line
NULL, // Process security attributes
NULL, // Primary thread security attributes
true, // Handles are inherited
CREATE_NEW_CONSOLE, // Creation flags
NULL, // Environment (use parent)
NULL, // Current directory (use parent)
&si, // STARTUPINFO pointer
&pi // PROCESS_INFORMATION pointer
);
if (result) {
printf("Child process has been created...\n");
}
else {
printf("Child process could not be created\n");
}
bool bStatus;
CHAR aBuf[BUFFSZ + 1];
DWORD dwRead;
DWORD dwWrite;
// GetStdHandle(STD_OUTPUT_HANDLE)
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);
}
// Wait until child process exits
WaitForSingleObject(pi.hProcess, INFINITE);
// Close process and thread handles
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
printf("Stopping...\n");
return 0;
}
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
g_hChildStd_IN_Wr
g_hChildStd_OUT_Rd
two of them are the child's end of the pipe
g_hChildStd_IN_Rd
g_hChildStd_OUT_Wr
 
╔══════════════════╗ ╔══════════════════╗
║ 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: g_hChildStd_IN_Wr
readable end of the child output pipe: g_hChildStd_OUT_Rd
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);
//Bonus chatter: 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.
// That's why you should call ShellExecute - it closes them for you.
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;
Waiting on the Child Process (aka the deadlock waiting to happen)
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 (i.e. Blocks) for the pipe to have some room.
you're blocked waiting on the child
the child attempts to write to the pipe
you're blocked waiting on the child, so you're not reading data out of the pipe
the pipe becomes full
the child blocks waiting on you
both parent and child are blocked waiting on the other
deadlock
As a result the child process will never terminate; you've deadlocked everything.
The Right Approach - let the client do it's thing
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.
 
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);
All without a dangerous MsgWaitForSingleObject - which is error-prone, difficult to use correctly, and causes the very bug you want to avoid.
Complete Example
We all know what we are using this for: run a child process, and capture it's console output.
Here is some sample Delphi code:
function ExecuteAndCaptureOutput(CommandLine: string): string;
var
securityAttributes: TSecurityAttributes;
stdOutRead, stdOutWrite: THandle;
startupInfo: TStartupInfo;
pi: TProcessInformation;
buffer: AnsiString;
bytesRead: DWORD;
bRes: Boolean;
le: DWORD;
begin
{
Execute a child process, and capture it's command line output.
}
Result := '';
securityAttributes.nlength := SizeOf(TSecurityAttributes);
securityAttributes.bInheritHandle := True;
securityAttributes.lpSecurityDescriptor := nil;
if not CreatePipe({var}stdOutRead, {var}stdOutWrite, #securityAttributes, 0) then
RaiseLastOSError;
try
// Set up members of the STARTUPINFO structure.
startupInfo := Default(TStartupInfo);
startupInfo.cb := SizeOf(startupInfo);
// This structure specifies the STDIN and STDOUT handles for redirection.
startupInfo.dwFlags := startupInfo.dwFlags or STARTF_USESTDHANDLES; //The hStdInput, hStdOutput, and hStdError handles will be valid.
startupInfo.hStdInput := GetStdHandle(STD_INPUT_HANDLE); //don't forget to make it valid (zero is not valid)
startupInfo.hStdOutput := stdOutWrite; //give the console app the writable end of the pipe
startupInfo.hStdError := stdOutWrite; //give the console app the writable end of the pipe
// We also want the console window to be hidden
startupInfo.dwFlags := startupInfo.dwFlags or STARTF_USESHOWWINDOW; //The nShowWindow member member will be valid.
startupInfo.wShowWindow := SW_HIDE; //default is that the console window is visible
// Set up members of the PROCESS_INFORMATION structure.
pi := Default(TProcessInformation);
//WARNING: The Unicode version of CreateProcess can modify the contents of CommandLine.
//Therefore CommandLine cannot point to read-only memory.
//We can ensure it's not read-only with the RTL function UniqueString
UniqueString({var}CommandLine);
bRes := CreateProcess(nil, PChar(CommandLine), nil, nil, True, 0, nil, nil, startupInfo, {var}pi);
if not bRes then
RaiseLastOSError;
//CreateProcess demands that we close these two populated handles when we're done with them. We're done with them.
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 console app.
When the app closes, it will close the pipe, and ReadFile will return 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)
}
CloseHandle(stdOutWrite);
stdOutWrite := 0;
SetLength(buffer, 4096);
//Read will return when the buffer is full, or if the pipe on the other end has been broken
while ReadFile(stdOutRead, buffer[1], Length(buffer), {var}bytesRead, nil) do
Result := Result + string(Copy(buffer, 1, bytesRead));
//ReadFile will either tell us that the pipe has closed, or give us an error
le := GetLastError;
if le <> ERROR_BROKEN_PIPE then //"The pipe has been ended."
RaiseLastOSError(le);
finally
CloseHandle(stdOutRead);
if stdOutWrite <> 0 then
CloseHandle(stdOutWrite);
end;
end;
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.
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);
}
}
}
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");
}
}
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.).
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;
}

Send command to pipe in C++

(Do the Z snap if you want, it'll lighten the mood)
I'm very far out of my comfort zone on this new project I decided to dive into, at least with parts of it.
The entire project will be a DLL that can be loaded into TeamSpeak 3, and allow people (via a small set of commands) to control Pianobar (a Pandora command line player).
The answer here guided me enough to get Pianobar (a console application) https://stackoverflow.com/a/17502224/1733365 up and running, I can get its STDOUT and it displays up until it the position where it displays the song's current time, and also where it accepts user input. The entire process locks at that point, I'm guessing because the ReadFromPipe() command thinks there is more to read as that line keeps getting refreshed.
I also took a stab at overriding the initial WriteToPipe(void) to WriteToPipe(char *cmd) in order to allow me to call it from an outside thread. (The one that listens to the chat of the TeamSpeak 3 server for specific commands.)
Right now my code is a giant mess, but I cleaned it up a bit so hopefully someone can help me understand.
Really this is just a summer project I decided to attempt while I'm out of school, and my first experience with creating a DLL.
Pianobar for Windows
Much of the code below was taken from Creating a Child Process with Redirected Input and Output
#include "pianobar.h"
//#include <windows.h>
//#include <tchar.h>
//#include <stdio.h>
#include <strsafe.h>
//#include <stdlib.h>
//#include <sys/types.h>
//#include <string.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;
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
SECURITY_ATTRIBUTES saAttr;
void CreateChildProcess(void);
void WriteToPipe(char *command);
void ReadFromPipe(void);
void ErrorExit(PTSTR);
int pianobar (struct TS3Functions ts3Functions) {
int iFound = 0;
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();
// 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.
// This should cause a help menu to be displayed on the next ReadFromPipe()
// However, ReadFromPipe() doesn't show help commands
//WriteToPipe("?\r\n");
// Read from pipe that is the standard output for child process.
// Reading causes a lock.
//ReadFromPipe();
printf("\n->End of parent execution.\n");
printf("\n->Pianobar started.\n");
iFound = 1;
return iFound;
}
void CloseChildProcess() {
//CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
TerminateProcess(piProcInfo.hProcess,0);
}
void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
TCHAR szCmdline[]=TEXT("c:\\pianobar\\pianobar.exe");
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,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
TEXT("c:\\pianobar\\"), // 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.
// I think I need these while I'm running...
//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;
DWORD dw;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
LPTSTR lpTStr;
printf("\n-> In WriteToPipe()\n");
bSuccess = WriteFile(g_hChildStd_IN_Wr, command, sizeof(command), &dwWritten, NULL);
if(bSuccess) {
printf("bSuccess was TRUE\n->Sent: ");
printf(command);
} else {
printf("bSuccess was FALSE\n");
}
// Close the pipe handle so the child process stops reading.
// my 2nd call to WriteToPipe results in a "The handle is invalid" error
if ( ! CloseHandle(g_hChildStd_IN_Wr) ) {
dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpTStr,
0, NULL );
printf(lpTStr);
}
if(command == "q\r\n") {
printf("Quit received.\n");
// this should have killed the process if it was received correctly...
CloseChildProcess();
}
}
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);
printf("\n-> In ReadFromPipe()\n");
for (;;)
{
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead == 0 ) break;
printf("In ReadFromPipe loop\n");
bSuccess = WriteFile(hParentStdOut, chBuf,
dwRead, &dwWritten, NULL);
if (! bSuccess ) {
// we never get to this, it just waits...
printf("Leaving loop\n");
break;
}
}
}
void ErrorExit(PTSTR lpszFunction)
// Format a readable error message, display a message box,
// and exit from the application.
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
I didn't really understand your setup, but regarding to this:
...and also where it accepts user input. The entire process locks at that
point, I'm guessing because the ReadFromPipe() command thinks there is
more to read as that line keeps getting refreshed.
That's very likely. If there's nothing to read from the pipe, then your process blocks, i.e. gets stuck inside the ReadFile() call. If you want to read only if there's something to read, you need async I/O or a notification mechanism. I don't really know Windows, but seems as if the're IO Completion Ports (IOCP) and async callback functions available for that matter. Maybe these links help:
What is the best epoll/kqueue/select equvalient on Windows?
IOCP and ReadFileEx usage

Cannot read from pipe in separate thread from suspended process

I am trying to create process with CreateProcess in suspended state and the read from it's stdout. For base I took MSDN code.
After creating process I'm going to set Job restrictions(not implemented yet) on the process and then I'm starting reading in separate thread from STDOUT pipe.
Before thread is initiated I resume suspended process.
In result I'm getting nothing from ReadFile call, it just stops and waits for data to arrive even when the process is finished.
Here is the 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_RdDup = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hSavedStd_OUT_Wr = NULL;
HANDLE g_hInputFile = NULL;
DWORD WINAPI CreateChildProcess(LPVOID param);
void WriteToPipe(void);
DWORD WINAPI ReadFromPipe(LPVOID param);
void ErrorExit(PTSTR);
PROCESS_INFORMATION piProcInfo;
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"));
STARTUPINFO siStartInfo;
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;
//CreateThread(&saAttr,0, CreateChildProcess, &siStartInfo, 0, NULL);
CreateChildProcess(&siStartInfo);
ResumeThread(piProcInfo.hThread);
printf( "\n->Contents of child process STDOUT:\n\n", argv[1]);
//ReadFromPipe(NULL);
HANDLE thread = CreateThread(&saAttr,0, ReadFromPipe, &siStartInfo, 0, NULL);
WaitForSingleObject(thread, INFINITE);
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;
}
DWORD WINAPI CreateChildProcess(LPVOID param)
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
TCHAR szCmdline[]=TEXT("C:\\GnuWin32\\bin\\ls.exe");
STARTUPINFO *siStartInfo = (STARTUPINFO*)param;
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.
// Create the child process.
bSuccess = CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
CREATE_SUSPENDED | CREATE_SEPARATE_WOW_VDM | CREATE_NO_WINDOW, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
//If resumethread is here - it works well
// 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);
}
return 0;
}
void WriteToPipe(void)
// 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;
for (;;)
{
bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
if ( ! bSuccess || dwRead == 0 ) break;
bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);
if ( ! bSuccess ) break;
}
// Close the pipe handle so the child process stops reading.
if ( ! CloseHandle(g_hChildStd_IN_Wr) )
ErrorExit(TEXT("StdInWr CloseHandle"));
}
DWORD WINAPI ReadFromPipe(LPVOID param)
// 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);
for (;;)
{
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;
}
return 0;
}
void ErrorExit(PTSTR lpszFunction)
// Format a readable error message, display a message box,
// and exit from the application.
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
And if I call ResumeThread(piProcInfo.hThread); just after CreateProcess function - It works well. But I need to keep creation in separate function anyway and call ResumeThread few functions after.
So, I would like to what to do to make program get suspended process's output in separate thread.
Always check return values.
You're trying to call ResumeThread on a handle that's already been closed. If you had checked the return value, you'd have noticed that this function wasn't succeeding, which would have told you what the problem was.
Remove CloseHandle(piProcInfo.hThread) from CreateChildProcess and the call to ResumeThread will work.
You should probably also be closing g_hChildStd_OUT_Wr and g_hChildStd_IN_Rd after creating the child process, so that you can tell when the child process exits.

How do I call ::CreateProcess in c++ to launch a Windows executable?

Looking for an example that:
Launches an EXE
Waits for the EXE to finish.
Properly closes all the handles when the executable finishes.
Something like this:
STARTUPINFO info={sizeof(info)};
PROCESS_INFORMATION processInfo;
if (CreateProcess(path, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo))
{
WaitForSingleObject(processInfo.hProcess, INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
There is an example at http://msdn.microsoft.com/en-us/library/ms682512(VS.85).aspx
Just replace the argv[1] with your constant or variable containing the program.
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
void _tmain( int argc, TCHAR *argv[] )
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
if( argc != 2 )
{
printf("Usage: %s [cmdline]\n", argv[0]);
return;
}
// Start the child process.
if( !CreateProcess( NULL, // No module name (use command line)
argv[1], // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
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
)
{
printf( "CreateProcess failed (%d).\n", GetLastError() );
return;
}
// Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}
If you application is a Windows GUI application then using the code below to do the waiting is not ideal as messages for your application will not be getting processing. To the user it will look like your application has hung.
WaitForSingleObject(&processInfo.hProcess, INFINITE)
Something like the untested code below might be better as it will keep processing the windows message queue and your application will remain responsive:
//-- wait for the process to finish
while (true)
{
//-- see if the task has terminated
DWORD dwExitCode = WaitForSingleObject(ProcessInfo.hProcess, 0);
if ( (dwExitCode == WAIT_FAILED )
|| (dwExitCode == WAIT_OBJECT_0 )
|| (dwExitCode == WAIT_ABANDONED) )
{
DWORD dwExitCode;
//-- get the process exit code
GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode);
//-- the task has ended so close the handle
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
//-- save the exit code
lExitCode = dwExitCode;
return;
}
else
{
//-- see if there are any message that need to be processed
while (PeekMessage(&message.msg, 0, 0, 0, PM_NOREMOVE))
{
if (message.msg.message == WM_QUIT)
{
return;
}
//-- process the message queue
if (GetMessage(&message.msg, 0, 0, 0))
{
//-- process the message
TranslateMessage(&pMessage->msg);
DispatchMessage(&pMessage->msg);
}
}
}
}
if your exe happens to be a console app, you might be interested in reading the stdout and stderr -- for that, I'll humbly refer you to this example:
http://support.microsoft.com/default.aspx?scid=kb;EN-US;q190351
It's a bit of a mouthful of code, but I've used variations of this code to spawn and read.
On a semi-related note, if you want to start a process that has more privileges than your current process (say, launching an admin app, which requires Administrator rights, from the main app running as a normal user), you can't do so using CreateProcess() on Vista since it won't trigger the UAC dialog (assuming it is enabled). The UAC dialog is triggered when using ShellExecute(), though.
Here is a new example that works on windows 10. When using the windows10 sdk you have to use CreateProcessW instead. This example is commented and hopefully self explanatory.
#ifdef _WIN32
#include <Windows.h>
#include <iostream>
#include <stdio.h>
#include <tchar.h>
#include <cstdlib>
#include <string>
#include <algorithm>
class process
{
public:
static PROCESS_INFORMATION launchProcess(std::string app, std::string arg)
{
// Prepare handles.
STARTUPINFO si;
PROCESS_INFORMATION pi; // The function returns this
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
//Prepare CreateProcess args
std::wstring app_w(app.length(), L' '); // Make room for characters
std::copy(app.begin(), app.end(), app_w.begin()); // Copy string to wstring.
std::wstring arg_w(arg.length(), L' '); // Make room for characters
std::copy(arg.begin(), arg.end(), arg_w.begin()); // Copy string to wstring.
std::wstring input = app_w + L" " + arg_w;
wchar_t* arg_concat = const_cast<wchar_t*>( input.c_str() );
const wchar_t* app_const = app_w.c_str();
// Start the child process.
if( !CreateProcessW(
app_const, // app path
arg_concat, // Command line (needs to include app path as first argument. args seperated by whitepace)
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
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
)
{
printf( "CreateProcess failed (%d).\n", GetLastError() );
throw std::exception("Could not create child process");
}
else
{
std::cout << "[ ] Successfully launched child process" << std::endl;
}
// Return process handle
return pi;
}
static bool checkIfProcessIsActive(PROCESS_INFORMATION pi)
{
// Check if handle is closed
if ( pi.hProcess == NULL )
{
printf( "Process handle is closed or invalid (%d).\n", GetLastError());
return FALSE;
}
// If handle open, check if process is active
DWORD lpExitCode = 0;
if( GetExitCodeProcess(pi.hProcess, &lpExitCode) == 0)
{
printf( "Cannot return exit code (%d).\n", GetLastError() );
throw std::exception("Cannot return exit code");
}
else
{
if (lpExitCode == STILL_ACTIVE)
{
return TRUE;
}
else
{
return FALSE;
}
}
}
static bool stopProcess( PROCESS_INFORMATION &pi)
{
// Check if handle is invalid or has allready been closed
if ( pi.hProcess == NULL )
{
printf( "Process handle invalid. Possibly allready been closed (%d).\n");
return 0;
}
// Terminate Process
if( !TerminateProcess(pi.hProcess,1))
{
printf( "ExitProcess failed (%d).\n", GetLastError() );
return 0;
}
// Wait until child process exits.
if( WaitForSingleObject( pi.hProcess, INFINITE ) == WAIT_FAILED)
{
printf( "Wait for exit process failed(%d).\n", GetLastError() );
return 0;
}
// Close process and thread handles.
if( !CloseHandle( pi.hProcess ))
{
printf( "Cannot close process handle(%d).\n", GetLastError() );
return 0;
}
else
{
pi.hProcess = NULL;
}
if( !CloseHandle( pi.hThread ))
{
printf( "Cannot close thread handle (%d).\n", GetLastError() );
return 0;
}
else
{
pi.hProcess = NULL;
}
return 1;
}
};//class process
#endif //win32
Perhaps this is the most complete?
http://goffconcepts.com/techarticles/createprocess.html
Bear in mind that using WaitForSingleObject can get you into trouble in this scenario. The following is snipped from a tip on my website:
The problem arises because your application has a window but isn't pumping messages. If the spawned application invokes SendMessage with one of the broadcast targets (HWND_BROADCAST or HWND_TOPMOST), then the SendMessage won't return to the new application until all applications have handled the message - but your app can't handle the message because it isn't pumping messages.... so the new app locks up, so your wait never succeeds.... DEADLOCK.
If you have absolute control over the spawned application, then there are measures you can take, such as using SendMessageTimeout rather than SendMessage (e.g. for DDE initiations, if anybody is still using that). But there are situations which cause implicit SendMessage broadcasts over which you have no control, such as using the SetSysColors API for instance.
The only safe ways round this are:
split off the Wait into a separate thread, or
use a timeout on the Wait and use PeekMessage in your Wait loop to ensure that you pump messages, or
use the MsgWaitForMultipleObjects API.
Here is a solution for CreateProcessA
STARTUPINFOW initInfo = { 0 };
initInfo.cb = sizeof(initInfo);
PROCESS_INFORMATION procInfo = { 0 };
CreateProcessA(PATH_FOR_EXE, NULL, NULL, NULL, FALSE, 0, NULL, NULL, (LPSTARTUPINFOA)&initInfo, &procInfo);
#include <Windows.h>
void my_cmd()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// CMD command here
char arg[] = "cmd.exe /c E:/Softwares/program.exe";
// Convert char string to required LPWSTR string
wchar_t text[500];
mbstowcs(text, arg, strlen(arg) + 1);
LPWSTR command = text;
// Run process
CreateProcess (NULL, command, NULL, NULL, 0,
CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
}
This works fine for me. No popup windows and cmd command runs as expected. Just needed to convert the CHAR pointer into WCHAR pointer and add extra "cmd.exe /c" before every command.