// logman.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "strings.h"
#include "unihelpr.h"
#include "utils.h"
#include "proputil.h"
#include "propbag.h"
#include "logman.h"
#include "resource.h"
#include "varg.c"

//
// Forward routines:
//

ARG_RECORD Commands[] = {

    IDS_HELP_DEBUG,    
    IDS_ARG1_DEBUG,  NULL, 
    IDS_ARG2_DEBUG,  NULL,
    IDS_PARAM_DEFAULT, NULL,
    ARG_TYPE_DEBUG, ARG_FLAG_OPTIONAL|ARG_FLAG_HIDDEN,  
    (CMD_TYPE)0,     
    0,  NULL,

    IDS_HELP_HELP,
    IDS_ARG1_HELP,     NULL,
    IDS_ARG2_HELP,     NULL,
    0, NULL,
    ARG_TYPE_HELP,   ARG_FLAG_OPTIONAL,
    (CMD_TYPE)FALSE,
    0,  NULL,

    IDS_HELP_COMPUTER,
    IDS_ARG1_COMPUTER, NULL,
    IDS_ARG2_COMPUTER, NULL,
    IDS_ARG1_COMPUTER, NULL,
    ARG_TYPE_STR,    ARG_FLAG_OPTIONAL,
    (CMD_TYPE)NULL,
    0,  NULL,

    IDS_HELP_NAME,
    IDS_ARG1_NAME, NULL,
    IDS_ARG2_NAME, NULL,
    IDS_ARG1_NAME, NULL,
    ARG_TYPE_STR,    ARG_FLAG_CONDITIONAL|ARG_FLAG_NOFLAG,
    (CMD_TYPE)NULL,
    0,  NULL,

    IDS_HELP_START,
    IDS_ARG1_START, NULL,
    IDS_ARG2_START, NULL,
    0, NULL,
    ARG_TYPE_BOOL,    ARG_FLAG_OPTIONAL,
    (CMD_TYPE)NULL,
    0,  NULL,

    IDS_HELP_STOP,
    IDS_ARG1_STOP, NULL,
    IDS_ARG2_STOP, NULL,
    0,NULL,
    ARG_TYPE_BOOL,    ARG_FLAG_OPTIONAL,
    (CMD_TYPE)NULL,
    0,  NULL,

    IDS_HELP_SETTINGS,
    IDS_ARG1_SETTINGS, NULL,
    IDS_ARG2_SETTINGS, NULL,
    IDS_PARAM_FILENAME, NULL,
    ARG_TYPE_STR,    ARG_FLAG_CONDITIONAL,
    (CMD_TYPE)NULL,
    0,  NULL,

    IDS_HELP_OVER,
    IDS_ARG1_OVER, NULL,
    IDS_ARG2_OVER, NULL,
    0, NULL,
    ARG_TYPE_BOOL,    ARG_FLAG_OPTIONAL,
    (CMD_TYPE)NULL,
    0,  NULL,

    ARG_TERMINATOR
};

enum _Commands {
    eDebug,
    eHelp,
    eComputer,
    eName,
    eStart,
    eStop,
    eSettings,
    eOverwrite
};

DWORD   _stdcall
ValidateComputerName (
    LPCTSTR  szComputerName );

DWORD   _stdcall
LoadSettingsFile (
    LPCTSTR szFileName,
    LPTSTR& szFileBuffer );

DWORD   _stdcall
DeleteQuery (
    HKEY            hkeyLogQueries,
    LPCWSTR         szQueryName );

DWORD   _stdcall
InitializeNewQuery (
    HKEY            hkeyLogQueries,
    HKEY&           rhKeyQuery,
    LPCWSTR         szQueryName );

DWORD _stdcall 
CreateDefaultLogQueries (
    HKEY hkeyLogQueries );

DWORD _stdcall 
Install (
    HKEY    hkeyMachine,
    HKEY&   rhkeyLogQueries );

DWORD   _stdcall
ConnectToRegistry (
    LPCTSTR szComputerName,
    HKEY& rhkeyLogQueries );

DWORD   _stdcall   
WriteRegistryLastModified ( 
    HKEY hkeyQuery );

DWORD   _stdcall
WriteToRegistry (
    HKEY            hkeyLogQueries,
    CPropertyBag*   pPropBag,
    LPWSTR          szQueryName,
    DWORD&          rdwLogType );

DWORD   _stdcall
ValidateProperties (
    HKEY            hkeyLogQueries,
    CPropertyBag*   pPropBag,
    DWORD*          pdwLogType,
    LPWSTR          szQueryName,
    DWORD*          pdwNameBufLen );

DWORD   _stdcall
ProcessSettingsObject (
    HKEY            hkeyLogQueries,
    CPropertyBag*   pPropBag );

DWORD   _stdcall
ProcessSettingsFile (
    LPCTSTR szComputerName,
    LPCTSTR szFileName );

DWORD   _stdcall
StartQuery (
    LPCTSTR szComputerName,
    LPCTSTR szQueryName );

DWORD   _stdcall
StopQuery (
    LPCTSTR szComputerName,
    LPCTSTR szQueryName );

DWORD   _stdcall
ConnectToQuery (
    LPCTSTR szComputerName,
    LPCTSTR szQueryName,
    HKEY*   phkeyQuery );

DWORD   _stdcall
GetState ( 
    LPCTSTR szComputerName,
    DWORD&  rdwState );

DWORD   _stdcall
Synchronize ( 
    LPCTSTR szComputerName );

//
//  Routines
//


DWORD   _stdcall
ValidateComputerName (
    LPCTSTR  szComputerName )
{
    DWORD   dwStatus = ERROR_SUCCESS;

    if ( NULL != szComputerName ) {
        if ( ! ( MAX_COMPUTERNAME_LENGTH < lstrlen ( szComputerName ) ) ) {
        } else {
            dwStatus = ERROR_INVALID_COMPUTERNAME;
        }
    }
        
    return dwStatus;
}

DWORD   _stdcall
LoadSettingsFile (
    LPCTSTR szFileName,
    LPTSTR& szFileBuffer )
{
    DWORD           dwStatus = ERROR_SUCCESS;
    DWORD           dwLogManStatus = ERROR_SUCCESS;
    HANDLE          hOpenFile = NULL;
    TCHAR           szLocalName [MAX_PATH];     // Todo:  Allocate
    LPTSTR          pFileNameStart = NULL;
    HANDLE          hFindFile = NULL;
    LPTSTR          szData = NULL;
    WIN32_FIND_DATA FindFileInfo;
    INT             iNameOffset;

    USES_CONVERSION;

    lstrcpy ( szLocalName, szFileName );       // Todo:  Handle error
    szFileBuffer = NULL;

    pFileNameStart = ExtractFileName (szLocalName) ;
    iNameOffset = (INT)(pFileNameStart - szLocalName);

    // convert short filename to long NTFS filename if necessary
    hFindFile = FindFirstFile ( szLocalName, &FindFileInfo) ;
    if (hFindFile && hFindFile != INVALID_HANDLE_VALUE) {

        // append the file name back to the path name
        lstrcpy (&szLocalName[iNameOffset], FindFileInfo.cFileName) ;
        FindClose (hFindFile) ;
        // Open the file
        hOpenFile = CreateFile (
                        szLocalName, 
                        GENERIC_READ,
                        0,                  // Not shared
                        NULL,               // Security attributes
                        OPEN_EXISTING,     
                        FILE_ATTRIBUTE_NORMAL,
                        NULL );

        if ( hOpenFile && hOpenFile != INVALID_HANDLE_VALUE ) {
            DWORD dwFileSize;
            DWORD dwFileSizeHigh;
        
            // Read the file contents into a memory buffer.
            dwFileSize = GetFileSize ( hOpenFile, &dwFileSizeHigh );

            assert ( 0 == dwFileSizeHigh );
            // Todo:  Handle non-zero file size high

            szData = new TCHAR[(dwFileSize + sizeof(TCHAR))/sizeof(TCHAR)];

            if ( NULL != szData ) {
                if ( FileRead ( hOpenFile, szData, dwFileSize ) ) {
                    szFileBuffer = szData;
                } else {
                    dwLogManStatus = LOGMAN_SETTINGS_FILE_NOT_LOADED;
                    dwStatus = GetLastError();
                }
            } else {
                dwLogManStatus = LOGMAN_SETTINGS_FILE_NOT_LOADED;
                dwStatus = ERROR_OUTOFMEMORY;
            }
            CloseHandle ( hOpenFile );
        } else {
            dwLogManStatus = LOGMAN_SETTINGS_FILE_NOT_OPEN;
            dwStatus = GetLastError();
        }
    } else {
        dwLogManStatus = LOGMAN_SETTINGS_FILE_NOT_OPEN;
        dwStatus = GetLastError();
    }

    if ( ERROR_SUCCESS != dwLogManStatus ) {
        DisplayErrorMessage ( dwLogManStatus, T2W(szFileName) );
    }

    if ( ERROR_SUCCESS != dwStatus ) {
        DisplayErrorMessage ( dwStatus );
    }

    dwStatus = dwLogManStatus;

    return dwStatus;
}

DWORD   _stdcall
DeleteQuery (
    HKEY            hkeyLogQueries,
    LPCWSTR         szQueryName )
{
    DWORD dwStatus = ERROR_SUCCESS;

    // Delete in the registry
    dwStatus = RegDeleteKeyW ( hkeyLogQueries, szQueryName );


    return dwStatus;
}

DWORD   _stdcall
InitializeNewQuery (
    HKEY            hkeyLogQueries,
    HKEY&           rhKeyQuery,
    LPCWSTR         szQueryName )
{
    DWORD   dwStatus = ERROR_SUCCESS;
    DWORD   dwDisposition = 0;

    // Create the query specified, checking for duplicate query.
    dwStatus = RegCreateKeyExW (
        hkeyLogQueries,
        szQueryName,
        0,
        NULL, 
        0,
        KEY_ALL_ACCESS,
        NULL,
        &rhKeyQuery,
        &dwDisposition);

    if ( ERROR_SUCCESS == dwStatus ) {
        if ( REG_OPENED_EXISTING_KEY == dwDisposition && !Commands[eOverwrite].bValue ) {
            dwStatus = LOGMAN_DUP_QUERY_NAME;
        } else {
            
            DWORD   dwRegValue;
            // Initialize the current state value.  After it is 
            // initialized, it is only modified when:
            //      1) Set to Stopped or Started by the service
            //      2) Set to Start Pending by the config snapin.
    
            dwRegValue = SLQ_QUERY_STOPPED;
            dwStatus = WriteRegistryDwordValue ( 
                        rhKeyQuery, 
                        cwszRegCurrentState, 
                        &dwRegValue );

            if ( ERROR_SUCCESS == dwStatus ) {
                // Initialize the log type to "new" to indicate partially created logs
                dwRegValue = SLQ_NEW_LOG;
                dwStatus = WriteRegistryDwordValue (
                            rhKeyQuery,
                            cwszRegLogType,
                            &dwRegValue );
            }
        }
    } 

    return dwStatus;
}

DWORD _stdcall 
CreateDefaultLogQueries (
    HKEY hkeyLogQueries )
{
    DWORD   dwStatus = ERROR_SUCCESS;
    HRESULT hr = NOERROR;
    LPWSTR  szPropValue;
    HKEY    hkeyQuery = NULL;
    LPWSTR  szMszBuf = NULL;
    DWORD   dwMszBufLen = 0;
    DWORD   dwMszStringLen = 0;


    // Create default "System Overview" counter log query

    // Todo:  check and translate HRESULT failures
    szPropValue = ResourceString ( IDS_DEFAULT_CTRLOG_QUERY_NAME );
    
    if ( ERROR_SUCCESS == dwStatus ) {    
    //Todo:  ResourceString return failure how?
        dwStatus = InitializeNewQuery ( 
                    hkeyLogQueries,
                    hkeyQuery,
                    szPropValue );
    } 
    
    if ( ERROR_SUCCESS == dwStatus ) {
        szPropValue = ResourceString ( IDS_DEFAULT_CTRLOG_CPU_PATH );

        hr = AddStringToMszBufferAlloc ( 
            szPropValue,
            &szMszBuf,
            &dwMszBufLen,
            &dwMszStringLen );

        if ( SUCCEEDED ( hr ) ) {

            szPropValue = ResourceString ( IDS_DEFAULT_CTRLOG_MEMORY_PATH );

            hr = AddStringToMszBufferAlloc ( 
                szPropValue,
                &szMszBuf,
                &dwMszBufLen,
                &dwMszStringLen );

            if ( SUCCEEDED ( hr ) ) {

                szPropValue = ResourceString ( IDS_DEFAULT_CTRLOG_DISK_PATH );

                hr = AddStringToMszBufferAlloc ( 
                    szPropValue,
                    &szMszBuf,
                    &dwMszBufLen,
                    &dwMszStringLen );
            }
        }

        if ( SUCCEEDED ( hr ) ) {
            dwStatus = WriteRegistryStringValue ( 
                            hkeyQuery, 
                            cwszRegCounterList, 
                            REG_MULTI_SZ, 
                            szMszBuf, 
                            dwMszStringLen );
            if ( ERROR_SUCCESS != dwStatus ) {
                hr = HRESULT_FROM_WIN32 ( dwStatus );
            }
        }

        if ( NULL != szMszBuf ) {
            delete szMszBuf;
        }
    }        
    // Counter strings are required fields.
    if ( SUCCEEDED ( hr ) && ERROR_SUCCESS == dwStatus ) {
        DWORD           dwTemp;
        SLQ_TIME_INFO   stiData;
        
        szPropValue = ResourceString ( IDS_DEFAULT_CTRLOG_COMMENT );

        dwStatus = WriteRegistryStringValue ( 
                        hkeyQuery, 
                        cwszRegComment, 
                        REG_SZ, 
                        szPropValue, 
                        lstrlenW ( szPropValue ) );

        dwTemp = SLF_NAME_NONE;
        dwStatus = WriteRegistryDwordValue ( hkeyQuery, cwszRegLogFileAutoFormat, &dwTemp );

        // Start mode and time set to manual        
        memset (&stiData, 0, sizeof(stiData));
        stiData.wTimeType = SLQ_TT_TTYPE_START;
        stiData.wDataType = SLQ_TT_DTYPE_DATETIME;
        stiData.dwAutoMode = SLQ_AUTO_MODE_NONE;
        stiData.llDateTime = MAX_TIME_VALUE;

        dwStatus = WriteRegistrySlqTime ( hkeyQuery, cwszRegStartTime, &stiData );

        // Stop and Sample time fields are defaults, but Smlogsvc generates
        // warning events if fields are missing.
        InitDefaultSlqTimeInfo ( SLQ_COUNTER_LOG, SLQ_TT_TTYPE_STOP, &stiData );
        dwStatus = WriteRegistrySlqTime ( hkeyQuery, cwszRegStopTime, &stiData );
        
        InitDefaultSlqTimeInfo ( SLQ_COUNTER_LOG, SLQ_TT_TTYPE_SAMPLE, &stiData );
        dwStatus = WriteRegistrySlqTime ( hkeyQuery, cwszRegSampleInterval, &stiData );
        
 
        // The following file fields are defaults, but Smlogsvc generates
        // warning events if fields are missing.
        dwStatus = WriteRegistryStringValue ( 
                        hkeyQuery, 
                        cwszRegLogFileFolder, 
                        REG_SZ, 
                        cwszDefaultLogFileFolder, 
                        lstrlenW ( cwszDefaultLogFileFolder ) );

        dwTemp = DEFAULT_LOG_FILE_SERIAL_NUMBER;
        dwStatus = WriteRegistryDwordValue ( hkeyQuery, cwszRegLogFileSerialNumber, &dwTemp );
        
        dwTemp = DEFAULT_LOG_FILE_MAX_SIZE;
        dwStatus = WriteRegistryDwordValue ( hkeyQuery, cwszRegLogFileMaxSize, &dwTemp );
        
        dwTemp = SLF_BIN_FILE;
        dwStatus = WriteRegistryDwordValue ( hkeyQuery, cwszRegLogFileType, &dwTemp );       
        
        szPropValue = ResourceString ( IDS_DEFAULT_CTRLOG_QUERY_NAME );
        dwStatus = WriteRegistryStringValue ( 
                        hkeyQuery, 
                        cwszRegLogFileBaseName, 
                        REG_SZ, 
                        szPropValue, 
                        lstrlenW ( szPropValue ) );

        // The sample query is "ExecuteOnly"
        dwTemp = 1;
        dwStatus = WriteRegistryDwordValue ( hkeyQuery, cwszRegExecuteOnly, &dwTemp );

        // Reset the log type from "new" to real type
        dwTemp = SLQ_COUNTER_LOG;
        dwStatus = WriteRegistryDwordValue ( hkeyQuery, cwszRegLogType, &dwTemp );

        dwStatus = WriteRegistryLastModified ( hkeyQuery );
    
        dwStatus = ERROR_SUCCESS;   // Non-required fields.
    } else {
        // Todo:  Translate status, display message re: default log not installed
        if ( ERROR_SUCCESS == dwStatus ) {
            dwStatus = (DWORD) hr;
        }
        szPropValue = ResourceString ( IDS_DEFAULT_CTRLOG_QUERY_NAME );

        if ( NULL != hkeyQuery ) {
            RegCloseKey ( hkeyQuery );
            hkeyQuery = NULL;
        }
        DeleteQuery ( hkeyLogQueries, szPropValue ); 
    }

    if ( NULL != hkeyQuery ) {
        RegCloseKey ( hkeyQuery );
    }

    return dwStatus;
}

DWORD _stdcall 
Install (
    HKEY    hkeyMachine,
    HKEY&   rhkeyLogQueries )
{
    DWORD   dwStatus = ERROR_SUCCESS;
    HKEY    hkeySysmonLog;
    DWORD   dwDisposition;
    
    assert ( NULL != hkeyMachine );

    rhkeyLogQueries = NULL;

    dwStatus = RegOpenKeyExW (
                    hkeyMachine,
                    cwszRegKeySysmonLog,
                    0,
                    KEY_READ | KEY_WRITE,
                    &hkeySysmonLog);
    
    if ( ERROR_SUCCESS == dwStatus ) {
        // Create registry subkey for Log Queries
        dwStatus = RegCreateKeyExW (
                        hkeySysmonLog,
                        cwszRegKeyLogQueries,
                        0,
                        NULL,
                        REG_OPTION_NON_VOLATILE,
                        KEY_READ | KEY_WRITE,
                        NULL,
                        &rhkeyLogQueries,
                        &dwDisposition);
    } 
    
    if ( ERROR_ACCESS_DENIED == dwStatus ) {
        dwStatus = LOGMAN_INSTALL_ACCESS_DENIED;
        DisplayErrorMessage ( dwStatus );
    } else if ( ERROR_SUCCESS == dwStatus ) {
        dwStatus = CreateDefaultLogQueries( rhkeyLogQueries );
        // Todo:  Handle failure
    }

    return dwStatus;
}   
    
DWORD   _stdcall
ConnectToRegistry (
    LPCTSTR szComputerName,
    HKEY& rhkeyLogQueries )
{
    DWORD   dwStatus = ERROR_SUCCESS;
    HKEY    hkeyMachine = HKEY_LOCAL_MACHINE;    
    WCHAR   wszComputerName[MAX_COMPUTERNAME_LENGTH+1];

    USES_CONVERSION;
    
    wszComputerName[0] = NULL_W;

    // Connect to registry on specified machine.
    if ( NULL != szComputerName ) {
        if ( NULL_T != szComputerName[0] ) {
        
            lstrcpyW ( wszComputerName, T2W ( szComputerName ) );

            dwStatus = RegConnectRegistryW (
                wszComputerName,
                HKEY_LOCAL_MACHINE,
                &hkeyMachine );        
        } else {
            hkeyMachine = HKEY_LOCAL_MACHINE;
        }
    } else {
        hkeyMachine = HKEY_LOCAL_MACHINE;
    }

    if ( ERROR_SUCCESS == dwStatus ) {
        assert ( NULL != hkeyMachine );
        dwStatus = RegOpenKeyExW (
            hkeyMachine,
            cwszRegKeyFullLogQueries,
            0,
            KEY_ALL_ACCESS,
            &rhkeyLogQueries ); 
        
        if ( ERROR_ACCESS_DENIED == dwStatus ) {
            dwStatus = IDS_LOGMAN_REG_ACCESS_DENIED;
        } else if ( ERROR_SUCCESS != dwStatus ) {
            // Install displays error messages
            dwStatus = Install ( hkeyMachine, rhkeyLogQueries );
        }         
    } else {
        if ( 0 == lstrlenW ( wszComputerName ) ) {
            lstrcpyW ( wszComputerName, cwszLocalComputer );
        }

        DisplayErrorMessage ( LOGMAN_NO_COMPUTER_CONNECT, wszComputerName );
        DisplayErrorMessage ( dwStatus );
        dwStatus = LOGMAN_NO_COMPUTER_CONNECT;
    }

    if ( NULL != hkeyMachine ) {
        RegCloseKey ( hkeyMachine );
    }

    return dwStatus;
}
    
//
//  WriteRegistryLastModified function.
//      Copies the current "last modified date" to the registry where 
//      it is read by the log service.
//
DWORD   _stdcall   
WriteRegistryLastModified ( HKEY hkeyQuery )
{
    LONG    dwStatus = ERROR_SUCCESS;

    SLQ_TIME_INFO   plqLastModified;
    SYSTEMTIME  stLocalTime;
    FILETIME    ftLocalTime;

    // Get local time for last modified value.
    GetLocalTime (&stLocalTime);
    SystemTimeToFileTime (&stLocalTime, &ftLocalTime);
    
    plqLastModified.wDataType = SLQ_TT_DTYPE_DATETIME;
    plqLastModified.wTimeType = SLQ_TT_TTYPE_LAST_MODIFIED;
    plqLastModified.dwAutoMode = SLQ_AUTO_MODE_NONE;    // not used.
    plqLastModified.llDateTime = *(LONGLONG *)&ftLocalTime;

    dwStatus = WriteRegistrySlqTime (
        hkeyQuery, 
        cwszRegLastModified,
        &plqLastModified);

    return dwStatus;
}

DWORD   _stdcall
ValidateProperties (
    HKEY            hkeyLogQueries,
    CPropertyBag*   pPropBag,
    DWORD*          pdwLogType,
    LPWSTR          szQueryName,
    DWORD*          pdwNameBufLen )
{
    DWORD   dwStatus = ERROR_SUCCESS;
    HRESULT hr = NOERROR;
    LPWSTR  szPropBagBuf = NULL;
    DWORD   dwPropBagBufLen = 0;
    DWORD   dwPropBagStringLen = 0;
    DWORD   dwLocalLogType;

    // Query key is not necessary for validation, so set it to NULL.
    CPropertyUtils  cPropertyUtils ( Commands[eComputer].strValue, NULL, pPropBag, NULL, hkeyLogQueries );

    USES_CONVERSION;

    *pdwLogType = 0;
    szQueryName[0] = NULL_W;

    // Todo:  Version info

    // Query type and name are required properties.

    hr = DwordFromPropertyBag (
                pPropBag,      
                cwszHtmlLogType,
                dwLocalLogType );
    
    if ( SUCCEEDED ( hr ) ) {
        if ( SLQ_ALERT != dwLocalLogType ) {
            hr = StringFromPropBagAlloc ( pPropBag, cwszHtmlLogName, &szPropBagBuf, &dwPropBagBufLen, &dwPropBagStringLen );
        } else {
            hr = StringFromPropBagAlloc ( pPropBag, cwszHtmlAlertName, &szPropBagBuf, &dwPropBagBufLen, &dwPropBagStringLen );
        }

        if ( FAILED ( hr ) ) {
            dwStatus = LOGMAN_NO_OBJECT_NAME;
/*        } else {

            // Validate query name.  
            // Todo:  Length check.  Must be <= MAX_PATH;
            lstrcpynW ( szTempCheck, szPropBagBuf, MAX_PATH );
            szTokenCheck = _tcstok ( szTempCheck, cwszInvalidLogNameChars );
            if ( 0 != lstrcmpi (szTempCheck, szTokenCheck ) ) {
                dwStatus = LOGMAN_INVALID_QUERY_NAME;
            }
*/
        }
    } else {
        dwStatus = LOGMAN_NO_OBJECT_LOGTYPE;
    }

    // At this point, any hr failure is reflected in dwStatus.

    if ( ERROR_SUCCESS == dwStatus ) {    
        if ( NULL == szQueryName 
                || ( *pdwNameBufLen < ( dwPropBagStringLen + 1 ) ) ) {
            dwStatus = ERROR_INSUFFICIENT_BUFFER;
        } else {
            lstrcpyW ( szQueryName, szPropBagBuf ) ;
        }
        *pdwNameBufLen = dwPropBagStringLen + 1;    // Room for NULL.

        *pdwLogType = dwLocalLogType;
    } 
    
    // If required query type and name exist, then validate all other properties
    if ( ERROR_SUCCESS != dwStatus ) {
        // Todo:  Error message re: Validation failure -or print that and write success messages
        // in the "Process object" method.
        // Todo:  Handle "invalid query name" elsewhere.
        if ( LOGMAN_INVALID_QUERY_NAME == dwStatus ) {
            DisplayErrorMessage ( dwStatus, szQueryName );
        } else {
            DisplayErrorMessage ( dwStatus );
        }
    } else {

        // Initialize the query name string in CPropertyUtils, for error message display.
        cPropertyUtils.SetQueryName ( szQueryName );

        // Validate required parameters first:

        if ( SLQ_TRACE_LOG == dwLocalLogType ) {
            //DWORD dwKernelValid;
            dwStatus = cPropertyUtils.Validate ( IdGuidListProp, dwLocalLogType );

            // Todo:  Check for kernel flags.  Must have Guids or Kernel, cannot have both.

        } else {
            assert ( SLQ_COUNTER_LOG == dwLocalLogType || SLQ_ALERT == dwLocalLogType );
            dwStatus = cPropertyUtils.Validate ( IdCounterListProp, dwLocalLogType );

            // Todo: Validate Alert action flags here.  If all subfields are messed up,
            // do not continue processing (?)
        }

        // All other fields are non-required
        if ( ERROR_SUCCESS == dwStatus ) {

            // Handle failures individually?  Use exception handling?

            if ( SLQ_TRACE_LOG == dwLocalLogType ) {
                
                dwStatus = cPropertyUtils.Validate ( IdTraceBufferSizeProp );
                dwStatus = cPropertyUtils.Validate ( IdTraceBufferMinCountProp );
                dwStatus = cPropertyUtils.Validate ( IdTraceBufferMaxCountProp );
                dwStatus = cPropertyUtils.Validate ( IdTraceBufferFlushIntProp );
                
            } else if ( SLQ_ALERT == dwLocalLogType ) {

                dwStatus = cPropertyUtils.Validate ( IdActionFlagsProp );
                dwStatus = cPropertyUtils.Validate ( IdCommandFileProp );
// Validated as part of action flags validation dwStatus = cPropertyUtils.Validate ( IdNetworkNameProp );
                dwStatus = cPropertyUtils.Validate ( IdUserTextProp );
                dwStatus = cPropertyUtils.Validate ( IdPerfLogNameProp );
            }

            // Properties common to counter and trace logs
            if ( SLQ_COUNTER_LOG == dwLocalLogType 
                    || SLQ_TRACE_LOG == dwLocalLogType ) {

                dwStatus = cPropertyUtils.Validate ( IdLogFileTypeProp );
                dwStatus = cPropertyUtils.Validate ( IdLogFileAutoFormatProp );
                dwStatus = cPropertyUtils.Validate ( IdLogFileSerialNumberProp );
                dwStatus = cPropertyUtils.Validate ( IdLogFileBaseNameProp );
                dwStatus = cPropertyUtils.Validate ( IdLogFileMaxSizeProp );
                dwStatus = cPropertyUtils.Validate ( IdLogFileFolderProp );
                dwStatus = cPropertyUtils.Validate ( IdEofCommandFileProp );
            }

            // Properties common to alerts and counter logs
            if ( SLQ_COUNTER_LOG == dwLocalLogType 
                    || SLQ_ALERT == dwLocalLogType ) {
                dwStatus = cPropertyUtils.Validate ( IdSampleProp );
            }

            // Properties common to all query types
            dwStatus = cPropertyUtils.Validate ( IdCommentProp );
            dwStatus = cPropertyUtils.Validate ( IdRestartProp, dwLocalLogType );

            dwStatus = cPropertyUtils.Validate ( IdStartProp, dwLocalLogType );
            dwStatus = cPropertyUtils.Validate ( IdStopProp, dwLocalLogType );
        
            // Todo:  Validation is currently ignored until fully implemented
            dwStatus = ERROR_SUCCESS;

        } else {
            // Display error message re: missing required property.
            // Need log-type specific message.
            if ( SLQ_COUNTER_LOG == dwLocalLogType ) {
        //        DisplayErrorMessage ( dwStatus );
            } else if ( SLQ_TRACE_LOG == dwLocalLogType ) {
                // Attempt to load kernel trace flags
//                dwStatus = cPropertyUtils.Validate ( IdTraceFlagsProp );
                if ( ERROR_SUCCESS == dwStatus ) {
                    // Todo:  Check for kernel flag.  Need either Kernel flag OR provider GUIDs
                    // and NOT both.
                }
            } else if ( SLQ_ALERT == dwLocalLogType ) {
            }

            // Todo:  Need status processing method to determine if any required subfields are incorrect.
            // If so, then stop processing this HTML file.
            
            
            // Todo:  Validation is currently ignored until fully implemented
            dwStatus = ERROR_SUCCESS;
        }
    }
    // Todo:  Validation is currently ignored until fully implemented
    dwStatus = ERROR_SUCCESS;
    return dwStatus;
}    
    
DWORD   _stdcall
WriteToRegistry (
    HKEY            hkeyLogQueries,
    CPropertyBag*   pPropBag,
    LPWSTR          szQueryName,
    DWORD&          rdwLogType )
{
    DWORD   dwStatus = ERROR_SUCCESS;
    HRESULT hr = NOERROR;
    HKEY    hkeyQuery = NULL;
    CPropertyUtils  cPropertyUtils ( Commands[eComputer].strValue );

    USES_CONVERSION;
    // Write all properties to the registry.

    // All errors are displayed as messages within this
    // routine or its subroutines.

    // Create log key 
    dwStatus = InitializeNewQuery (
            hkeyLogQueries,
            hkeyQuery,
            szQueryName );     
    if ( ERROR_SUCCESS != dwStatus ) {
        if ( LOGMAN_DUP_QUERY_NAME == dwStatus ) {
            if ( Commands[eOverwrite].bValue ) {
                // If Overwrite option specified, overwrite after displaying warning.
                DisplayErrorMessage ( LOGMAN_OVERWRITE_DUP_QUERY, szQueryName );
                
                dwStatus = DeleteQuery ( hkeyLogQueries, szQueryName );
                if ( ERROR_SUCCESS == dwStatus ) {
                    dwStatus = InitializeNewQuery (
                                hkeyLogQueries,
                                hkeyQuery,
                                szQueryName ); 
                    if ( ERROR_SUCCESS != dwStatus ) {
                        DisplayErrorMessage ( dwStatus );
                    }
                }
            } else {
               DisplayErrorMessage ( dwStatus, szQueryName );
            }
        } else {
            DisplayErrorMessage ( dwStatus );
        }
    }
    
    // If failed before this point, do not continue loading.

    if ( ERROR_SUCCESS == dwStatus ) {
    
        // Use log key to write properties to the registry.
        // When loading properties, continue even if errors.
        //
        // On error, nothing is written to the registry.  
        // In this case, the default value will  
        // be read in by the log service.

        // Note:  StringFromPropBagAlloc and AddStringToMszBuffer 
        // allocate data buffers that must be deleted by the caller.
        // These methods also indicate length of string returned
        // vs. length of buffer returned.

        // hkeyQueryList is not required for writing to the the registry, 
        // so leave it NULL.
        // Todo:  Some fields will require validation in this method,
        // to ensure that default values are used instead of incorrect values.
        cPropertyUtils.SetQueryName ( szQueryName );
        cPropertyUtils.SetPropertyBag ( pPropBag );
        cPropertyUtils.SetQueryKey ( hkeyQuery );

        // Required properties:  Counter list for counter logs and alerts,
        // provider guid list or kernel flags for trace logs.
        if ( SLQ_TRACE_LOG == rdwLogType ) {
            hr = cPropertyUtils.BagToRegistry ( IdGuidListProp, rdwLogType );
        } else {
            assert ( SLQ_COUNTER_LOG == rdwLogType || SLQ_ALERT == rdwLogType );
            hr = cPropertyUtils.BagToRegistry ( IdCounterListProp, rdwLogType );
        }

        if ( FAILED ( hr ) ) {
            // Todo:  At this point, the problem would be an internal error,
            // because validated previous to this.
            // Todo:  Type - specific error message
            if ( SLQ_COUNTER_LOG == rdwLogType ) {
            } else if ( SLQ_TRACE_LOG == rdwLogType ) {
                // Attemp to load kernel trace flags
                hr = cPropertyUtils.BagToRegistry ( IdTraceFlagsProp );
                if ( FAILED ( hr ) ) {
                    //Todo:  Check for kernel flag.  Need either Kernel flag OR provider GUIDs
                }
            } else if ( SLQ_ALERT == rdwLogType ) {
                // Todo:  Special case to write action properties.  Only write correct ones?
                // Or stop processing if any invalid?
            }
        } else {
            if ( SLQ_TRACE_LOG == rdwLogType ) {
                
                hr = cPropertyUtils.BagToRegistry ( IdTraceBufferSizeProp );
                hr = cPropertyUtils.BagToRegistry ( IdTraceBufferMinCountProp );
                hr = cPropertyUtils.BagToRegistry ( IdTraceBufferMaxCountProp );
                hr = cPropertyUtils.BagToRegistry ( IdTraceBufferFlushIntProp );
                
            } else if ( SLQ_ALERT == rdwLogType ) {

                hr = cPropertyUtils.BagToRegistry ( IdActionFlagsProp );
                hr = cPropertyUtils.BagToRegistry ( IdCommandFileProp );
                hr = cPropertyUtils.BagToRegistry ( IdNetworkNameProp );
                hr = cPropertyUtils.BagToRegistry ( IdUserTextProp );
                hr = cPropertyUtils.BagToRegistry ( IdPerfLogNameProp );
            }
            hr = NOERROR;
            // Todo:  ensure dwLogType is valid

            // Properties common to counter and trace logs
            if ( SLQ_COUNTER_LOG == rdwLogType 
                    || SLQ_TRACE_LOG == rdwLogType ) {

                hr = cPropertyUtils.BagToRegistry ( IdLogFileMaxSizeProp );
                hr = cPropertyUtils.BagToRegistry ( IdLogFileTypeProp );
                hr = cPropertyUtils.BagToRegistry ( IdLogFileAutoFormatProp );
                hr = cPropertyUtils.BagToRegistry ( IdLogFileSerialNumberProp );
                hr = cPropertyUtils.BagToRegistry ( IdLogFileBaseNameProp );
                hr = cPropertyUtils.BagToRegistry ( IdLogFileMaxSizeProp );
                hr = cPropertyUtils.BagToRegistry ( IdLogFileFolderProp );
                hr = cPropertyUtils.BagToRegistry ( IdEofCommandFileProp );
                hr = NOERROR;
            }
    
            // Properties common to alerts and counter logs
            if ( SLQ_COUNTER_LOG == rdwLogType 
                    || SLQ_ALERT == rdwLogType ) {
                // Time values default if error
                hr = cPropertyUtils.BagToRegistry ( IdSampleProp );
                hr = NOERROR;
            }

            // Properties common to all query types
            hr = cPropertyUtils.BagToRegistry ( IdCommentProp );
            hr = cPropertyUtils.BagToRegistry ( IdRestartProp, rdwLogType );

            hr = cPropertyUtils.BagToRegistry ( IdStartProp, rdwLogType );
            hr = cPropertyUtils.BagToRegistry ( IdStopProp, rdwLogType );
        
            hr = NOERROR;
            dwStatus = ERROR_SUCCESS;   // Non-required fields

            // Required fields, ending the creation process.
            // Reset the log type from "new" to real type
            dwStatus = WriteRegistryDwordValue ( hkeyQuery, cwszRegLogType, &rdwLogType );
            if ( ERROR_SUCCESS == dwStatus ) {
                dwStatus = WriteRegistryLastModified ( hkeyQuery );
            }
            if ( ERROR_SUCCESS == dwStatus ) {
                DisplayErrorMessage ( IDS_LOGMAN_QUERY_CONFIG_SUCCESS, szQueryName );
            } /* else //Todo:  Error message re: unable to complete query in the registry */
        }

        if ( FAILED ( hr ) || ( ERROR_SUCCESS != dwStatus ) ) {
            RegCloseKey ( hkeyQuery );
            hkeyQuery = NULL;
            DeleteQuery ( hkeyLogQueries, szQueryName );
        }
    }

    if ( NULL != hkeyQuery ) {
        RegCloseKey ( hkeyQuery );
    }

    return dwStatus;
}

DWORD   _stdcall
ProcessSettingsObject (
    HKEY            hkeyLogQueries,
    CPropertyBag*   pPropBag )
{
    DWORD dwStatus = ERROR_SUCCESS;
    DWORD dwLogType;
    WCHAR szQueryName [MAX_PATH];       // Todo:  Remove length restriction
    DWORD dwNameBufLen = MAX_PATH;

    // Validate all properties
    // Todo:  Ensure that at least one provider, counter, or alert was added. 
    dwStatus = ValidateProperties ( 
                    hkeyLogQueries,
                    pPropBag,
                    &dwLogType,
                    szQueryName,
                    &dwNameBufLen );
    // ValidateProperties() displays messages for all errors

    if ( ERROR_SUCCESS == dwStatus ) {

        // Write all properties to the registry.
        // WriteToRegistry() displays messages for all errors
        dwStatus = WriteToRegistry (
                        hkeyLogQueries,
                        pPropBag,
                        szQueryName,
                        dwLogType );
    }

    return dwStatus;
}

DWORD   _stdcall
ProcessSettingsFile (
    LPCTSTR szComputerName,
    LPCTSTR szFileName )
{
    DWORD           dwStatus = ERROR_SUCCESS;
    LPTSTR          szFirstObject = NULL;

    // Open file

    dwStatus = LoadSettingsFile ( szFileName, szFirstObject );

    if ( ERROR_SUCCESS == dwStatus && NULL != szFirstObject ) {
        HKEY    hkeyLogQueries = NULL;
                
        dwStatus = ConnectToRegistry (
                    szComputerName,
                    hkeyLogQueries );

        if ( ERROR_SUCCESS == dwStatus ) {
            LPTSTR  szCurrentObject = NULL;
            LPTSTR  szNextObject = NULL;
            BOOL    bAtLeastOneSysmonObjectRead = FALSE;

            assert ( NULL != hkeyLogQueries );

            szCurrentObject = szFirstObject;

            while ( ERROR_SUCCESS == dwStatus && NULL != szCurrentObject ) {
                CPropertyBag    PropBag;

                dwStatus = PropBag.LoadData ( szCurrentObject, szNextObject );

                if ( ERROR_SUCCESS == dwStatus ) {
                    dwStatus = ProcessSettingsObject (
                                    hkeyLogQueries,
                                    &PropBag );

                    if ( ERROR_SUCCESS == dwStatus ) {
                        bAtLeastOneSysmonObjectRead = TRUE;
                    } else {
                        if ( LOGMAN_NO_SYSMON_OBJECT != dwStatus ) {
                            bAtLeastOneSysmonObjectRead = TRUE;
                        }
                        // Error messages handled (displayed) within 
                        // ProcessSettingsObject(), so reset dwStatus
                        dwStatus = ERROR_SUCCESS;
                    }

                } else {
                    // Handle (display) error message, then reset
                    // dwStatus to continue.
                    if ( LOGMAN_NO_SYSMON_OBJECT != dwStatus ) {
                        DisplayErrorMessage ( dwStatus );
                    }
                    dwStatus = ERROR_SUCCESS;   
                }
                szCurrentObject = szNextObject;
            }

            if ( !bAtLeastOneSysmonObjectRead ) {
                dwStatus = LOGMAN_NO_SYSMON_OBJECT;
                DisplayErrorMessage ( dwStatus );
            }

            RegCloseKey ( hkeyLogQueries );
        } // else error message displayed by ConnectToRegistry()
    } // else error message displayed by LoadSettingsFile()


    // Delete data buffer allocated by LoadSettingsFile
    if ( NULL != szFirstObject ) {
        delete szFirstObject;
    }
    return dwStatus;
}

DWORD   _stdcall
ConnectToQuery (
    LPCTSTR szComputerName,
    LPCTSTR szQueryName,
    HKEY&   rhkeyQuery )
{
    DWORD dwStatus = ERROR_SUCCESS;
    HKEY  hkeyQuery = NULL;
    HKEY  hkeyLogQueries = NULL;
                
    USES_CONVERSION;

    dwStatus = ConnectToRegistry (
                szComputerName,
                hkeyLogQueries );

    if ( ERROR_SUCCESS == dwStatus ) {
        dwStatus = RegOpenKeyEx (
            hkeyLogQueries,
            szQueryName,
            0,
            KEY_ALL_ACCESS,
            &hkeyQuery );

        if ( ERROR_SUCCESS != dwStatus ) {
            if ( ERROR_ACCESS_DENIED == dwStatus ) {
                dwStatus = LOGMAN_REG_ACCESS_DENIED;
                DisplayErrorMessage ( dwStatus );
            } else if ( ERROR_FILE_NOT_FOUND == dwStatus ) {
                dwStatus = LOGMAN_NO_QUERY_CONNECT;
                DisplayErrorMessage ( dwStatus, T2W( szQueryName ) );
            }
        }
    }

    RegCloseKey ( hkeyLogQueries );

    rhkeyQuery = hkeyQuery;
    
    return dwStatus;
}

#pragma warning ( disable : 4706 ) 
DWORD   _stdcall
StartQuery (
    LPCTSTR szComputerName,
    LPCTSTR szQueryName )
{
    DWORD   dwStatus = ERROR_SUCCESS;
    HKEY    hkeyQuery = NULL;

    USES_CONVERSION;

    // ConnectToQuery displays any errors
    dwStatus = ConnectToQuery ( szComputerName, szQueryName, hkeyQuery );

    if ( ERROR_SUCCESS == dwStatus ) {
        SLQ_TIME_INFO   stiData;
        DWORD           dwRegValue;
        BOOL            bSetStopToMax;
   
        // Todo:  Warn the user if start mode is not manual.
        // Set start mode to manual, start time = MIN_TIME_VALUE
        memset (&stiData, 0, sizeof(stiData));
        stiData.wTimeType = SLQ_TT_TTYPE_START;
        stiData.wDataType = SLQ_TT_DTYPE_DATETIME;
        stiData.dwAutoMode = SLQ_AUTO_MODE_NONE;
        stiData.llDateTime = MIN_TIME_VALUE;

        dwStatus = WriteRegistrySlqTime ( hkeyQuery, cwszRegStartTime, &stiData );
    
        if ( ERROR_SUCCESS == dwStatus ) {
            // If stop time mode set to manual, or StopAt with time before Now,
            // set the mode to Manual, value to MAX_TIME_VALUE
            bSetStopToMax = FALSE;
            dwStatus = ReadRegistrySlqTime ( hkeyQuery, cwszRegStopTime, &stiData );

            if ( ERROR_SUCCESS == dwStatus ) {
                if ( SLQ_AUTO_MODE_NONE == stiData.dwAutoMode ) {
                    bSetStopToMax = TRUE;
                } else if ( SLQ_AUTO_MODE_AT == stiData.dwAutoMode ) {
                    SYSTEMTIME      stLocalTime;
                    FILETIME        ftLocalTime;
                    LONGLONG        llLocalTime;

                    // get local time
                    GetLocalTime (&stLocalTime);
                    SystemTimeToFileTime (&stLocalTime, &ftLocalTime);
        
                    llLocalTime = *(LONGLONG*)&ftLocalTime;

                    if ( llLocalTime >= stiData.llDateTime ) {
                        bSetStopToMax = TRUE;
                    }    
                }
            }

            if ( ERROR_SUCCESS == dwStatus && bSetStopToMax ) {    
                assert( SLQ_TT_DTYPE_DATETIME == stiData.wDataType );
                stiData.dwAutoMode = SLQ_AUTO_MODE_NONE;
                stiData.llDateTime = MAX_TIME_VALUE;
                dwStatus = WriteRegistrySlqTime ( hkeyQuery, cwszRegStopTime, &stiData );
            }
        }
        // Service needs to distinguish between Running and Start Pending
        // at service startup, so always set state to start pending.
        
        // Todo:  Check to see if running, before executing this.
        if ( ERROR_SUCCESS == dwStatus ) {
            dwRegValue = SLQ_QUERY_START_PENDING;
            dwStatus = WriteRegistryDwordValue ( 
                        hkeyQuery, 
                        cwszRegCurrentState, 
                        &dwRegValue );
        }

        // Set LastModified
        if ( ERROR_SUCCESS == dwStatus ) { 
            dwStatus = WriteRegistryLastModified ( hkeyQuery );
        }
        
        // Start the service on the target machine
        if ( ERROR_SUCCESS == dwStatus ) { 
            DWORD   dwTimeout = 3;
            DWORD   dwState = 0;

            dwStatus = Synchronize ( szComputerName );

            while (--dwTimeout && ERROR_SUCCESS == dwStatus ) {
                dwStatus = GetState ( szComputerName, dwState  );

                if ( SERVICE_RUNNING == dwState ) {
                    break;
                }

            }
            if ( ERROR_SUCCESS == dwStatus && SERVICE_RUNNING != dwState ) {
                dwStatus = LOGMAN_START_TIMED_OUT;
            }
        }   

        if ( ERROR_SUCCESS != dwStatus ) {
            if ( LOGMAN_START_TIMED_OUT == dwStatus ) {
                DisplayErrorMessage ( dwStatus, T2W(szQueryName) );
            } else {
                DisplayErrorMessage ( LOGMAN_START_FAILED, T2W(szQueryName) );
                DisplayErrorMessage ( dwStatus );
            }
        }
    }

    if ( NULL != hkeyQuery ) {
        RegCloseKey ( hkeyQuery );
    }

    if ( ERROR_SUCCESS == dwStatus ) {
        DisplayErrorMessage ( LOGMAN_QUERY_START_SUCCESS, T2W(szQueryName) );
    }

    return dwStatus;
}
#pragma warning ( default: 4706 ) 

#pragma warning ( disable: 4706 ) 
DWORD   _stdcall
StopQuery (
    LPCTSTR szComputerName,
    LPCTSTR szQueryName )
{
    DWORD   dwStatus = ERROR_SUCCESS;
    HKEY    hkeyQuery = NULL;

    USES_CONVERSION;

    // ConnectToQuery displays any errors
    dwStatus = ConnectToQuery ( szComputerName, szQueryName, hkeyQuery );

    if ( ERROR_SUCCESS == dwStatus ) {
        SLQ_TIME_INFO stiData;
        DWORD dwRestartMode = 0;
        
        // If query is set to restart on end, clear the restart flag.
        dwStatus = ReadRegistryDwordValue ( hkeyQuery, cwszRegRestart, &dwRestartMode );

        // Todo:  Warn user
        if ( ERROR_SUCCESS == dwStatus && SLQ_AUTO_MODE_NONE != dwRestartMode ) {
            dwRestartMode = SLQ_AUTO_MODE_NONE;
            dwStatus = WriteRegistryDwordValue ( hkeyQuery, cwszRegRestart, &dwRestartMode, REG_BINARY );
        }

        // Set stop mode to manual, stop time to MIN_TIME_VALUE
        if ( ERROR_SUCCESS == dwStatus ) {
            memset (&stiData, 0, sizeof(stiData));
            stiData.wTimeType = SLQ_TT_TTYPE_STOP;
            stiData.wDataType = SLQ_TT_DTYPE_DATETIME;
            stiData.dwAutoMode = SLQ_AUTO_MODE_NONE;
            stiData.llDateTime = MIN_TIME_VALUE;

            dwStatus = WriteRegistrySlqTime ( hkeyQuery, cwszRegStopTime, &stiData );
        }

        // If start time mode set to manual, set the value to MAX_TIME_VALUE
        if ( ERROR_SUCCESS == dwStatus ) {
            dwStatus = ReadRegistrySlqTime ( hkeyQuery, cwszRegStartTime, &stiData );

            if ( ERROR_SUCCESS == dwStatus 
                    && SLQ_AUTO_MODE_NONE == stiData.dwAutoMode ) {
                assert( SLQ_TT_DTYPE_DATETIME == stiData.wDataType );
                stiData.llDateTime = MAX_TIME_VALUE;
                dwStatus = WriteRegistrySlqTime ( hkeyQuery, cwszRegStartTime, &stiData );
            }
        }

        // Set LastModified
        if ( ERROR_SUCCESS == dwStatus ) { 
            dwStatus = WriteRegistryLastModified ( hkeyQuery );
        }
        // Start the service on the target machine
        if ( ERROR_SUCCESS == dwStatus ) { 
            DWORD   dwTimeout = 3;
            DWORD   dwState = 0;

            dwStatus = Synchronize ( szComputerName );

            while (--dwTimeout && ERROR_SUCCESS == dwStatus ) {
                dwStatus = GetState ( szComputerName, dwState  );

                if ( SERVICE_STOPPED == dwState ) {
                    break;
                }

            }
            if ( ERROR_SUCCESS == dwStatus && SERVICE_STOPPED != dwState ) {
                dwStatus = LOGMAN_STOP_TIMED_OUT;
            }
        }   
        if ( ERROR_SUCCESS != dwStatus ) {
            if ( LOGMAN_STOP_TIMED_OUT == dwStatus ) {
                DisplayErrorMessage ( dwStatus, T2W(szQueryName) );
            } else {
                DisplayErrorMessage ( LOGMAN_STOP_FAILED, T2W(szQueryName) );
                DisplayErrorMessage ( dwStatus );
            }
        }
    }
    if ( NULL != hkeyQuery ) {
        RegCloseKey ( hkeyQuery );
    }

    if ( ERROR_SUCCESS == dwStatus ) {
        DisplayErrorMessage ( LOGMAN_QUERY_STOP_SUCCESS, T2W(szQueryName) );
    }

    return dwStatus;
}
#pragma warning ( default: 4706 ) 

DWORD   _stdcall
GetState ( 
    LPCTSTR szComputerName,
    DWORD&  rdwState )
{
    DWORD dwStatus = ERROR_SUCCESS;
    SERVICE_STATUS  ssData;
    SC_HANDLE   hSC;
    SC_HANDLE   hLogService;
    
    rdwState = 0;       // Error by default.

    // open SC database
    hSC = OpenSCManager ( szComputerName, NULL, SC_MANAGER_CONNECT);

    if (hSC != NULL) {
    
        // open service
        hLogService = OpenServiceW (
                        hSC, 
                        cwszLogService,
                        SERVICE_INTERROGATE );
    
        if (hLogService != NULL) {
            if ( ControlService (
                    hLogService, 
                    SERVICE_CONTROL_INTERROGATE,
                    &ssData)) {

                rdwState = ssData.dwCurrentState;
            } else {
                dwStatus = GetLastError();
                rdwState = SERVICE_STOPPED;
                // *** error message
                assert (dwStatus != 0);
            }

            CloseServiceHandle (hLogService);
        
        } else {
            // *** error message
            dwStatus = GetLastError();
            assert (dwStatus != 0);
        }

        CloseServiceHandle (hSC);
    } else {
        // *** error message
        dwStatus = GetLastError();
        assert (dwStatus != 0);
    } // OpenSCManager

    if ( ERROR_SERVICE_NOT_ACTIVE == dwStatus
            || ERROR_SERVICE_REQUEST_TIMEOUT == dwStatus ) {
        rdwState = SERVICE_STOPPED;
        dwStatus = ERROR_SUCCESS;
    }

    return dwStatus;
}

#pragma warning ( disable: 4706 ) 
DWORD   _stdcall
Synchronize ( 
    LPCTSTR szComputerName )
{
    // If the service is running, tell it to synchronize itself,
    // Check the state afterwards to see if it got the message.
    // If stop pending or stopped, wait until the service is
    // stopped and then attempt to start it.  The service 
    // synchronizes itself from the registry when it is started.

    // Return ERROR_SUCCESS for success, other for failure.

    SC_HANDLE   hSC = NULL;
    SC_HANDLE   hLogService = NULL;
    SERVICE_STATUS  ssData;
    DWORD       dwCurrentState;
    DWORD       dwTimeout = 25;
    LONG        dwStatus = ERROR_SUCCESS;

    dwStatus = GetState ( szComputerName, dwCurrentState );

    if ( ERROR_SUCCESS == dwStatus && 0 != dwCurrentState ) {
        // open SC database
        hSC = OpenSCManager ( szComputerName, NULL, SC_MANAGER_CONNECT);

        if ( NULL != hSC ) {
            // open service
            hLogService = OpenServiceW (
                            hSC, 
                            cwszLogService,
                            SERVICE_USER_DEFINED_CONTROL 
                            | SERVICE_START );
    
            if ( NULL != hLogService ) {

                if ( ( SERVICE_STOPPED != dwCurrentState ) 
                        && ( SERVICE_STOP_PENDING != dwCurrentState ) ) {

                    // Wait 100 milliseconds before synchronizing service,
                    // to ensure that registry values are written.
                    Sleep ( 100 );

                    ControlService ( 
                        hLogService, 
                        SERVICE_CONTROL_SYNCHRONIZE, 
                        &ssData);
                
                    dwCurrentState = ssData.dwCurrentState;
                }

                // Make sure that the ControlService call reached the service
                // while it was in run state.
                if ( ( SERVICE_STOPPED == dwCurrentState ) 
                    || ( SERVICE_STOP_PENDING == dwCurrentState ) ) {

                    if ( SERVICE_STOP_PENDING == dwCurrentState ) {
                        // wait for the service to stop before starting it.
                        while ( --dwTimeout && ERROR_SUCCESS == dwStatus ) {
                            dwStatus = GetState ( szComputerName, dwCurrentState );
                            if ( SERVICE_STOP_PENDING == dwCurrentState ) {
                                Sleep(200);
                            } else {
                                break;
                            }
                        }
                    }
                    dwTimeout = 25;
                    if ( SERVICE_STOPPED == dwCurrentState ) {
                        if ( StartService (hLogService, 0, NULL) ) {
                            // wait for the service to start or stop 
                            // before returning
                            while ( --dwTimeout && ERROR_SUCCESS == dwStatus ) {
                                dwStatus = GetState ( szComputerName, dwCurrentState );
                                if ( SERVICE_START_PENDING == dwCurrentState ) {
                                    Sleep(200);
                                } else {
                                    break;
                                }
                            }
                        } else {
                            dwStatus = GetLastError();
                        }
                    } else {
                        // *** error message
                    }
                    // *** ensure that dwCurrentState is not stopped?
                }
            }
            CloseServiceHandle ( hLogService );

        } else {
            dwStatus = GetLastError();
        }

        CloseServiceHandle (hSC);

    } else {
        dwStatus = GetLastError();
    }

    // Todo:  Update Auto Start service config
    return dwStatus;
}
#pragma warning ( default : 4706 ) 

int __cdecl
_tmain (
    INT argc, 
    LPTSTR argv[] )
{
    DWORD   dwStatus = 0;

    // Accept user input.
    // The command line arguments are the objects to sample. 
    // If no user input, then display help.
    ParseCmd( argc, argv );

    if (Commands[eSettings].bDefined) {
        dwStatus = ProcessSettingsFile (
                Commands[eComputer].strValue,
                Commands[eSettings].strValue );
    // Error messages displayed within ProcessSettingsFile method
    }
    if ( Commands[eStart].bDefined ) {
        dwStatus = StartQuery ( 
                Commands[eComputer].strValue,
                Commands[eName].strValue
                );
    // Error messages displayed within StartQuery method
    }
    if ( Commands[eStop].bDefined ) {
        dwStatus = StopQuery ( 
                Commands[eComputer].strValue,
                Commands[eName].strValue
                );
    // Error messages displayed within StopQuery method
    }


    // Error messages displayed within submethods, so reset dwStatus.

    dwStatus = 0;
    FreeCmd();
    return dwStatus;
}