1836 lines
50 KiB
C++
1836 lines
50 KiB
C++
|
//=============================================================================
|
||
|
// File: gather.cpp
|
||
|
// Author: a-jammar
|
||
|
// Covers: CDataGatherer
|
||
|
//
|
||
|
// Copyright (c) 1998-1999 Microsoft Corporation
|
||
|
//
|
||
|
// For usage, see the header file.
|
||
|
//
|
||
|
// The data gathering object maintains a tree of categories. Category
|
||
|
// information is maintained internally, referenced by a ID. These IDs are
|
||
|
// used so that category objects, which will be created and passed outside
|
||
|
// of this object, can refer to the category information stored internally.
|
||
|
// If a category object held externally is no longer valid (possibly from
|
||
|
// a refresh operation), the ID will no longer be used within this object.
|
||
|
// IDs are DWORDS, will start at 1, and increase sequentially. Running out
|
||
|
// of IDs will not be a problem.
|
||
|
//=============================================================================
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
#include "gather.h"
|
||
|
#include "gathint.h"
|
||
|
#pragma warning(disable : 4099)
|
||
|
#include "wbemcli.h"
|
||
|
#pragma warning(default : 4099)
|
||
|
#include "resource.h"
|
||
|
#include "resrc1.h"
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// The constructor needs to note that the object isn't initialized.
|
||
|
// The destructor removes all the categories.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
CDataGatherer::CDataGatherer()
|
||
|
{
|
||
|
m_fInitialized = FALSE;
|
||
|
m_pProvider = NULL;
|
||
|
m_dwRootID = 0; // zero is used as a null category ID
|
||
|
m_dwNextFreeID = 1; // so the first real ID should be one
|
||
|
m_complexity = BASIC;
|
||
|
m_fDeferredPending = FALSE;
|
||
|
m_dwDeferredError = GATH_ERR_NOERROR;
|
||
|
m_fTemplatesLoaded = FALSE;
|
||
|
m_cInRefresh = 0;
|
||
|
|
||
|
ResetLastError();
|
||
|
}
|
||
|
|
||
|
CDataGatherer::~CDataGatherer()
|
||
|
{
|
||
|
if (m_pProvider)
|
||
|
delete m_pProvider;
|
||
|
|
||
|
if (m_fInitialized)
|
||
|
RemoveAllCategories();
|
||
|
}
|
||
|
|
||
|
//=============================================================================
|
||
|
// Functions called directly on CDataGatherer objects.
|
||
|
//=============================================================================
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method is used to get more information about the last error in a
|
||
|
// gatherer or category member function call. This will return an error code,
|
||
|
// or zero for OK. Note that a successful method call will reset the value
|
||
|
// returned by this method.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
DWORD CDataGatherer::GetLastError()
|
||
|
{
|
||
|
return m_dwLastError;
|
||
|
}
|
||
|
|
||
|
DWORD CDataGatherer::GetLastError(DWORD dwID)
|
||
|
{
|
||
|
INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
|
||
|
ASSERT(pInternal);
|
||
|
if (pInternal)
|
||
|
return pInternal->m_dwLastError;
|
||
|
return GATH_ERR_NOERROR;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This Create method delegates the work to the methods for setting a
|
||
|
// connection to another machine.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::Create(LPCTSTR szMachine)
|
||
|
{
|
||
|
ResetLastError();
|
||
|
|
||
|
// Create can only be called once in the lifetime of the object.
|
||
|
|
||
|
ASSERT(!m_fInitialized);
|
||
|
if (m_fInitialized)
|
||
|
return FALSE;
|
||
|
|
||
|
m_fInitialized = TRUE; // set initialize flag so SetConnect will run
|
||
|
m_fInitialized = SetConnect(szMachine);
|
||
|
return m_fInitialized;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// LoadTemplates is used to load the template information from this DLL (the
|
||
|
// default info) and any other DLLs with registry entries.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
void CDataGatherer::LoadTemplates()
|
||
|
{
|
||
|
TCHAR szBaseKey[] = _T("SOFTWARE\\Microsoft\\Shared Tools\\MSInfo\\templates");
|
||
|
HKEY hkeyBase;
|
||
|
|
||
|
if (!m_fTemplatesLoaded)
|
||
|
{
|
||
|
m_fTemplatesLoaded = TRUE;
|
||
|
|
||
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBaseKey, 0, KEY_READ, &hkeyBase))
|
||
|
{
|
||
|
CTemplateFileFunctions::LoadTemplateDLLs(hkeyBase, this);
|
||
|
RegCloseKey(hkeyBase);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CTemplateFileFunctions::LoadTemplateDLLs(NULL, this);
|
||
|
}
|
||
|
|
||
|
if (!m_strCategory.IsEmpty())
|
||
|
CTemplateFileFunctions::ApplyCategories(m_strCategory, this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method is used to create a WBEM connection to the specified machine.
|
||
|
// If the string parameter is null or empty, then we connect to this machine.
|
||
|
// Create the connection by creating a CDataProvider object.
|
||
|
//
|
||
|
// UPDATE: we're going to delay creating the provider until it is actually
|
||
|
// needed. This should speed up our creation process, and keep us being
|
||
|
// good citizens in computer management.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::SetConnect(LPCTSTR szMachine)
|
||
|
{
|
||
|
ASSERT(m_fInitialized);
|
||
|
|
||
|
ResetLastError();
|
||
|
if (!m_fInitialized)
|
||
|
{
|
||
|
SetLastError(GATH_ERR_NOTINITIALIZED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Delete an existing provider object (it isn't possible to redirect
|
||
|
// an existing provider - we need to create a new one).
|
||
|
|
||
|
if (m_pProvider)
|
||
|
{
|
||
|
delete m_pProvider;
|
||
|
m_pProvider = NULL;
|
||
|
}
|
||
|
|
||
|
// Save the name of the machine for the deferred creation, and note that
|
||
|
// there is deferred work to do.
|
||
|
|
||
|
m_strDeferredProvider = szMachine;
|
||
|
m_fDeferredPending = TRUE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Refresh all the information by refreshing the root category with a
|
||
|
// recursive flag.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::Refresh(volatile BOOL *pfCancel)
|
||
|
{
|
||
|
ASSERT(m_fInitialized);
|
||
|
|
||
|
ResetLastError();
|
||
|
if (!m_fInitialized)
|
||
|
{
|
||
|
SetLastError(GATH_ERR_NOTINITIALIZED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Multiple calls to LoadTemplates don't matter - it will only load
|
||
|
// the DLL template information once.
|
||
|
|
||
|
LoadTemplates();
|
||
|
|
||
|
if (m_dwRootID != 0)
|
||
|
return RefreshCategory(m_dwRootID, TRUE, pfCancel);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Set the complexity of the data shown to the user.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::SetDataComplexity(DataComplexity complexity)
|
||
|
{
|
||
|
ASSERT(m_fInitialized);
|
||
|
|
||
|
SetLastError(GATH_ERR_NOERROR);
|
||
|
if (!m_fInitialized)
|
||
|
{
|
||
|
SetLastError(GATH_ERR_NOTINITIALIZED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
ASSERT(complexity == BASIC || complexity == ADVANCED);
|
||
|
m_complexity = complexity;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method is used to allocate a new CDataCategory object to represent
|
||
|
// the root category, and return a pointer to the new object. The caller is
|
||
|
// responsible for eventually deallocating the object. We use a helper function
|
||
|
// to construct an object for a given ID number.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
CDataCategory * CDataGatherer::GetRootDataCategory()
|
||
|
{
|
||
|
ASSERT(m_fInitialized);
|
||
|
|
||
|
SetLastError(GATH_ERR_NOERROR);
|
||
|
if (!m_fInitialized)
|
||
|
{
|
||
|
SetLastError(GATH_ERR_NOTINITIALIZED);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
LoadTemplates();
|
||
|
|
||
|
if (m_dwRootID != 0)
|
||
|
return BuildDataCategory(m_dwRootID);
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method is used to convert a category identifier (static, non-localized)
|
||
|
// to a path to a category (all the category names to the identified category,
|
||
|
// not including the root, delimited by backslashes).
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::GetCategoryPath(const CString & strIdentifier, CString & strPath)
|
||
|
{
|
||
|
CString strTempPath;
|
||
|
DWORD dwID = m_dwRootID;
|
||
|
|
||
|
ASSERT(m_fInitialized);
|
||
|
|
||
|
SetLastError(GATH_ERR_NOERROR);
|
||
|
if (!m_fInitialized)
|
||
|
{
|
||
|
SetLastError(GATH_ERR_NOTINITIALIZED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (dwID && FindCategoryByIdentifer(strIdentifier, strTempPath, dwID))
|
||
|
{
|
||
|
// The path we have right now includes the root category name. This is
|
||
|
// unnecessary, so we remove it.
|
||
|
|
||
|
int iFirstDelimiter = strTempPath.Find(_T('\\'));
|
||
|
if (iFirstDelimiter > 0)
|
||
|
strPath = strTempPath.Right(strTempPath.GetLength() - iFirstDelimiter);
|
||
|
else
|
||
|
strPath = strTempPath;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
SetLastError(GATH_ERR_BADCATIDENTIFIER);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Find the strSearch string in the gathered data. Start at the strPath
|
||
|
// category on line iLine (if strPath is empty, start at the root). Return
|
||
|
// the first match in strPath & iLine. If iLine is -1, then search the
|
||
|
// category name as well.
|
||
|
//
|
||
|
// We want to search from top to bottom on a fully expanded tree view of the
|
||
|
// data categories - meaning we should use a depth first search.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::Find(MSI_FIND_STRUCT *pFind)
|
||
|
{
|
||
|
// Parameter checking.
|
||
|
|
||
|
ASSERT(m_fInitialized);
|
||
|
|
||
|
SetLastError(GATH_ERR_NOERROR);
|
||
|
if (!m_fInitialized)
|
||
|
{
|
||
|
SetLastError(GATH_ERR_NOTINITIALIZED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
ASSERT(pFind);
|
||
|
if (!pFind)
|
||
|
return FALSE;
|
||
|
|
||
|
if (m_dwRootID == 0)
|
||
|
return FALSE;
|
||
|
|
||
|
ASSERT(pFind->m_fSearchCategories || pFind->m_fSearchData); // should search something
|
||
|
ASSERT(!pFind->m_strSearch.IsEmpty()); // should search for something
|
||
|
ASSERT(pFind->m_fSearchCategories || pFind->m_iLine != -1); // contradiction
|
||
|
ASSERT(!pFind->m_strPath.IsEmpty() || pFind->m_iLine <= 0); // can't start in middle of non-category
|
||
|
|
||
|
// The path passed in doesn't start with the root category, but internally
|
||
|
// we want to deal with paths that include the root.
|
||
|
|
||
|
CString strRootName;
|
||
|
if (GetName(m_dwRootID, strRootName))
|
||
|
{
|
||
|
if (!pFind->m_strPath.IsEmpty() && pFind->m_strPath[0] == _T('\\'))
|
||
|
pFind->m_strPath = strRootName + pFind->m_strPath;
|
||
|
else
|
||
|
pFind->m_strPath = strRootName + CString("\\") + pFind->m_strPath;
|
||
|
|
||
|
if (pFind->m_strParentPath.Find(strRootName) != 0)
|
||
|
{
|
||
|
if (!pFind->m_strParentPath.IsEmpty() && pFind->m_strParentPath[0] == _T('\\'))
|
||
|
pFind->m_strParentPath = strRootName + pFind->m_strParentPath;
|
||
|
else
|
||
|
pFind->m_strParentPath = strRootName + CString("\\") + pFind->m_strParentPath;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Find the ID of the category to start searching from.
|
||
|
|
||
|
CString strPath = pFind->m_strPath;
|
||
|
int iLine;
|
||
|
DWORD dwID = FindCategoryByPath(strPath);
|
||
|
INTERNAL_CATEGORY * pInternalCat;
|
||
|
|
||
|
// A line number of -1 is used to identify a category name. If there
|
||
|
// is a category path, use whatever line was passed in. Otherwise,
|
||
|
// use 0 or -1 depending on whether we are supposed to search cat names.
|
||
|
|
||
|
if (pFind->m_strPath.IsEmpty())
|
||
|
iLine = (pFind->m_fSearchCategories) ? -1 : 0;
|
||
|
else
|
||
|
iLine = pFind->m_iLine;
|
||
|
|
||
|
// If we couldn't find an ID (empty path or bad path), start from the root.
|
||
|
|
||
|
if (dwID == 0)
|
||
|
dwID = m_dwRootID;
|
||
|
|
||
|
// If this search is case insensitive, convert the string we're looking
|
||
|
// for to all uppercase (also done to strings we're scanning).
|
||
|
|
||
|
if (!pFind->m_fCaseSensitive)
|
||
|
pFind->m_strSearch.MakeUpper();
|
||
|
|
||
|
// This loop is used to perform a recursive search of the current category,
|
||
|
// and then continue with first the next sibling of the category, then the
|
||
|
// next sibling of the parent category. We traverse up the tree until
|
||
|
// we reach a category which is not a child of m_strParentPath. In this way,
|
||
|
// we can search a the scope pane's tree control from top to bottom starting
|
||
|
// at any arbitrary category.
|
||
|
|
||
|
pFind->m_fNotFound = FALSE;
|
||
|
pFind->m_fFound = FALSE;
|
||
|
pFind->m_fCancelled = FALSE;
|
||
|
|
||
|
while (dwID)
|
||
|
{
|
||
|
// Get the internal category pointer for the ID we're searching.
|
||
|
|
||
|
pInternalCat = GetInternalRep(dwID);
|
||
|
|
||
|
// This should never happen, but it would be better to fail the find than crash.
|
||
|
|
||
|
if (pInternalCat == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
// Search this ID and all of it's children using a recursive function.
|
||
|
|
||
|
if (RecursiveFind(pInternalCat, pFind, iLine, strPath))
|
||
|
{
|
||
|
// Strip off the root category from the front of the path before we return it.
|
||
|
|
||
|
int iFirstDelimiter = strPath.Find(_T('\\'));
|
||
|
if (iFirstDelimiter > 0)
|
||
|
strPath = strPath.Right(strPath.GetLength() - iFirstDelimiter);
|
||
|
else
|
||
|
strPath.Empty(); // There is no delimiter, the path is just the root.
|
||
|
|
||
|
pFind->m_strPath = strPath;
|
||
|
pFind->m_iLine = iLine;
|
||
|
pFind->m_fFound = TRUE;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// If the caller has cancelled the find, return.
|
||
|
|
||
|
if (pFind->m_pfCancel && *pFind->m_pfCancel)
|
||
|
{
|
||
|
pFind->m_fCancelled = TRUE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// If we didn't find it in the first category we were looking in, reset
|
||
|
// the line variable so we search all of each of the next categories.
|
||
|
|
||
|
iLine = ((pFind->m_fSearchCategories) ? -1 : 0);
|
||
|
|
||
|
// If we didn't find a match, keep looking - first in the next siblings
|
||
|
// of the current ID, then in the parent's next sibling, and so on.
|
||
|
|
||
|
while (pInternalCat && pInternalCat->m_dwNextID == 0)
|
||
|
pInternalCat = (pInternalCat->m_dwParentID) ? GetInternalRep(pInternalCat->m_dwParentID) : NULL;
|
||
|
|
||
|
if (pInternalCat)
|
||
|
{
|
||
|
dwID = pInternalCat->m_dwNextID;
|
||
|
|
||
|
if (!pFind->m_strParentPath.IsEmpty() && !IsChildPath(pInternalCat, pFind->m_strParentPath))
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// If we reach here, the data wasn't found (and the find wasn't cancelled),
|
||
|
// so set the struct members accordingly and return TRUE.
|
||
|
|
||
|
pFind->m_fNotFound = TRUE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//=============================================================================
|
||
|
// Functions implementing CDataCategory (and derived) object behavior.
|
||
|
//=============================================================================
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method (usually called by a category object) returns a BOOL indicating
|
||
|
// is the supplied ID refers to a valid category. Note: this method should have
|
||
|
// no side effects, as it is used extensively within ASSERTs.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::IsValidDataCategory(DWORD dwID)
|
||
|
{
|
||
|
INTERNAL_CATEGORY * pCheck;
|
||
|
|
||
|
if (dwID != 0 && m_mapCategories.Lookup((WORD) dwID, (void * &) pCheck))
|
||
|
{
|
||
|
ASSERT(pCheck && pCheck->m_dwID == dwID);
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method (called by a category object) is used to get the name
|
||
|
// of the category.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::GetName(DWORD dwID, CString & strName)
|
||
|
{
|
||
|
INTERNAL_CATEGORY * pInternalCat;
|
||
|
|
||
|
ASSERT(m_fInitialized);
|
||
|
|
||
|
SetLastError(GATH_ERR_NOERROR);
|
||
|
if (!m_fInitialized)
|
||
|
{
|
||
|
SetLastError(GATH_ERR_NOTINITIALIZED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Look up the internal representation for the specified category.
|
||
|
|
||
|
pInternalCat = GetInternalRep(dwID);
|
||
|
|
||
|
if (pInternalCat)
|
||
|
{
|
||
|
strName = pInternalCat->m_categoryName.m_strText;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// GetRelative is used by the CDataCategory (or derived) object to navigate
|
||
|
// through the category tree. The relative enumeration tells which direction
|
||
|
// in the tree to navigate.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
CDataCategory * CDataGatherer::GetRelative(DWORD dwID, Relative relative)
|
||
|
{
|
||
|
INTERNAL_CATEGORY * pInternalCat;
|
||
|
DWORD dwRelativeID = 0;
|
||
|
|
||
|
ASSERT(m_fInitialized);
|
||
|
|
||
|
SetLastError(GATH_ERR_NOERROR);
|
||
|
if (!m_fInitialized)
|
||
|
{
|
||
|
SetLastError(GATH_ERR_NOTINITIALIZED);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// The first call to GetRelative will be using the root node, looking
|
||
|
// for it's children. This is when we want to do all of the work we've
|
||
|
// deferred, like creating the provider and loading the template files.
|
||
|
|
||
|
if (m_fDeferredPending)
|
||
|
{
|
||
|
m_dwDeferredError = GATH_ERR_NOERROR;
|
||
|
|
||
|
// Call GetProvider to connect to WBEM.
|
||
|
|
||
|
// if (GetProvider() == NULL)
|
||
|
// m_dwDeferredError = m_dwLastError;
|
||
|
|
||
|
m_fDeferredPending = FALSE;
|
||
|
}
|
||
|
|
||
|
if (m_dwDeferredError != GATH_ERR_NOERROR)
|
||
|
return NULL;
|
||
|
|
||
|
// Look up the internal representation for the specified category.
|
||
|
|
||
|
pInternalCat = GetInternalRep(dwID);
|
||
|
if (pInternalCat == NULL)
|
||
|
{
|
||
|
SetLastError(GATH_ERR_BADCATEGORYID);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// Then try to return the relative category, if there is one.
|
||
|
|
||
|
switch (relative)
|
||
|
{
|
||
|
case PARENT:
|
||
|
dwRelativeID = pInternalCat->m_dwParentID;
|
||
|
break;
|
||
|
case CHILD:
|
||
|
dwRelativeID = pInternalCat->m_dwChildID;
|
||
|
break;
|
||
|
case NEXT_SIBLING:
|
||
|
dwRelativeID = pInternalCat->m_dwNextID;
|
||
|
break;
|
||
|
case PREV_SIBLING:
|
||
|
dwRelativeID = pInternalCat->m_dwPrevID;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (pInternalCat && dwRelativeID)
|
||
|
return BuildDataCategory(dwRelativeID);
|
||
|
else
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method returns whether or not a specified category is dynamic. This
|
||
|
// can be determined by checking a flag.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::IsCategoryDynamic(DWORD dwID)
|
||
|
{
|
||
|
ASSERT(m_fInitialized);
|
||
|
if (!m_fInitialized)
|
||
|
return FALSE;
|
||
|
|
||
|
INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
|
||
|
if (pInternal)
|
||
|
return pInternal->m_fDynamic;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method returns whether or not a specified category has dynamic
|
||
|
// children. We need to use recursion to look at all the children, making
|
||
|
// this an expensive operation.
|
||
|
//
|
||
|
// TBD: compute this all in one pass and save it
|
||
|
// NOTE: currently we don't have dynamic categories
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::HasDynamicChildren(DWORD dwID, BOOL /* fRecursive */)
|
||
|
{
|
||
|
if (dwID == 0)
|
||
|
return FALSE;
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Get the count of columns from the internal representation - only count
|
||
|
// columns with the appropriate data complexity (BASIC or ADVANCED).
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
DWORD CDataGatherer::GetColumnCount(DWORD dwID)
|
||
|
{
|
||
|
DWORD dwCount = 0;
|
||
|
|
||
|
ASSERT(m_fInitialized);
|
||
|
if (!m_fInitialized)
|
||
|
return 0;
|
||
|
|
||
|
if (GetLastError() || GetLastError(dwID))
|
||
|
return 1;
|
||
|
|
||
|
INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
|
||
|
ASSERT(pInternal && pInternal->m_fListView);
|
||
|
if (pInternal && pInternal->m_fListView)
|
||
|
{
|
||
|
GATH_FIELD * pCol = pInternal->m_pColSpec;
|
||
|
while (pCol)
|
||
|
{
|
||
|
if (m_complexity == ADVANCED || pCol->m_datacomplexity == BASIC)
|
||
|
dwCount++;
|
||
|
pCol = pCol->m_pNext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return dwCount;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Get the count of rows from the internal representation - taking into
|
||
|
// account the data complexity.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
DWORD CDataGatherer::GetRowCount(DWORD dwID)
|
||
|
{
|
||
|
DWORD dwCount = 0;
|
||
|
|
||
|
ASSERT(m_fInitialized);
|
||
|
if (!m_fInitialized)
|
||
|
return 0;
|
||
|
|
||
|
if (GetLastError() || GetLastError(dwID))
|
||
|
return 1;
|
||
|
|
||
|
INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
|
||
|
ASSERT(pInternal && pInternal->m_fListView);
|
||
|
|
||
|
if(NULL == pInternal)
|
||
|
return 0;
|
||
|
|
||
|
if (pInternal && pInternal->m_fListView)
|
||
|
{
|
||
|
for (int i = 0; i < (int)pInternal->m_dwLineCount; i++)
|
||
|
if (m_complexity == ADVANCED || pInternal->m_apLines[i]->m_datacomplexity == BASIC)
|
||
|
dwCount++;
|
||
|
}
|
||
|
|
||
|
// If the row count is zero, return a count of one instead. This allows us
|
||
|
// to display a message for no data. This is not true if there are
|
||
|
// sub-categories for this category.
|
||
|
|
||
|
if (dwCount == 0 && pInternal->m_dwChildID == 0)
|
||
|
dwCount = 1;
|
||
|
|
||
|
return dwCount;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method returns the data complexity (BASIC or ADVANCED) for a row.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::GetRowDataComplexity(DWORD dwID, DWORD nRow, DataComplexity & complexity)
|
||
|
{
|
||
|
DWORD dwCount = nRow;
|
||
|
|
||
|
ASSERT(m_fInitialized);
|
||
|
if (!m_fInitialized)
|
||
|
return 0;
|
||
|
|
||
|
INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
|
||
|
ASSERT(pInternal && pInternal->m_fListView);
|
||
|
if (pInternal && pInternal->m_fListView)
|
||
|
{
|
||
|
// If there are no lines and we're be queried about the first line,
|
||
|
// it means that information is being requested for the "no instances..."
|
||
|
// message. Return as if it were BASIC information.
|
||
|
|
||
|
if (nRow == 0 && pInternal->m_dwLineCount == 0)
|
||
|
{
|
||
|
complexity = BASIC;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < (int)pInternal->m_dwLineCount; i++)
|
||
|
if (m_complexity == ADVANCED || pInternal->m_apLines[i]->m_datacomplexity == BASIC)
|
||
|
{
|
||
|
if (dwCount == 0)
|
||
|
{
|
||
|
complexity = pInternal->m_apLines[i]->m_datacomplexity;
|
||
|
return TRUE;
|
||
|
}
|
||
|
dwCount -= 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Get the caption for the specified column. Stored in internal representation.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::GetColumnCaption(DWORD dwID, DWORD nColumn, CString & strCaption)
|
||
|
{
|
||
|
INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
|
||
|
ASSERT(pInternal && pInternal->m_fListView);
|
||
|
|
||
|
ASSERT(m_fInitialized);
|
||
|
if (!m_fInitialized)
|
||
|
return FALSE;
|
||
|
|
||
|
if (GetLastError() || GetLastError(dwID))
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
strCaption.LoadString(IDS_DESCRIPTION);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// The column caption is a refreshed value (not something read from the
|
||
|
// template file). We need to look it up in the array of refreshed
|
||
|
// column headers.
|
||
|
|
||
|
if (pInternal && pInternal->m_fListView)
|
||
|
if (nColumn < pInternal->m_dwColCount)
|
||
|
{
|
||
|
// Get the actual index for the column information. This may be
|
||
|
// higher than the index parameter, if we are currently showing
|
||
|
// BASIC information (we need to skip over advanced columns).
|
||
|
|
||
|
DWORD nActualColumn = 0;
|
||
|
|
||
|
if (m_complexity == BASIC)
|
||
|
{
|
||
|
GATH_FIELD * pCol = pInternal->m_pColSpec;
|
||
|
int iCount = (int) nColumn;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
// Skip over any advanced categories.
|
||
|
|
||
|
while (pCol && pCol->m_datacomplexity == ADVANCED)
|
||
|
{
|
||
|
pCol = pCol->m_pNext;
|
||
|
nActualColumn++;
|
||
|
}
|
||
|
|
||
|
iCount--;
|
||
|
nActualColumn++;
|
||
|
pCol = pCol->m_pNext;
|
||
|
} while (pCol && (iCount >= 0));
|
||
|
|
||
|
if (iCount >= 0)
|
||
|
return FALSE;
|
||
|
else
|
||
|
nActualColumn--; // we went one too far
|
||
|
}
|
||
|
else
|
||
|
nActualColumn = nColumn;
|
||
|
|
||
|
strCaption = pInternal->m_aCols[nActualColumn].m_strText;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Return the width of the specified column. Stored in internal representation.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::GetColumnWidth(DWORD dwID, DWORD nColumn, DWORD &cxWidth)
|
||
|
{
|
||
|
ASSERT(m_fInitialized);
|
||
|
if (!m_fInitialized)
|
||
|
return FALSE;
|
||
|
|
||
|
if (GetLastError() || GetLastError(dwID))
|
||
|
{
|
||
|
cxWidth = 600;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
GATH_FIELD * pField = GetColumnField(dwID, nColumn);
|
||
|
if (pField)
|
||
|
{
|
||
|
cxWidth = pField->m_usWidth;
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method returns how a specified column should be sorted.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::GetColumnSort(DWORD dwID, DWORD nColumn, MSIColumnSortType & sorttype)
|
||
|
{
|
||
|
ASSERT(m_fInitialized);
|
||
|
if (!m_fInitialized)
|
||
|
return FALSE;
|
||
|
|
||
|
if (GetLastError() || GetLastError(dwID))
|
||
|
{
|
||
|
sorttype = NOSORT;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
GATH_FIELD * pField = GetColumnField(dwID, nColumn);
|
||
|
if (pField)
|
||
|
{
|
||
|
sorttype = pField->m_sort;
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method returns the data complexity (BASIC or ADVANCED) for a column.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::GetColumnDataComplexity(DWORD dwID, DWORD nColumn, DataComplexity & complexity)
|
||
|
{
|
||
|
ASSERT(m_fInitialized);
|
||
|
if (!m_fInitialized)
|
||
|
return FALSE;
|
||
|
|
||
|
if (GetLastError() || GetLastError(dwID))
|
||
|
{
|
||
|
complexity = BASIC;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
GATH_FIELD * pField = GetColumnField(dwID, nColumn);
|
||
|
if (pField)
|
||
|
{
|
||
|
complexity = pField->m_datacomplexity;
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This (private) method is used to get the internal field representation
|
||
|
// of the specified column.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
GATH_FIELD * CDataGatherer::GetColumnField(DWORD dwID, DWORD nColumn)
|
||
|
{
|
||
|
INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
|
||
|
ASSERT(pInternal && pInternal->m_fListView);
|
||
|
|
||
|
if (pInternal && pInternal->m_fListView)
|
||
|
{
|
||
|
// We need to scan the collection of column fields to find the requested one.
|
||
|
|
||
|
GATH_FIELD * pField = pInternal->m_pColSpec;
|
||
|
DWORD dwIndex = nColumn;
|
||
|
|
||
|
while (pField)
|
||
|
{
|
||
|
if (m_complexity == ADVANCED || pField->m_datacomplexity == BASIC)
|
||
|
{
|
||
|
if (dwIndex <= 0)
|
||
|
return pField;
|
||
|
dwIndex--;
|
||
|
}
|
||
|
pField = pField->m_pNext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method returns the value for a specified row and column number,
|
||
|
// in both string and DWORD format.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::GetValue(DWORD dwID, DWORD nRow, DWORD nColumn, CString &strValue, DWORD &dwValue)
|
||
|
{
|
||
|
ASSERT(m_fInitialized);
|
||
|
if (!m_fInitialized)
|
||
|
return FALSE;
|
||
|
|
||
|
if (GetLastError() || GetLastError(dwID))
|
||
|
{
|
||
|
if (nRow == 0 && nColumn == 0)
|
||
|
{
|
||
|
if (GetLastError())
|
||
|
strValue = GetErrorText();
|
||
|
else
|
||
|
strValue = GetErrorText(dwID);
|
||
|
}
|
||
|
else
|
||
|
strValue.Empty();
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
|
||
|
ASSERT(pInternal && pInternal->m_fListView);
|
||
|
|
||
|
if (pInternal && pInternal->m_fListView)
|
||
|
{
|
||
|
if (nRow == 0 && pInternal->m_dwLineCount == 0)
|
||
|
{
|
||
|
// Return the string from the template file for "no instances",
|
||
|
// for the first column.
|
||
|
|
||
|
if (nColumn == 0)
|
||
|
strValue = pInternal->m_strNoInstances;
|
||
|
else
|
||
|
strValue.Empty();
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// Otherwise, look up the cached value. First get the real row and
|
||
|
// column indices, which might be different from the specified
|
||
|
// indices (because of the BASIC/ADVANCED issue).
|
||
|
|
||
|
int iActualRow = 0;
|
||
|
int iActualCol = 0;
|
||
|
|
||
|
// Get the actual row index.
|
||
|
|
||
|
if (m_complexity == BASIC)
|
||
|
{
|
||
|
int iCount = (int) nRow;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
// Skip over any advanced rows.
|
||
|
|
||
|
while (iActualRow < (int)pInternal->m_dwLineCount && pInternal->m_apLines[iActualRow]->m_datacomplexity == ADVANCED)
|
||
|
iActualRow++;
|
||
|
|
||
|
iCount -= 1;
|
||
|
iActualRow += 1;
|
||
|
} while ((iActualRow < (int)pInternal->m_dwLineCount) && (iCount >= 0));
|
||
|
|
||
|
if (iCount >= 0)
|
||
|
return FALSE;
|
||
|
else
|
||
|
iActualRow -= 1; // we went one too far
|
||
|
}
|
||
|
else
|
||
|
iActualRow = nRow;
|
||
|
|
||
|
// Get the actual column index.
|
||
|
|
||
|
if (m_complexity == BASIC)
|
||
|
{
|
||
|
GATH_FIELD * pCol = pInternal->m_pColSpec;
|
||
|
int iCount = (int) nColumn;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
// Skip over any advanced columns.
|
||
|
|
||
|
while (pCol && pCol->m_datacomplexity == ADVANCED)
|
||
|
{
|
||
|
pCol = pCol->m_pNext;
|
||
|
iActualCol++;
|
||
|
}
|
||
|
|
||
|
iCount--;
|
||
|
iActualCol++;
|
||
|
pCol = pCol->m_pNext;
|
||
|
} while (pCol && (iCount >= 0));
|
||
|
|
||
|
if (iCount >= 0)
|
||
|
return FALSE;
|
||
|
else
|
||
|
iActualCol--; // we went one too far
|
||
|
}
|
||
|
else
|
||
|
iActualCol = nColumn;
|
||
|
|
||
|
// Retrieve the data using the actual indices.
|
||
|
|
||
|
if (iActualRow >= 0 && iActualRow < (int)pInternal->m_dwLineCount)
|
||
|
if (iActualCol >= 0 && iActualCol < (int)pInternal->m_dwColCount)
|
||
|
{
|
||
|
strValue = pInternal->m_apLines[iActualRow]->m_aValue[iActualCol].m_strText;
|
||
|
dwValue = pInternal->m_apLines[iActualRow]->m_aValue[iActualCol].m_dwValue;
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//=============================================================================
|
||
|
// Functions used internally to CDataGatherer, or by other friend classes.
|
||
|
//=============================================================================
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This methods deletes all of the internal category representations
|
||
|
// and empties the map.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
void CDataGatherer::RemoveAllCategories()
|
||
|
{
|
||
|
INTERNAL_CATEGORY * pInternalCat;
|
||
|
WORD key;
|
||
|
|
||
|
ASSERT(m_fInitialized);
|
||
|
if (!m_fInitialized)
|
||
|
return;
|
||
|
|
||
|
for (POSITION pos = m_mapCategories.GetStartPosition(); pos != NULL;)
|
||
|
{
|
||
|
m_mapCategories.GetNextAssoc(pos, key, (void * &) pInternalCat);
|
||
|
ASSERT(pInternalCat);
|
||
|
if (pInternalCat)
|
||
|
delete pInternalCat;
|
||
|
}
|
||
|
m_mapCategories.RemoveAll();
|
||
|
|
||
|
// Reset the root ID. Don't reset the next free ID, because we don't want
|
||
|
// the IDs to overlap during the lifetime of the object (otherwise some
|
||
|
// categories passed out might refer to the wrong internal category).
|
||
|
|
||
|
m_dwRootID = 0;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method is used to return a pointer to the internal representation of
|
||
|
// the category.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
INTERNAL_CATEGORY * CDataGatherer::GetInternalRep(DWORD dwID)
|
||
|
{
|
||
|
INTERNAL_CATEGORY * pInternalCat;
|
||
|
|
||
|
ASSERT(m_fInitialized);
|
||
|
if (!m_fInitialized)
|
||
|
return NULL;
|
||
|
|
||
|
if (m_mapCategories.Lookup((WORD) dwID, (void * &) pInternalCat))
|
||
|
{
|
||
|
ASSERT(pInternalCat && pInternalCat->m_dwID == dwID);
|
||
|
return pInternalCat;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ASSERT(FALSE);
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Return the CDataProvider object (well, a pointer to the object). If there
|
||
|
// isn't one yet, then create one.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
CDataProvider * CDataGatherer::GetProvider()
|
||
|
{
|
||
|
ASSERT(m_fInitialized);
|
||
|
if (!m_fInitialized)
|
||
|
return NULL;
|
||
|
|
||
|
if (m_pProvider == NULL)
|
||
|
{
|
||
|
// If the m_pProvider member is NULL, then we need to create a new one.
|
||
|
// Create the provider for the value stored in the deferred provider. If
|
||
|
// it is set, then we are doing the deferred creation of a provider for
|
||
|
// another machine. If it's empty, we're looking at the local machine.
|
||
|
// If the Create fails, it will call SetLastError saying why.
|
||
|
|
||
|
m_pProvider = new CDataProvider;
|
||
|
if (m_pProvider == NULL)
|
||
|
SetLastError(GATH_ERR_ALLOCATIONFAILED);
|
||
|
|
||
|
if (!m_pProvider->Create(m_strDeferredProvider, this))
|
||
|
{
|
||
|
delete m_pProvider;
|
||
|
m_pProvider = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return m_pProvider;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method resets the internal refreshed flag. It's called if the provider
|
||
|
// is pointed to a different computer, for example, to make each category get
|
||
|
// fresh data to display.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
void CDataGatherer::ResetCategoryRefresh()
|
||
|
{
|
||
|
INTERNAL_CATEGORY * pInternalCat;
|
||
|
WORD key;
|
||
|
|
||
|
for (POSITION pos = m_mapCategories.GetStartPosition(); pos != NULL;)
|
||
|
{
|
||
|
m_mapCategories.GetNextAssoc(pos, key, (void * &) pInternalCat);
|
||
|
ASSERT(pInternalCat);
|
||
|
if (pInternalCat)
|
||
|
pInternalCat->m_fRefreshed = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method is used to refresh the contents of the internal category struct.
|
||
|
// Doing this means refreshing the category name, its columns, and building
|
||
|
// a list of lines based on the list of line specifiers in the category.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::RefreshCategory(DWORD dwID, BOOL fRecursive, volatile BOOL *pfCancel, BOOL fSoftRefresh)
|
||
|
{
|
||
|
INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
|
||
|
|
||
|
ASSERT(m_fInitialized);
|
||
|
if (!m_fInitialized)
|
||
|
return FALSE;
|
||
|
|
||
|
if (pInternal == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
// Check to see if the refresh operation has been cancelled.
|
||
|
|
||
|
if (pfCancel && *pfCancel == TRUE)
|
||
|
{
|
||
|
TRACE0("-- CDataGatherer::RefreshCategory() refresh cancelled by caller\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// If this is the first call to this recursive function, reset the
|
||
|
// cache of WBEM enumerator pointers.
|
||
|
|
||
|
if (m_cInRefresh == 0 && m_pProvider)
|
||
|
m_pProvider->m_enumMap.Reset();
|
||
|
m_cInRefresh++;
|
||
|
|
||
|
if (m_pProvider)
|
||
|
m_pProvider->m_dwRefreshingCategoryID = dwID;
|
||
|
|
||
|
// Remove the cached items in the CDataProvider object.
|
||
|
|
||
|
if (m_pProvider)
|
||
|
m_pProvider->ClearCache();
|
||
|
|
||
|
// If this is a soft refresh, and this category has been refreshed at least once,
|
||
|
// skip the refresh operation. An example of a soft refresh would be the user
|
||
|
// clicking on the category for the first time, where we would want to skip the
|
||
|
// refresh if a global refresh had been done previously.
|
||
|
|
||
|
if (!pInternal->m_fRefreshed || !fSoftRefresh)
|
||
|
{
|
||
|
if (!CRefreshFunctions::RefreshColumns(this, pInternal))
|
||
|
{
|
||
|
TRACE0("-- CDataGatherer::RefreshCategory() failed at RefreshColumns\n");
|
||
|
m_cInRefresh--;
|
||
|
if (m_pProvider)
|
||
|
m_pProvider->m_dwRefreshingCategoryID = 0;
|
||
|
return FALSE; // RefreshValue will set last error
|
||
|
}
|
||
|
|
||
|
// The RefreshLines function returns a CPtrList of pointers to line structures. These
|
||
|
// pointers need to be copied to the pInternal->m_apLines array.
|
||
|
|
||
|
CPtrList listLinePtrs;
|
||
|
if (CRefreshFunctions::RefreshLines(this, pInternal->m_pLineSpec, pInternal->m_dwColCount, listLinePtrs, pfCancel))
|
||
|
{
|
||
|
if (pInternal->m_apLines && pInternal->m_dwLineCount)
|
||
|
{
|
||
|
for (DWORD dwIndex = 0; dwIndex < pInternal->m_dwLineCount; dwIndex++)
|
||
|
delete pInternal->m_apLines[dwIndex];
|
||
|
delete [] pInternal->m_apLines;
|
||
|
}
|
||
|
|
||
|
// Move the contents of listLinePtrs to the array of line pointers in the internal struct.
|
||
|
|
||
|
pInternal->m_dwLineCount = (DWORD) listLinePtrs.GetCount();
|
||
|
if (pInternal->m_dwLineCount)
|
||
|
{
|
||
|
pInternal->m_apLines = new GATH_LINE *[pInternal->m_dwLineCount];
|
||
|
if (pInternal->m_apLines)
|
||
|
{
|
||
|
DWORD dwIndex = 0;
|
||
|
for (POSITION pos = listLinePtrs.GetHeadPosition(); pos != NULL;)
|
||
|
{
|
||
|
ASSERT(dwIndex < (DWORD) listLinePtrs.GetCount());
|
||
|
pInternal->m_apLines[dwIndex] = (GATH_LINE *) listLinePtrs.GetNext(pos);
|
||
|
dwIndex++;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// If there was an error, we need to deallocate the lines.
|
||
|
|
||
|
GATH_LINE * pLine;
|
||
|
for (POSITION pos = listLinePtrs.GetHeadPosition(); pos != NULL;)
|
||
|
{
|
||
|
pLine = (GATH_LINE *) listLinePtrs.GetNext(pos) ;
|
||
|
if (pLine)
|
||
|
delete pLine;
|
||
|
}
|
||
|
|
||
|
TRACE0("-- CDataGatherer::RefreshCategory() failed allocating m_apLines\n");
|
||
|
SetLastError(GATH_ERR_ALLOCATIONFAILED);
|
||
|
m_cInRefresh--;
|
||
|
if (m_pProvider)
|
||
|
m_pProvider->m_dwRefreshingCategoryID = 0;
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pInternal->m_fRefreshed = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TRACE0("-- CDataGatherer::RefreshCategory() failed at RefreshLines\n");
|
||
|
m_cInRefresh--;
|
||
|
if (m_pProvider)
|
||
|
m_pProvider->m_dwRefreshingCategoryID = 0;
|
||
|
return FALSE; // RefreshLines will set last error
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (fRecursive)
|
||
|
{
|
||
|
INTERNAL_CATEGORY * pChild;
|
||
|
DWORD dwChildID = pInternal->m_dwChildID;
|
||
|
|
||
|
while (dwChildID)
|
||
|
{
|
||
|
if (!RefreshCategory(dwChildID, TRUE, pfCancel))
|
||
|
{
|
||
|
m_cInRefresh--;
|
||
|
if (m_pProvider)
|
||
|
m_pProvider->m_dwRefreshingCategoryID = 0;
|
||
|
return FALSE;
|
||
|
}
|
||
|
pChild = GetInternalRep(dwChildID);
|
||
|
if (pChild)
|
||
|
dwChildID = pChild->m_dwNextID;
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Even if we aren't recursive, we should refresh the names of the sub
|
||
|
// categories, since they might be enumerated before they are refreshed.
|
||
|
|
||
|
INTERNAL_CATEGORY * pChild;
|
||
|
DWORD dwChildID = pInternal->m_dwChildID;
|
||
|
|
||
|
while (dwChildID)
|
||
|
{
|
||
|
pChild = GetInternalRep(dwChildID);
|
||
|
if (pChild)
|
||
|
{
|
||
|
if (!CRefreshFunctions::RefreshValue(this, &pChild->m_categoryName, &pChild->m_fieldName))
|
||
|
{
|
||
|
m_cInRefresh--;
|
||
|
if (m_pProvider)
|
||
|
m_pProvider->m_dwRefreshingCategoryID = 0;
|
||
|
return FALSE;
|
||
|
}
|
||
|
dwChildID = pChild->m_dwNextID;
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_cInRefresh--;
|
||
|
if (m_pProvider)
|
||
|
m_pProvider->m_dwRefreshingCategoryID = 0;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Sets the last error (the value returned by GetLastError) to the specified
|
||
|
// DWORD value.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
void CDataGatherer::SetLastError(DWORD dwError)
|
||
|
{
|
||
|
// Making a change - disable all of the error reset calls (a few will
|
||
|
// be done explicitly.
|
||
|
|
||
|
if (dwError != GATH_ERR_NOERROR)
|
||
|
m_dwLastError = dwError;
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
if (dwError)
|
||
|
TRACE1("-- SetLastError(0x%08x)\n", dwError);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void CDataGatherer::SetLastError(DWORD dwError, DWORD dwID)
|
||
|
{
|
||
|
if (dwError != GATH_ERR_NOERROR)
|
||
|
{
|
||
|
INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
|
||
|
ASSERT(pInternal);
|
||
|
if (pInternal)
|
||
|
pInternal->m_dwLastError = dwError;
|
||
|
}
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
if (dwError)
|
||
|
TRACE2("-- SetLastError(0x%08x, %d)\n", dwError, dwID);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Sets the error flag based on an HRESULT returned value.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
void CDataGatherer::SetLastErrorHR(HRESULT hrError)
|
||
|
{
|
||
|
DWORD dwError;
|
||
|
|
||
|
switch (hrError)
|
||
|
{
|
||
|
case WBEM_E_OUT_OF_MEMORY:
|
||
|
dwError = GATH_ERR_NOWBEMOUTOFMEM;
|
||
|
break;
|
||
|
|
||
|
case WBEM_E_ACCESS_DENIED:
|
||
|
dwError = GATH_ERR_NOWBEMACCESSDENIED;
|
||
|
break;
|
||
|
|
||
|
case WBEM_E_INVALID_NAMESPACE:
|
||
|
dwError = GATH_ERR_NOWBEMBADSERVER;
|
||
|
break;
|
||
|
|
||
|
case WBEM_E_TRANSPORT_FAILURE:
|
||
|
dwError = GATH_ERR_NOWBEMNETWORKFAILURE;
|
||
|
break;
|
||
|
|
||
|
case WBEM_E_FAILED:
|
||
|
case WBEM_E_INVALID_PARAMETER:
|
||
|
default:
|
||
|
dwError = GATH_ERR_NOWBEMCONNECT;
|
||
|
}
|
||
|
|
||
|
SetLastError(dwError);
|
||
|
}
|
||
|
|
||
|
void CDataGatherer::SetLastErrorHR(HRESULT hrError, DWORD dwID)
|
||
|
{
|
||
|
DWORD dwError;
|
||
|
|
||
|
switch (hrError)
|
||
|
{
|
||
|
case WBEM_E_OUT_OF_MEMORY:
|
||
|
dwError = GATH_ERR_NOWBEMOUTOFMEM;
|
||
|
break;
|
||
|
|
||
|
case WBEM_E_ACCESS_DENIED:
|
||
|
dwError = GATH_ERR_NOWBEMACCESSDENIED;
|
||
|
break;
|
||
|
|
||
|
case WBEM_E_INVALID_NAMESPACE:
|
||
|
dwError = GATH_ERR_NOWBEMBADSERVER;
|
||
|
break;
|
||
|
|
||
|
case WBEM_E_TRANSPORT_FAILURE:
|
||
|
dwError = GATH_ERR_NOWBEMNETWORKFAILURE;
|
||
|
break;
|
||
|
|
||
|
case WBEM_E_FAILED:
|
||
|
case WBEM_E_INVALID_PARAMETER:
|
||
|
default:
|
||
|
dwError = GATH_ERR_NOWBEMCONNECT;
|
||
|
}
|
||
|
|
||
|
SetLastError(dwError, dwID);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Resets the error flag to a no error state (added because SetLastError no
|
||
|
// longer allows this).
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
void CDataGatherer::ResetLastError()
|
||
|
{
|
||
|
m_dwLastError = GATH_ERR_NOERROR;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Return a text representation of the error for display.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
CString CDataGatherer::GetErrorText()
|
||
|
{
|
||
|
CString strErrorText(_T(""));
|
||
|
CString strMachine(m_strDeferredProvider);
|
||
|
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
|
||
|
switch (m_dwLastError)
|
||
|
{
|
||
|
case GATH_ERR_ALLOCATIONFAILED:
|
||
|
case GATH_ERR_NOWBEMOUTOFMEM:
|
||
|
strErrorText.LoadString(IDS_OUTOFMEMERROR);
|
||
|
break;
|
||
|
case GATH_ERR_NOWBEMLOCATOR:
|
||
|
strErrorText.LoadString(IDS_NOLOCATOR);
|
||
|
break;
|
||
|
case GATH_ERR_NOWBEMCONNECT:
|
||
|
strErrorText.Format(IDS_NOGATHERER, strMachine);
|
||
|
break;
|
||
|
case GATH_ERR_NOWBEMACCESSDENIED:
|
||
|
strErrorText.Format(IDS_GATHERACCESS, strMachine);
|
||
|
break;
|
||
|
case GATH_ERR_NOWBEMBADSERVER:
|
||
|
strErrorText.Format(IDS_BADSERVER, strMachine);
|
||
|
break;
|
||
|
case GATH_ERR_NOWBEMNETWORKFAILURE:
|
||
|
strErrorText.Format(IDS_NETWORKERROR, strMachine);
|
||
|
break;
|
||
|
default:
|
||
|
case GATH_ERR_BADCATEGORYID:
|
||
|
strErrorText.LoadString(IDS_UNEXPECTED);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return strErrorText;
|
||
|
}
|
||
|
|
||
|
CString CDataGatherer::GetErrorText(DWORD dwID)
|
||
|
{
|
||
|
CString strErrorText(_T(""));
|
||
|
CString strMachine(m_strDeferredProvider);
|
||
|
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
|
||
|
INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
|
||
|
ASSERT(pInternal);
|
||
|
if (pInternal)
|
||
|
{
|
||
|
switch (pInternal->m_dwLastError)
|
||
|
{
|
||
|
case GATH_ERR_ALLOCATIONFAILED:
|
||
|
case GATH_ERR_NOWBEMOUTOFMEM:
|
||
|
strErrorText.LoadString(IDS_OUTOFMEMERROR);
|
||
|
break;
|
||
|
case GATH_ERR_NOWBEMLOCATOR:
|
||
|
strErrorText.LoadString(IDS_NOLOCATOR);
|
||
|
break;
|
||
|
case GATH_ERR_NOWBEMCONNECT:
|
||
|
strErrorText.Format(IDS_NOGATHERER, strMachine);
|
||
|
break;
|
||
|
case GATH_ERR_NOWBEMACCESSDENIED:
|
||
|
strErrorText.Format(IDS_GATHERACCESS, strMachine);
|
||
|
break;
|
||
|
case GATH_ERR_NOWBEMBADSERVER:
|
||
|
strErrorText.Format(IDS_BADSERVER, strMachine);
|
||
|
break;
|
||
|
case GATH_ERR_NOWBEMNETWORKFAILURE:
|
||
|
strErrorText.Format(IDS_NETWORKERROR, strMachine);
|
||
|
break;
|
||
|
default:
|
||
|
case GATH_ERR_BADCATEGORYID:
|
||
|
strErrorText.LoadString(IDS_UNEXPECTED);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return strErrorText;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method is used to construct a CDataCategory object for the passed ID.
|
||
|
// The caller is responsible for ultimately deallocating the object. We use
|
||
|
// the m_mapCategories to retrieve an internal representation of the category,
|
||
|
// construct a CDataCategory object and set it up to refer the this category.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
CDataCategory * CDataGatherer::BuildDataCategory(DWORD dwID)
|
||
|
{
|
||
|
CDataCategory * pReturnCategory;
|
||
|
INTERNAL_CATEGORY * pInternalCat;
|
||
|
|
||
|
ASSERT(m_fInitialized);
|
||
|
ASSERT(dwID != 0);
|
||
|
|
||
|
SetLastError(GATH_ERR_NOERROR);
|
||
|
if (!m_fInitialized)
|
||
|
{
|
||
|
SetLastError(GATH_ERR_NOTINITIALIZED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// First, try to look up an internal category representation of the category.
|
||
|
|
||
|
if (!m_mapCategories.Lookup((WORD) dwID, (void * &) pInternalCat))
|
||
|
{
|
||
|
SetLastError(GATH_ERR_BADCATEGORYID);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
ASSERT(pInternalCat);
|
||
|
if (pInternalCat == NULL)
|
||
|
return NULL; // might be that this category was hidden
|
||
|
|
||
|
// Create the object to return (either a CDataCategory or CDataListCategory,
|
||
|
// depending on the information in pInternalCat).
|
||
|
|
||
|
if (pInternalCat->m_fListView)
|
||
|
pReturnCategory = (CDataCategory *) new CDataListCategory;
|
||
|
else
|
||
|
pReturnCategory = new CDataCategory;
|
||
|
|
||
|
if (pReturnCategory == NULL)
|
||
|
{
|
||
|
SetLastError(GATH_ERR_ALLOCATIONFAILED);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// All the external category theoretically needs is a pointer to this object
|
||
|
// and its ID number.
|
||
|
|
||
|
pReturnCategory->m_pGatherer = this;
|
||
|
pReturnCategory->m_dwID = dwID;
|
||
|
|
||
|
return pReturnCategory;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Recursive method used internally to find a category path based on a
|
||
|
// category identifier.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::FindCategoryByIdentifer(const CString & strIdentifier, CString & strPath, DWORD dwID)
|
||
|
{
|
||
|
INTERNAL_CATEGORY * pInternalCat;
|
||
|
|
||
|
// Look up the internal representation for the specified category.
|
||
|
|
||
|
pInternalCat = GetInternalRep(dwID);
|
||
|
|
||
|
// If this category is the one we're looking for, add the name to the
|
||
|
// path variable and return true.
|
||
|
|
||
|
if (strIdentifier.CompareNoCase(pInternalCat->m_strIdentifier) == 0)
|
||
|
{
|
||
|
if (!strPath.IsEmpty())
|
||
|
strPath += CString(_T("\\"));
|
||
|
strPath += pInternalCat->m_categoryName.m_strText;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// Otherwise, look through the children.
|
||
|
|
||
|
DWORD dwChildID = pInternalCat->m_dwChildID;
|
||
|
while (dwChildID)
|
||
|
{
|
||
|
if (FindCategoryByIdentifer(strIdentifier, strPath, dwChildID))
|
||
|
return TRUE;
|
||
|
dwChildID = pInternalCat->m_dwNextID;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method is used to convert a string path (category names, starting at
|
||
|
// the root category, delimited by backslashes) into the ID for the category.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
DWORD CDataGatherer::FindCategoryByPath(const CString & strPath)
|
||
|
{
|
||
|
INTERNAL_CATEGORY * pInternalCat;
|
||
|
CString strWorkingPath(strPath), strNextCategory;
|
||
|
DWORD dwID = 0, dwSearchID, dwCurrentID = 0;
|
||
|
|
||
|
while (!strWorkingPath.IsEmpty())
|
||
|
{
|
||
|
GetToken(strNextCategory, strWorkingPath, _T('\\'));
|
||
|
|
||
|
// Look for the child of the current category to match the name. If the
|
||
|
// current category ID is zero, make sure the root category name matches.
|
||
|
|
||
|
if (dwCurrentID == 0)
|
||
|
{
|
||
|
pInternalCat = GetInternalRep(m_dwRootID);
|
||
|
if (pInternalCat == NULL || pInternalCat->m_categoryName.m_strText.CompareNoCase(strNextCategory))
|
||
|
return 0;
|
||
|
dwCurrentID = m_dwRootID;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Start looking through the children of the current node.
|
||
|
|
||
|
ASSERT(pInternalCat && pInternalCat->m_dwID == dwCurrentID);
|
||
|
dwSearchID = pInternalCat->m_dwChildID;
|
||
|
while (dwSearchID)
|
||
|
{
|
||
|
pInternalCat = GetInternalRep(dwSearchID);
|
||
|
if (pInternalCat == NULL)
|
||
|
return 0;
|
||
|
|
||
|
if (pInternalCat->m_categoryName.m_strText.CompareNoCase(strNextCategory) == 0)
|
||
|
break;
|
||
|
|
||
|
dwSearchID = pInternalCat->m_dwNextID;
|
||
|
}
|
||
|
|
||
|
if (dwSearchID == 0)
|
||
|
return 0;
|
||
|
else
|
||
|
dwCurrentID = dwSearchID;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return dwCurrentID;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// This method searches the specified category and all of it's children
|
||
|
// for a string. It starts from the iLineth line. If a match is found, the
|
||
|
// line number and path to the category where the match was made are set,
|
||
|
// and we return TRUE.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::RecursiveFind(INTERNAL_CATEGORY * pCat, MSI_FIND_STRUCT *pFind, int & iLine, CString & strPath)
|
||
|
{
|
||
|
ASSERT(pCat);
|
||
|
ASSERT(pFind);
|
||
|
|
||
|
// Look through the lines in the current category for a match with strSearch.
|
||
|
// Note: only look through lines and columns with the appropriate complexity
|
||
|
// (BASIC or ADVANCED). The line number should take this into account as well.
|
||
|
|
||
|
int iResultLine = 0, iCurrentLine = 0;
|
||
|
CString strValue;
|
||
|
|
||
|
// A line number of -1 indicates that we should check the category name.
|
||
|
|
||
|
if (iLine == -1)
|
||
|
{
|
||
|
strValue = pCat->m_categoryName.m_strText;
|
||
|
if (!pFind->m_fCaseSensitive)
|
||
|
strValue.MakeUpper();
|
||
|
|
||
|
if (strValue.Find(pFind->m_strSearch) != -1)
|
||
|
{
|
||
|
GetCategoryPath(pCat, strPath);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
iLine = 0;
|
||
|
}
|
||
|
|
||
|
// Otherwise, look through lines for this category.
|
||
|
|
||
|
if (pFind->m_fSearchData && pCat->m_fListView)
|
||
|
{
|
||
|
// We need to search the message which is displayed when there is no
|
||
|
// data as well (searching a saved file does this).
|
||
|
|
||
|
if (pCat->m_dwLineCount == 0)
|
||
|
{
|
||
|
if (iLine == 0)
|
||
|
{
|
||
|
strValue = pCat->m_strNoInstances;
|
||
|
if (!pFind->m_fCaseSensitive)
|
||
|
strValue.MakeUpper();
|
||
|
|
||
|
if (strValue.Find(pFind->m_strSearch) != -1)
|
||
|
{
|
||
|
iLine = 0;
|
||
|
GetCategoryPath(pCat, strPath);
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// This category does have data - search through it.
|
||
|
|
||
|
while (iCurrentLine < (int)pCat->m_dwLineCount)
|
||
|
{
|
||
|
// Check to see if the Find has been cancelled.
|
||
|
|
||
|
if (pFind->m_pfCancel && *pFind->m_pfCancel)
|
||
|
return FALSE;
|
||
|
|
||
|
if (m_complexity == ADVANCED || pCat->m_apLines[iCurrentLine]->m_datacomplexity == BASIC)
|
||
|
{
|
||
|
// Search through the columns of data for a match. Start looking only after
|
||
|
// we've skipped any lines indicated by the iLine parameter.
|
||
|
|
||
|
if (iResultLine >= iLine)
|
||
|
{
|
||
|
GATH_FIELD * pCol = pCat->m_pColSpec;
|
||
|
int iCurrentCol = 0;
|
||
|
|
||
|
while (pCol)
|
||
|
{
|
||
|
if (m_complexity == ADVANCED || pCol->m_datacomplexity == BASIC)
|
||
|
{
|
||
|
strValue = pCat->m_apLines[iCurrentLine]->m_aValue[iCurrentCol].m_strText;
|
||
|
if (!pFind->m_fCaseSensitive)
|
||
|
strValue.MakeUpper();
|
||
|
|
||
|
if (strValue.Find(pFind->m_strSearch) != -1)
|
||
|
{
|
||
|
iLine = iResultLine;
|
||
|
GetCategoryPath(pCat, strPath);
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pCol = pCol->m_pNext;
|
||
|
iCurrentCol++;
|
||
|
}
|
||
|
}
|
||
|
iResultLine++;
|
||
|
}
|
||
|
iCurrentLine++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// No matches were found. Search through the children of this category for a
|
||
|
// match. Create a temporary path and line variable to pass to the children.
|
||
|
|
||
|
CString strTempPath;
|
||
|
int iTempLine;
|
||
|
INTERNAL_CATEGORY * pChildCat;
|
||
|
|
||
|
DWORD dwChildID = pCat->m_dwChildID;
|
||
|
while (dwChildID)
|
||
|
{
|
||
|
pChildCat = GetInternalRep(dwChildID);
|
||
|
if (pChildCat)
|
||
|
{
|
||
|
iTempLine = (pFind->m_fSearchCategories) ? -1 : 0;
|
||
|
if (RecursiveFind(pChildCat, pFind, iTempLine, strTempPath))
|
||
|
{
|
||
|
strPath = strTempPath;
|
||
|
iLine = iTempLine;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
dwChildID = pChildCat->m_dwNextID;
|
||
|
}
|
||
|
else
|
||
|
dwChildID = 0;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Return the path of category names to get from the root category to this
|
||
|
// category, separated by backslashes.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
void CDataGatherer::GetCategoryPath(INTERNAL_CATEGORY * pCat, CString & strPath)
|
||
|
{
|
||
|
INTERNAL_CATEGORY * pParent;
|
||
|
CString strWorking;
|
||
|
|
||
|
pParent = pCat;
|
||
|
while (pParent)
|
||
|
{
|
||
|
if (!strWorking.IsEmpty())
|
||
|
strWorking = CString(_T("\\")) + strWorking;
|
||
|
strWorking = pParent->m_categoryName.m_strText + strWorking;
|
||
|
|
||
|
if (pParent->m_dwParentID)
|
||
|
pParent = GetInternalRep(pParent->m_dwParentID);
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
ASSERT(pParent); // if not set we tried to get a ptr for a bad ID, or bad param
|
||
|
strPath = strWorking;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Determine is the passed category pointer is a child category of the passed
|
||
|
// category path. Do this by using the pointer to get a category path, and
|
||
|
// doing a string search for the parent (the parent path will be in any child's
|
||
|
// path).
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CDataGatherer::IsChildPath(INTERNAL_CATEGORY * pInternalCat, const CString & strParentPath)
|
||
|
{
|
||
|
CString strParent, strChild;
|
||
|
|
||
|
GetCategoryPath(pInternalCat, strChild);
|
||
|
strParent = strParentPath;
|
||
|
|
||
|
strParent.MakeUpper();
|
||
|
strChild.MakeUpper();
|
||
|
|
||
|
return (strChild.Find(strParent) != -1 && strChild.Compare(strParent) != 0);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Dump out the contents of the CDataGatherer object (DEBUG only).
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
void CDataGatherer::Dump()
|
||
|
{
|
||
|
ASSERT(m_fInitialized);
|
||
|
if (!m_fInitialized)
|
||
|
return;
|
||
|
|
||
|
TRACE0("Dumping CDataGatherer object...\n");
|
||
|
TRACE1(" m_fInitialized = %s\n", (m_fInitialized ? "TRUE" : "FALSE"));
|
||
|
TRACE1(" m_dwNextFreeID = %ld\n", m_dwNextFreeID);
|
||
|
TRACE1(" m_dwRootID = %ld\n", m_dwRootID);
|
||
|
|
||
|
INTERNAL_CATEGORY * pCat = GetInternalRep(m_dwRootID);
|
||
|
if (pCat)
|
||
|
pCat->DumpCategoryRecursive(4, this);
|
||
|
}
|
||
|
#endif
|
||
|
|