CreateProcess - not capturing stdout

I am using some code i found to capture the stdout when using CreateProcess.  I am not getting anything, and not sure if my process is even running although i get no errors.  One thing i am thinking of is, when using the lpCurrentDirectory parameter, does this need a trailing \ or should the backslash be removed?  Currently it is removed.
// .h file
#pragma once
#include <string.h>
#include <iostream>
#include <sstream>
#include <stdlib.h>
//#include <windows.h>
#include <afxwin.h>
#include <string.h>
#include <tchar.h>
 
class Shell
{
public:
	Shell();
	//int Execute(char* cmd, char* outbuf, char* startDir = NULL);
	int Execute(char* cmd, std::string* outstr, char* startDir = NULL);
	HANDLE ExecuteAsync(char* cmd, std::string* outstr, char* startDir = NULL);
 
   HANDLE hChildProcess;
   HANDLE hStdIn; // Handle to parents std input.
   HANDLE hPipeWrite; // Handle to parents std input.
   BOOL bRunThread;
 
   std::string str;
	//DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam);
	void ReadAndHandleOutput(HANDLE hPipeRead);
	HANDLE PrepAndLaunchRedirectedChild(char* cmd, HANDLE hChildStdOut,
                                     HANDLE hChildStdIn,
                                     HANDLE hChildStdErr, char* startDir);
 
	void ErrorExit(LPTSTR lpszFunction);
	void DebugError(char *pszAPI);
 
	char* _cmd;
	std::string* _outstr;
};
 
struct EXECARGS
{
	Shell* Shell;
	char* Cmd;
	char* StartDir;
	std::string* OutStr;
};
 
 
//C++ file
#define WINVER 0x0400
#include "Shell.h"
#include <process.h>
   /*++
 
      Copyright (c) 1998  Microsoft Corporation
 
      Module Name:
 
         Redirect.c
 
      Description:
          This sample illustrates how to spawn a child console based
          application with redirected standard handles.
 
          The following import libraries are required:
          user32.lib
 
      Dave McPherson (davemm)   11-March-98
 
   --*/ 
 
DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam);
 
Shell::Shell()
{
   hChildProcess = NULL;
   hStdIn = NULL; // Handle to parents std input.
   bRunThread = TRUE;
   hPipeWrite = NULL;
}
 
unsigned int __stdcall ExecuteOnThread(void* lpvThreadParam)
{
	EXECARGS* args = (EXECARGS*)lpvThreadParam;
	int ret = args->Shell->Execute(args->Cmd,args->OutStr,args->StartDir);
	delete args;
	return ret;
}
 
HANDLE Shell::ExecuteAsync(char* cmd, std::string* outstr, char* startDir)
{
	EXECARGS* args = new EXECARGS;
	args->Shell = this;
	args->Cmd = cmd;
	args->OutStr = outstr;
	args->StartDir = startDir;
 
	unsigned int dwThreadId;
	HANDLE hThread = (HANDLE)_beginthreadex(
		NULL,       // pointer to security attributes
		0,          // initial thread stack size
		ExecuteOnThread, // pointer to thread function
		args,          // argument for new thread
		0,          // creation flags (immediate)
		&dwThreadId // pointer to receive thread ID
	);
	return hThread;
}
 
//outbuf will be assigned new memory, so we must send in an uninitialized pointer
int Shell::Execute(char* cmd, std::string* outstr, char* startDir)
{
	HANDLE hProc;
	DWORD ret;
 
	#ifdef _DEBUG
	printf("Shell: cmd=%s\nShell: startDir=%s\n",cmd,startDir);
	#endif
	//if we aren't looking for the console output
	if( outstr == NULL)
	{
	printf("Executing with no console capture\n");
		hProc = PrepAndLaunchRedirectedChild(cmd,NULL,NULL,NULL,startDir);
		ret = STILL_ACTIVE;
		do
		{
			Sleep(200);
			GetExitCodeProcess(hProc,&ret);
		}
		while(ret == STILL_ACTIVE);
		return ret;
	}
 
    HANDLE hOutputReadTmp,hOutputRead,hOutputWrite;
    HANDLE hInputWriteTmp,hInputRead,hInputWrite;
    HANDLE hErrorWrite;
    HANDLE hThread;
    DWORD ThreadId;
    SECURITY_ATTRIBUTES sa;
 
    // Set up the security attributes struct.
    sa.nLength= sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;
 
    // Create the child output pipe.
    if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0))
        DebugError("CreatePipe");
 
 
    // Create a duplicate of the output write handle for the std error
    // write handle. This is necessary in case the child application
    // closes one of its std output handles.
    if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite,
                        GetCurrentProcess(),&hErrorWrite,0,
                        TRUE,DUPLICATE_SAME_ACCESS))
        DebugError("DuplicateHandle");
 
 
    // Create the child input pipe.
    if (!CreatePipe(&hInputRead,&hInputWriteTmp,&sa,0))
        DebugError("CreatePipe");
 
 
    // Create new output read handle and the input write handles. Set
    // the Properties to FALSE. Otherwise, the child inherits the
    // properties and, as a result, non-closeable handles to the pipes
    // are created.
    if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
                        GetCurrentProcess(),
                        &hOutputRead, // Address of new handle.
                        0,FALSE, // Make it uninheritable.
                        DUPLICATE_SAME_ACCESS))
        DebugError("DupliateHandle");
 
    if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp,
                        GetCurrentProcess(),
                        &hInputWrite, // Address of new handle.
                        0,FALSE, // Make it uninheritable.
                        DUPLICATE_SAME_ACCESS))
    DebugError("DupliateHandle");
 
 
    // Close inheritable copies of the handles you do not want to be
    // inherited.
    if (!CloseHandle(hOutputReadTmp)) DebugError("CloseHandle");
    if (!CloseHandle(hInputWriteTmp)) DebugError("CloseHandle");
 
 
    // Get std input handle so you can close it and force the ReadFile to
    // fail when you want the input thread to exit.
    if ( (hStdIn = GetStdHandle(STD_INPUT_HANDLE)) ==
                                            INVALID_HANDLE_VALUE )
        DebugError("GetStdHandle");
 
 
	hProc = PrepAndLaunchRedirectedChild(cmd,hOutputWrite,hInputRead,hErrorWrite,startDir);
	if(!hProc)
	{
		#ifdef _DEBUG
		printf("Shell: Error PrepAndLaunchRedirectChild\n");
		#endif
 
		return -1;
	}
 
    // Close pipe handles (do not continue to modify the parent).
    // You need to make sure that no handles to the write end of the
    // output pipe are maintained in this process or else the pipe will
    // not close when the child process exits and the ReadFile will hang.
    if (!CloseHandle(hOutputWrite)) DebugError("CloseHandle");
    if (!CloseHandle(hInputRead )) DebugError("CloseHandle");
    if (!CloseHandle(hErrorWrite)) DebugError("CloseHandle");
 
	if(1)  // != NULL)
	{
    // Launch the thread that gets the input and sends it to the child.
	hPipeWrite = hInputWrite;
//    hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)GetAndSendInputThread,
//                            (LPVOID)hInputWrite,0,&ThreadId);
//TODO:  change to _createthread
    hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)GetAndSendInputThread,
                            (LPVOID)this,0,&ThreadId);
 
	if (hThread == NULL) DebugError("CreateThread");
 
 
    // Read the child's output.
 
	str = "";
    ReadAndHandleOutput(hOutputRead);
	// Redirection is complete
	*outstr = str;
 
    // Force the read on the input to return by closing the stdin handle.
    //this is causing a problem.. so just ignore it and it will go away!
	if (!CloseHandle(hStdIn)) DebugError("CloseHandle");
 
 
    // Tell the thread to exit and wait for thread to die.
    bRunThread = FALSE;
 
    if (WaitForSingleObject(hThread,INFINITE) == WAIT_FAILED)
        DebugError("WaitForSingleObject");
 
    if (!CloseHandle(hOutputRead)) DebugError("CloseHandle");
    if (!CloseHandle(hInputWrite)) DebugError("CloseHandle");
	}
	ret = STILL_ACTIVE;
	do
	{
		Sleep(200);
		GetExitCodeProcess(hProc,&ret);
	}
	while(ret == STILL_ACTIVE);
 
	#ifdef _DEBUG
	printf("Shell: terminated properly\n");
	#endif
 
	return ret;
}
 
 
/////////////////////////////////////////////////////////////////////// 
// PrepAndLaunchRedirectedChild
// Sets up STARTUPINFO structure, and launches redirected child.
/////////////////////////////////////////////////////////////////////// 
HANDLE Shell::PrepAndLaunchRedirectedChild(char* cmd, HANDLE hChildStdOut,
                                    HANDLE hChildStdIn,
                                    HANDLE hChildStdErr, char* startDir)
{
    PROCESS_INFORMATION pi;
    STARTUPINFO si;
 
    // Set up the start up info struct.
    ZeroMemory(&si,sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    si.hStdOutput = hChildStdOut;
    si.hStdInput  = hChildStdIn;
    si.hStdError  = hChildStdErr;
    // Use this if you want to hide the child:
        si.wShowWindow = SW_HIDE;
    // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
    // use the wShowWindow flags.
 
 
    // Launch the process that you want to redirect (in this case,
    // Child.exe). Make sure Child.exe is in the same directory as
    // redirect.c launch redirect from a command line to prevent location
    // confusion.
//      if (!CreateProcess(NULL,"dir",NULL,NULL,TRUE,
//                         CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi))
//         DebugError("CreateProcess");
 
	//LPTSTR szCmdline=TEXT("c:\\windows\\system32\\ipconfig.exe");
	SECURITY_ATTRIBUTES sa;
	//ZeroMemory(&sa,sizeof(SECURITY_ATTRIBUTES));
	sa.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR) malloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
	InitializeSecurityDescriptor(sa.lpSecurityDescriptor, PROCESS_QUERY_INFORMATION);
	SetSecurityDescriptorDacl(sa.lpSecurityDescriptor,TRUE,(PACL) NULL,FALSE);
	//sa.lpSecurityDescriptor = PROCESS_QUERY_INFORMATION;
	sa.bInheritHandle = 1;
	sa.nLength = sizeof(sa);
	char* startDirParam = startDir;
	if(startDir == 0)
		startDirParam = 0;
	else
	{
		if(strlen(startDir) == 0)  //strlen throws exception if startDir == 0
			startDirParam = 0;
	}
 
//	BOOL procCreated = CreateProcess(NULL, cmd, NULL, 0, 1, CREATE_NO_WINDOW, 0, startDirParam, &si, &pi);
	BOOL procCreated = CreateProcess(NULL, cmd, NULL, 0, 1, 0, 0, startDirParam, &si, &pi);
	if(!procCreated)
	{
		#ifdef _DEBUG
		CString debugStr;
 
		LPVOID lpMsgBuf;
		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 );
 
		debugStr.Format("Shell CreateProc: %u, %s",dw,lpMsgBuf);
		OutputDebugString(debugStr.GetBuffer());
		printf("%s\n",debugStr.GetBuffer());
		#endif
	}
	delete sa.lpSecurityDescriptor;  //because of malloc
	if(!procCreated)
		DebugError("CreateProcess");
 
    // Set global child process handle to cause threads to exit.
    hChildProcess = pi.hProcess;
 
    // Close any unnecessary handles.
    if (!CloseHandle(pi.hThread)) DebugError("CloseHandle");
	return hChildProcess;
}
 
 
/////////////////////////////////////////////////////////////////////// 
// ReadAndHandleOutput
// Monitors handle for input. Exits when child exits or pipe breaks.
/////////////////////////////////////////////////////////////////////// 
void Shell::ReadAndHandleOutput(HANDLE hPipeRead)
{
    CHAR lpBuffer[256];
    DWORD nBytesRead;
 
    while(TRUE)
    {
		BOOL readS = ReadFile(hPipeRead,lpBuffer,sizeof(lpBuffer),
                                        &nBytesRead,NULL);
        if (!readS)
        {
        if (GetLastError() == ERROR_BROKEN_PIPE)
            break; // pipe done - normal exit path.
        else
            DebugError("ReadFile"); // Something bad happened.
        }
		str.append(lpBuffer,nBytesRead);
    }
}
 
 
/////////////////////////////////////////////////////////////////////// 
// GetAndSendInputThread
// Thread procedure that monitors the console for input and sends input
// to the child process through the input pipe.
// This thread ends when the child application exits.
/////////////////////////////////////////////////////////////////////// 
DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam)
{
    CHAR read_buff[256];
    DWORD nBytesRead,nBytesWrote;
    //HANDLE hPipeWrite = (HANDLE)lpvThreadParam;
	Shell* tthis = (Shell*)lpvThreadParam;
 
    // Get input from our console and send it to child through the pipe.
    while (tthis->bRunThread)
    {
		nBytesRead = 0;
		BOOL readS = ReadConsole(tthis->hStdIn,read_buff,1,&nBytesRead,NULL);
        if(!readS)
		{
			break;
        //DebugError("ReadConsole");
		}
 
        read_buff[nBytesRead] = '\0'; // Follow input with a NULL.
 
        if (!WriteFile(tthis->hPipeWrite,read_buff,nBytesRead,&nBytesWrote,NULL))
        {
        if (GetLastError() == ERROR_NO_DATA)
            break; // Pipe was closed (normal exit path).
        else
        tthis->DebugError("WriteFile");
        }
    }
 
    return 1;
}
 
void Shell::DebugError(char *pszAPI)
{
    LPVOID lpvMessageBuffer;
    CHAR szPrintBuffer[512];
 
    FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
            NULL, GetLastError(),
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPTSTR)&lpvMessageBuffer, 0, NULL);
 
    wsprintf(szPrintBuffer,
        "ERROR: API    = %s.\n   error code = %d.\n   message    = %s.\n",
            pszAPI, GetLastError(), (char *)lpvMessageBuffer);
 
	OutputDebugString(szPrintBuffer);
 
    LocalFree(lpvMessageBuffer);
}
 
void Shell::ErrorExit(LPTSTR lpszFunction) 
{ 
    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError(); 
 
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM,
        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)); 
    wsprintf((LPTSTR)lpDisplayBuf, 
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 
 
    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(dw); 
}

Open in new window

LVL 1
bowser17Asked:
Who is Participating?
 
bowser17Connect With a Mentor Author Commented:
The problem is something else.  I found this after determining a differnet way to get the ouput.  there truely isn't any, thats why i dont see any.  Thanks for trying.  jkr, this was the first part to the questions you are answering now (2-19-08).  Thanks
0
 
cupCommented:
Has it been built as a console program or a windows program?
0
 
bowser17Author Commented:
The process to be created is console.  I am investigating one thing right now.   i am not using the applicationname, but rather the command line.  WHat i am also not sure of is what the current directory is.  I assumed it was the dir of the calling exe, but now i am not so sure.  I may need to prepend the directory path to the exe in the commandline param (#2 of createprocess).  THat will prove to be slightly complex when parsing the command as it is dynamic.
0
The new generation of project management tools

With monday.com’s project management tool, you can see what everyone on your team is working in a single glance. Its intuitive dashboards are customizable, so you can create systems that work for you.

 
cupCommented:
Shouldn't need to do that - if you get __argv[0], it will tell you the full name of the process.
0
 
jkrCommented:
So you are having trouble starting the process or reading the output?
0
 
bowser17Author Commented:
Sorry i haven't responded in a while, i was on vacation.  so now i m back.  I have dertimed some scenarios where the code snippet works, and where it doesn't.

When running directly from the console command line, the call to create process works as expected.  I tested by creating a sample program that would give different output based on the start directory.  Where this breaks down is when i make the call from a windows service.  I dont get any output, not even incorrect output.  Is there something limiting me here?  One thing to note, the service is set to NOT interact with the desktop.  could this be what is blocking me?  If so, any proposed suggestions on how i get the output?  I'd prefer to handle it all in memory, no disk or file usage.
0
All Courses

From novice to tech pro — start learning today.