//*********************************************************************** // trapreg.cpp // // This file contains the implementation of the classes for the objects // that are read from the registry, manipulated and written back to the // registry. // // Author: Larry A. French // // History: // 20-Febuary-1996 Larry A. French // Totally rewrote it to fix the spagetti code and huge // methods. The original author seemed to have little or // no ability to form meaningful abstractions. // // // Copyright (C) 1995, 1996 Microsoft Corporation. All rights reserved. // //************************************************************************ // // Some of the interesting class implementations contained here are: // // CTrapReg // This is the container class for the registry information. It is // composed of the configuration "parameters" and an EventLog array. // // CXEventLogArray // This class implements an array of CXEventLog objects, where the // event logs are "application", "security", "system" and so on. // // CXEventLog // This class implements a single event log. All information // relevent to an event log can be accesssed through this class. // // CXEventSourceArray // Each event log contains an event source array. The event source // represents an application that can generate an Event. // // CXEventSource // An eventsource represents an application that can generate some // number of event-log events. The event source contains a CXEventArray // and CXMessageArray. The CXEventArray contains all the events // that will be converted to traps. The CXMessageArray contains all the // possible messages that a particular event source can generate. // // CXMessageArray // This class implements an array of CXMessage objects. // // CXMessage // This class contains all the information relevent to a message // that a message source can generate. // // // CXEventArray // This class implements an array of CXEvent objects. // // CXEvent // This class represents an event that the user has selected to be // converted to a trap. The event contains a message plus some // additional information. // //************************************************************************** // The Registry: // // These classes are loaded from the registry and written back to the // registry when the user clicks OK. To understand the format of the // registry, use the registry editor while looking though the "Serialize" // and "Deserialize" member function for each of these classes. //************************************************************************** #include "stdafx.h" #include "trapreg.h" #include "regkey.h" #include "busy.h" #include "utils.h" #include "globals.h" #include "utils.h" #include "dlgsavep.h" #include "remote.h" /////////////////////////////////////////////////////////////////// // Class: CBaseArray // // This class extends the CObArray class by adding the DeleteAll // method. // ////////////////////////////////////////////////////////////////// //**************************************************************** // CBaseArray::DeleteAll // // Delete all the objects contained in this array. // // Parameters: // None. // // Returns: // Nothing. // //**************************************************************** void CBaseArray::DeleteAll() { LONG nObjects = (LONG)GetSize(); for (LONG iObject = nObjects-1; iObject >= 0; --iObject) { CObject* pObject = GetAt(iObject); delete pObject; } RemoveAll(); } ///////////////////////////////////////////////////////////////////////////////////// // Class: CTrapReg // // This is the container class for all the registry information for eventrap.exe. // //////////////////////////////////////////////////////////////////////////////////// CTrapReg::CTrapReg() : m_pdlgLoadProgress(FALSE), m_pbtnApply(FALSE) { m_bNeedToCloseKeys = FALSE; m_pdlgSaveProgress = NULL; m_pdlgLoadProgress = NULL; m_bDidLockRegistry = FALSE; m_bRegIsReadOnly = FALSE; SetDirty(FALSE); m_nLoadSteps = LOAD_STEPS_IN_TRAPDLG; m_bShowConfigTypeBox = TRUE; m_dwConfigType = CONFIG_TYPE_CUSTOM; } CTrapReg::~CTrapReg() { delete m_pdlgSaveProgress; delete m_pdlgLoadProgress; if (!g_bLostConnection) { if (m_bDidLockRegistry) { UnlockRegistry(); } if (m_bNeedToCloseKeys) { m_regkeySource.Close(); m_regkeySnmp.Close(); m_regkeyEventLog.Close(); } } } //********************************************************************************* // CTrapReg::SetConfigType // // Set the configuration type to CONFIG_TYPE_CUSTOM or CONFIG_TYPE_DEFAULT // When the configuration type is changed, the change is reflected in the // registry immediately so that the config tool can know whether or not it // should update the event to trap configuration. // // Parameters: // DWORD dwConfigType // This parameter must be CONFIG_TYPE_CUSTOM or CONFIG_TYPE_DEFAULT. // // Returns: // SCODE // S_OK if the configuration type was set, otherwise E_FAIL. // //********************************************************************************* SCODE CTrapReg::SetConfigType(DWORD dwConfigType) { ASSERT(dwConfigType==CONFIG_TYPE_CUSTOM || dwConfigType==CONFIG_TYPE_DEFAULT_PENDING); if (dwConfigType != m_dwConfigType) { SetDirty(TRUE); } m_dwConfigType = dwConfigType; return S_OK; } //********************************************************************************* // CTrapReg::LockRegistry // // Lock the registry to prevent two concurrent edits of the event-to-trap configuration // information. // // Parameters: // None. // // Returns: // SCODE // S_OK if successful. // E_FAIL if the configuration information was already locked. // E_REGKEY_NO_CREATE if the "CurrentlyOpen" registry key can't // be created. // //********************************************************************************** SCODE CTrapReg::LockRegistry() { if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } CRegistryKey regkey; if (m_regkeyEventLog.GetSubKey(SZ_REGKEY_CURRENTLY_OPEN, regkey)) { if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } if (AfxMessageBox(IDS_ERR_REGISTRY_BUSY, MB_YESNO | MB_ICONSTOP | MB_DEFBUTTON2) == IDNO) { regkey.Close(); return E_FAIL; } } // Create the "CurrentlyOpen" key as a volatile key so that it will disappear the next // time the machine is restarted in the event that the application that locked the // event-to-trap configuration crashed before it could clear this lock. if (!m_regkeyEventLog.CreateSubKey(SZ_REGKEY_CURRENTLY_OPEN, regkey, NULL, NULL, TRUE)) { if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } AfxMessageBox(IDS_WARNING_CANT_WRITE_CONFIG, MB_OK | MB_ICONSTOP); return E_REGKEY_NO_CREATE; } regkey.Close(); m_bDidLockRegistry = TRUE; return S_OK; } //*********************************************************************** // CTrapReg::UnlockRegistry // // Unlock the event-to-trap configuration so that others can edit it. // // Parameters: // None. // // Returns: // Nothing. // //*********************************************************************** void CTrapReg::UnlockRegistry() { m_regkeyEventLog.DeleteSubKey(SZ_REGKEY_CURRENTLY_OPEN); } //*********************************************************************** // CTrapReg::Connect // // Connect to a registry. The registry may exist on a remote computer. // // Parameters: // LPCTSTR pszComputerName // The computer who's registry you want to edit. An empty string // specifies a request to connect to the local machine. // // Returns: // SCODE // S_OK if the connection was made. // E_FAIL if an error occurred. In this event, the appropriate // error message boxes will have already been displayed. // //*********************************************************************** SCODE CTrapReg::Connect(LPCTSTR pszComputerName, BOOL bIsReconnecting) { SCODE sc; g_bLostConnection = FALSE; if (pszComputerName) { m_sComputerName = pszComputerName; } // There are eight steps here, plus there are three initial steps in // CTrapReg::Deserialize. After that the step count will be reset // and then stepped again for each log where each log will have // ten sub-steps. if (!bIsReconnecting) { m_pdlgLoadProgress->SetStepCount(LOAD_STEP_COUNT); } CRegistryValue regval; CRegistryKey regkeyEventLog; if (m_regkeySource.Connect(pszComputerName) != ERROR_SUCCESS) { if (m_regkeySource.m_lResult == ERROR_ACCESS_DENIED) { AfxMessageBox(IDS_ERR_REG_NO_ACCESS, MB_OK | MB_ICONSTOP); return E_ACCESS_DENIED; } goto CONNECT_FAILURE; } if (!bIsReconnecting) { if (m_pdlgLoadProgress->StepProgress()) { return S_LOAD_CANCELED; } ++m_nLoadSteps; } if (m_regkeySnmp.Connect(pszComputerName) != ERROR_SUCCESS) { if (m_regkeySnmp.m_lResult == ERROR_ACCESS_DENIED) { AfxMessageBox(IDS_ERR_REG_NO_ACCESS, MB_OK | MB_ICONSTOP); return E_ACCESS_DENIED; } goto CONNECT_FAILURE; } if (!bIsReconnecting) { if (m_pdlgLoadProgress->StepProgress()) { return S_LOAD_CANCELED; } ++m_nLoadSteps; } // SOFTWARE\\Microsoft\\SNMP_EVENTS if (m_regkeySnmp.Open(SZ_REGKEY_SNMP_EVENTS, KEY_READ | KEY_WRITE | KEY_CREATE_SUB_KEY) != ERROR_SUCCESS) { if (m_regkeySnmp.Open(SZ_REGKEY_SNMP_EVENTS, KEY_READ) == ERROR_SUCCESS) { m_bRegIsReadOnly = TRUE; } else { // At this point we know the SNMP_EVENTS key could not be opened. This // could either be because we don't have access to the registry or we // weren't installed yet. We now check to see if we can access the // registry at all. CRegistryKey regkeyMicrosoft; if (regkeyMicrosoft.Open(SZ_REGKEY_MICROSOFT, KEY_READ) == ERROR_SUCCESS) { regkeyMicrosoft.Close(); AfxMessageBox(IDS_ERR_NOT_INSTALLED, MB_OK | MB_ICONSTOP); } else { // We couldn't even access SOFTWARE\Microsoft, so we know that // we don't have access to the registry. AfxMessageBox(IDS_ERR_REG_NO_ACCESS, MB_OK | MB_ICONSTOP); return E_ACCESS_DENIED; } } return E_FAIL; } if (!bIsReconnecting) { if (m_pdlgLoadProgress->StepProgress()) { return S_LOAD_CANCELED; } ++m_nLoadSteps; } // SYSTEM\\CurrentControlSet\\Services\\EventLog if (m_regkeySource.Open(SZ_REGKEY_SOURCE_EVENTLOG, KEY_ENUMERATE_SUB_KEYS | KEY_READ | KEY_QUERY_VALUE ) != ERROR_SUCCESS) { m_regkeySnmp.Close(); AfxMessageBox(IDS_ERR_REG_NO_ACCESS, MB_OK | MB_ICONSTOP); return E_ACCESS_DENIED; } if (!bIsReconnecting) { if (m_pdlgLoadProgress->StepProgress()) { return S_LOAD_CANCELED; } ++m_nLoadSteps; } if (!m_regkeySnmp.GetSubKey(SZ_REGKEY_EVENTLOG, m_regkeyEventLog)) { if (m_regkeySnmp.m_lResult == ERROR_ACCESS_DENIED) { AfxMessageBox(IDS_ERR_REG_NO_ACCESS, MB_OK | MB_ICONSTOP); sc = E_ACCESS_DENIED; } else { AfxMessageBox(IDS_WARNING_CANT_READ_CONFIG, MB_OK | MB_ICONSTOP); sc = E_REGKEY_NOT_FOUND; } m_regkeySnmp.Close(); m_regkeySource.Close(); return sc; } if (!bIsReconnecting) { if (m_pdlgLoadProgress->StepProgress()) { return S_LOAD_CANCELED; } ++m_nLoadSteps; } m_bNeedToCloseKeys = TRUE; sc = LockRegistry(); if (FAILED(sc)) { if (sc == E_REGKEY_LOST_CONNECTION) { return sc; } else { return E_REGKEY_NO_CREATE; } } if (!bIsReconnecting) { if (m_pdlgLoadProgress->StepProgress()) { return S_LOAD_CANCELED; } ++m_nLoadSteps; } if (!bIsReconnecting) { if (m_pdlgLoadProgress->StepProgress()) { return S_LOAD_CANCELED; } ++m_nLoadSteps; } m_bShowConfigTypeBox = TRUE; if (FAILED(sc)) { if (sc == E_ACCESS_DENIED) { AfxMessageBox(IDS_ERR_REG_NO_ACCESS, MB_OK | MB_ICONSTOP); return E_ACCESS_DENIED; } else { goto CONNECT_FAILURE; } } if (!bIsReconnecting) { if (m_pdlgLoadProgress->StepProgress()) { return S_LOAD_CANCELED; } ++m_nLoadSteps; } return S_OK; CONNECT_FAILURE: CString sMessage; sMessage.LoadString(IDS_CANTCONNECT); if (pszComputerName != NULL) { sMessage += pszComputerName; } AfxMessageBox((LPCTSTR) sMessage, MB_OK | MB_ICONSTOP); return E_FAIL; } //**************************************************************************** // CTrapReg::BuildSourceHasTrapsMap // // This method fills the m_mapEventSources CMapStringToPtr object with the // names of all the event sources that actually have events configured for them. // When this map is used later, we only need to know whether or not a particular // entry exists in the map, so the value associated with each entry is irrelevant. // // Why do we need m_mapEventSources? The reason is that we need a quick way to // determine whether or not a particular source has events configured for it. // This is used when all the event sources are being enumerated and we need to know // whether or not to load the messages for the event source (an expensive operation). // If a particular event source has events configured for it, then we need to load // the messages so that the message text can be displayed. This is because the // event configuration stored in the registry only contains the event id and not the // message text. // // Parameters: // None. // // Returns: // SCODE // S_OK if successful, otherwise E_FAIL. // //****************************************************************************** SCODE CTrapReg::BuildSourceHasTrapsMap() { CRegistryKey regkey; if (!g_reg.m_regkeySnmp.GetSubKey(SZ_REGKEY_SOURCES, regkey)) { // For a fresh installation, there is no source subkey. if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } return S_OK; } CStringArray* pasEventSources = regkey.EnumSubKeys(); regkey.Close(); if (pasEventSources == NULL) { if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } return S_OK; } CString sEventSource; LONG nEventSources = (LONG)pasEventSources->GetSize(); for (LONG iEventSource = 0; iEventSource < nEventSources; ++iEventSource) { sEventSource = pasEventSources->GetAt(iEventSource); sEventSource.MakeUpper(); m_mapSourceHasTraps.SetAt(sEventSource, NULL); } delete pasEventSources; return S_OK; } //************************************************************************** // CTrapReg::Deserialize // // Read all the registry information (not including the event source messages) that // is required by eventrap.exe into this object. Reading the messages for most // event sources is delayed until the user actually requests it by selecting // an event source in the event source tree control. If an event source has // events that are being mapped into traps, then the messages for that event // source are loaded because an event description in the registry does not contain // the message text. // // Parameters: // None. // // Returns: // SCODE // S_OK if successful. // E_FAIL if a failure was detected. In the event of a failure, all // of the appropriate message boxes will have been displayed. // //*************************************************************************** SCODE CTrapReg::Deserialize() { m_bSomeMessageWasNotFound = FALSE; SetDirty(FALSE); // Get the value for the configuration type. CRegistryValue regval; if (m_regkeyEventLog.GetValue(SZ_NAME_REGVAL_CONFIGTYPE, regval)) { m_dwConfigType = *(DWORD*)regval.m_pData; } else { if (g_bLostConnection) { AfxMessageBox(IDS_ERROR_NOT_RESPONDING); return E_REGKEY_LOST_CONNECTION; } // If the config type value doesn't exist, assume a custom configuration. // This can happen because the setup program doesn't necessarily create // this value. m_dwConfigType = CONFIG_TYPE_CUSTOM; } if (m_pdlgLoadProgress->StepProgress()) { return S_LOAD_CANCELED; } ++m_nLoadSteps; SCODE sc = BuildSourceHasTrapsMap(); if (SUCCEEDED(sc)) { if (m_pdlgLoadProgress->StepProgress()) { return S_LOAD_CANCELED; } ++m_nLoadSteps; // Load the event log list, the current event list and so on. sc = m_params.Deserialize(); if (sc == S_LOAD_CANCELED) { return sc; } if (SUCCEEDED(sc)) { if (m_pdlgLoadProgress->StepProgress()) { return S_LOAD_CANCELED; } ++m_nLoadSteps; sc = m_aEventLogs.Deserialize(); if (sc == S_LOAD_CANCELED) { return sc; } if (SUCCEEDED(sc)) { if (m_nLoadSteps < LOAD_STEP_COUNT) { if (m_pdlgLoadProgress->StepProgress(LOAD_STEP_COUNT - m_nLoadSteps)) { return S_LOAD_CANCELED; } } } } } if (FAILED(sc)) { if (sc == E_REGKEY_LOST_CONNECTION) { AfxMessageBox(IDS_ERROR_NOT_RESPONDING); } else { AfxMessageBox(IDS_WARNING_CANT_READ_CONFIG); } } return sc; } //************************************************************************** // CTrapReg::GetSaveProgressStepCount // // Get the number of steps for the save progress dialog. The number of steps // is the number of events that will be written to SNMP_EVENTS\EventLog in // the registry. // // Parameters: // None. // // Returns: // The number of steps to use for the save progress dialog. // //************************************************************************* LONG CTrapReg::GetSaveProgressStepCount() { LONG nSteps = 0; LONG nEventLogs = m_aEventLogs.GetSize(); for (LONG iEventLog = 0; iEventLog < nEventLogs; ++iEventLog) { CXEventLog* pEventLog = m_aEventLogs[iEventLog]; LONG nEventSources = pEventLog->m_aEventSources.GetSize(); for (LONG iEventSource = 0; iEventSource < nEventSources; ++iEventSource) { CXEventSource* pEventSource = pEventLog->m_aEventSources.GetAt(iEventSource); nSteps += pEventSource->m_aEvents.GetSize(); } } return nSteps; } //************************************************************************** // CTrapReg::Serialize // // Write eventrap's current configuration out to the registry. // // Parameters: // None. // // Returns: // SCODE // S_OK if successful. // E_FAIL if a failure was detected. In the event of a failure, all // of the appropriate message boxes will have been displayed. // //*************************************************************************** SCODE CTrapReg::Serialize() { SCODE sc; if (g_bLostConnection) { sc = Connect(m_sComputerName, TRUE); if (FAILED(sc)) { if (g_bLostConnection) { AfxMessageBox(IDS_ERROR_NOT_RESPONDING); return E_REGKEY_LOST_CONNECTION; } return S_SAVE_CANCELED; } } if (!m_bIsDirty) { // The configuration state was not changed, so there is nothing to do. return S_OK; } LONG nProgressSteps = GetSaveProgressStepCount(); if (nProgressSteps > 0) { m_pdlgSaveProgress = new CDlgSaveProgress; m_pdlgSaveProgress->Create(IDD_SAVE_PROGRESS); m_pdlgSaveProgress->SetStepCount( nProgressSteps ); } CRegistryValue regval; regval.Set(SZ_NAME_REGVAL_CONFIGTYPE, REG_DWORD, sizeof(DWORD), (LPBYTE)&m_dwConfigType); if (!m_regkeyEventLog.SetValue(regval)) { if (g_bLostConnection) { AfxMessageBox(IDS_ERROR_NOT_RESPONDING); sc = E_REGKEY_LOST_CONNECTION; } else { AfxMessageBox(IDS_WARNING_CANT_WRITE_CONFIG); sc = S_SAVE_CANCELED; } } else { sc = m_aEventLogs.Serialize(); if (sc != S_SAVE_CANCELED) { if (SUCCEEDED(sc)) { sc = m_params.Serialize(); } if (sc != S_SAVE_CANCELED) SetDirty(FALSE); if (FAILED(sc)) { if (g_bLostConnection) { AfxMessageBox(IDS_ERROR_NOT_RESPONDING); } else { AfxMessageBox(IDS_WARNING_CANT_WRITE_CONFIG); } } } } delete m_pdlgSaveProgress; m_pdlgSaveProgress = NULL; return sc; } void CTrapReg::SetDirty(BOOL bDirty) { m_bIsDirty = bDirty; if (m_pbtnApply) { m_pbtnApply->EnableWindow(m_bIsDirty); } } /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// // Class: CTrapParams // // This class represents the information stored in the // SNMP_EVENTS\EventLog\Parameters registry key. // // Question: Why is it that the horizontal space in the gap between // the lines at the top of this header appears to be very irregular? ////////////////////////////////////////////////////////////////// //**************************************************************** // CTrapParams::CTrapParams // // Constructor for CTrapParams. // // // Parameters: // None. // // Returns: // Nothing. // //**************************************************************** CTrapParams::CTrapParams() { m_trapsize.m_bTrimFlag = TRUE; m_trapsize.m_dwMaxTrapSize = 4096; m_trapsize.m_bTrimMessages = FALSE; } //******************************************************************** // CTrapParams::Deserialize // // Read the contents of this CTrapParams object from the registry. // // Parameters: // None. // // Returns: // SCODE // S_OK if successful. // E_FAIL if there was a problem reading the required information // from the registry. //******************************************************************** SCODE CTrapParams::Deserialize() { CRegistryKey regkeyParams; if (!g_reg.m_regkeySnmp.GetSubKey(SZ_REGKEY_PARAMETERS, regkeyParams)) { if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } else { return E_REGKEY_NOT_FOUND; } } CRegistryValue regval; // !!!CR: There is no longer any reason to load the BASE OID if (!regkeyParams.GetValue(SZ_REGKEY_PARAMS_BASE_ENTERPRISE_OID, regval)) goto REGISTRY_FAILURE; m_sBaseEnterpriseOID = (LPCTSTR)regval.m_pData; if (!regkeyParams.GetValue(SZ_REGKEY_PARAMS_TRIMFLAG, regval)) m_trapsize.m_bTrimFlag = FALSE; else m_trapsize.m_bTrimFlag = (*(DWORD*)regval.m_pData == 1); if (!regkeyParams.GetValue(SZ_REGKEY_PARAMS_MAXTRAP_SIZE, regval)) m_trapsize.m_dwMaxTrapSize = MAX_TRAP_SIZE; else m_trapsize.m_dwMaxTrapSize = *(DWORD*)regval.m_pData; if (!regkeyParams.GetValue(SZ_REGKEY_PARAMS_TRIM_MESSAGE, regval)) m_trapsize.m_bTrimMessages = TRUE; else m_trapsize.m_bTrimMessages = (*(DWORD*)regval.m_pData) != 0; if (!regkeyParams.GetValue(SZ_REGKEY_PARAMS_THRESHOLDENABLED, regval)) m_throttle.m_bIsEnabled = TRUE; else m_throttle.m_bIsEnabled = (*(DWORD*)regval.m_pData) != THROTTLE_DISABLED; // Threshold trap count. if (!regkeyParams.GetValue(SZ_REGKEY_PARAMS_THRESHOLDCOUNT, regval) || *(DWORD*)regval.m_pData < 2) m_throttle.m_nTraps = THRESHOLD_COUNT; else m_throttle.m_nTraps = *(DWORD*)regval.m_pData; // Threshold time in seconds if (!regkeyParams.GetValue(SZ_REGKEY_PARAMS_THRESHOLDTIME, regval)) m_throttle.m_nSeconds = THRESHOLD_TIME; else m_throttle.m_nSeconds = *(DWORD*)regval.m_pData; if (regkeyParams.Close() != ERROR_SUCCESS) { goto REGISTRY_FAILURE; } return S_OK; REGISTRY_FAILURE: if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } else { return E_FAIL; } } //**************************************************************** // CTrapParams::Serialize // // Write SNMP_EVENTS\EventLog\Parameters information to the // registry. // // Parameters: // None. // // Returns: // S_OK if everything went OK. // E_REGKEY_NOT_FOUND if an expected registry key was missing. //***************************************************************** SCODE CTrapParams::Serialize() { if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } // Open the Parameters key. // Create simply opens the key if already present. CRegistryKey regkey; if (!g_reg.m_regkeySnmp.CreateSubKey(SZ_REGKEY_PARAMETERS, regkey)) { if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } else { return E_REGKEY_NOT_FOUND; } } CRegistryValue regval; // Save the Message Length and the TrimMessage. DWORD dwTrim; if (m_trapsize.m_bTrimFlag) dwTrim = 1; else dwTrim = 0; regval.Set(SZ_REGKEY_PARAMS_TRIMFLAG, REG_DWORD, sizeof(DWORD), (LPBYTE)&dwTrim); regkey.SetValue(regval); if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } if (m_trapsize.m_bTrimFlag) { // Save the maximum trap size regval.Set(SZ_REGKEY_PARAMS_MAXTRAP_SIZE, REG_DWORD, sizeof(DWORD), (LPBYTE)&m_trapsize.m_dwMaxTrapSize); regkey.SetValue(regval); if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } // Save the trim message length DWORD dwTrimMessages = m_trapsize.m_bTrimMessages; regval.Set(SZ_REGKEY_PARAMS_TRIM_MESSAGE, REG_DWORD, sizeof(DWORD), (LPBYTE)&dwTrimMessages); regkey.SetValue(regval); if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } } // Threshold enabled flag DWORD dwValue = (m_throttle.m_bIsEnabled ? THROTTLE_ENABLED : THROTTLE_DISABLED); regval.Set(SZ_REGKEY_PARAMS_THRESHOLDENABLED, REG_DWORD, sizeof(DWORD), (LPBYTE)&dwValue); regkey.SetValue(regval); if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } // If throttle is not enabled, do not write the ThresholdCount and ThresholdTime parameters if (m_throttle.m_bIsEnabled) { // Threshold trap count. regval.Set(SZ_REGKEY_PARAMS_THRESHOLDCOUNT, REG_DWORD, sizeof(DWORD), (LPBYTE)&m_throttle.m_nTraps); regkey.SetValue(regval); if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } // Threshold time in seconds regval.Set(SZ_REGKEY_PARAMS_THRESHOLDTIME, REG_DWORD, sizeof(DWORD), (LPBYTE)&m_throttle.m_nSeconds); regkey.SetValue(regval); if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } } regkey.Close(); if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } return S_OK; } //******************************************************************* // CTrapParams::ResetExtensionAgent // // Reset the extension agent. This is done by setting the "Threshold" // parameter to zero in the registry. The extension agent monitors this // value and will reset itself when a zero is written there. // // The user may want to reset the extension agent if its throttle limit // has been tripped. // // Parameters: // None. // // Returns: // SCODE // S_OK if successful. E_FAIL if the extension agent could not // be reset. If a failure occurs, the appropriate message box // is displayed. // //********************************************************************* SCODE CTrapParams::ResetExtensionAgent() { CRegistryKey regkey; if (!g_reg.m_regkeySnmp.GetSubKey(SZ_REGKEY_PARAMETERS, regkey)) { return E_REGKEY_NOT_FOUND; } CRegistryValue regval; // Set the "Threshold" value under the Parameters key to zero to reset // the extension agent. DWORD dwValue = THROTTLE_RESET; SCODE sc = S_OK; regval.Set(SZ_REGKEY_PARAMS_THRESHOLD, REG_DWORD, sizeof(DWORD), (LPBYTE)&dwValue); if (!regkey.SetValue(regval)) { AfxMessageBox(IDS_WARNING_CANT_WRITE_CONFIG); sc = E_FAIL; } regkey.Close(); return sc; } //*********************************************************************** // CTrapParams::ThrottleIsTripped // // Check the registry to determine whether or not the extension agent // throttle was tripped. // // Parameters: // None. // // Returns: // TRUE if the extension agent's throttle was tripped, FALSE otherwise. // //************************************************************************ BOOL CTrapParams::ThrottleIsTripped() { CRegistryKey regkey; if (!g_reg.m_regkeySnmp.GetSubKey(SZ_REGKEY_PARAMETERS, regkey)) { return FALSE; } CRegistryValue regval; // SNMP_EVENTS\Parameters\Threshold value BOOL bThrottleIsTripped = FALSE; if (regkey.GetValue(SZ_REGKEY_PARAMS_THRESHOLD, regval)) { if (*(DWORD*)regval.m_pData == THROTTLE_TRIPPED) { bThrottleIsTripped = TRUE; } } regkey.Close(); return bThrottleIsTripped; } /////////////////////////////////////////////////////////////////// // Class: CXEventLogArray // // This class implements an array of CXEventLog objects. // ////////////////////////////////////////////////////////////////// //**************************************************************** // CXEventLogArray::Deserialize // // Examine the registry find all the event logs and load all the // relevent information for all the event logs into this array. // // Parameters: // None. // // Returns: // S_OK if successful. // E_FAIL if a failure was detected. // //**************************************************************** SCODE CXEventLogArray::Deserialize() { if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } CStringArray* pasEventLogs = g_reg.m_regkeySource.EnumSubKeys(); // Prefix bug 445192 if (pasEventLogs == NULL) return E_FAIL; SCODE sc = S_OK; // Iterate through all the event log names and create each log. LONG nEventLogs = (LONG)pasEventLogs->GetSize(); if (nEventLogs > 0) { g_reg.m_nLoadStepsPerLog = LOAD_LOG_ARRAY_STEP_COUNT / nEventLogs; } LONG nUnusedSteps = LOAD_LOG_ARRAY_STEP_COUNT - (nEventLogs * g_reg.m_nLoadStepsPerLog); for (LONG iEventLog=0; iEventLog < nEventLogs; ++iEventLog) { CString sEventLog = pasEventLogs->GetAt(iEventLog); CXEventLog* pEventLog = new CXEventLog(sEventLog); sc = pEventLog->Deserialize(); if ((sc==S_LOAD_CANCELED) || FAILED(sc)) { delete pEventLog; break; } else if (sc == S_NO_SOURCES) { delete pEventLog; sc = S_OK; } else { Add(pEventLog); } } delete pasEventLogs; if (g_reg.m_pdlgLoadProgress->StepProgress(nUnusedSteps)) { sc = S_LOAD_CANCELED; } return sc; } //**************************************************************** // CXEventLogArray::Serialize // // Write the current configuration of all the EventLogs out to the // registry. Only those logs and sources that actually have events // are written. // // Parameters: // None. // // Returns: // S_OK if successful. // E_FAIL if a failure was detected. // //**************************************************************** SCODE CXEventLogArray::Serialize() { if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } // This is where the eventlog stuff should be cleaned up. CRegistryKey regkey; if (!g_reg.m_regkeySnmp.CreateSubKey(SZ_REGKEY_EVENTLOG, regkey)) { if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } else { return E_REGKEY_NOT_FOUND; } } regkey.Close(); if (!g_reg.m_regkeySnmp.CreateSubKey(SZ_REGKEY_SOURCES, regkey)) { if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } else { return E_REGKEY_NOT_FOUND; } } // Delete the keys for the sources and events for which we no longer // trap. I'm going to be lazy and just delete them all. // !!!CR: It could potentially save a lot of time if this was made smarter // !!!CR: so that it only replaced items that had been deleted. LONG nEventSources, iEventSource; CStringArray* pasEventSources = regkey.EnumSubKeys(); nEventSources = (LONG)pasEventSources->GetSize(); for (iEventSource=0; iEventSourceGetAt(iEventSource); regkey.DeleteSubKey(sSource); } delete pasEventSources; SCODE sc = S_OK; LONG nEventLogs = GetSize(); for (LONG iEventLog = 0; iEventLog < nEventLogs; ++iEventLog) { sc = GetAt(iEventLog)->Serialize(regkey); if (sc == S_SAVE_CANCELED) { break; } else if (g_bLostConnection) { sc = E_REGKEY_LOST_CONNECTION; break; } } regkey.Close(); return sc; } //**************************************************************** // CXEventLogArray::FindEventSource // // Given the name of an event log and the name of the event source // within the event log, return a pointer to the requested CXEventSource. // // Parameters: // CString& sLog // The name of the event log. // // CString& sEventSource // The name of the event source. // // Returns: // CXEventSource* // A pointer to the requested event source if it was found. NULL // if no such event source exists. // //**************************************************************** CXEventSource* CXEventLogArray::FindEventSource(CString& sLog, CString& sEventSource) { LONG nLogs = GetSize(); for (LONG iLog = 0; iLog < nLogs; ++iLog) { CXEventLog* pEventLog = GetAt(iLog); if (pEventLog->m_sName.CompareNoCase(sLog) == 0) { return pEventLog->FindEventSource(sEventSource); } } return NULL; } /////////////////////////////////////////////////////////////////// // Class: CXEventLog // // This class contains all the information for a particular event log. // ////////////////////////////////////////////////////////////////// //************************************************************************ // CXEventLog::Deserialize // // Load the contents of this EventLog object from the registry. // // Parameters: // g_reg is a global parameter. // // Returns: // SCODE // S_OK or S_NO_SOURCES if successful. E_FAIL if there was // a failure of any kind. // //************************************************************************ SCODE CXEventLog::Deserialize() { return m_aEventSources.Deserialize(this); } //************************************************************************ // CXEventLog::Serialize // // Write the current configuration for this log to the registry. // // Parameters: // CRegistryKey& regkey // This registry key points to SOFTWARE\Microsoft\SNMP_EVENTS\EventLog // // Returns: // SCODE // S_OK or S_SAVE_CANCELED if successful. E_FAIL for an error condition. // a failure of any kind. // //************************************************************************ SCODE CXEventLog::Serialize(CRegistryKey& regkey) { return m_aEventSources.Serialize(regkey); } /////////////////////////////////////////////////////////////////// // Class: CXEventSourceArray // // This class implements an array of CXEventSource pointers and // related methods. // ////////////////////////////////////////////////////////////////// //************************************************************************* // CXEventSourceArray::Deserialize // // Load all the information pertaining to the event sources associated with // the given event log. This information is loaded from the registry. // // Parameters: // CXEventLog* pEventLog // Pointer to the event log. The sources associated with this // event log are loaded into this object. // // Returns: // SCODE // S_OK or S_NO_SOURCES if successful. E_FAIL if there was // a failure of any kind. //************************************************************************* SCODE CXEventSourceArray::Deserialize(CXEventLog* pEventLog) { // Get the registry entry for this log. This registry key will be // used to enumerate the event sources for this log. CRegistryKey regkey; if (!g_reg.m_regkeySource.GetSubKey(pEventLog->m_sName, regkey)) { if (g_reg.m_pdlgLoadProgress->StepProgress(g_reg.m_nLoadStepsPerLog)) { return S_LOAD_CANCELED; } if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } else { return E_FAIL; } } SCODE sc = S_OK; // Enumerate the event sources for this log. CStringArray* pasSources = regkey.EnumSubKeys(); if (pasSources == NULL) { regkey.Close(); if (g_reg.m_pdlgLoadProgress->StepProgress(g_reg.m_nLoadStepsPerLog)) { return S_LOAD_CANCELED; } if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } else { return E_FAIL; } } // Iterate though all the event sources and add them as a sub-item // under the log. LONG nEventSources = (LONG)pasSources->GetSize(); LONG nScaledStepSize = 0; g_reg.m_nLoadStepsPerSource = 0; if (nEventSources > 0) { nScaledStepSize = (g_reg.m_nLoadStepsPerLog * 1000) / nEventSources; g_reg.m_nLoadStepsPerSource = g_reg.m_nLoadStepsPerLog / nEventSources; } LONG nLoadSteps = 0; LONG nProgress = 0; // Set the load progress step count. Since we don't know how many events are saved // for each event source, we will assume some small number for LOAD_STEPS_FOR_SOURCE // and divide the actual number of steps up as evenly as possible once we know the actual // event count. for (LONG iEventSource=0; iEventSource< nEventSources; ++iEventSource) { nProgress += nScaledStepSize; g_reg.m_nLoadStepsPerSource = nProgress / 1000; if (g_reg.m_nLoadStepsPerSource > 0) { nProgress -= g_reg.m_nLoadStepsPerSource * 1000; nLoadSteps += g_reg.m_nLoadStepsPerSource; } CString sEventSource = pasSources->GetAt(iEventSource); CXEventSource* pEventSource = new CXEventSource(pEventLog, sEventSource); sc = pEventSource->Deserialize(regkey); if ((sc==S_LOAD_CANCELED) || FAILED(sc)) { delete pEventSource; break; } else if (sc == S_NO_EVENTS) { // If there are no events, then this is not a valid event source. delete pEventSource; sc = S_OK; } else { Add(pEventSource); } } delete pasSources; if (SUCCEEDED(sc)) { // We only close the registry key if we succeeded to avoid hanging if we loose // a remote connection. regkey.Close(); if (GetSize() == 0) { sc = S_NO_SOURCES; } } if (nLoadSteps < g_reg.m_nLoadStepsPerLog) { if (g_reg.m_pdlgLoadProgress->StepProgress(g_reg.m_nLoadStepsPerLog - nLoadSteps)) { return S_LOAD_CANCELED; } g_reg.m_nLoadSteps += g_reg.m_nLoadStepsPerLog - nLoadSteps; } return sc; } //************************************************************************ // CXEventSourceArray::Serialize // // Write the current configuration for this event source array to the registry. // // Parameters: // CRegistryKey& regkey // This registry key points to SOFTWARE\Microsoft\SNMP_EVENTS\EventLog\Sources // // Returns: // SCODE // S_OK or S_SAVE_CANCELED if successful. E_FAIL for an error condition. // a failure of any kind. // //************************************************************************ SCODE CXEventSourceArray::Serialize(CRegistryKey& regkey) { // Write the subkeys under SNMP_EVENTS\EventLog SCODE sc = S_OK; LONG nEventSources = GetSize(); for (LONG iEventSource = 0; iEventSource < nEventSources; ++iEventSource) { SCODE scTemp = GetAt(iEventSource)->Serialize(regkey); if (g_bLostConnection) { sc = E_REGKEY_LOST_CONNECTION; break; } if (FAILED(scTemp)) { sc = E_FAIL; break; } if (scTemp == S_SAVE_CANCELED) { sc = S_SAVE_CANCELED; break; } } return sc; } //************************************************************************ // CXEventSourceArray::FindEventSource // // Given an event source name, find the specified event source in this // event source array. // // Parameters: // CString& sEventSource // The name of the event source to search for. // // Returns: // CXEventSource* // Pointer to the event source if it is found, otherwise NULL. // //*********************************************************************** CXEventSource* CXEventSourceArray::FindEventSource(CString& sEventSource) { LONG nSources = GetSize(); for (LONG iSource = 0; iSource < nSources; ++iSource) { CXEventSource* pEventSource = GetAt(iSource); if (pEventSource->m_sName.CompareNoCase(sEventSource)==0) { return pEventSource; } } return NULL; } /////////////////////////////////////////////////////////////////// // Class: CXEventSource // // This class implements an an event source object. An event source // corresponds to an application that can generate events. The // event sources are enumerated from the registry in // "SYSTEM\CurrentControlSet\Services\EventLogs" under each particular // eventlog found there. // // An event source has an array of messages and an array of events // associated with it. // // The message array comes from the message .DLL file(s) pointed to by // the "EventMessageFile" value attached to the source's key in the registry. // The message array is read-only in the sense that it is loaded from the // registry and never written back to it. // // The event array comes from SNMP_EVENTS\EventLog\. These // events are loaded when the configuration program starts up and written // back out when the user clicks "OK". Note that the events stored in the // registry contain the event ID, but not the message text. The message text // for an event is found by searching the message array in the CXEventSource // object for the event's ID. // ////////////////////////////////////////////////////////////////// //************************************************************************* // CXEventSource::CXEventSource // // Construct the CXEventSource object. // // Parameters: // CXEventLog* pEventLog // Pointer to the event log that contains this event source. // // CString& sName // The name of this event source. // // Returns: // Nothing. // //************************************************************************* CXEventSource::CXEventSource(CXEventLog* pEventLog, CString& sName) { m_pEventLog = pEventLog; m_sName = sName; m_aMessages.Initialize(this); } //************************************************************************ // CXEventSource::~CXEventSource // // Destroy thus event source object. // // Parameters: // None. // // Returns: // Nothing. // //************************************************************************ CXEventSource::~CXEventSource() { // We must explicitly delete the contents of the event array and message // array. Note that this is different behavior from the CXEventLogArray // and CXEventSourceArray. This is because it was useful to create // message and event arrays as temporary containers for a set of pointers. // Thus, there were situations where you did not want to delete the // objects contained in these arrays when the arrays were destroyed. m_aEvents.DeleteAll(); m_aMessages.DeleteAll(); } //********************************************************************** // CXEventSource::Deserialize // // Load this event source from the registry given the registry key // for the event log that contains this source. // // Parameters: // CRegistryKey& regkeyLog // An open registry key for the event log containing this // event source. This key points to somewhere in // SYSTEM\CurrentControlSet\Services\EventLog // // Returns: // SCODE // S_OK = the source has events and no errors were encountered. // S_NO_EVENTS = the source has no events and no errors were encountered. // E_FAIL = an condition was encountered. // //*********************************************************************** SCODE CXEventSource::Deserialize(CRegistryKey& regkeyLog) { CRegistryKey regkeySource; if (!regkeyLog.GetSubKey(m_sName, regkeySource)) { if (g_reg.m_pdlgLoadProgress->StepProgress(g_reg.m_nLoadStepsPerSource)) { return S_LOAD_CANCELED; } g_reg.m_nLoadSteps += g_reg.m_nLoadStepsPerSource; if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } else { return E_FAIL; } } SCODE sc = E_FAIL; if (SUCCEEDED(GetLibPath(regkeySource))) { sc = m_aEvents.Deserialize(this); } else { if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } if (g_reg.m_pdlgLoadProgress->StepProgress(g_reg.m_nLoadStepsPerSource)) { return S_LOAD_CANCELED; } g_reg.m_nLoadSteps += g_reg.m_nLoadStepsPerSource; sc = S_NO_EVENTS; } regkeySource.Close(); if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } // Delay deserializing the messages for this source until they are // needed. return sc; } #if 0 //************************************************************************* // CXEventSource::GetLibPath // // Get the path the the EventMessageFile for this event source. // // Parameters: // CRegistryKey& regkeySource // An open registry key corresponding to this source in // SYSTEM\CurrentControlSet\Services\EventLog\ // // Returns: // SCODE // S_OK if successful, otherwise E_FAIL. // //************************************************************************* SCODE CXEventSource::GetLibPath(CRegistryKey& regkeySource) { CRegistryValue regval; if (!regkeySource.GetValue(SZ_REGKEY_SOURCE_EVENT_MESSAGE_FILE, regval)) return E_FAIL; TCHAR szLibPath[MAX_STRING]; if (ExpandEnvironmentStrings((LPCTSTR)regval.m_pData, szLibPath, sizeof(szLibPath)) == 0) return E_FAIL; m_sLibPath = szLibPath; return S_OK; } #else //************************************************************************* // CXEventSource::GetLibPath // // Get the path the the EventMessageFile for this event source. // // Parameters: // CRegistryKey& regkeySource // An open registry key corresponding to this source in // SYSTEM\CurrentControlSet\Services\EventLog\ // // Returns: // SCODE // S_OK if successful, otherwise E_FAIL. // //************************************************************************* SCODE CXEventSource::GetLibPath(CRegistryKey& regkeySource) { static CEnvCache cache; CRegistryValue regval; if (!regkeySource.GetValue(SZ_REGKEY_SOURCE_EVENT_MESSAGE_FILE, regval)) return E_FAIL; SCODE sc = S_OK; if (g_reg.m_sComputerName.IsEmpty()) { // Editing the local computer computer's registry, so the local environment // variables are in effect. TCHAR szLibPath[MAX_STRING]; if (ExpandEnvironmentStrings((LPCTSTR)regval.m_pData, szLibPath, sizeof(szLibPath)/sizeof(szLibPath[0]))) { m_sLibPath = szLibPath; } else { sc = E_FAIL; } } else { // Editing a remote computer's registry, so the remote environment strings are in // effect. Also, file paths must be mapped to the UNC path for the machine. For // example, C:Foo will be mapped to \\Machine\C$\Foo m_sLibPath = regval.m_pData; sc = RemoteExpandEnvStrings(g_reg.m_sComputerName, cache, m_sLibPath); if (SUCCEEDED(sc)) { sc = MapPathToUNC(g_reg.m_sComputerName, m_sLibPath); } } return S_OK; } #endif //************************************************************************ // CXEventSource::Serialize // // Write the configuration information for this event source to the registry. // // Parameters: // CRegistryKey& regkeyParent // An open registry key pointing to SNMP_EVENTS\EventLog\Sources // // Returns: // SCODE // S_OK if successful. // S_SAVE_CANCELED if no errors, but the user canceled the save. // //************************************************************************ SCODE CXEventSource::Serialize(CRegistryKey& regkeyParent) { if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } SCODE sc = S_OK; if (m_aEvents.GetSize() > 0) { CRegistryKey regkey; if (!regkeyParent.CreateSubKey(m_sName, regkey)) { if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } else { return E_REGKEY_NOT_FOUND; } } CString sEnterpriseOID; GetEnterpriseOID(sEnterpriseOID); CRegistryValue regval; regval.Set(SZ_REGKEY_SOURCE_ENTERPRISE_OID, REG_SZ, (sEnterpriseOID.GetLength() + 1) * sizeof(TCHAR), (LPBYTE)(LPCTSTR)sEnterpriseOID); regkey.SetValue(regval); DWORD dwAppend = 1; regval.Set(SZ_REGKEY_SOURCE_APPEND, REG_DWORD, sizeof(DWORD), (LPBYTE) &dwAppend); regkey.SetValue(regval); sc = m_aEvents.Serialize(regkey); regkey.Close(); } if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } return sc; } //******************************************************************* // CXEventSource::GetEnterpriseOID // // Get the enterprise OID for this event source. The enterprise OID // is composed of a prefix and suffix string concatenated together. The // prefix string is an ASCII decimal value for the length of the suffix // string. The suffix string is composed by separating each character of // the name of this source by a "." character. // // Parameters: // CString& sEnterpriseOID // A reference to the string where the enterprise OID for this // source will be returned. // // Returns: // The enterprise OID in via the sEnterpriseOID reference. // //******************************************************************** void CXEventSource::GetEnterpriseOID(CString& sEnterpriseOID, BOOL bGetFullID) { CString sValue; // Form the prefix string in sEnterpriseOID and compute the length // of the prefix and suffix strings. DecString(sValue, m_sName.GetLength()); if (bGetFullID) { sEnterpriseOID = g_reg.m_params.m_sBaseEnterpriseOID + _T('.') + sValue; } else { sEnterpriseOID = sValue; } // Append the suffix string to the prefix string by getting a pointer to // the sEnterpriseOID buffer and allocating enough space to hold the // combined strings. LPCTSTR pszSrc = m_sName; // Append the suffix by copying it to the destination buffer and inserting the // "." separator characters as we go. LONG iCh; while (iCh = *pszSrc++) { switch(sizeof(TCHAR)) { case 1: iCh &= 0x0ff; break; case 2: iCh &= 0x0ffffL; break; default: ASSERT(FALSE); break; } DecString(sValue, iCh); sEnterpriseOID += _T('.'); sEnterpriseOID += sValue; } } /////////////////////////////////////////////////////////////////// // Class: CXEventArray // // This class implements an array of pointers to CXEvent objects. // The events contained in this array correspond to the events that // the user has configured in the main dialog. Don't confuse events // with messages. Events are the subset of the messages that the // user has selected to be translated into traps. // // For further information on how this CXEventArray fits into the // scheme of things, please see the CXEventSource class header. ////////////////////////////////////////////////////////////////// //************************************************************************ // CXEventArray::Deserialize // // Read an array of events from the registry for the given // source. // // Parameters: // CXEventSource* pEventSource // Pointer to the event source who's events should be read. // // Returns: // SCODE // S_OK if successful. // E_FAIL if an error occurs. // //************************************************************************ SCODE CXEventArray::Deserialize(CXEventSource* pEventSource) { if (!g_reg.SourceHasTraps(pEventSource->m_sName)) { if (g_reg.m_pdlgLoadProgress->StepProgress(g_reg.m_nLoadStepsPerSource)) { return S_LOAD_CANCELED; } g_reg.m_nLoadSteps += g_reg.m_nLoadStepsPerSource; return S_OK; } // Control comes here if we know that there are events configured // for the event source that this event array is part of. We now // need to load the events for this source by enumerating them // from SNMP_EVENTS\EventLog\ CString sKey; sKey = sKey + SZ_REGKEY_SOURCES + _T("\\") + pEventSource->m_sName; CRegistryKey regkey; if (!g_reg.m_regkeySnmp.GetSubKey(sKey, regkey)) { if (g_reg.m_pdlgLoadProgress->StepProgress(g_reg.m_nLoadStepsPerSource)) { return S_LOAD_CANCELED; } g_reg.m_nLoadSteps += g_reg.m_nLoadStepsPerSource; return S_OK; } // Enumerate the events for this source CStringArray* pasEvents = regkey.EnumSubKeys(); if (pasEvents == NULL) { if (g_bLostConnection) { return E_REGKEY_LOST_CONNECTION; } regkey.Close(); if (g_reg.m_pdlgLoadProgress->StepProgress(g_reg.m_nLoadStepsPerSource)) { return S_LOAD_CANCELED; } g_reg.m_nLoadSteps += g_reg.m_nLoadStepsPerSource; return E_FAIL; } // Iterate though all the events and add them as a sub-item // under the event source. LONG nEvents = (LONG)pasEvents->GetSize(); LONG nStepsDone = 0; LONG nEventsPerStep = 0; if (g_reg.m_nLoadStepsPerSource > 0) { nEventsPerStep = nEvents / g_reg.m_nLoadStepsPerSource; } for (LONG iEvent=0; iEvent< nEvents; ++iEvent) { CString sEvent = pasEvents->GetAt(iEvent); CXEvent* pEvent = new CXEvent(pEventSource); SCODE sc = pEvent->Deserialize(regkey, sEvent); if (sc == E_MESSAGE_NOT_FOUND) { delete pEvent; if (!g_reg.m_bSomeMessageWasNotFound) { AfxMessageBox(IDS_ERR_MESSAGE_NOT_FOUND, MB_OK | MB_ICONEXCLAMATION); g_reg.m_bSomeMessageWasNotFound = TRUE; g_reg.SetDirty(TRUE); } continue; } if ((sc == S_LOAD_CANCELED) || FAILED(sc) ) { delete pEvent; delete pasEvents; return sc; } if (nEventsPerStep > 0) { if ((iEvent % nEventsPerStep) == (nEventsPerStep - 1)) { if (g_reg.m_pdlgLoadProgress->StepProgress()) { delete pasEvents; return S_LOAD_CANCELED; } ++g_reg.m_nLoadSteps; ++nStepsDone; } } } delete pasEvents; regkey.Close(); if (nStepsDone < g_reg.m_nLoadStepsPerSource) { if (g_reg.m_pdlgLoadProgress->StepProgress(g_reg.m_nLoadStepsPerSource - nStepsDone)) { return S_LOAD_CANCELED; } g_reg.m_nLoadSteps += g_reg.m_nLoadStepsPerSource - nStepsDone; } return S_OK; } //************************************************************************ // CXEventArray::Serialize // // Write the current configuration for the events contained in this array // out to the registry. // // Parameters: // CRegistryKey& regkeyParent // An open registry key for the source that owns these events. // The source key is located in SNMP_EVENTS\EventLogs\ // // Returns: // SCODE // S_OK = All events saved without errors. // S_SAVE_CANCELED = No errors, but the user canceled the save. // E_FAIL = An error occurs. // //************************************************************************ SCODE CXEventArray::Serialize(CRegistryKey& regkeyParent) { SCODE sc = S_OK; LONG nEvents = GetSize(); for (LONG iEvent = 0; iEvent < nEvents; ++iEvent) { SCODE scTemp = GetAt(iEvent)->Serialize(regkeyParent); if (scTemp == S_SAVE_CANCELED) { sc = S_SAVE_CANCELED; break; } if (FAILED(scTemp)) { if (g_bLostConnection) { sc = E_REGKEY_LOST_CONNECTION; } else { sc = E_FAIL; } break; } } return sc; } //*********************************************************************** // CXEventArray::Add // // Add an event pointer to this array. Note that there is no assumption // that the array owns the pointer. Someone must explicitly call the DeleteAll // member to delete the pointers stored in this array. // // Parameters: // CXEvent* pEvent // Pointer to the event to add to this array. // // Returns: // Nothing. // //*********************************************************************** void CXEventArray::Add(CXEvent* pEvent) { CBaseArray::Add(pEvent); } //*********************************************************************** // CXEventArray::FindEvent // // Given an event id, find the corresponding event in this array. // // Note that this array should never contain duplicate events. // // Parameters: // DWORD dwId // The event ID. // // Returns: // CXEvent* // A pointer to the desired event. NULL if the event was // not found. // //*********************************************************************** CXEvent* CXEventArray::FindEvent(DWORD dwId) { LONG nEvents = GetSize(); for (LONG iEvent=0; iEvent < nEvents; ++iEvent) { CXEvent* pEvent = GetAt(iEvent); if (pEvent->m_message.m_dwId == dwId) { return pEvent; } } return NULL; } //*********************************************************************** // CXEventArray::FindEvent // // Given an event pointer, remove the event from this array. // // Parameters: // CXEvent* pEventRemove // A pointer to the event to remove. // // Returns: // SCODE // S_OK if the event was removed. // E_FAIL if the event was not found in this array. // //*********************************************************************** SCODE CXEventArray::RemoveEvent(CXEvent* pEventRemove) { // Iterate through the event array to search for the specified event. LONG nEvents = GetSize(); for (LONG iEvent=0; iEvent < nEvents; ++iEvent) { CXEvent* pEvent = GetAt(iEvent); if (pEvent == pEventRemove) { RemoveAt(iEvent); return S_OK; } } return E_FAIL; } /////////////////////////////////////////////////////////////////// // Class: CXEvent // // This class implements an event. Events are the subset of the // messages that the user selects to be translated into traps. // Events, and not messages, are what the user configures. // // For further information on how this class fits into the // scheme of things, please see the CXEventSource class header. ////////////////////////////////////////////////////////////////// //********************************************************************* // CXEvent::CXEvent // // Construct the event. // // Parameters: // CXEventSource* pEventSource // Pointer to the event source that has the potential to generate // this event. // // Returns: // Nothing. // //********************************************************************* CXEvent::CXEvent(CXEventSource* pEventSource) : m_message(pEventSource) { m_dwCount = 0; m_dwTimeInterval = 0; m_pEventSource = pEventSource; m_pEventSource->m_aEvents.Add(this); } //********************************************************************** // CXEvent::CXEvent // // Construct an event. This form of the constructor creates an event // directly from a CXMessage object. This is possible because the // CXMessage object contains a back-pointer to its source. // // Parameters: // CXMessage* pMessage // Pointer to the message that is used as the event template. // // Returns: // Nothing. //********************************************************************** CXEvent::CXEvent(CXMessage* pMessage) : m_message(pMessage->m_pEventSource) { m_pEventSource = pMessage->m_pEventSource; m_message = *pMessage; m_dwCount = 0; m_dwTimeInterval = 0; m_pEventSource->m_aEvents.Add(this); } //********************************************************************** // CXEvent::~CXEvent // // Destroy this event. // // Parameters: // None. // // Returns: // Nothing. //********************************************************************** CXEvent::~CXEvent() { // Remove this event from the source m_pEventSource->m_aEvents.RemoveEvent(this); } //********************************************************************** // CXEvent::Deserialize // // Read this event from the registry. // // Parameters: // CRegistryKey& regkeyParent // An open registry key pointing to the event source in // SNMP_EVENTS\EventLog // // CString& sName // The name of the event to load. // // Returns: // SCODE // S_OK if successful. // E_FAIL if an error occurred. // //********************************************************************* SCODE CXEvent::Deserialize(CRegistryKey& regkeyParent, CString& sName) { CRegistryKey regkey; if (!regkeyParent.GetSubKey(sName, regkey)) { return E_FAIL; } SCODE sc = E_FAIL; CRegistryValue regval; // Get the count and time interval. m_dwCount = 0; m_dwTimeInterval = 0; if (regkey.GetValue(SZ_REGKEY_EVENT_COUNT, regval)) { m_dwCount = *(DWORD*)regval.m_pData; if (regkey.GetValue(SZ_REGKEY_EVENT_TIME, regval)) { m_dwTimeInterval = *(DWORD*)regval.m_pData; } } if (regkey.GetValue(SZ_REGKEY_EVENT_FULLID, regval)) { DWORD dwFullId = *(DWORD*)regval.m_pData; CXMessage* pMessage = m_pEventSource->FindMessage(dwFullId); if (pMessage == NULL) { sc = E_MESSAGE_NOT_FOUND; } else { m_message = *pMessage; sc = S_OK; } } regkey.Close(); return sc; } //********************************************************************** // CXEvent::Deserialize // // Write the configuration for this event to the registry. // // Parameters: // CRegistryKey& regkeyParent // An open registry key pointing to the event source in // SNMP_EVENTS\EventLog // // Returns: // SCODE // S_OK = the event was successful written out. // S_SAVE_CANCELED = no errors, but the user canceled the save. // E_FAIL = if an error occurred. // //********************************************************************* SCODE CXEvent::Serialize(CRegistryKey& regkeyParent) { if (g_reg.m_pdlgSaveProgress) { if (g_reg.m_pdlgSaveProgress->StepProgress()) { return S_SAVE_CANCELED; } } CRegistryKey regkey; CString sName; GetName(sName); if (!regkeyParent.CreateSubKey(sName, regkey)) { return E_REGKEY_NO_CREATE; } CRegistryValue regval; if (m_dwCount > 0) { regval.Set(SZ_REGKEY_EVENT_COUNT, REG_DWORD, sizeof(DWORD), (LPBYTE) &m_dwCount); regkey.SetValue(regval); if (m_dwTimeInterval > 0) { regval.Set(SZ_REGKEY_EVENT_TIME, REG_DWORD, sizeof(DWORD), (LPBYTE) &m_dwTimeInterval); regkey.SetValue(regval); } } regval.Set(SZ_REGKEY_EVENT_FULLID, REG_DWORD, sizeof(DWORD), (LPBYTE) &m_message.m_dwId); regkey.SetValue(regval); regkey.Close(); return S_OK; } //************************************************************************* // CXEvent::GetCount // // Get the ASCII decimal value for the m_dwCount member. // // Using this method to do the conversion ensures that the count value is // presented to the user in a consistent form throughout the program. // // Parameters: // CString& sText // This is where the count value is returned. // // Returns: // The ASCII value for the count is returned via sText. // // Note: m_dwCount and m_dwTimeInterval work together. A trap is sent only if // m_dwCount events are registered withing m_dwTimeInterval seconds. //************************************************************************* void CXEvent::GetCount(CString& sText) { DecString(sText, (long) m_dwCount); } //************************************************************************* // CXEvent::GetTimeInterval // // Get the ASCII decimal value for the m_dwTimeInterval member. // // Using this method to do the conversion ensures that the time-interval value is // presented to the user in a consistent form throughout the program. // // Parameters: // CString& sText // This is where the count value is returned. // // Returns: // The ASCII value for the count is returned via sText. // // Note: m_dwCount and m_dwTimeInterval work together. A trap is sent only if // m_dwCount events are registered withing m_dwTimeInterval seconds. //************************************************************************* void CXEvent::GetTimeInterval(CString& sText) { DecString(sText, (long) m_dwTimeInterval); } /////////////////////////////////////////////////////////////////// // Class: CXMessage // // This class implements a message. Each event source has some // number of messages associated with it. A user may select some // subset of the messages to be converted into "events". The user // configures events, not messages. // // For further information on how this class fits into the // scheme of things, please see the CXEventSource class header. ////////////////////////////////////////////////////////////////// CXMessage::CXMessage(CXEventSource* pEventSource) { m_pEventSource = pEventSource; } CXMessage& CXMessage::operator=(CXMessage& message) { m_pEventSource = message.m_pEventSource; m_dwId = message.m_dwId; m_sText = message.m_sText; return *this; } //*************************************************************************** // // CMessage::GetSeverity // // Get the severity level of the event. This is the human-readable string // corresponding to the top two bits of the event ID. // // Parameters: // CString& sSeverity // A reference to the place to return the severity string. // // Returns: // Nothing. // // Status: // //*************************************************************************** void CXMessage::GetSeverity(CString& sSeverity) { MapEventToSeverity(m_dwId, sSeverity); } //*************************************************************************** // // CMessage::GetTrappingString // // This method returns the trapping string "yes" if the event is being // trapped and "no" if its not being trapped. // // Parameters: // CString& sTrapping // A reference to the place to return the trapping string. // // Returns: // Nothing. // // Status: // //*************************************************************************** void CXMessage::IsTrapping(CString& sIsTrapping) { CXEvent* pEvent = m_pEventSource->FindEvent(m_dwId); sIsTrapping.LoadString( pEvent != NULL ? IDS_IS_TRAPPING : IDS_NOT_TRAPPING); } //**************************************************************************** // // CMessage::SetAndCleanText // // Set the m_sText data member to a cleaned up version of a source string. // The text is cleaned by converting all funny whitespace characters such // as carriage return, tabs and so on to ordinary space characters. All // leading space is stripped from the beginning of the string. // //**************************************************************************** void CXMessage::SetAndCleanText(PMESSAGE_RESOURCE_ENTRY pEntry) { BOOL bIsLeadingSpace = TRUE; USHORT i; if (pEntry->Flags == 0x00000) // ANSI char set { CHAR *pszSrc = (CHAR *)pEntry->Text; CHAR chSrc; LPTSTR pszDst = m_sText.GetBuffer(strlen(pszSrc) + 1); for (i=0; iLength && *pszSrc; i++, pszSrc++) { chSrc = *pszSrc; if (chSrc >= 0x09 && chSrc <= 0x0d) chSrc = ' '; if (chSrc == ' ' && bIsLeadingSpace) continue; *pszDst++ = (TCHAR)chSrc; if (bIsLeadingSpace) // testing only is less costly bIsLeadingSpace = FALSE; } *pszDst = _T('\0'); } else // UNICODE char set { wchar_t *pwszSrc = (wchar_t *)pEntry->Text; wchar_t wchSrc; LPTSTR pszDst = m_sText.GetBuffer(wcslen(pwszSrc) + 1); for (i=0; iLength/sizeof(wchar_t) && *pwszSrc; i++, pwszSrc++) { wchSrc = *pwszSrc; if (wchSrc >= (wchar_t)0x09 && wchSrc <= (wchar_t)0x0d) wchSrc = (wchar_t)' '; if (wchSrc == (wchar_t)' ' && bIsLeadingSpace) continue; *pszDst++ = (TCHAR)wchSrc; if (bIsLeadingSpace) // testing only is less costly bIsLeadingSpace = FALSE; } *pszDst = _T('\0'); } m_sText.ReleaseBuffer(); } //**************************************************************************** // CXMessage::GetShortId // // This method returns the message's "short ID" that users see for events and // messages. The short ID is the ASCII decimal value for the low-order 16 bits // of the message ID. // // Using this method to do the conversion ensures that the short-ID value is // presented to the user in a consistent form throughout the program. // // Parameters: // CString& sShortId // This is where the ID string is returned. // // Returns: // The message ID string is returned via sShortId // //**************************************************************************** void CXMessage::GetShortId(CString& sShortId) { TCHAR szBuffer[MAX_STRING]; _ltot((LONG) LOWORD(m_dwId), szBuffer, 10); sShortId = szBuffer; } /////////////////////////////////////////////////////////////////// // Class: CXMessageArray // // This class implements an array of pointers to CXMessage objects. // // For further information on how this CXMessageArray fits into the // scheme of things, please see the CXEventSource class header. ////////////////////////////////////////////////////////////////// //**************************************************************** // CXMessageArray::CXMessageArray // // Constructor. // // Parameters: // None. // // Returns: // Nothing. // //**************************************************************** CXMessageArray::CXMessageArray() { m_bDidLoadMessages = FALSE; m_pEventSource = NULL; } //******************************************************************* // CXMessageArray::FindMessage // // Search this array for a message given its ID. // // Parameters: // DWORD dwId // The full message ID // // Returns: // CXMessage* // Pointer to the message if it was found. NULL if it was // not found. // // Note: // Duplicate messages are not allowed in the array, but no code // enforces this for the sake of efficiency. // //******************************************************************* CXMessage* CXMessageArray::FindMessage(DWORD dwId) { if (!m_bDidLoadMessages) { if (FAILED(LoadMessages())) { return NULL; } } LONG nMessages = GetSize(); for (LONG iMessage = 0; iMessage < nMessages; ++iMessage) { CXMessage* pMessage = GetAt(iMessage); if (pMessage->m_dwId == dwId) { return pMessage; } } return NULL; } //**************************************************************************** // // XProcessMsgTable // // This function processes a the message table contained in a message .DLL file // and adds all the messages it contains to the given CXMessageArray object. // // Parameters: // HANDLE hModule // The module handle for the .DLL file. // // LPCTSTR lpszType // Ignored. // // LPTSTR lpszName // The name of the module. // // LONG lParam // A pointer to a CXMessageArray object where the messages will be // stored. // // Returns: // BOOL // Always returns TRUE. // // //**************************************************************************** static BOOL CALLBACK XProcessMsgTable(HANDLE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam) { CXMessageArray* paMessages = (CXMessageArray*)(LPVOID) (LONG_PTR)lParam; // Found a message table. Process it! HRSRC hResource = FindResource((HINSTANCE)hModule, lpszName, RT_MESSAGETABLE); if (hResource == NULL) return TRUE; HGLOBAL hMem = LoadResource((HINSTANCE)hModule, hResource); if (hMem == NULL) return TRUE; PMESSAGE_RESOURCE_DATA pMsgTable = (PMESSAGE_RESOURCE_DATA)::LockResource(hMem); if (pMsgTable == NULL) return TRUE; ULONG ulBlock, ulId, ulOffset; for (ulBlock=0; ulBlockNumberOfBlocks; ulBlock++) { ulOffset = pMsgTable->Blocks[ulBlock].OffsetToEntries; for (ulId = pMsgTable->Blocks[ulBlock].LowId; ulId <= pMsgTable->Blocks[ulBlock].HighId; ulId++) { PMESSAGE_RESOURCE_ENTRY pEntry = (PMESSAGE_RESOURCE_ENTRY)((ULONG_PTR)pMsgTable + ulOffset); CXMessage *pMessage = new CXMessage(paMessages->m_pEventSource); pMessage->m_dwId = (DWORD) ulId; pMessage->SetAndCleanText(pEntry); paMessages->Add(pMessage); ulOffset += pEntry->Length; } } return TRUE; } //**************************************************************************** // CXMessageArray::LoadMessages // // Load the messages from the message .DLL file(s) for the source into this // message array. // // Parameters: // None. // // Returns: // SCODE // S_OK if successful. // E_FAIL if an error occurs. // //***************************************************************************** SCODE CXMessageArray::LoadMessages() { ASSERT(m_pEventSource != NULL); if (m_bDidLoadMessages) { return S_OK; } CBusy busy; CString sLibPathList = m_pEventSource->m_sLibPath; CString sLibPath; while (GetNextPath(sLibPathList, sLibPath) != E_FAIL) { // Load the library and get a list of all the messages. HINSTANCE hInstMsgFile = LoadLibraryEx((LPCTSTR) sLibPath, NULL, LOAD_LIBRARY_AS_DATAFILE); if (hInstMsgFile == NULL) { TCHAR szMessage[MAX_STRING]; CString sFormat; sFormat.LoadString(IDS_ERR_LOAD_MESSAGE_FILE_FAILED); _stprintf(szMessage, (LPCTSTR) sFormat, (LPCTSTR) sLibPath); AfxMessageBox(szMessage, MB_OK | MB_ICONSTOP); continue; } EnumResourceNames(hInstMsgFile, RT_MESSAGETABLE, (ENUMRESNAMEPROC)XProcessMsgTable, (LONG_PTR) this); GetLastError(); FreeLibrary(hInstMsgFile); } m_bDidLoadMessages = TRUE; return S_OK; } //************************************************************** // CXMessageArray::GetNextPath // // This function extracts the next path element from a list // of semi-colon separated paths. It also removes the extracted // element and the semi-colon from the path list. // // Paramters: // CString& sPathlist // A reference to a string consisting of one or more paths separated // by semi-colons. // // CString& sPath // A reference to the place where the extracted path string // will be returned. // // Returns: // SCODE // S_OK if a path was extracted, E_FAIL otherwise. // // The path is returned via sPath. sPathlist is updated // so that sPath and the trailing semi-colon is removed // //************************************************************** SCODE CXMessageArray::GetNextPath(CString& sPathlist, CString& sPath) { CString sPathTemp; sPath.Empty(); while (sPath.IsEmpty() && !sPathlist.IsEmpty()) { // Copy the next path from the sPathlist to sPath and // remove it from sPathlist INT ich = sPathlist.Find(_T(';')); if (ich == -1) { sPathTemp = sPathlist; sPathlist = _T(""); } else { sPathTemp = sPathlist.Left(ich); sPathlist = sPathlist.Right( sPathlist.GetLength() - (ich + 1)); } // Trim any leading or trailing space characters from // the path. // Find the first non-space character LPTSTR pszStart = sPathTemp.GetBuffer(sPathTemp.GetLength() + 1); while (*pszStart) { if (!_istspace(*pszStart)) { break; } ++pszStart; } // here, pszStart either points to the 1st non-space character or a string // of zero length // Find the first non-space character in reverse direction LPTSTR pszEnd = pszStart + _tcslen(pszStart); // point to the null character if (pszStart != pszEnd) { pszEnd--; // point to the last character while (_istspace(*pszEnd)) { pszEnd--; } // here, pszEnd points to the first non-space character in reverse direction pszEnd++; *pszEnd = _T('\0'); } sPath = pszStart; sPathTemp.ReleaseBuffer(); } if (sPath.IsEmpty()) { return E_FAIL; } else { return S_OK; } }