IPC using Pipes on Windows - c++

My program invokes cl.exe and communicates using pipes. When I was trying to understand it, I tried to search about IPC using pipes on windows, but couldn't find much resources. I found below link which is 90% similar to my code, but there is very less description about each steps.
http://support.microsoft.com/en-us/kb/190351
Can somebody explains me how the parent and child relationship works in this program? why duplicate handle is used?
Code snippet:
{
HANDLE hOutputReadTmp,hOutputRead,hOutputWrite;
HANDLE hInputWriteTmp,hInputRead,hInputWrite;
HANDLE hErrorWrite;
SECURITY_ATTRIBUTES sa;
// Set up the security attributes struct.
sa.nLength= sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
// Create the child output pipe.
if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0))
{
error("Creation of child process output pipe failed");
return false;
}
// Create a duplicate of the output write handle for the std error
// write handle. This is necessary in case the child application
// closes one of its std output handles.
if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite,
GetCurrentProcess(),&hErrorWrite,0,
TRUE,DUPLICATE_SAME_ACCESS))
{
error("Duplication of child process output pipe failed");
return false;
}
// Create the child input pipe.
if (!CreatePipe(&hInputRead,&hInputWriteTmp,&sa,0))
{
error("Creation of child process input pipe failed");
return false;
}
// Create new output read handle and the input write handles. Set
// the Properties to FALSE. Otherwise, the child inherits the
// properties and, as a result, non-closeable handles to the pipes
// are created.
if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
GetCurrentProcess(),
&hOutputRead, // Address of new handle.
0,FALSE, // Make it uninheritable.
DUPLICATE_SAME_ACCESS))
{
error("Creation of child process read handle failed");
return false;
}
if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp,
GetCurrentProcess(),
&hInputWrite, // Address of new handle.
0,FALSE, // Make it uninheritable.
DUPLICATE_SAME_ACCESS))
{
error("Creation of child process write handle failed");
return false;
}
// Close inheritable copies of the handles you do not want to be
// inherited.
CloseHandle(hOutputReadTmp);
CloseHandle(hInputWriteTmp);
// Get std input handle so you can close it and force the ReadFile to
// fail when you want the input thread to exit.
HANDLE hStdIn = NULL;
if ( (hStdIn = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE )
TRACE("GetStdHandle");
STARTUPINFO Si;
Si.lpDesktop = NULL;
Si.cb = sizeof(Si);
Si.lpReserved = NULL;
Si.lpTitle = NULL;
Si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW | STARTF_USECOUNTCHARS;
Si.dwXCountChars = 10;
Si.dwYCountChars = 8;
Si.wShowWindow = SW_HIDE;
Si.cbReserved2 = 0;
Si.lpReserved2 = NULL;
Si.hStdOutput = hOutputWrite;
Si.hStdInput = hInputRead;
Si.hStdError = hErrorWrite;
PROCESS_INFORMATION Pi;
// Create and start the child process
BOOL processCreated = CreateProcess( NULL,
const_cast<char*>(m_command.c_str()),
NULL,
NULL,
TRUE,
GetCreationFlags(),
NULL,
m_workingDir.c_str(),
&Si,
&Pi);
if (!processCreated)
{
error("Creation of child process failed");
return false;
}
// Close pipe handles (do not continue to modify the parent).
// You need to make sure that no handles to the write end of the
// output pipe are maintained in this process or else the pipe will
// not close when the child process exits and the ReadFile will hang.
CloseHandle(hOutputWrite);
CloseHandle(hInputRead);
CloseHandle(hErrorWrite);
// Read the child's output.
if (!ReadAndHandleOutput(hOutputRead))
{
// Something went wrong so kill and exit
CloseHandle(hOutputRead);
CloseHandle(hInputWrite);
error("Read of compile process result failed");
TerminateProcess(Pi.hProcess, -1);
CloseHandle(Pi.hProcess);
CloseHandle(Pi.hThread);
return false;
}
CloseHandle(hOutputRead);
CloseHandle(hInputWrite);
// Wait for the child process to die
WaitForSingleObject(Pi.hProcess, INFINITE);
if (!GetExitCodeProcess(Pi.hProcess, &m_exitCode))
{
error("Read of child process exit code failed");
CloseHandle(Pi.hProcess);
CloseHandle(Pi.hThread);
return false;
}
CloseHandle(Pi.hProcess);
CloseHandle(Pi.hThread);
}

CreatePipe - create named pipes pair by using ZwCreateNamedPipe[CreateNamedPipe] and ZwOpenFile [ CreateFile(OPEN_EXISTING)].
one instance of pair will be live in our process(no not have inherit flag) and second will be duplicated to child process (mast have inherit flag). however we have only one common parameter LPSECURITY_ATTRIBUTES , where we can set inherit flag. as a result if this flag is set - both instances will have the inherited property. here can be several solutions. we can not use CreatePipe, but direct call ZwCreateNamedPipe and ZwOpenFile for just correct inherit settings. or we can change inherit settings by using SetHandleInformation. but author of example select worst (by logic and efficiency) - use DuplicateHandle for create another handle for pipe instance - already without inherit flag. and than close original handle. however can be used DUPLICATE_CLOSE_SOURCE flag for avoid next CloseHandle call - stunning ignorance. after call CreateProcess - inherited handles is duplicated to child process, and we can close it in original process. so at this point we have 2 pipe channels and begin read/write..

Related

Redirected stdin pipe being ignored by created child process

EDIT: The fix, as suggested by Harry Johnston, was to close the Child_In_Write handle.
Somewhat ironically, I had earlier tried closing the Child_In_Read handle. This does NOT work, the write handle is the only one that should be closed.
For the tool I'm trying to make, I need to be able to launch a process and give it data through stdin - as if I was calling it via command line with piping
Simple enough idea.
I've primarily I've followed this guide from Microsoft and got things "working".
In my own test program I can read from stdin just fine. But when I try to use other programs, like cat for instance, they do nothing but hang - as if they are still waiting for input.
The full repo is here.
Here are the relevant code bits:
Initialize the pipes.
// From Cao/Main.cpp
static HANDLE Child_In_Read = NULL;
static HANDLE Child_In_Write = NULL;
static HANDLE Child_Out_Read = NULL;
static HANDLE Child_Out_Write = NULL;
// Create and initialize standard in pipe.
{
bool createPipeSuccess =
CreatePipe(
&Child_In_Read,
&Child_In_Write,
&secAttr,
0);
if (!createPipeSuccess)
{
// #logging log error.
printf("Could not create standard in pipe!\n");
goto textData_cleanup;
}
bool setPipeFlagSuccess = SetHandleInformation(Child_In_Write, HANDLE_FLAG_INHERIT, 0);
if (!setPipeFlagSuccess)
{
// #logging log error.
printf("Could not set standard in pipe information!\n");
goto textData_cleanup;
}
}
Write to the pipe that was just initialized then start the process.
// From Cao/Main.cpp
// Write to the processes' standard in.
{
DWORD inBytesWritten = 0;
bool writeSuccess =
WriteFile(
Child_In_Write,
text, // Simple char array.
text_numBytes,
&inBytesWritten,
NULL);
if (!writeSuccess)
{
// #logging log error.
printf("Could not write to child's standard in!\n");
goto textData_cleanup;
}
}
// Create the child process.
{
STARTUPINFO startupInfo = { 0 };
startupInfo.cb = sizeof(startupInfo);
startupInfo.hStdInput = Child_In_Read;
startupInfo.hStdError = Child_Out_Write;
startupInfo.hStdOutput = Child_Out_Write;
startupInfo.dwFlags = STARTF_USESTDHANDLES;
bool createProcessSuccess = CreateProcessW(
NULL,
commandLine,
NULL,
NULL,
true,
0,
NULL,
NULL,
&startupInfo,
&ChildProcInfo);
if (!createProcessSuccess)
{
printf("Could not start child process with command line: %ls", commandLine);
goto textData_cleanup;
}
isChildRunning = true;
ModifyMenu(IconMenu, IconMenu_RunCancel, MF_BYCOMMAND, IconMenu_RunCancel, L"Cancel");
// newHandle is always 0x00000000 so I'm assuming I don't need to clean it up.
HANDLE newHandle;
RegisterWaitForSingleObject(&newHandle, ChildProcInfo.hProcess, LaunchedProcessExitedOrCancelled, NULL, INFINITE, WT_EXECUTEONLYONCE);
}
My reading code that appears to work fine:
// From Echoer/Main.cpp
printf("via stdin:\n");
{
const int readBuffer_size = 5000;
char *readBuffer[readBuffer_size];
{
HANDLE standardIn = GetStdHandle(STD_INPUT_HANDLE);
DWORD bytesRead = 0;
bool readSuccess =
ReadFile(
standardIn,
readBuffer,
readBuffer_size,
&bytesRead,
NULL);
if (!readSuccess)
{
printf("Could not read from standard in!\n");
}
CloseHandle(standardIn);
}
printf("%s", readBuffer);
}
Am I missing something that needs to be sent to "get the ball rolling"? Do I need to append "\r\n" or something like that? How do shells manage this?
Some applications, including cat, will wait for end-of-file on standard input before exiting. You can make this happen by closing your end of the pipe.
(You must also make certain that the handle to your end of the pipe has not been inherited by the child or by any other process, but your code already handles this correctly.)

WaitForSingleObject is hanged in VC12 while it was working with VC9 compiler

The below code was working with VC9 complier while the same code is not working in VC12. Do need to change any structure?
The problem is coming at WaitForSingleObject ( pi.hProcess, INFINITE ); line.
after this line code is not executing.
bool MyClass::check()
{
CSA_TRY
{
HANDLE hPipeOutputRead = NULL;
HANDLE hPipeOutputWrite = NULL;
PROCESS_INFORMATION pi = {0};
STARTUPINFO si = {0};
SECURITY_ATTRIBUTES sa = {0};
// Create a pipe for the child's STDOUT.
sa.nLength = sizeof( SECURITY_ATTRIBUTES );
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
// Create pipe for standard output redirection.
CreatePipe( &hPipeOutputRead, // read handle
&hPipeOutputWrite, // write handle
&sa, // security attributes
0 // number of bytes reserved for pipe - 0 default
); // Create pipe for standard input redirection.
// Make child process use hPipeOutputWrite as standard out,
// and make sure it does not show on screen.
si.cb = sizeof(STARTUPINFO);
//si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdOutput = hPipeOutputWrite;
si.hStdError = hPipeOutputWrite;
TCHAR commandLine[ MAX_PATH ] = _T("cmd.exe ver");
//spawn a new child process...
CreateProcess( NULL,
commandLine,
NULL, NULL,
TRUE, 0,
NULL, NULL,
&si, &pi
);
// Now that handles have been inherited, close it to be safe.
// You don't want to read or write to them accidentally.
CloseHandle( hPipeOutputWrite );
// Now capture the application output by reading hPipeOutputRead.
DWORD dwNumberOfBytesRead = 0;
CHAR szBuffer[ 65535 ];
CString conOut;
BOOL bTest = TRUE;
bTest = ReadFile( hPipeOutputRead, // handle of the read end of our pipe
&szBuffer, // address of buffer that receives data
256, // number of bytes to read
&dwNumberOfBytesRead, // address of number of bytes read
NULL // non-overlapped.
);
szBuffer[dwNumberOfBytesRead] = 0; // null terminate
conOut = szBuffer;
WaitForSingleObject ( pi.hProcess, INFINITE );
// Close all remaining handles
CloseHandle ( pi.hProcess );
CloseHandle ( hPipeOutputRead );
return true;
}
CSA_CATCH_ANY
{
//code
}
}
You are starting the process cmd.exe ver. If you run this manually in Explorer (WindowsR) you can see that cmd.exe ver prints out the Windows version and then stays open. Your program waits until cmd.exe ver exits.
So you need to either close the opened cmd.exe ver (for example typing exit) or you need to change the command line to cmd.exe /c ver.
Another problem in your code: you are not setting si.hStdInput.
You really should add error checking to see what exactly fails with which error code.

ReadFile works fine inside VS C++ IDE but fails stand-alone

Currently I have two apps, one with a GUI (written using MFC) and the other as a standard executable. The GUI app (parent) triggers the standard app (child) using CreateProcessW call and parent receives messages from its child via an anonymous pipe. The message receiving process works fine when I run the parent inside the VS IDE. However, if I run the parent standalone, parent does not receive any messages from its child (i.e. parent get hang in ReadFile call, waiting for messages).
Any thoughts on this?
Note: After creation of anonymous pipe, all read operations happens inside a separate thread and it does not block either UI or main thread. Some code related to child process creation and used parameters are given below.
// pipe creation code
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(&m_hChildStd_OUT_Rd, &m_hChildStd_OUT_Wr, &saAttr, 0) )
{
m_logger->log( __FILE__, __LINE__, EventSeverity::WARNING, "Pipe cannot be created, will not receive meassages from child processes" );
}
// Ensure the read handle to the pipe for STDOUT is not inherited.
if ( ! SetHandleInformation(m_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
{
m_logger->log( __FILE__, __LINE__, EventSeverity::WARNING, "Could not make the read handler of the anonymous pipe not-inheritable" );
}
SetStdHandle(STD_OUTPUT_HANDLE, m_hChildStd_OUT_Wr);
SetStdHandle(STD_ERROR_HANDLE, m_hChildStd_OUT_Wr);
//Child process creation code
m_startupInfo.lpDesktop = NULL;
m_startupInfo.lpReserved = NULL;
m_startupInfo.lpReserved2 = NULL;
m_startupInfo.lpTitle = NULL;
m_startupInfo.hStdError = GetStdHandle(STD_OUTPUT_HANDLE);
m_startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
m_startupInfo.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
m_startupInfo.cb = sizeof( m_startupInfo );
// launch the executable
m_isExecuting = CreateProcessW( app.exe, // lpApplicationName
m_pwszParam, // lpCommandLine
0, // lpProcessAttributes
0, // lpThreadAttributes
TRUE, // bInheritHandles
CREATE_NEW_PROCESS_GROUP, // dwCreationFlags
NULL, // lpEnvironment
curent working directory // lpCurrentDirectory
&m_startupInfo, // lpStartupInfo
&m_processInfo // lpProcessInformation
);
managed to solve this issue. Previously I was updating parent's output and error handlers to newly created handlers in order to retrieve them when creating a child process. However, this doesn't seem to be working. When I modify the code to pass the pipe handlers via a pointer or a reference then things started to work fine.
However this does not explain the reason for running inside IDE and failing it in stand-alone mode.
//set the handler
SetStdHandle(STD_OUTPUT_HANDLE, m_hChildStd_OUT_Wr);
SetStdHandle(STD_ERROR_HANDLE, m_hChildStd_OUT_Wr);
//get the handler
m_startupInfo.hStdError = GetStdHandle(STD_OUTPUT_HANDLE);
m_startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);

How do you write to a second console?

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.

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