I'm working on Windows 10.
Here's my code based on this document:
Creating a Child Process with Redirected Input and Output
Here is the code of the Parent process (I have modified only CreateChildProcess(). The rest are the same):
void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
TCHAR szCmdline[] = TEXT("ChildProcess");
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;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create the child process.
//"C:\\Windows\\System32\\notepad.exe"
char(*exeName) = "C:\\Users\\kch79\\source\\repos\\MainProcess\\Debug\\ChildProcess.exe";
bSuccess = CreateProcess(exeName,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
CREATE_NEW_CONSOLE, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
//WaitForInputIdle(piProcInfo.hProcess, INFINITE);
// 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.
WaitForSingleObject(piProcInfo.hProcess, INFINITE);
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
// Close handles to the stdin and stdout pipes no longer needed by the child process.
// If they are not explicitly closed, there is no way to recognize that the child process has ended.
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_IN_Rd);
}
}
And here is the code of the Child process (I have added only scanf()):
#include <windows.h>
#include <stdio.h>
#pragma warning(disable:4996)
#define BUFSIZE 4096
int main(void)
{
CHAR chBuf[BUFSIZE];
DWORD dwRead, dwWritten;
HANDLE hStdin, hStdout;
BOOL bSuccess;
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
hStdin = GetStdHandle(STD_INPUT_HANDLE);
if (
(hStdout == INVALID_HANDLE_VALUE) ||
(hStdin == INVALID_HANDLE_VALUE)
)
ExitProcess(1);
// Send something to this process's stdout using printf.
printf("\n ** This is a message from the child process. ** \n");
int number = 0;
scanf("%d", &number);
printf("%d\n", number);
//SetConsoleMode(hStdin, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT);
//SetConsoleMode(hStdout, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
// This simple algorithm uses the existence of the pipes to control execution.
// It relies on the pipe buffers to ensure that no data is lost.
// Larger applications would use more advanced process control.
for (;;)
{
// Read from standard input and stop on error or no data.
bSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL);
if (!bSuccess || dwRead == 0)
break;
// Write to standard output and stop on error.
bSuccess = WriteFile(hStdout, chBuf, dwRead, &dwWritten, NULL);
if (!bSuccess)
break;
}
return 0;
}
These codes are working well for only printf() but not for scanf().
New Console Window created by CreateProcess() does nothing.
I want to control the console, but how?
Please let me know what I am doing wrong.
Because you redirect the input handle of the child process to the pipe through the following code:
CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)
......
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
So your child process cannot input in the new console, but needs to be input through the parent process's pipeline. Of course, you can also set the input handle of the child process to NULL:
siStartInfo.hStdInput = NULL;
In this way, you can input in the child process, but at the same time the parent process cannot input messages to the child process.
Of course, it is recommended that you put the following code at the end of the main function, so that you can observe that the parent process gets the input of the child process.
WaitForSingleObject(piProcInfo.hProcess, INFINITE);
The complete code:
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#define BUFSIZE 4096
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);
PROCESS_INFORMATION piProcInfo;
int _tmain()
{
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"));
if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
ErrorExit(TEXT("Stdin SetHandleInformation"));
// Create the child process.
CreateChildProcess();
// Get a handle to an input file for the parent.
// This example assumes a plain text file and uses string output to verify data flow.
g_hInputFile = CreateFile(
L"D:\\test\\test.txt",
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL);
if (g_hInputFile == INVALID_HANDLE_VALUE)
ErrorExit(TEXT("CreateFile"));
// Write to the pipe that is the standard input for a child process.
// Data is written to the pipe's buffers, so it is not necessary to wait
// until the child process is running before writing data.
WriteToPipe();
printf("\n->Contents of %S written to child STDIN pipe.\n", L"D:\\test\\test.txt");
// Read from pipe that is the standard output for child process.
printf("\n->Contents of child process STDOUT:\n\n");
ReadFromPipe();
printf("\n->End of parent execution.\n");
// The remaining open handles are cleaned up when this process terminates.
// To avoid resource leaks in a larger application, close handles explicitly.
WaitForSingleObject(piProcInfo.hProcess, INFINITE);
return 0;
}
void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
TCHAR szCmdline[] = TEXT("ChildProcess");
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 = NULL;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create the child process.
TCHAR exeName[] = L"D:\\VS_test_projects\\Project2\\Debug\\c.exe";
bSuccess = CreateProcess(exeName,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
CREATE_NEW_CONSOLE, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
// If an error occurs, exit the application.
if (!bSuccess)
ErrorExit(TEXT("CreateProcess"));
}
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"));
}
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);
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;
}
}
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);
}
Related
Is there a way to check if the child process created with CreateThread has finished execution
This code does print the output of the command but only when a proper wait time is added to WaitForSingleObject in this case 5 seconds is enough but this is not always the case
#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 handle = NULL;
DWORD grab_output(void * args)
{
DWORD dwRead, total=0;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
for (;;)
{
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess ) break;
printf("%s", chBuf);
}
printf("\n");
return 0;
}
int run()
{
BOOL res;
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
DWORD n_size;
memset(&pi, 0, sizeof(pi));
memset(&sa, 0, sizeof(sa));
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0))
return GetLastError();
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
return GetLastError();
memset(&si, 0 ,sizeof(si));
si.cb = sizeof(STARTUPINFOA);
si.hStdError = g_hChildStd_OUT_Wr;
si.hStdOutput = g_hChildStd_OUT_Wr;
si.dwFlags |= STARTF_USESTDHANDLES;
if(!CreateProcess(NULL,
"C:\\Windows\\System32\\cmd.exe /c ping google.com",
NULL,
NULL,
TRUE,
CREATE_NO_WINDOW,
NULL,
NULL,
&si,
&pi
))
{
}
else
{
handle = CreateThread(0, 0, grab_output, NULL, 0, NULL);
WaitForSingleObject(handle, INFINITE);
}
return 0;
}
int main()
{
run();
return 0;
}
I tried using GetExitCodeThread in the run function but it does not seem to work properly and keeps outputting the same exit code before and after the child has finished execution, I also tried using WaitForSingleObject and I checked the return code but it too keeps outputting WAIT_TIMEOUT
the output
$ gcc temp2.c && ./a.exe
Pinging google.com [172.217.168.238] with 32 bytes of data:
2.217.168.238] Reply from 172.217.168.238: 7.168.238] bytes=32 time=87ms TTL=57
7.168.238] Reply from 172.217.168.238: 7.168.238] bytes=32 time=96ms TTL=57
7.168.238] Reply from 172.217.168.238: 7.168.238] bytes=32 time=86ms TTL=57
7.168.238] Reply from 172.217.168.238: 7.168.238] bytes=32 time=86ms TTL=57
Ping statistics for 172.217.168.238:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 86ms, Maximum = 96ms, Average = 88ms
this never seems to return back the console because of INFINITE
How do I properly check if the child process has finished execution
Your entire code and explanation make literally no sense.
the child process created with CreateThread
CreateThread doesn't create processes, this isn't Linux.
if(!CreateProcess(NULL,
"C:\\Windows\\System32\\cmd.exe /c ping google.com",
NULL,
NULL,
TRUE,
CREATE_NO_WINDOW,
NULL,
NULL,
&si,
&pi
))
{
You create a process and don't store it's handle anywhere, what is your thinking here?
I tried using GetExitCodeThread in the run function but it does not seem to work properly
Blaming your tools because you use them wrong peg your skill level as a programmer very appropriately. How can you even check the exit code if you don't store the process handle anywhere?
I also tried using WaitForSingleObject
Not on your process you didn't, you used it on some random thread you felt like creating for no reason.
In short, if you insist on doing things the wrong, brittle, inefficient and un-portable way, store your process handle and use WaitForSingleObject on it. If you want to wait until it's over (ie freeze your application until then, which is in line with the rest of your code), use an INFINITE parameter. If you want to simply check if it's done, use 0.
Otherwise, use IcmpSendEcho2 like I said.
Blindy's concerns are valid if you intend to wait for the process to exit. If you instead intend to wait until your thread reads all output from the process (and you should), the fix is much simpler.
In this loop, you failed to account for the "end of file" condition:
for (;;)
{
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess ) break;
printf("%s", chBuf);
}
End-of-file is indicated by a successful read with zero length.
You also call printf on data that isn't NUL-terminated.
Fixing both those issues:
for (;;) {
bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if (!bSuccess) break;
if (dwRead == 0) break;
fwrite(chBuf, 1, dwRead, stdout);
}
Now, when the child process exits, it will close its pipe handle. Your reading thread will read an end-of-file and exit its loop, but only after printing all the data it read from the pipe. Returning from your thread procedure will wake the WaitForSingleObject waiting on the thread.
Following Blindy's advice would exit your program too early, without the "only after printing all the data it read from the pipe" behavior.
However, Blindy is right that you didn't have any particular reason to create that thread. You could just call grab_output(nullptr) instead. With the above fixes, that will work perfectly.
I am trying to launch a process with CreateProcess() with stdin and stdout redirected to pipes. When the child process consists of just printf() statements, I see them piped up to the parent and displayed just fine. If my child process does a printf() and a _getch() statement, then things fail. I have considered a possible deadlock between the pipes in several ways to no avail:
changing the order of things,
applying PeekNamedPipe() and Sleep() statements, and
overlapped I/O using a named pipe.
I'm left suspecting a subtle configuration issue somewhere. This is part of an issue in a larger program but I've reduced it to this simple test case. I started with the Microsoft example for "Creating a Child Process with Redirected Input and Output". That worked, so maybe the child process using ReadFile() works, but my problem is _getch() (among other programs that seem to have related failures). I replaced the child process with my test program and it stalls. I try solving deadlocks as above, with the overlapped I/O achieved following this example for using named pipes to this purpose (in my reading someone mentioned that the Windows implementation of named and anonymous pipes are reasonably unified).
Again, works if the child issues only printfs but fails with _getch(). Of note is that if a _getch() is present in the child program then even the printfs don't show up - even printfs() issued before the _getch(). I've read that pipes have buffering and as above they have potential deadlocks waiting on the other end of the pipe, but I can't think what else I can do to avoid that besides what's done below.
Just in case, I also made sure I had a large heap buffer for the command-line buffer since CreateProcess() is known to modify it.
Here is my parent test code, with those first booleans configuring overlapped/not overlapped behavior:
#include <string>
#include <Windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#include <conio.h>
#include <assert.h>
TCHAR szCmdline[] = TEXT("child.exe");
bool OverlappedStdOutRd = true;
bool OverlappedStdInWr = true;
#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;
using namespace std;
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);
}
static ULONG PipeSerialNumber = 1;
static BOOL APIENTRY MyCreatePipeEx(
OUT LPHANDLE lpReadPipe,
OUT LPHANDLE lpWritePipe,
IN LPSECURITY_ATTRIBUTES lpPipeAttributes,
IN DWORD nSize,
DWORD dwReadMode,
DWORD dwWriteMode
)
/*++
Routine Description:
The CreatePipeEx API is used to create an anonymous pipe I/O device.
Unlike CreatePipe FILE_FLAG_OVERLAPPED may be specified for one or
both handles.
Two handles to the device are created. One handle is opened for
reading and the other is opened for writing. These handles may be
used in subsequent calls to ReadFile and WriteFile to transmit data
through the pipe.
Arguments:
lpReadPipe - Returns a handle to the read side of the pipe. Data
may be read from the pipe by specifying this handle value in a
subsequent call to ReadFile.
lpWritePipe - Returns a handle to the write side of the pipe. Data
may be written to the pipe by specifying this handle value in a
subsequent call to WriteFile.
lpPipeAttributes - An optional parameter that may be used to specify
the attributes of the new pipe. If the parameter is not
specified, then the pipe is created without a security
descriptor, and the resulting handles are not inherited on
process creation. Otherwise, the optional security attributes
are used on the pipe, and the inherit handles flag effects both
pipe handles.
nSize - Supplies the requested buffer size for the pipe. This is
only a suggestion and is used by the operating system to
calculate an appropriate buffering mechanism. A value of zero
indicates that the system is to choose the default buffering
scheme.
Return Value:
TRUE - The operation was successful.
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
HANDLE ReadPipeHandle, WritePipeHandle;
DWORD dwError;
CHAR PipeNameBuffer[MAX_PATH];
//
// Only one valid OpenMode flag - FILE_FLAG_OVERLAPPED
//
if ((dwReadMode | dwWriteMode) & (~FILE_FLAG_OVERLAPPED)) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
//
// Set the default timeout to 120 seconds
//
if (nSize == 0) {
nSize = 4096;
}
sprintf_s(PipeNameBuffer,
"\\\\.\\Pipe\\TruthPipe.%08x.%08x",
GetCurrentProcessId(),
PipeSerialNumber++ // TODO: Should use InterlockedIncrement() here to be thread-safe.
);
ReadPipeHandle = CreateNamedPipeA(
PipeNameBuffer,
PIPE_ACCESS_INBOUND | dwReadMode,
PIPE_TYPE_BYTE | PIPE_WAIT,
1, // Number of pipes
nSize, // Out buffer size
nSize, // In buffer size
1000, // Timeout in ms
lpPipeAttributes
);
if (!ReadPipeHandle) {
return FALSE;
}
WritePipeHandle = CreateFileA(
PipeNameBuffer,
GENERIC_WRITE,
0, // No sharing
lpPipeAttributes,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | dwWriteMode,
NULL // Template file
);
if (INVALID_HANDLE_VALUE == WritePipeHandle) {
dwError = GetLastError();
CloseHandle(ReadPipeHandle);
SetLastError(dwError);
return FALSE;
}
*lpReadPipe = ReadPipeHandle;
*lpWritePipe = WritePipeHandle;
return(TRUE);
}
bool OutstandingWrite = false;
OVERLAPPED WriteOverlapped;
CHAR chWriteBuf[BUFSIZE];
DWORD dwBytesWritten;
DWORD dwBytesToWrite;
bool OutstandingRead = false;
OVERLAPPED ReadOverlapped;
CHAR chReadBuf[BUFSIZE];
DWORD dwBytesRead;
void OnReadComplete();
void StartOverlappedRead();
void WaitForIO(bool Wait)
{
HANDLE hEvents[2];
int iEvent = 0;
int iReadEvent = -1;
int iWriteEvent = -1;
if (OutstandingRead) {
hEvents[iEvent] = ReadOverlapped.hEvent;
iReadEvent = iEvent;
iEvent++;
}
if (OutstandingWrite) {
hEvents[iEvent] = WriteOverlapped.hEvent;
iWriteEvent = iEvent;
iEvent++;
}
DWORD dwStatus = WaitForMultipleObjects(iEvent, hEvents, FALSE, Wait ? INFINITE : 250 /*ms*/);
int Index = -2;
switch (dwStatus)
{
case WAIT_OBJECT_0: Index = 0; break;
case WAIT_OBJECT_0 + 1: Index = 1; break;
case WAIT_TIMEOUT: return;
default:
ErrorExit(TEXT("WaitForMultipleObjects"));
}
if (Index == iReadEvent)
{
if (!GetOverlappedResult(
g_hChildStd_OUT_Rd, // handle to pipe
&ReadOverlapped, // OVERLAPPED structure
&dwBytesRead, // bytes transferred
FALSE)) // do not wait
ErrorExit(TEXT("GetOverlappedResult"));
OutstandingRead = false;
if (dwBytesRead > 0) OnReadComplete();
StartOverlappedRead();
}
else if (Index == iWriteEvent)
{
if (!GetOverlappedResult(
g_hChildStd_IN_Wr, // handle to pipe
&WriteOverlapped, // OVERLAPPED structure
&dwBytesWritten, // bytes transferred
FALSE)) // do not wait
ErrorExit(TEXT("GetOverlappedResult"));
if (dwBytesWritten != dwBytesToWrite) ErrorExit(TEXT("Write incomplete."));
OutstandingWrite = false;
}
else ErrorExit(TEXT("WaitForMultipleObjects indexing"));
}
void WriteToPipe(string text)
{
BOOL bSuccess = FALSE;
printf("Writing: %s\n", text.c_str());
if (!OverlappedStdInWr)
{
bSuccess = WriteFile(g_hChildStd_IN_Wr, text.c_str(), (DWORD)text.length(), &dwBytesWritten, NULL);
if (!bSuccess) ErrorExit(TEXT("WriteToPipe"));
return;
}
else
{
while (OutstandingWrite) WaitForIO(true); // Can only have one outstanding write at a time.
WriteOverlapped.Offset = 0;
WriteOverlapped.OffsetHigh = 0;
WriteOverlapped.Pointer = nullptr;
if (text.length() > BUFSIZE) ErrorExit(TEXT("Attempt to write too long a message!"));
CopyMemory(chWriteBuf, text.c_str(), text.length());
dwBytesToWrite = text.length();
bSuccess = WriteFile(g_hChildStd_IN_Wr, chWriteBuf, dwBytesToWrite, &dwBytesWritten, &WriteOverlapped);
if (bSuccess) return;
if (!bSuccess)
{
if (GetLastError() == ERROR_IO_PENDING) {
OutstandingWrite = true;
return;
}
ErrorExit(TEXT("WriteToPipe"));
}
}
}
void OnReadComplete()
{
chReadBuf[dwBytesRead] = '\0';
printf("Rx: ");
for (DWORD ii = 0; ii < dwBytesRead; ii++)
{
if (chReadBuf[ii] >= 0x20 && chReadBuf[ii] <= 0x7e) printf("%c", chReadBuf[ii]);
else
{
printf("\\0x%02X", chReadBuf[ii]);
}
if (chReadBuf[ii] == '\n') printf("\n");
}
printf("\n");
}
void StartOverlappedRead()
{
int loops = 0;
for (;; loops++)
{
if (loops > 10) ErrorExit(TEXT("Read stuck in loop"));
assert(!OutstandingRead);
ReadOverlapped.Offset = 0;
ReadOverlapped.OffsetHigh = 0;
ReadOverlapped.Pointer = nullptr;
BOOL Success = ReadFile(g_hChildStd_OUT_Rd, chReadBuf, BUFSIZE - 1, &dwBytesRead, &ReadOverlapped);
if (!Success && GetLastError() != ERROR_IO_PENDING)
ErrorExit(TEXT("ReadFile"));
if (Success)
{
if (dwBytesRead > 0)
OnReadComplete();
continue;
}
else {
OutstandingRead = true; return;
}
}
}
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.
{
BOOL bSuccess = FALSE;
if (!OverlappedStdOutRd)
{
for (;;)
{
DWORD total_available_bytes;
if (FALSE == PeekNamedPipe(g_hChildStd_OUT_Rd,
0,
0,
0,
&total_available_bytes,
0))
{
ErrorExit(TEXT("ReadFromPipe - peek"));
return;
}
else if (total_available_bytes == 0)
{
// printf("No new pipe data to read at this time.\n");
return;
}
bSuccess = ReadFile(g_hChildStd_OUT_Rd, chReadBuf, BUFSIZE - 1, &dwBytesRead, NULL);
if (!bSuccess) ErrorExit(TEXT("ReadFromPipe"));
if (dwBytesRead == 0) return;
OnReadComplete();
}
}
else
{
if (!OutstandingRead) StartOverlappedRead();
WaitForIO(false);
}
}
void Create()
{
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;
if (!OverlappedStdOutRd)
{
// As per the MS example, create anonymous pipes
// 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"));
}
else
{
// Create overlapped I/O pipes (only one side is overlapped).
if (!MyCreatePipeEx(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0, FILE_FLAG_OVERLAPPED, 0))
ErrorExit(TEXT("Stdout MyCreatePipeEx"));
ZeroMemory(&ReadOverlapped, sizeof(ReadOverlapped));
ReadOverlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); // Manual-reset event, unnamed, initially signalled.
if (ReadOverlapped.hEvent == NULL)
ErrorExit(TEXT("CreateEvent Read"));
}
// 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"));
if (!OverlappedStdInWr)
{
// 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"));
}
else
{
// Create overlapped I/O pipes (only one side is overlapped).
if (!MyCreatePipeEx(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0, 0, FILE_FLAG_OVERLAPPED))
ErrorExit(TEXT("Stdin MyCreatePipeEx"));
ZeroMemory(&WriteOverlapped, sizeof(WriteOverlapped));
WriteOverlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); // Manual-reset event, unnamed, initially signalled.
if (WriteOverlapped.hEvent == NULL)
ErrorExit(TEXT("CreateEvent Write"));
}
// 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.
TCHAR* szMutableCmdline = new TCHAR[1024];
ZeroMemory(szMutableCmdline, 1024 * sizeof(TCHAR));
CopyMemory(szMutableCmdline, szCmdline, _tcslen(szCmdline) * sizeof(TCHAR));
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
szMutableCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
// If an error occurs, exit the application.
if (!bSuccess)
ErrorExit(TEXT("CreateProcess"));
else
{
// Close handles to the child process and its primary thread.
// Some applications might keep these handles to monitor the status
// of the child process, for example.
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
}
}
int main()
{
printf("Launching...\n");
Create();
Sleep(500);
ReadFromPipe();
Sleep(250);
WriteToPipe("A\r\n");
Sleep(250);
ReadFromPipe();
WriteToPipe("\r\n");
Sleep(250);
ReadFromPipe();
WriteToPipe("X\r\n");
Sleep(250);
ReadFromPipe();
Sleep(250);
ReadFromPipe();
printf("Press any key to exit.\n");
_getch();
// TODO: Not doing proper cleanup in this test app. Overlapped I/O, CloseHandles, etc. are outstanding. Bad.
return 0;
}
And child code can be as simple as:
#include <conio.h>
int main()
{
printf("Hello!\n");
_getch();
printf("Bye!\n");
return 0;
}
Edit: As #Rbmm points out, _getch() uses ReadConsoleInput(). I assume it uses CONIN$ as opposed to STDIN. So the question becomes: can I redirect CONIN$ or have the parent process write to it?
In child process after printf you can add fflush(stdout);. This will immediately transfer data from stdout buffer to pipe. In some configurations stdout buffer data is automatically flushed on end of line character \n, but I'm not sure if it is in this case - probably not.
If your child should read data from pipe (not from console) use getchar, fgets, fread, fscanf giving them stdin as stream argument.
int main()
{
printf("Hello!\n");
fflush(stdout);
getchar();
printf("Bye!\n");
fflush(stdout);
return 0;
}
And you don't have dead lock. Your child just waits for char from console. Press Enter key to revive it.
I'm having strange issues spawning a process, i found almost nothing related to this...
I need to spawn a command-line child process and be capable of getting its output and sending input before it ends.
I can do it fine with cmd.exe, and runs smooth...
BUT when i try to do it with external command-line applications they doesn't output nothing to my program until end of execution. I can send input, it processes normally, the number of times i want to, and at finish of child i get ALL the accumulated output.
The related code is:
SECURITY_ATTRIBUTES secatr;
secatr.nLength = sizeof(SECURITY_ATTRIBUTES);
secatr.bInheritHandle = TRUE;
secatr.lpSecurityDescriptor = NULL;
CreatePipe(&stdin_read, &stdin_write, &secatr, 0);
CreatePipe(&stdout_read, &stdout_write, &secatr, 0);
SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0);
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(PROCESS_INFORMATION));
memset(&si, 0, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdError = si.hStdOutput = stdout_write;
si.hStdInput = stdin_read;
CreateProcess(NULL,c:\\\\app.exe, NULL, NULL, TRUE, 0, NULL, "c:\\\\", &si, &pi);
handleproceso = pi.hProcess;
handlethread = (HANDLE)_beginthreadex(NULL,0,&Read,NULL,0,NULL);
_beginthreadex(NULL,0,&Write,NULL,0,NULL);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
unsigned __stdcall Read(void *a){
char buff[100];
while(1){
memset(buff,0,sizeof(buff));
ReadFile( stdout_read, buff, sizeof(buff)-1, &dwRead, NULL);
send(socketcliente,buff,strlen(buff),0);
if(exit1){
exit1 = false;
break;
}
}
return 0;
}
unsigned __stdcall Write(void *a){
char ddd[100];
while(1){
memset(ddd,0,sizeof(ddd));
if(recv(socketcliente,ddd,sizeof(ddd)-1,0) == SOCKET_ERROR){
CloseHandle(handlethread);
TerminateProcess(handleproceso,1);
break;
}
WriteFile(stdin_write, ddd, strlen(ddd), &dwWritten, NULL);
if(strncmp(ddd,"exit",4) == 0){
exit1 = true;
//CloseHandle(handlethread);
break;
}
}
return 0;
}
I tried the MSDN example of How to spawn console processes with redirected standard handles , with the child process fflushing output buffer it receives the output fine...if I remove that fflush i get the same problem of other programs.
The portion of code looks as:
printf("Child echoing [%s]\n",szInput);
fflush(NULL); // Must flush output buffers or else redirection
// will be problematic.
What can I do if the program I want to spawn doesn't behave that way?Can I force its output or something?
I cannot find any response to that in the whole internet...Thank you in advance and apologies for my basic english.
Try:
fflush(stdout);
or at the beginning of your program just disable buffering:
setbuf(stdout, NULL);
Im trying to print the output and input of cmd commands recieved to stdout just like a normal cmd.exe would do.
I could use the function _popen but when i start a program like Python or Powershell whith it, it doesnt work. So i Need the Output of the child process and be able to send commands to the child process.
there is a simmilar question here
So i modified the code from this link to look like this:
void WriteToPipe(char* command){
DWORD dwRead, dwWritten;
BOOL bSuccess = FALSE;
bSuccess = WriteFile(g_hChildStd_IN_Wr, command, strlen(command), &dwWritten, NULL);
// Close the pipe handle so the child process stops reading.
if (!CloseHandle(g_hChildStd_IN_Wr))
ErrorExit(TEXT("StdInWr CloseHandle"));
}
void ReadFromPipe(void){
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
bSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL);
//bSuccess = PeekNamedPipe(g_hChildStd_OUT_Rd, NULL, BUFSIZE, &dwRead, &dwTotalAvailBytes, &dwBytesLeft);
}
the main loop looks like this:
CreateChildProcess("C:\\Python27\\python.exe");
char input_buffer[100] = { 0 };
while (1){
fgets(input_buffer, 99, stdin);
WriteToPipe(input_buffer);
ReadFromPipe();
}
everything else (from the code) stayed the same.
Now my Problem is, i want to enter multiple commands to the same process, but there is a CloseHandle fucntion in WriteToPipe, after the handle is closed i cant enter more commands.
How to get a valid HANDLE to write more than 1 command to the process ?
int WriteToPipe(char* command){
DWORD dwRead, dwWritten;
BOOL bSuccess = FALSE;
bSuccess = WriteFile(g_hChildStd_IN_Wr, command, strlen(command), &dwWritten, NULL);
return bSuccess ;
}
close the handle in main() function since g_hChildStd_IN_Wr is global. also, return bSuccess; notify you if write to pipe has succeeded
put ReadFromPipe() in a separate Thread;
my code here is a garbage but it is working code, it is for test purpose only.
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#define BUFSIZE 4096
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hInputFile = NULL;
int CreateChildProcess(TCHAR *szCmdline);
void PrintError(char *text,int err);
int InitPipes();
int WriteToPipe(char* command){
DWORD dwRead, dwWritten;
BOOL bSuccess = FALSE;
SetLastError(0);
WriteFile(g_hChildStd_IN_Wr, command, strlen(command), &dwWritten, NULL);
bSuccess=GetLastError();
PrintError("WriteToPipe",bSuccess);
return (bSuccess==0)||(bSuccess==ERROR_IO_PENDING);
}
int ReadFromPipe(void){
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetLastError(0);
while(
ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL)
&&
WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL)
);
bSuccess=GetLastError();
PrintError("ReadFromPipe",bSuccess);
return (bSuccess==0)||(bSuccess==ERROR_IO_PENDING);
}
HANDLE hThread;
int __stdcall ThreadProc(int arg){
while(ReadFromPipe())
;
return 0;
}
int main(){
char input_buffer[100] = { 0 };
if(!InitPipes()){
printf("Failed to CreatePipes\n");
return -1;
}
if(!CreateChildProcess("cmd.exe")){
printf("Failed to create child process\n");
return -2;
}
hThread=CreateThread(NULL,0,ThreadProc,NULL,0,NULL);
while (1){
fgets(input_buffer, 99, stdin);
if(!WriteToPipe(input_buffer)) break;
}
printf("Program terminated\n");
return 0;
}
int CreateChildProcess(TCHAR *szCmdline){
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
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
// If an error occurs, exit the application.
if ( bSuccess ){
// 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 bSuccess;
}
int InitPipes(){
SECURITY_ATTRIBUTES saAttr;
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )
return 0;
if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
return 0;
if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
return 0;
if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
return 0;
return 1;
}
void PrintError(char *text,int err){
DWORD retSize;
LPTSTR pTemp=NULL;
if(!err) return;
retSize=FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM|
FORMAT_MESSAGE_ARGUMENT_ARRAY,
NULL,
err,
LANG_NEUTRAL,
(LPTSTR)&pTemp,
0,
NULL );
if(pTemp) printf("%s: %s\n",text,pTemp);
LocalFree((HLOCAL)pTemp);
return ;
}
I am trying to send a message to stdin of an existing process. This process writes to stdout, and therefore a command prompt is visible when the process is running.
I have looked here, but it is specific to .net. I would like to use C with the winapi.
Here is what I have tried:
I launch an exe that runs in a command prompt. When launched, the Process ID and Process Handle for the exe are captured by calling GetHandleOfProcessByExeName() (below). My understanding , through reading the MSDN page on WriteFile(), that I should be able to pass the handle of the process along with some text to the process pointed to by the handle. Say "prg.exe", is running on Windows 7. I get its process handle, then pass it along with a message to WriteToProcess().
I expect to see text appear on the command prompt, but this has not happened yet.
Relevant code:
int main(void)
{
HANDLE h = GetHandleOfProcessByExeName("prg.exe");
//this continually fails (returns FALSE)
BOOL status = WriteToProcess("test message", sizeof("test message"));
return 0;
}
HANDLE GetHandleOfProcessByExeName(char *exe)
{
PROCESSENTRY32 entry;
HANDLE hProcess=0;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (Process32First(snapshot, &entry) == TRUE)
{
while (Process32Next(snapshot, &entry) == TRUE)
{
if (stricmp(entry.szExeFile, exe) == 0)
{
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
CloseHandle(hProcess);
}
}
}
CloseHandle(snapshot);
return hProcess;
}
BOOL WriteToProcess(char *msg, size_t size)
{
BOOL status = FALSE;
status = WriteFile(gStdinWrite, msg, size, NULL, NULL);
return status; //TRUE for success
}
Edit to response to comments:
defined as file globals:
HANDLE gStdinRead = NULL;
HANDLE gStdinWrite = NULL;
int SystemX(char *cmd, int index)
{
STARTUPINFO sj;
PROCESS_INFORMATION pj;
SECURITY_ATTRIBUTES saAttr;
HANDLE h = 0;
int exit;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
ZeroMemory( &sj, sizeof(sj) );
sj.cb = sizeof(sj);
ZeroMemory( &pj, sizeof(pj) );
//create pipe and pass read end to CreateProcess
CreatePipe(&gStdinRead, &gStdinWrite, &saAttr, 0);
sj.hStdInput = gStdinRead;
sj.hStdOutput = gStdinWrite;
if(!CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &sj, &pj))
{
return h;
}
//Wait until child processes exit.
WaitForSingleObject( pj.hProcess, IGNORE ); //ingnore signal
//Get exit code
GetExitCodeProcess(pj.hProcess, (LPDWORD)(&exit));
return exit;
}