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:
g_hChildStd_IN_Wr
g_hChildStd_OUT_Rd
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:
g_hChildStd_IN_Wr
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;
The common problem with most solutions is that people try to wait on a process handle.
That's wrong. That's all wrong.
There are many problems with these ideas; the main one being:
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.
As a result the child process will never terminate; you've deadlocked everything.
The correct solution comes by simply reading from the pipe.
CloseHandle
on its end of the pipes.ERROR_BROKEN_PIPE
).
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.
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:
DIR
command doesn't require user input (but, I left it in the code, since it is a good template for running other commands)hStdInPipeRead
& hStdInPipeWrite
can be omittedsi.hStdInput
can be omittedL"C:\\Windows\\System32\\cmd.exe"
with reading the COMSPEC
environment variable.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");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With