How can I read a child process's output? - c

I have written a function that attempts to read a child process's command line output via a pipe. This should be a simple subset of the MSDN Creating a Child Process with Redirected Input and Output article, but I am clearly making an error of some sort.
The ReadFile(...) call below blocks forever no matter if I place it before or after the WaitForSingleObject(...) call that should signal the end of the child process.
I have read all the answers that suggest "Use asynchronous ReadFile" and I am open to that suggestion if someone could give me some idea how that is accomplished on a pipe. Although I don't understand why asynchronous I/O should be needed for this case.
#include "stdafx.h"
#include <string>
#include <windows.h>
unsigned int launch( const std::string & cmdline );
int _tmain(int argc, _TCHAR* argv[])
{
launch( std::string("C:/windows/system32/help.exe") );
return 0;
}
void print_error( unsigned int err )
{
char* msg = NULL;
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&msg,
0, NULL );
std::cout << "------ Begin Error Msg ------" << std::endl;
std::cout << msg << std::endl;
std::cout << "------ End Error Msg ------" << std::endl;
LocalFree( msg );
}
unsigned int launch( const std::string & cmdline )
{
TCHAR cl[_MAX_PATH*sizeof(TCHAR)];
memset( cl, 0, sizeof(cl) );
cmdline.copy( cl, (_MAX_PATH*sizeof(TCHAR)) - 1);
HANDLE stdoutReadHandle = NULL;
HANDLE stdoutWriteHandle = NULL;
SECURITY_ATTRIBUTES saAttr;
memset( &saAttr, 0, sizeof(saAttr) );
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if ( ! CreatePipe(&stdoutReadHandle, &stdoutWriteHandle, &saAttr, 5000) )
throw std::runtime_error( "StdoutRd CreatePipe" );
// Ensure the read handle to the pipe for STDOUT is not inherited.
if ( ! SetHandleInformation(stdoutReadHandle, HANDLE_FLAG_INHERIT, 0) )
throw std::runtime_error( "Stdout SetHandleInformation" );
STARTUPINFO startupInfo;
memset( &startupInfo, 0, sizeof(startupInfo) );
startupInfo.cb = sizeof(startupInfo);
startupInfo.hStdError = stdoutWriteHandle;
startupInfo.hStdOutput = stdoutWriteHandle;
startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
char* rawEnvVars = GetEnvironmentStringsA();
//__asm _emit 0xcc;
PROCESS_INFORMATION processInfo;
memset( &processInfo, 0, sizeof(processInfo) );
std::cout << "Start [" << cmdline << "]" << std::endl;
if ( CreateProcessA( 0, &cl[0], 0, 0, false,
CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
rawEnvVars, 0, &startupInfo, &processInfo ) )
{
//CloseHandle( stdoutWriteHandle );
DWORD wordsRead;
char tBuf[257] = {'\0'};
bool success = true;
std::string outBuf("");
unsigned int t;
while(success) {
//__asm _emit 0xcc;
std::cout << "Just before ReadFile(...)" << std::endl;
success = ReadFile( stdoutReadHandle, tBuf, 256, &wordsRead, NULL);
(t=GetLastError())?print_error(t):t=t;
std::cout << "Just after ReadFile(...) | read " << wordsRead<< std::endl;
std::cout << ".";
if( success == false ) break;
outBuf += tBuf;
tBuf[0] = '\0';
}
std::cout << "output = [" << outBuf << "]" << std::endl;
if ( WaitForSingleObject( processInfo.hProcess, INFINITE ) == WAIT_OBJECT_0 )
{
unsigned int exitcode = 0;
GetExitCodeProcess( processInfo.hProcess, (LPDWORD)&exitcode );
std::cout << "exitcode = [" << exitcode << "]" << std::endl;
//__asm _emit 0xcc;
CloseHandle( processInfo.hProcess );
CloseHandle( processInfo.hThread );
return exitcode;
}
}
else
{
DWORD procErr = GetLastError();
std::cout << "FAILED TO CREATE PROCESS!" << std::endl;
print_error( procErr );
}
return -1;
} // end launch()

There are a few bugs in your code, but the most important is that you've specified FALSE for the bInheritHandles argument to CreateProcess. The new process can't use the pipe if it doesn't inherit the handle to it. In order for a handle to be inherited, the bInheritHandles argument must be TRUE and the handle must have inheritance enabled.
Other issues:
You're specifying CREATE_UNICODE_ENVIRONMENT but passing an ANSI environment block. Note that it is easier to pass NULL for lpEnvironment and let the system copy the environment block for you. You should still specify CREATE_UNICODE_ENVIRONMENT in this case, as described in the documentation, because your environment block might contain Unicode characters.
Similarly, if you're calling CreateProcessA you should be using STARTUPINFOA.
You don't zero-terminate tBuf each time around the loop, so you'll get spurious extra characters in your output buffer.
You need to close stdoutWriteHandle before you enter your read loop, or you won't know when the subprocess exits. (Or you could use asynchronous IO and check for process exit explicitly.)
GetLastError() is undefined if an API function succeeds, so you should only be calling it if ReadFile returns FALSE. (Of course, in this case this is purely cosmetic since you aren't acting on the error code.)
For reference, here is my corrected version of your code. I've turned it into plain C (sorry!) because that's what I'm familiar with. I compiled and tested in Unicode mode, but I think it should work without modification in ANSI mode too.
#define _WIN32_WINNT _WIN32_WINNT_WIN7
#include <windows.h>
#include <stdio.h>
void launch(const char * cmdline_in)
{
PROCESS_INFORMATION processInfo;
STARTUPINFOA startupInfo;
SECURITY_ATTRIBUTES saAttr;
HANDLE stdoutReadHandle = NULL;
HANDLE stdoutWriteHandle = NULL;
char cmdline[256];
char outbuf[32768];
DWORD bytes_read;
char tBuf[257];
DWORD exitcode;
strcpy_s(cmdline, sizeof(cmdline), cmdline_in);
memset(&saAttr, 0, sizeof(saAttr));
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&stdoutReadHandle, &stdoutWriteHandle, &saAttr, 5000))
{
printf("CreatePipe: %u\n", GetLastError());
return;
}
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(stdoutReadHandle, HANDLE_FLAG_INHERIT, 0))
{
printf("SetHandleInformation: %u\n", GetLastError());
return;
}
memset(&startupInfo, 0, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
startupInfo.hStdError = stdoutWriteHandle;
startupInfo.hStdOutput = stdoutWriteHandle;
startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
// memset(&processInfo, 0, sizeof(processInfo)); // Not actually necessary
printf("Starting.\n");
if (!CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, NULL, 0, &startupInfo, &processInfo))
{
printf("CreateProcessA: %u\n", GetLastError());
return;
}
CloseHandle(stdoutWriteHandle);
strcpy_s(outbuf, sizeof(outbuf), "");
for (;;) {
printf("Just before ReadFile(...)\n");
if (!ReadFile(stdoutReadHandle, tBuf, 256, &bytes_read, NULL))
{
printf("ReadFile: %u\n", GetLastError());
break;
}
printf("Just after ReadFile, read %u byte(s)\n", bytes_read);
if (bytes_read > 0)
{
tBuf[bytes_read] = '\0';
strcat_s(outbuf, sizeof(outbuf), tBuf);
}
}
printf("Output: %s\n", outbuf);
if (WaitForSingleObject(processInfo.hProcess, INFINITE) != WAIT_OBJECT_0)
{
printf("WaitForSingleObject: %u\n", GetLastError());
return;
}
if (!GetExitCodeProcess(processInfo.hProcess, &exitcode))
{
printf("GetExitCodeProcess: %u\n", GetLastError());
return;
}
printf("Exit code: %u\n", exitcode);
CloseHandle( processInfo.hProcess );
CloseHandle( processInfo.hThread );
return;
}
int main(int argc, char** argv)
{
launch("C:\\windows\\system32\\help.exe");
return 0;
}

There is an "LPOVERLAPPED lpOverlapped" parameter to ReadFile() which you have set to NULL. Looks like the only way to go is to allow overlapped I/O on your pipe and then use the WaitForSingleObject() for the "overlapped.hEvent".
Another way is to use the ConnectNamedPipe function and create the OVERLAPPED struct for the pipe.

Related

Child process (via CreateProcess) stalls on getch() with redirected stdout and stdin

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.

Windows CreateProcess and output redirection

I'm working in a C project, windows environment using WinAPI.
My function execs a command (e.g. : "dir C:\Users") it received as a param, and returns its output.
It is ran from a DLL so it must not spawn a cmd.exe windows, which means I can't use _popen. So I use CreateProcessA, and a pipe to catch stdout.
Here is the code :
char *runExec(char *command, int *contentLen) {
char *ret=NULL,*tmp=NULL;
DWORD readBytes;
int size=0;
char buffer[128],cmdBuf[4096];
HANDLE StdOutHandles[2];
CreatePipe(&StdOutHandles[0], &StdOutHandles[1], NULL, 4096);
STARTUPINFOA si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdOutput = StdOutHandles[1];
si.hStdError = StdOutHandles[1];
PROCESS_INFORMATION pi;
snprintf(cmdBuf,4096,"cmd /C %s", command);
if (!CreateProcessA(NULL, cmdBuf, NULL, NULL, FALSE, CREATE_NO_WINDOW | DETACHED_PROCESS, NULL, NULL, &si, &pi)) {
printf("Error createProcess : %d\n",GetLastError());
return NULL;
}
CloseHandle(StdOutHandles[1]);
printf("Before read\n");
while (ReadFile(StdOutHandles[0], buffer, 127, &readBytes, NULL)){
printf("IN WHILE\n");
buffer[readBytes] = 0;
printf("read %d bytes\n", readBytes);
size += readBytes;
tmp = (char *)realloc(ret, size + 1);
if (tmp == NULL) {
free(ret);
return NULL;
}
ret = tmp;
strncpy(ret + (size - readBytes), buffer, readBytes);
ret[size] = 0;
}
printf("Readfile returned with %d, read %d\n", GetLastError(),readBytes);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(StdOutHandles[0]);
printf("#%s#\n", ret);
return ret;
}
And the output is :
Before read
Readfile returned with 109, read 0
#(null)#
So from that I understand that :
- My loop isn't even executed once
- ReadFile reads nothing and returns Broken Pipe immediately
I have read various stackoverflow and MSDN pages, but I can't seem to make it work.
As pointed out in the comment, handles must be inheritable, which means replacing the CreatePipe like this :
SECURITY_ATTRIBUTES pipeAttrib;
memset(&pipeAttrib, 0, sizeof(pipeAttrib));
pipeAttrib.nLength = sizeof(SECURITY_ATTRIBUTES);
pipeAttrib.bInheritHandle = NULL;
pipeAttrib.bInheritHandle = TRUE;
if (!CreatePipe(&StdOutHandles[0], &StdOutHandles[1],&pipeAttrib, 4096)) {
printf("Create pipe error %d\n", GetLastError());
return NULL;
}
And setting CreateProcess 5th parameter (bInheritHandles) to TRUE instead of FALSE.

Reading asynchronous pipe - loosing data [duplicate]

Modified code from
Named Pipe Server Using Overlapped I/O
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365603(v=vs.85).aspx
The server code is as follows:
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#include <iostream>
#define CONNECTING_STATE 0
#define READING_STATE 1
#define INSTANCES 4
#define PIPE_TIMEOUT 5000
#define BUFSIZE 4096
typedef struct
{
OVERLAPPED oOverlap;
HANDLE hPipeInst;
TCHAR chRequest[BUFSIZE];
DWORD cbRead;
TCHAR chReply[BUFSIZE];
DWORD cbToWrite;
DWORD dwState;
BOOL fPendingIO;
} PIPEINST, *LPPIPEINST;
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
PIPEINST Pipe[INSTANCES];
HANDLE hEvents[INSTANCES];
int _tmain(VOID)
{
DWORD i, dwWait, cbRet, dwErr;
BOOL fSuccess;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
for (i = 0; i < INSTANCES; i++)
{
hEvents[i] = CreateEvent(
NULL, // default security attribute
FALSE, // manual-reset event
TRUE, // initial state = signaled
NULL); // unnamed event object
if (hEvents[i] == NULL)
{
printf("CreateEvent failed with %d.\n", GetLastError());
return 0;
}
Pipe[i].oOverlap.hEvent = hEvents[i];
DWORD dwOpenMode = PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED;
Pipe[i].oOverlap.Offset = 0;
Pipe[i].oOverlap.OffsetHigh = 0;
Pipe[i].hPipeInst = CreateNamedPipe(
lpszPipename,
dwOpenMode,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
INSTANCES,
BUFSIZE*sizeof(TCHAR),
BUFSIZE*sizeof(TCHAR),
PIPE_TIMEOUT,
NULL);
if (Pipe[i].hPipeInst == INVALID_HANDLE_VALUE)
{
printf("CreateNamedPipe failed with %d.\n", GetLastError());
return 0;
}
BOOL rc = ConnectNamedPipe(Pipe[i].hPipeInst, &Pipe[i].oOverlap); // Overlapped ConnectNamedPipe should return FALSE.
if (!rc && GetLastError() == ERROR_PIPE_CONNECTED) {
std::cout<<"pipe connected setting event " << std::endl;
rc = SetEvent(&Pipe[i].oOverlap.hEvent);
} else if (rc || GetLastError() != ERROR_IO_PENDING) {
std::cout<<"exiting... " << std::endl;
rc = CloseHandle(Pipe[i].hPipeInst);
return 0;
}
}// for INSTANCES
while (1)
{
dwWait = WaitForMultipleObjects(
INSTANCES, // number of event objects
hEvents, // array of event objects
FALSE, // does not wait for all
INFINITE); // waits indefinitely
i = dwWait - WAIT_OBJECT_0; // determines which pipe
if (i < 0 || i > (INSTANCES - 1))
{
printf("Index out of range.\n");
return 0;
}
fSuccess = GetOverlappedResult(
Pipe[i].hPipeInst, // handle to pipe
&Pipe[i].oOverlap, // OVERLAPPED structure
&cbRet, // bytes transferred
FALSE); // do not wait
std::cout<<"GetOverlappedResult " << cbRet;
std::cout<<" success " << fSuccess;
std::cout<<" state " << Pipe[i].dwState;
std::cout<<" GetLastError " << GetLastError() << std::endl;
fSuccess = ReadFile(
Pipe[i].hPipeInst,
Pipe[i].chRequest,
BUFSIZE*sizeof(TCHAR),
&Pipe[i].cbRead,
&Pipe[i].oOverlap);
if(!fSuccess)
std::wcout<<L" Error: "<< GetLastError() <<std::endl;
if (fSuccess && Pipe[i].cbRead != 0)
{
Pipe[i].fPendingIO = FALSE;
Pipe[i].dwState = READING_STATE;
std::wcout<<L"Message " << Pipe[i].chRequest << std::endl;
continue;
}
dwErr = GetLastError();
if (! fSuccess && (dwErr == ERROR_IO_PENDING))
{
std::cout<<"Error IO is still pending" << std::endl;
Pipe[i].fPendingIO = TRUE;
continue;
}
break;
}
return 0;
}
The client code is as follows:
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <string>
#include <sstream>
#include <time.h>
#include <iostream>
#define BUFSIZE 4096
int _tmain(int argc, TCHAR *argv[])
{
HANDLE hPipe;
TCHAR chBuf[BUFSIZE];
BOOL fSuccess = FALSE;
DWORD cbRead, cbToWrite, cbWritten, dwMode;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
BOOL rc;
do {
hPipe = CreateFileW(lpszPipename, GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (hPipe == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_PIPE_BUSY) {
// wait for the pipe to become available
rc = WaitNamedPipeW(lpszPipename, NMPWAIT_USE_DEFAULT_WAIT);
if (!rc) return false;
} else {
return false;
}
}
} while (hPipe == INVALID_HANDLE_VALUE);
dwMode = PIPE_READMODE_MESSAGE;
fSuccess = SetNamedPipeHandleState(
hPipe, // pipe handle
&dwMode, // new pipe mode
NULL, // don't set maximum bytes
NULL); // don't set maximum time
if ( ! fSuccess)
{
_tprintf( TEXT("SetNamedPipeHandleState failed. GLE=%d\n"), GetLastError() );
return -1;
}
//===================================================================
while(1)
{
std::cout<<"press a key to send " << std::endl;
_getch();
fSuccess = WriteFile(
hPipe,
"A",
1,
&cbWritten,
NULL);
if ( ! fSuccess)
{
_tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError() );
return -1;
}
fSuccess = WriteFile(
hPipe,
"B",
1,
&cbWritten,
NULL);
if ( ! fSuccess)
{
_tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError() );
return -1;
}
//Sleep(1000);
fSuccess = WriteFile(
hPipe,
"C",
1,
&cbWritten,
NULL);
if ( ! fSuccess)
{
_tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError() );
return -1;
}
fSuccess = WriteFile(
hPipe,
"D",
1,
&cbWritten,
NULL);
if ( ! fSuccess)
{
_tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError() );
return -1;
}
}// loop
printf("\n<End of message, press ENTER to terminate connection and exit>");
_getch();
CloseHandle(hPipe);
return 0;
}
The server never receives message A but B, C, D are received OK.
If you uncomment //Sleep(1000) in the client, only B and D are received.
Any idea as to why this happens? The server output with out the sleep is as follows:
GetOverlappedResult 0 success 1 state 0 GetLastErr
or 997
Error: 997
Error IO is still pending
GetOverlappedResult 1 success 1 state 0 GetLastErr
or 997
Message B
GetOverlappedResult 1 success 1 state 1 GetLastErr
or 997
Message C
GetOverlappedResult 1 success 1 state 1 GetLastErr
or 997
Message D
GetOverlappedResult 1 success 1 state 1 GetLastErr
or 997
Error: 997
Error IO is still pending
In the server's read loop, you are discarding any data that arrives asynchronously.
After GetOverlappedResult() has reported that the pending I/O operation is complete, the buffer contains the data from that operation. You're ignoring that data and issuing a new read operation into the same buffer.
The only reason you get any of the messages is that (on most runs) all four messages will be written into the pipe's internal buffer at the same time. The first message arrives asynchronously, so you miss it, but the remaining three messages are already in the pipe so those reads can complete immediately.

Problems with libbz2

I can't get libbz2 to work. After 10 000 characters it terminates with end of stream although the input file contains several gigabytes. I just cannot figure out what goes wrong. Thank you for any remarks.
Here is my code:
string read_bz2_file(const string& filename) {
string result;
FILE* f;
BZFILE* b;
const int BUFSIZE = 1000;
char buf[BUFSIZE];
int bzerror;
f = fopen(filename.c_str(), "r");
if (!f) {
/* handle error */
cerr << "cannot read file " << filename << endl;
return "";
}
b = BZ2_bzReadOpen(&bzerror, f, 0, 0, NULL, 0);
if (bzerror != BZ_OK) {
BZ2_bzReadClose(&bzerror, b);
cerr << "cannot read file " << filename << endl;
return "";
/* handle error */
}
bzerror = BZ_OK;
while (bzerror == BZ_OK /* arbitrary other conditions */) {
BZ2_bzRead(&bzerror, b, buf, BUFSIZE /* size of buf */);
cout << "bzerror: " << bzerror << endl;
if (bzerror == BZ_OK) {
/* do something with buf[0 .. nBuf-1] */
result.append(buf);
}
}
if (bzerror != BZ_STREAM_END) {
BZ2_bzReadClose(&bzerror, b);
cerr << "error while reading file " << filename << endl;
return "";
/* handle error */
} else {
result.append(buf);
BZ2_bzReadClose(&bzerror, b);
return result;
}
return result;
}
You can try to open the file in binary mode, adding a "b" to the fopen mode:
f = fopen(filename.c_str(), "rb");

Finding address range of process ( C / Windows )

I am trying to inspect Notepad's memory in real time, so I can detect when a specific string is written there.
I guess step 1 would be to find out what is the memory range of that process.
This is what I have until now :
int main()
{
MEMORY_BASIC_INFORMATION meminfo;
unsigned char *addr = 0;
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,FALSE,7280);
VirtualQueryEx(hProcess,addr,&meminfo,sizeof(meminfo));
CloseHandle(hProcess);
}
I suppose I need to manipulate meminfo in some way to grab the information from there.
I need to find the full address range so I can iterate trough the addresses and search for the string.
Any help will be much appreciated, I apologize in advance if this is a stupid question but I'm just starting out with C.
Thanks
To get a list of all allocated, valid memory regions you can use VirtualQueryEx in a while loop like so:
#include <iostream>
#include <windows.h>
#include <TlHelp32.h>
#include <tchar.h>
DWORD GetProcId(const TCHAR* procName)
{
DWORD procId = 0;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32 procEntry;
procEntry.dwSize = sizeof(procEntry);
if (Process32First(hSnap, &procEntry))
{
do
{
if (!_tcsicmp(procEntry.szExeFile, procName))
{
procId = procEntry.th32ProcessID;
break;
}
} while (Process32Next(hSnap, &procEntry));
}
}
CloseHandle(hSnap);
return procId;
}
int main()
{
DWORD procid = GetProcId("ac_client.exe");
MEMORY_BASIC_INFORMATION meminfo;
unsigned char* addr = 0;
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
MEMORY_BASIC_INFORMATION mbi;
while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)))
{
if (mbi.State != MEM_COMMIT || mbi.Protect == PAGE_NOACCESS)
{
std::cout << "base : 0x" << std::hex << mbi.BaseAddress << " end : 0x" << std::hex << (uintptr_t)mbi.BaseAddress + mbi.RegionSize << "\n";
}
addr += mbi.RegionSize;
}
CloseHandle(hProc);
}

Resources