windows-nt/Source/XPSP1/NT/net/snmp/subagent/snmpevnt/evntwin/trapreg.cpp
2020-09-26 16:20:57 +08:00

2758 lines
79 KiB
C++

//***********************************************************************
// 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; iEventSource<nEventSources; iEventSource++)
{
CString sSource;
sSource = pasEventSources->GetAt(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\<source-subkey>. 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\<event log>
//
// 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\<event log>
//
// 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\<event source>
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\<source-key>
//
// 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; i<pEntry->Length && *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; i<pEntry->Length/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; ulBlock<pMsgTable->NumberOfBlocks; 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;
}
}