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.
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.)