// Copyright(C)2010 All Rights Reserved by Junseok Oh
//
// author: Junseok Oh
// email: batt22@naver.com, swolf22@nate.com, batt842@gmail.com
// 08.Nov.2010
//
// This source code is free to deploy and modify.
// You don't need to keep this license header.
// An acknowledgement is not required.
// Because it's simple. :-)
class ChildProcess {
public:
ChildProcess() :
m_bInit(FALSE),
m_hOutputRead(INVALID_HANDLE_VALUE),
m_hOutputWrite(INVALID_HANDLE_VALUE),
m_hInputRead(INVALID_HANDLE_VALUE),
m_hInputWrite(INVALID_HANDLE_VALUE),
m_hErrorWrite(INVALID_HANDLE_VALUE),
m_nShowCmd(SW_SHOWNORMAL),
m_doMonitor(FALSE),
m_nMaxSilentPeriod(5000),
m_bMonitorResult(TRUE),
m_bVerbose(FALSE),
m_hReadActionEvent(INVALID_HANDLE_VALUE)
{
HANDLE hOutputReadTmp, hInputWriteTmp;
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,&m_hOutputWrite,&sa,0))
return;
// 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(),m_hOutputWrite,
GetCurrentProcess(),&m_hErrorWrite,0,
TRUE,DUPLICATE_SAME_ACCESS))
return;
// Create the child input pipe.
if (!CreatePipe(&m_hInputRead,&hInputWriteTmp,&sa,0))
return;
// 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(),
&m_hOutputRead, // Address of new handle.
0,FALSE, // Make it uninheritable.
DUPLICATE_SAME_ACCESS))
return;
if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp,
GetCurrentProcess(),
&m_hInputWrite, // Address of new handle.
0,FALSE, // Make it uninheritable.
DUPLICATE_SAME_ACCESS))
return;
// Close inheritable copies of the handles you do not want to be
// inherited.
if (!CloseHandle(hOutputReadTmp))
return;
if (!CloseHandle(hInputWriteTmp))
return;
m_bInit = TRUE;
}
~ChildProcess()
{
if(!m_bInit)
return;
CloseHandlesForParent();
CloseHandlesForChild();
}
void SetInfomation( LPCTSTR szCmdLine, LPCTSTR szWorkingDirectory, int nShowCmd )
{
if(!m_bInit)
return;
_tcscpy_s( m_szCmdLine, MAX_PATH, szCmdLine );
_tcscpy_s( m_szWorkingDirectory, MAX_PATH, szWorkingDirectory );
m_nShowCmd = nShowCmd;
}
void SetMonitorPolicy( BOOL doMonitor, long nMaxSilentPeriod, BOOL bVerbose=FALSE )
{
if(!m_bInit)
return;
m_doMonitor = doMonitor;
m_nMaxSilentPeriod = nMaxSilentPeriod;
m_bVerbose = bVerbose;
}
BOOL CreateProcess()
{
int nResult = 0;
DWORD ExitCode = 0;
LPTSTR szWorkingDirectory = NULL;
STARTUPINFO si;
PROCESS_INFORMATION pi;
if( _tcslen(m_szWorkingDirectory)>0 )
szWorkingDirectory = m_szWorkingDirectory;
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW ;
si.hStdInput = m_hInputRead;//GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = m_hOutputWrite;//GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = m_hErrorWrite;//GetStdHandle(STD_ERROR_HANDLE);
si.wShowWindow = m_nShowCmd;
nResult = ::CreateProcess( NULL, m_szCmdLine, NULL, NULL, TRUE, CREATE_NEW_CONSOLE|CREATE_SUSPENDED, NULL, szWorkingDirectory, &si, &pi );
CloseHandlesForChild();
if( m_doMonitor )
CreateThread( NULL, 0, DoMonitorThread, (LPVOID)this, 0, NULL);
CreateThread( NULL, 0, ReadChildProcessConsoleThread, (LPVOID)this, 0, NULL);
ResumeThread( pi.hThread );
m_bMonitorResult = TRUE;
if (nResult==TRUE)
{
while( WaitForSingleObject( pi.hProcess, 100 )==WAIT_TIMEOUT && m_bMonitorResult )
{};
}
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandlesForParent();
return nResult&m_bMonitorResult;
}
private:
void DoMonitor()
{
if(!m_bInit)
return;
m_bMonitorResult = TRUE;
m_hReadActionEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
while( WaitForSingleObject( m_hReadActionEvent, m_nMaxSilentPeriod )==WAIT_OBJECT_0 )
{}
// Timeout or process terminated
m_bMonitorResult = FALSE;
CloseHandle( m_hReadActionEvent );
m_hReadActionEvent = INVALID_HANDLE_VALUE;
}
static DWORD WINAPI DoMonitorThread( void* p )
{
((ChildProcess*)p)->DoMonitor();
return 0;
}
void ReadChildProcessConsole()
{
if(!m_bInit)
return;
const int nBufferSize = 256;
CHAR lpBuffer[nBufferSize+1];
DWORD nBytesRead;
while(TRUE)
{
if (!ReadFile(m_hOutputRead,lpBuffer,nBufferSize,&nBytesRead,NULL) || !nBytesRead)
{
if (GetLastError() == ERROR_BROKEN_PIPE)
break; // pipe done - normal exit path.
}
if(m_bVerbose)
{
lpBuffer[nBytesRead] = '\0';
fputs( lpBuffer, stdout );
}
if( m_hReadActionEvent!=INVALID_HANDLE_VALUE )
SetEvent( m_hReadActionEvent );
}
}
static DWORD WINAPI ReadChildProcessConsoleThread( void* p )
{
((ChildProcess*)p)->ReadChildProcessConsole();
return 0;
}
void CloseHandlesForParent()
{
if(!m_bInit)
return;
if( hOutputRead!=INVALID_HANDLE_VALUE )
{
CloseHandle(hOutputRead);
hOutputRead = INVALID_HANDLE_VALUE;
}
if( hInputWrite!=INVALID_HANDLE_VALUE )
{
CloseHandle(hInputWrite);
hInputWrite = INVALID_HANDLE_VALUE;
}
}
void CloseHandlesForChild()
{
if(!m_bInit)
return;
// 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( hOutputWrite!=INVALID_HANDLE_VALUE )
{
CloseHandle(hOutputWrite);
hOutputWrite = INVALID_HANDLE_VALUE;
}
if( hInputRead!=INVALID_HANDLE_VALUE )
{
CloseHandle(hInputRead);
hInputRead = INVALID_HANDLE_VALUE;
}
if( hErrorWrite!=INVALID_HANDLE_VALUE )
{
CloseHandle(hErrorWrite);
hErrorWrite = INVALID_HANDLE_VALUE;
}
}
private:
BOOL m_bInit;
HANDLE m_hOutputRead, m_hOutputWrite;
HANDLE m_hInputRead, m_hInputWrite;
HANDLE m_hErrorWrite;
TCHAR m_szCmdLine[MAX_PATH];
TCHAR m_szWorkingDirectory[MAX_PATH];
int m_nShowCmd;
BOOL m_doMonitor;
BOOL m_bMonitorResult;
BOOL m_bVerbose;
int m_nMaxSilentPeriod;
HANDLE m_hReadActionEvent;
};
int _tmain(int argc, _TCHAR* argv[])
{
BOOL bResult = FALSE;
ChildProcess childProcess;
childProcess.SetInfomation( _T("netstat"), _T(""), SW_SHOWNORMAL );
childProcess.SetMonitorPolicy( TRUE, 4000, TRUE );
bResult = childProcess.CreateProcess();
}