Child Process가 제대로 동작하는지 테스트하기 위해 개발한 클래스.
당연하게도 콘솔이 있는 애플리케이션에게만 사용이 가능하고,
콘솔에다 뭔가를 출력하고 있어야 정상 동작한다고 인식한다.

This class is developed for checking whether an child process is working properly by monitoring its console.
It determines the child process is working properly, if the child process outputs some texts on its console.

for more information, please refer to the following link. This article is based on it.

//////////////////////////////////////////////////////////////////////////
// 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();
}
Posted by 배트
,