// DnsDbgLauncher.cpp : implementation file
//

#include "stdafx.h"
#include "DnsDbgLauncher.h"
#include "StartProcCmdLineInfo.h"
#include "commrcfg.h"
#include "Trace.h"
#include "DnsDbgLauncher.tmh"


// CDnsDbgLauncherApp

CDnsDbgLauncherApp theApp;

IMPLEMENT_DYNCREATE(CDnsDbgLauncherApp, CWinApp)

CDnsDbgLauncherApp::CDnsDbgLauncherApp()
: m_bCmdLineError(false)
, m_sDebuggedFileName(_T(""))
, m_sModuleFileName(_T(""))
, m_nRetCode(0)
, m_sModuleFolder(_T(""))
, m_sInjectedDllName(_T(""))
, m_bDbgActive(false)
, m_dwProcessId(0)
, m_dwThreadId(0)
, m_hLaunchedProcess(NULL)
, m_hLaunchedThread(NULL)
, m_bEventLoopExit(false)
, m_bDbgProcessStarted(false)
, m_hDbgExeFile(NULL)
, m_hDbgProcess(NULL)
, m_hDbgThread(NULL)
, m_pLoadRemoteDLL(NULL)
, m_pBaseOfImage(NULL)
, m_bRestart(false)
, m_dwLauncherId(0)
, m_oDnsSvcStopedEvent(FALSE, TRUE, _T("Global\\$DNS$SVC$STOPED$"))
, m_oRestartInitEvent(FALSE, TRUE, _T("Global\\$DNS$RESTART$INIT$"))
, m_oDnsSvcRestartNeedEvent(FALSE, TRUE, _T("Global\\$DNS$RESTART$NEED$"))
{
    WPP_INIT_TRACING(L"DnsDbgLauncher");
}

CDnsDbgLauncherApp::~CDnsDbgLauncherApp()
{
    WPP_CLEANUP();
}

BOOL CDnsDbgLauncherApp::InitInstance()
{
    BOOL                  bRet;
    CStartProcCmdLineInfo oCmdLineInfo;
    TCHAR                 szPath[MAX_PATH];
    DWORD                 dwModuleNameLength;
    int                   nCharPos;
    CString               sCommandLine;
    STARTUPINFO           stStartupInfo;
    PROCESS_INFORMATION   stProcessInformation;
    SERVICE_STATUS        stServiceStatus;
    HANDLE                hLauncherProcess;
    DWORD                 dwWaitCode;
    SC_HANDLE             schSCManager;
    SC_HANDLE             schService;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    _tsetlocale(LC_ALL, _T("Russian"));
    bRet = CWinApp::InitInstance();
    // Измените раздел реестра, в котором хранятся параметры
    // TODO: следует изменить эту строку на что-нибудь подходящее,
    // например на название организации
    SetRegistryKey(_T("Real Time Systems DD\\Public DNS WS System"));
    if (m_pszProfileName != NULL)
    {
        free((void*) m_pszProfileName);
    }
    m_pszProfileName = _tcsdup(_T("PublicDNSPluginConfig"));
    ParseCommandLine(oCmdLineInfo);
    if (m_bCmdLineError)
    {
        m_nRetCode = -1;
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSDBGLAUNCHER,
            "%!FUNC! line:%!LINE! %!LEVEL! Command line Parse error"
            );
        return FALSE;
    }
    if (m_bRestart)
    {
        DWORD                   cbBytesNeeded, dwResult;
        SERVICE_STATUS_PROCESS  stServiceProcessStatus;
        hLauncherProcess = OpenProcess(
            PROCESS_ALL_ACCESS,
            FALSE,
            m_dwLauncherId
            );
        if (NULL == hLauncherProcess)
        {
            m_nRetCode = GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSDBGLAUNCHER,
                "%!FUNC! line:%!LINE! %!LEVEL! Call \"OpenProcess\" fail with error %!WINERROR!",
                m_nRetCode
                );
            bRet = m_oRestartInitEvent.SetEvent();
        }
        else
        {
            bRet = m_oRestartInitEvent.SetEvent();
            dwWaitCode = WaitForSingleObject(
                hLauncherProcess,
                INFINITE
                );
            bRet = CloseHandle(hLauncherProcess);
        }
        schSCManager = OpenSCManager(
            NULL,                 // local machine
            NULL,                 // ServicesActive database
            SC_MANAGER_ALL_ACCESS // full access rights
            );
        if (NULL == schSCManager)
        {
            m_nRetCode = ::GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSDBGLAUNCHER,
                "%!FUNC! line:%!LINE! %!LEVEL! Call \"OpenSCManager\" failed with error %!WINERROR!",
                m_nRetCode
                );
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                TRACE_DNSDBGLAUNCHER,
                "%!FUNC! line:%!LINE! %!LEVEL! Exit"
                );
            return FALSE;
        }
        assert(NULL != schSCManager);
        schService = OpenService(
            schSCManager,
            _T("DNS"),
            SERVICE_ALL_ACCESS
            );
        if (NULL == schService)
        {
            m_nRetCode = ::GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSDBGLAUNCHER,
                "%!FUNC! line:%!LINE! %!LEVEL! Call \"OpenService\" failed with error %!WINERROR!",
                m_nRetCode
                );
            bRet = CloseServiceHandle(schSCManager);
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                TRACE_DNSDBGLAUNCHER,
                "%!FUNC! line:%!LINE! %!LEVEL! Exit"
                );
            return FALSE;
        }
        bRet = CloseServiceHandle(schSCManager);
    __service_status_loop:
        RtlZeroMemory(
            &stServiceProcessStatus,
            sizeof(stServiceProcessStatus)
            );
        bRet = QueryServiceStatusEx(
            schService,
            SC_STATUS_PROCESS_INFO,
            (LPBYTE) &stServiceProcessStatus,
            sizeof(stServiceProcessStatus),
            &cbBytesNeeded
            );
        if (!bRet)
        {
            m_nRetCode = ::GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSDBGLAUNCHER,
                "%!FUNC! line:%!LINE! %!LEVEL! Call \"QueryServiceStatusEx\" failed with error %!WINERROR!",
                m_nRetCode
                );
            bRet = CloseServiceHandle(schService);
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                TRACE_DNSDBGLAUNCHER,
                "%!FUNC! line:%!LINE! %!LEVEL! Exit"
                );
            return FALSE;
        }
        switch (stServiceProcessStatus.dwCurrentState)
        {
        case SERVICE_STOPPED:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                TRACE_DNSDBGLAUNCHER,
                "%!FUNC! line:%!LINE! %!LEVEL! Service \"DNS\" will STARTED."
                );
            bRet = StartService(
                schService,
                0,
                NULL
                );
            if (!bRet)
            {
                m_nRetCode = ::GetLastError();
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNSDBGLAUNCHER,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"StartService\" failed with error %!WINERROR!",
                    m_nRetCode
                    );
                bRet = CloseServiceHandle(schService);
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    TRACE_DNSDBGLAUNCHER,
                    "%!FUNC! line:%!LINE! %!LEVEL! Exit"
                    );
                return FALSE;
            }
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                TRACE_DNSDBGLAUNCHER,
                "%!FUNC! line:%!LINE! %!LEVEL! Service \"DNS\" was STARTED."
                );
            break;
        case SERVICE_RUNNING:
            TraceEvents(
                TRACE_LEVEL_WARNING,
                TRACE_DNSDBGLAUNCHER,
                "%!FUNC! line:%!LINE! %!LEVEL! Service \"DNS\" already running."
                );
            RtlZeroMemory(
                &stServiceStatus,
                sizeof(stServiceStatus)
                );
            bRet = ControlService(
                schService,
                SERVICE_CONTROL_STOP,
                &stServiceStatus
                );
            goto __service_status_loop;
        case SERVICE_START_PENDING:
            TraceEvents(
                TRACE_LEVEL_WARNING,
                TRACE_DNSDBGLAUNCHER,
                "%!FUNC! line:%!LINE! %!LEVEL! Service \"DNS\" already start pending."
                );
            dwResult = SleepEx(1000, TRUE);
            goto __service_status_loop;
        case SERVICE_STOP_PENDING:
            TraceEvents(
                TRACE_LEVEL_WARNING,
                TRACE_DNSDBGLAUNCHER,
                "%!FUNC! line:%!LINE! %!LEVEL! Service \"DNS\" stop pending."
                );
            dwResult = SleepEx(1000, TRUE);
            goto __service_status_loop;
        default:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSDBGLAUNCHER,
                "%!FUNC! line:%!LINE! %!LEVEL! Unexpected service status = 0x%08X.",
                stServiceProcessStatus.dwCurrentState
                );
            break;
        }
        bRet = CloseServiceHandle(schService);
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSDBGLAUNCHER,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit"
            );
        return FALSE;
    }
    if (m_sDebuggedFileName.IsEmpty())
    {
        m_nRetCode = -1;
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSDBGLAUNCHER,
            "%!FUNC! line:%!LINE! %!LEVEL! Debugged File Name Is Empty"
            );
        return FALSE;
    }
    dwModuleNameLength = GetModuleFileName(NULL, szPath, MAX_PATH);
    m_sModuleFileName = szPath;
    nCharPos = m_sModuleFileName.ReverseFind(_T('\\'));
    m_sModuleFolder = m_sModuleFileName.Left(nCharPos);
    m_sInjectedDllName = m_sModuleFolder + _T("\\WS2InterceptDLL.dll");
    sCommandLine = L'\"';
    sCommandLine += m_sDebuggedFileName;
    sCommandLine += L'\"';
    RtlZeroMemory(
        &stStartupInfo,
        sizeof(stStartupInfo)
        );
    stStartupInfo.cb = sizeof(stStartupInfo);
    RtlZeroMemory(
        &stProcessInformation,
        sizeof(stProcessInformation)
        );
    bRet = CreateProcess(
        NULL,
        sCommandLine.GetBuffer(),
        NULL,
        NULL,
        TRUE,
        CREATE_UNICODE_ENVIRONMENT | DEBUG_ONLY_THIS_PROCESS,
        NULL,
        NULL,
        &stStartupInfo,
        &stProcessInformation
        );
    if (!bRet)
    {
        m_nRetCode = GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSDBGLAUNCHER,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"CreateProcess\" fail with error %!WINERROR!",
            m_nRetCode
            );
        return FALSE;
    }
    m_dwProcessId = stProcessInformation.dwProcessId;
    m_dwThreadId = stProcessInformation.dwThreadId;
    m_hLaunchedProcess = stProcessInformation.hProcess;
    m_hLaunchedThread = stProcessInformation.hThread;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL! Created process:"
        );
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL!     dwProcessId = %u",
        m_dwProcessId
        );
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL!     dwThreadId = %u",
        m_dwThreadId
        );
    bRet = DebugSetProcessKillOnExit(FALSE);
    m_bDbgActive = true;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    return TRUE;
}

int CDnsDbgLauncherApp::ExitInstance()
{
    BOOL    bRet;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    if (m_bDbgActive)
    {
        bRet = DebugActiveProcessStop(m_dwProcessId);
        m_bDbgActive = false;
        m_hDbgExeFile = NULL;
        m_hDbgProcess = NULL;
        m_hDbgThread = NULL;
    }
    if (NULL != m_pLoadRemoteDLL)
    {
        delete m_pLoadRemoteDLL;
        m_pLoadRemoteDLL = NULL;
    }
    if (NULL != m_hLaunchedThread)
    {
        bRet = CloseHandle(m_hLaunchedThread);
        m_hLaunchedThread = NULL;
    }
    if (NULL != m_hLaunchedProcess)
    {
        bRet = CloseHandle(m_hLaunchedProcess);
        m_hLaunchedProcess = NULL;
    }
    CWinApp::ExitInstance();
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    return m_nRetCode;
}

BEGIN_MESSAGE_MAP(CDnsDbgLauncherApp, CWinApp)
END_MESSAGE_MAP()


// CDnsDbgLauncherApp message handlers


int CDnsDbgLauncherApp::Run()
{
    int                 nRet;
    bool                bResult, bWaitProcessEventLoop = true;
    BOOL                bRet;
    DWORD               dwSuspendCount, dwWaitCode, dwStopCount = 0;
    DWORD               dwContinueStatus = DBG_CONTINUE;
    EXCEPTION_RECORD*   pExceptoinRecord = &m_stDebugEvent.u.Exception.ExceptionRecord;
    DWORD               dwCurrentProcessId;
    CString             sCommandLine;
    STARTUPINFO         stStartupInfo;
    PROCESS_INFORMATION stProcessInformation;
    SC_HANDLE           schSCManager;
    SC_HANDLE           schService;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    do
    {
        bRet = WaitForDebugEvent(
            &m_stDebugEvent,
            INFINITE
            );
        switch (m_stDebugEvent.dwDebugEventCode)
        {
        case CREATE_PROCESS_DEBUG_EVENT:
            dwContinueStatus = OnCreateProcessDebugEvent();
            break;
        case EXIT_PROCESS_DEBUG_EVENT:
            dwContinueStatus = OnExitProcessDebugEvent();
            break;
        case EXCEPTION_DEBUG_EVENT:
            if (EXCEPTION_BREAKPOINT == pExceptoinRecord->ExceptionCode)
            {
                dwContinueStatus = OnInitialBreakpointDebugEvent(pExceptoinRecord);
            }
            else
            {
                dwContinueStatus = DBG_CONTINUE;
            }
            break;
        default:
            dwContinueStatus = DBG_CONTINUE;
            break;
        }
        bRet = ContinueDebugEvent(
            m_stDebugEvent.dwProcessId,
            m_stDebugEvent.dwThreadId,
            dwContinueStatus
            );
    } while (!m_bEventLoopExit);
    if (!m_bDbgProcessStarted)
    {
        m_bDbgActive = false;
        m_hDbgExeFile = NULL;
        m_hDbgProcess = NULL;
        m_hDbgThread = NULL;
        nRet = ExitInstance();
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSDBGLAUNCHER,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (Launched process Exit)"
            );
        return nRet;
    }
    bRet = DebugActiveProcessStop(m_dwProcessId);
    m_bDbgActive = false;
    m_hDbgExeFile = NULL;
    m_hDbgProcess = NULL;
    m_hDbgThread = NULL;
    m_pLoadRemoteDLL = new CLoadRemoteDLL(
        m_sInjectedDllName,
        m_hLaunchedProcess
        );
    bResult = m_pLoadRemoteDLL->ElaborateAll(
        NULL,
        NULL
        );
    delete m_pLoadRemoteDLL;
    m_pLoadRemoteDLL = NULL;
    if (!bResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSDBGLAUNCHER,
            "%!FUNC! line:%!LINE! %!LEVEL! Elaborate Remote DLL failed"
            );
        nRet = ExitInstance();
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSDBGLAUNCHER,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit"
            );
        return nRet;
    }
    bRet = m_oDnsSvcStopedEvent.ResetEvent();
    bRet = m_oRestartInitEvent.ResetEvent();
    dwSuspendCount = ::ResumeThread(m_hLaunchedThread);
    if (dwSuspendCount == DWORD(-1))
    {
        m_nRetCode = ::GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSDBGLAUNCHER,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"ResumeThread\" failed with error %!WINERROR!",
            m_nRetCode
            );
        nRet = ExitInstance();
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            TRACE_DNSDBGLAUNCHER,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit"
            );
        return nRet;
    }
    while (bWaitProcessEventLoop)
    {
        HANDLE  ahWaitedHandles [] = {
            m_oDnsSvcRestartNeedEvent,
            m_oDnsSvcStopedEvent,
            m_hLaunchedProcess
        };
        dwWaitCode = WaitForMultipleObjects(
            sizeof(ahWaitedHandles) / sizeof(HANDLE),
            ahWaitedHandles,
            FALSE,
            INFINITE
            );
        switch (dwWaitCode)
        {
        case WAIT_OBJECT_0:
            bWaitProcessEventLoop = false;
            bRet = m_oDnsSvcRestartNeedEvent.ResetEvent();
            // остановка DNS сервера
            schSCManager = OpenSCManager(
                NULL,                 // local machine
                NULL,                 // ServicesActive database
                SC_MANAGER_ALL_ACCESS // full access rights
                );
            assert(NULL != schSCManager);
            schService = OpenService(
                schSCManager,
                _T("DNS"),
                SERVICE_ALL_ACCESS
                );
            assert(NULL != schService);
            bRet = CloseServiceHandle(schSCManager);
            for (dwStopCount = 0; dwStopCount < 120; dwStopCount++)
            {
                DWORD                   cbBytesNeeded;
                SERVICE_STATUS          stServiceStatus;
                SERVICE_STATUS_PROCESS  stServiceProcessStatus;
                RtlZeroMemory(
                    &stServiceProcessStatus,
                    sizeof(stServiceProcessStatus)
                    );
                RtlZeroMemory(
                    &stServiceStatus,
                    sizeof(stServiceStatus)
                    );
                bRet = QueryServiceStatusEx(
                    schService,
                    SC_STATUS_PROCESS_INFO,
                    (LPBYTE) &stServiceProcessStatus,
                    sizeof(stServiceProcessStatus),
                    &cbBytesNeeded
                    );
                assert(bRet);
                if (SERVICE_RUNNING == stServiceProcessStatus.dwCurrentState)
                {
                    if (0 == dwStopCount)
                    {
                        bRet = ControlService(
                            schService,
                            SERVICE_CONTROL_STOP,
                            &stServiceStatus
                            );
                        assert(bRet);
                    }
                    dwWaitCode = SleepEx(1000, TRUE);
                }
                else
                {
                    break;
                }
            }
            if (dwStopCount >= 120)
            {
                bRet = TerminateProcess(
                    m_hLaunchedProcess,
                    1
                    );
                for (dwStopCount = 0; dwStopCount < 120; dwStopCount++)
                {
                    dwWaitCode = WaitForSingleObject(
                        m_hLaunchedProcess,
                        1000
                        );
                    if (WAIT_OBJECT_0 == dwWaitCode)
                    {
                        break;
                    }
                }
            }
            else
            {
                for (dwStopCount = 0; dwStopCount < 120; dwStopCount++)
                {
                    dwWaitCode = WaitForSingleObject(
                        m_hLaunchedProcess,
                        1000
                        );
                    if (WAIT_OBJECT_0 == dwWaitCode)
                    {
                        break;
                    }
                }
                if (dwStopCount >= 120)
                {
                    bRet = TerminateProcess(
                        m_hLaunchedProcess,
                        1
                        );
                    for (dwStopCount = 0; dwStopCount < 120; dwStopCount++)
                    {
                        dwWaitCode = WaitForSingleObject(
                            m_hLaunchedProcess,
                            1000
                            );
                        if (WAIT_OBJECT_0 == dwWaitCode)
                        {
                            break;
                        }
                    }
                }
            }
            bRet = CloseHandle(m_hLaunchedProcess);
            m_hLaunchedProcess = NULL;
            bRet = m_oRestartInitEvent.ResetEvent();
            dwCurrentProcessId = GetCurrentProcessId();
            sCommandLine.Format(
                L"\"%s\" -rs %u",
                PCTSTR(m_sModuleFileName),
                dwCurrentProcessId
                );
            RtlZeroMemory(
                &stStartupInfo,
                sizeof(stStartupInfo)
                );
            stStartupInfo.cb = sizeof(stStartupInfo);
            RtlZeroMemory(
                &stProcessInformation,
                sizeof(stProcessInformation)
                );
            bRet = CreateProcess(
                NULL,
                sCommandLine.GetBuffer(),
                NULL,
                NULL,
                FALSE,
                CREATE_UNICODE_ENVIRONMENT,
                NULL,
                NULL,
                &stStartupInfo,
                &stProcessInformation
                );
            if (!bRet)
            {
                m_nRetCode = GetLastError();
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNSDBGLAUNCHER,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"CreateProcess\" fail with error %!WINERROR!",
                    m_nRetCode
                    );
                nRet = ExitInstance();
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    TRACE_DNSDBGLAUNCHER,
                    "%!FUNC! line:%!LINE! %!LEVEL! Exit"
                    );
                return nRet;
            }
            bRet = m_oRestartInitEvent.Lock();
            break;
        case WAIT_OBJECT_0+1:
            bWaitProcessEventLoop = false;
            dwWaitCode = WaitForSingleObject(
                m_hLaunchedProcess,
                INFINITE
                );
            bRet = CloseHandle(m_hLaunchedProcess);
            m_hLaunchedProcess = NULL;
            break;
        case WAIT_OBJECT_0+2:
            bWaitProcessEventLoop = false;
            bRet = m_oDnsSvcStopedEvent.ResetEvent();
            TraceEvents(
                TRACE_LEVEL_WARNING,
                TRACE_DNSDBGLAUNCHER,
                "%!FUNC! line:%!LINE! %!LEVEL! Unexpected end of service \"DNS\""
                );
            bRet = CloseHandle(m_hLaunchedProcess);
            m_hLaunchedProcess = NULL;
            bRet = m_oRestartInitEvent.ResetEvent();
            dwCurrentProcessId = GetCurrentProcessId();
            sCommandLine.Format(
                L"\"%s\" -rs %u",
                PCTSTR(m_sModuleFileName),
                dwCurrentProcessId
                );
            RtlZeroMemory(
                &stStartupInfo,
                sizeof(stStartupInfo)
                );
            stStartupInfo.cb = sizeof(stStartupInfo);
            RtlZeroMemory(
                &stProcessInformation,
                sizeof(stProcessInformation)
                );
            bRet = CreateProcess(
                NULL,
                sCommandLine.GetBuffer(),
                NULL,
                NULL,
                FALSE,
                CREATE_UNICODE_ENVIRONMENT,
                NULL,
                NULL,
                &stStartupInfo,
                &stProcessInformation
                );
            if (!bRet)
            {
                m_nRetCode = GetLastError();
                TraceEvents(
                    TRACE_LEVEL_ERROR,
                    TRACE_DNSDBGLAUNCHER,
                    "%!FUNC! line:%!LINE! %!LEVEL! Call \"CreateProcess\" fail with error %!WINERROR!",
                    m_nRetCode
                    );
                nRet = ExitInstance();
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    TRACE_DNSDBGLAUNCHER,
                    "%!FUNC! line:%!LINE! %!LEVEL! Exit"
                    );
                return nRet;
            }
            bRet = m_oRestartInitEvent.Lock();
            break;
        default:
            TraceEvents(
                TRACE_LEVEL_ERROR,
                TRACE_DNSDBGLAUNCHER,
                "%!FUNC! line:%!LINE! %!LEVEL! Unexpected value returned \"WaitForMultipleObjects\" = 0x%08X",
                dwWaitCode
                );
            break;
        }
    }
    nRet = ExitInstance();
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    return nRet;
}


DWORD CDnsDbgLauncherApp::OnCreateProcessDebugEvent()
{
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    m_hDbgExeFile = m_stDebugEvent.u.CreateProcessInfo.hFile;
    m_hDbgProcess = m_stDebugEvent.u.CreateProcessInfo.hProcess;
    m_hDbgThread = m_stDebugEvent.u.CreateProcessInfo.hThread;
    m_pBaseOfImage = PBYTE(m_stDebugEvent.u.CreateProcessInfo.lpBaseOfImage);
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    return DBG_CONTINUE;
}


DWORD CDnsDbgLauncherApp::OnExitProcessDebugEvent()
{
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter"
        );
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL! Process terminated"
        );
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL!     dwProcessId = %u",
        m_stDebugEvent.dwProcessId
        );
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL!     dwExitCode = %!WINERROR!",
        m_stDebugEvent.u.ExitProcess.dwExitCode
        );
    m_bEventLoopExit = true;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    return DBG_CONTINUE;
}


DWORD CDnsDbgLauncherApp::OnInitialBreakpointDebugEvent(EXCEPTION_RECORD* pExceptionInfo)
{
    DWORD   dwSuspendCount, dwError;
    pExceptionInfo;
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL! Enter (dwThreadId = %u)",
        m_stDebugEvent.dwThreadId
        );
    dwSuspendCount = ::SuspendThread(m_hLaunchedThread);
    if (DWORD(-1) == dwSuspendCount)
    {
        dwError = GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            TRACE_DNSDBGLAUNCHER,
            "%!FUNC! line:%!LINE! %!LEVEL! Call \"SuspendThread\" faled with error %!WINERROR!",
            dwError
            );
    }
    else
    {
        m_bDbgProcessStarted = true;
        m_bEventLoopExit = true;
    }
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        TRACE_DNSDBGLAUNCHER,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit"
        );
    return DBG_CONTINUE;
}
