Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1835 lines
50 KiB

  1. //=============================================================================
  2. // File: gather.cpp
  3. // Author: a-jammar
  4. // Covers: CDataGatherer
  5. //
  6. // Copyright (c) 1998-1999 Microsoft Corporation
  7. //
  8. // For usage, see the header file.
  9. //
  10. // The data gathering object maintains a tree of categories. Category
  11. // information is maintained internally, referenced by a ID. These IDs are
  12. // used so that category objects, which will be created and passed outside
  13. // of this object, can refer to the category information stored internally.
  14. // If a category object held externally is no longer valid (possibly from
  15. // a refresh operation), the ID will no longer be used within this object.
  16. // IDs are DWORDS, will start at 1, and increase sequentially. Running out
  17. // of IDs will not be a problem.
  18. //=============================================================================
  19. #include "stdafx.h"
  20. #include "gather.h"
  21. #include "gathint.h"
  22. #pragma warning(disable : 4099)
  23. #include "wbemcli.h"
  24. #pragma warning(default : 4099)
  25. #include "resource.h"
  26. #include "resrc1.h"
  27. //-----------------------------------------------------------------------------
  28. // The constructor needs to note that the object isn't initialized.
  29. // The destructor removes all the categories.
  30. //-----------------------------------------------------------------------------
  31. CDataGatherer::CDataGatherer()
  32. {
  33. m_fInitialized = FALSE;
  34. m_pProvider = NULL;
  35. m_dwRootID = 0; // zero is used as a null category ID
  36. m_dwNextFreeID = 1; // so the first real ID should be one
  37. m_complexity = BASIC;
  38. m_fDeferredPending = FALSE;
  39. m_dwDeferredError = GATH_ERR_NOERROR;
  40. m_fTemplatesLoaded = FALSE;
  41. m_cInRefresh = 0;
  42. ResetLastError();
  43. }
  44. CDataGatherer::~CDataGatherer()
  45. {
  46. if (m_pProvider)
  47. delete m_pProvider;
  48. if (m_fInitialized)
  49. RemoveAllCategories();
  50. }
  51. //=============================================================================
  52. // Functions called directly on CDataGatherer objects.
  53. //=============================================================================
  54. //-----------------------------------------------------------------------------
  55. // This method is used to get more information about the last error in a
  56. // gatherer or category member function call. This will return an error code,
  57. // or zero for OK. Note that a successful method call will reset the value
  58. // returned by this method.
  59. //-----------------------------------------------------------------------------
  60. DWORD CDataGatherer::GetLastError()
  61. {
  62. return m_dwLastError;
  63. }
  64. DWORD CDataGatherer::GetLastError(DWORD dwID)
  65. {
  66. INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
  67. ASSERT(pInternal);
  68. if (pInternal)
  69. return pInternal->m_dwLastError;
  70. return GATH_ERR_NOERROR;
  71. }
  72. //-----------------------------------------------------------------------------
  73. // This Create method delegates the work to the methods for setting a
  74. // connection to another machine.
  75. //-----------------------------------------------------------------------------
  76. BOOL CDataGatherer::Create(LPCTSTR szMachine)
  77. {
  78. ResetLastError();
  79. // Create can only be called once in the lifetime of the object.
  80. ASSERT(!m_fInitialized);
  81. if (m_fInitialized)
  82. return FALSE;
  83. m_fInitialized = TRUE; // set initialize flag so SetConnect will run
  84. m_fInitialized = SetConnect(szMachine);
  85. return m_fInitialized;
  86. }
  87. //-----------------------------------------------------------------------------
  88. // LoadTemplates is used to load the template information from this DLL (the
  89. // default info) and any other DLLs with registry entries.
  90. //-----------------------------------------------------------------------------
  91. void CDataGatherer::LoadTemplates()
  92. {
  93. TCHAR szBaseKey[] = _T("SOFTWARE\\Microsoft\\Shared Tools\\MSInfo\\templates");
  94. HKEY hkeyBase;
  95. if (!m_fTemplatesLoaded)
  96. {
  97. m_fTemplatesLoaded = TRUE;
  98. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBaseKey, 0, KEY_READ, &hkeyBase))
  99. {
  100. CTemplateFileFunctions::LoadTemplateDLLs(hkeyBase, this);
  101. RegCloseKey(hkeyBase);
  102. }
  103. else
  104. {
  105. CTemplateFileFunctions::LoadTemplateDLLs(NULL, this);
  106. }
  107. if (!m_strCategory.IsEmpty())
  108. CTemplateFileFunctions::ApplyCategories(m_strCategory, this);
  109. }
  110. }
  111. //-----------------------------------------------------------------------------
  112. // This method is used to create a WBEM connection to the specified machine.
  113. // If the string parameter is null or empty, then we connect to this machine.
  114. // Create the connection by creating a CDataProvider object.
  115. //
  116. // UPDATE: we're going to delay creating the provider until it is actually
  117. // needed. This should speed up our creation process, and keep us being
  118. // good citizens in computer management.
  119. //-----------------------------------------------------------------------------
  120. BOOL CDataGatherer::SetConnect(LPCTSTR szMachine)
  121. {
  122. ASSERT(m_fInitialized);
  123. ResetLastError();
  124. if (!m_fInitialized)
  125. {
  126. SetLastError(GATH_ERR_NOTINITIALIZED);
  127. return FALSE;
  128. }
  129. // Delete an existing provider object (it isn't possible to redirect
  130. // an existing provider - we need to create a new one).
  131. if (m_pProvider)
  132. {
  133. delete m_pProvider;
  134. m_pProvider = NULL;
  135. }
  136. // Save the name of the machine for the deferred creation, and note that
  137. // there is deferred work to do.
  138. m_strDeferredProvider = szMachine;
  139. m_fDeferredPending = TRUE;
  140. return TRUE;
  141. }
  142. //-----------------------------------------------------------------------------
  143. // Refresh all the information by refreshing the root category with a
  144. // recursive flag.
  145. //-----------------------------------------------------------------------------
  146. BOOL CDataGatherer::Refresh(volatile BOOL *pfCancel)
  147. {
  148. ASSERT(m_fInitialized);
  149. ResetLastError();
  150. if (!m_fInitialized)
  151. {
  152. SetLastError(GATH_ERR_NOTINITIALIZED);
  153. return FALSE;
  154. }
  155. // Multiple calls to LoadTemplates don't matter - it will only load
  156. // the DLL template information once.
  157. LoadTemplates();
  158. if (m_dwRootID != 0)
  159. return RefreshCategory(m_dwRootID, TRUE, pfCancel);
  160. return TRUE;
  161. }
  162. //-----------------------------------------------------------------------------
  163. // Set the complexity of the data shown to the user.
  164. //-----------------------------------------------------------------------------
  165. BOOL CDataGatherer::SetDataComplexity(DataComplexity complexity)
  166. {
  167. ASSERT(m_fInitialized);
  168. SetLastError(GATH_ERR_NOERROR);
  169. if (!m_fInitialized)
  170. {
  171. SetLastError(GATH_ERR_NOTINITIALIZED);
  172. return FALSE;
  173. }
  174. ASSERT(complexity == BASIC || complexity == ADVANCED);
  175. m_complexity = complexity;
  176. return TRUE;
  177. }
  178. //-----------------------------------------------------------------------------
  179. // This method is used to allocate a new CDataCategory object to represent
  180. // the root category, and return a pointer to the new object. The caller is
  181. // responsible for eventually deallocating the object. We use a helper function
  182. // to construct an object for a given ID number.
  183. //-----------------------------------------------------------------------------
  184. CDataCategory * CDataGatherer::GetRootDataCategory()
  185. {
  186. ASSERT(m_fInitialized);
  187. SetLastError(GATH_ERR_NOERROR);
  188. if (!m_fInitialized)
  189. {
  190. SetLastError(GATH_ERR_NOTINITIALIZED);
  191. return NULL;
  192. }
  193. LoadTemplates();
  194. if (m_dwRootID != 0)
  195. return BuildDataCategory(m_dwRootID);
  196. return NULL;
  197. }
  198. //-----------------------------------------------------------------------------
  199. // This method is used to convert a category identifier (static, non-localized)
  200. // to a path to a category (all the category names to the identified category,
  201. // not including the root, delimited by backslashes).
  202. //-----------------------------------------------------------------------------
  203. BOOL CDataGatherer::GetCategoryPath(const CString & strIdentifier, CString & strPath)
  204. {
  205. CString strTempPath;
  206. DWORD dwID = m_dwRootID;
  207. ASSERT(m_fInitialized);
  208. SetLastError(GATH_ERR_NOERROR);
  209. if (!m_fInitialized)
  210. {
  211. SetLastError(GATH_ERR_NOTINITIALIZED);
  212. return FALSE;
  213. }
  214. if (dwID && FindCategoryByIdentifer(strIdentifier, strTempPath, dwID))
  215. {
  216. // The path we have right now includes the root category name. This is
  217. // unnecessary, so we remove it.
  218. int iFirstDelimiter = strTempPath.Find(_T('\\'));
  219. if (iFirstDelimiter > 0)
  220. strPath = strTempPath.Right(strTempPath.GetLength() - iFirstDelimiter);
  221. else
  222. strPath = strTempPath;
  223. return TRUE;
  224. }
  225. SetLastError(GATH_ERR_BADCATIDENTIFIER);
  226. return FALSE;
  227. }
  228. //-----------------------------------------------------------------------------
  229. // Find the strSearch string in the gathered data. Start at the strPath
  230. // category on line iLine (if strPath is empty, start at the root). Return
  231. // the first match in strPath & iLine. If iLine is -1, then search the
  232. // category name as well.
  233. //
  234. // We want to search from top to bottom on a fully expanded tree view of the
  235. // data categories - meaning we should use a depth first search.
  236. //-----------------------------------------------------------------------------
  237. BOOL CDataGatherer::Find(MSI_FIND_STRUCT *pFind)
  238. {
  239. // Parameter checking.
  240. ASSERT(m_fInitialized);
  241. SetLastError(GATH_ERR_NOERROR);
  242. if (!m_fInitialized)
  243. {
  244. SetLastError(GATH_ERR_NOTINITIALIZED);
  245. return FALSE;
  246. }
  247. ASSERT(pFind);
  248. if (!pFind)
  249. return FALSE;
  250. if (m_dwRootID == 0)
  251. return FALSE;
  252. ASSERT(pFind->m_fSearchCategories || pFind->m_fSearchData); // should search something
  253. ASSERT(!pFind->m_strSearch.IsEmpty()); // should search for something
  254. ASSERT(pFind->m_fSearchCategories || pFind->m_iLine != -1); // contradiction
  255. ASSERT(!pFind->m_strPath.IsEmpty() || pFind->m_iLine <= 0); // can't start in middle of non-category
  256. // The path passed in doesn't start with the root category, but internally
  257. // we want to deal with paths that include the root.
  258. CString strRootName;
  259. if (GetName(m_dwRootID, strRootName))
  260. {
  261. if (!pFind->m_strPath.IsEmpty() && pFind->m_strPath[0] == _T('\\'))
  262. pFind->m_strPath = strRootName + pFind->m_strPath;
  263. else
  264. pFind->m_strPath = strRootName + CString("\\") + pFind->m_strPath;
  265. if (pFind->m_strParentPath.Find(strRootName) != 0)
  266. {
  267. if (!pFind->m_strParentPath.IsEmpty() && pFind->m_strParentPath[0] == _T('\\'))
  268. pFind->m_strParentPath = strRootName + pFind->m_strParentPath;
  269. else
  270. pFind->m_strParentPath = strRootName + CString("\\") + pFind->m_strParentPath;
  271. }
  272. }
  273. // Find the ID of the category to start searching from.
  274. CString strPath = pFind->m_strPath;
  275. int iLine;
  276. DWORD dwID = FindCategoryByPath(strPath);
  277. INTERNAL_CATEGORY * pInternalCat;
  278. // A line number of -1 is used to identify a category name. If there
  279. // is a category path, use whatever line was passed in. Otherwise,
  280. // use 0 or -1 depending on whether we are supposed to search cat names.
  281. if (pFind->m_strPath.IsEmpty())
  282. iLine = (pFind->m_fSearchCategories) ? -1 : 0;
  283. else
  284. iLine = pFind->m_iLine;
  285. // If we couldn't find an ID (empty path or bad path), start from the root.
  286. if (dwID == 0)
  287. dwID = m_dwRootID;
  288. // If this search is case insensitive, convert the string we're looking
  289. // for to all uppercase (also done to strings we're scanning).
  290. if (!pFind->m_fCaseSensitive)
  291. pFind->m_strSearch.MakeUpper();
  292. // This loop is used to perform a recursive search of the current category,
  293. // and then continue with first the next sibling of the category, then the
  294. // next sibling of the parent category. We traverse up the tree until
  295. // we reach a category which is not a child of m_strParentPath. In this way,
  296. // we can search a the scope pane's tree control from top to bottom starting
  297. // at any arbitrary category.
  298. pFind->m_fNotFound = FALSE;
  299. pFind->m_fFound = FALSE;
  300. pFind->m_fCancelled = FALSE;
  301. while (dwID)
  302. {
  303. // Get the internal category pointer for the ID we're searching.
  304. pInternalCat = GetInternalRep(dwID);
  305. // This should never happen, but it would be better to fail the find than crash.
  306. if (pInternalCat == NULL)
  307. return FALSE;
  308. // Search this ID and all of it's children using a recursive function.
  309. if (RecursiveFind(pInternalCat, pFind, iLine, strPath))
  310. {
  311. // Strip off the root category from the front of the path before we return it.
  312. int iFirstDelimiter = strPath.Find(_T('\\'));
  313. if (iFirstDelimiter > 0)
  314. strPath = strPath.Right(strPath.GetLength() - iFirstDelimiter);
  315. else
  316. strPath.Empty(); // There is no delimiter, the path is just the root.
  317. pFind->m_strPath = strPath;
  318. pFind->m_iLine = iLine;
  319. pFind->m_fFound = TRUE;
  320. return TRUE;
  321. }
  322. // If the caller has cancelled the find, return.
  323. if (pFind->m_pfCancel && *pFind->m_pfCancel)
  324. {
  325. pFind->m_fCancelled = TRUE;
  326. return TRUE;
  327. }
  328. // If we didn't find it in the first category we were looking in, reset
  329. // the line variable so we search all of each of the next categories.
  330. iLine = ((pFind->m_fSearchCategories) ? -1 : 0);
  331. // If we didn't find a match, keep looking - first in the next siblings
  332. // of the current ID, then in the parent's next sibling, and so on.
  333. while (pInternalCat && pInternalCat->m_dwNextID == 0)
  334. pInternalCat = (pInternalCat->m_dwParentID) ? GetInternalRep(pInternalCat->m_dwParentID) : NULL;
  335. if (pInternalCat)
  336. {
  337. dwID = pInternalCat->m_dwNextID;
  338. if (!pFind->m_strParentPath.IsEmpty() && !IsChildPath(pInternalCat, pFind->m_strParentPath))
  339. break;
  340. }
  341. else
  342. break;
  343. }
  344. // If we reach here, the data wasn't found (and the find wasn't cancelled),
  345. // so set the struct members accordingly and return TRUE.
  346. pFind->m_fNotFound = TRUE;
  347. return TRUE;
  348. }
  349. //=============================================================================
  350. // Functions implementing CDataCategory (and derived) object behavior.
  351. //=============================================================================
  352. //-----------------------------------------------------------------------------
  353. // This method (usually called by a category object) returns a BOOL indicating
  354. // is the supplied ID refers to a valid category. Note: this method should have
  355. // no side effects, as it is used extensively within ASSERTs.
  356. //-----------------------------------------------------------------------------
  357. BOOL CDataGatherer::IsValidDataCategory(DWORD dwID)
  358. {
  359. INTERNAL_CATEGORY * pCheck;
  360. if (dwID != 0 && m_mapCategories.Lookup((WORD) dwID, (void * &) pCheck))
  361. {
  362. ASSERT(pCheck && pCheck->m_dwID == dwID);
  363. return TRUE;
  364. }
  365. return FALSE;
  366. }
  367. //-----------------------------------------------------------------------------
  368. // This method (called by a category object) is used to get the name
  369. // of the category.
  370. //-----------------------------------------------------------------------------
  371. BOOL CDataGatherer::GetName(DWORD dwID, CString & strName)
  372. {
  373. INTERNAL_CATEGORY * pInternalCat;
  374. ASSERT(m_fInitialized);
  375. SetLastError(GATH_ERR_NOERROR);
  376. if (!m_fInitialized)
  377. {
  378. SetLastError(GATH_ERR_NOTINITIALIZED);
  379. return FALSE;
  380. }
  381. // Look up the internal representation for the specified category.
  382. pInternalCat = GetInternalRep(dwID);
  383. if (pInternalCat)
  384. {
  385. strName = pInternalCat->m_categoryName.m_strText;
  386. return TRUE;
  387. }
  388. return FALSE;
  389. }
  390. //-----------------------------------------------------------------------------
  391. // GetRelative is used by the CDataCategory (or derived) object to navigate
  392. // through the category tree. The relative enumeration tells which direction
  393. // in the tree to navigate.
  394. //-----------------------------------------------------------------------------
  395. CDataCategory * CDataGatherer::GetRelative(DWORD dwID, Relative relative)
  396. {
  397. INTERNAL_CATEGORY * pInternalCat;
  398. DWORD dwRelativeID = 0;
  399. ASSERT(m_fInitialized);
  400. SetLastError(GATH_ERR_NOERROR);
  401. if (!m_fInitialized)
  402. {
  403. SetLastError(GATH_ERR_NOTINITIALIZED);
  404. return NULL;
  405. }
  406. // The first call to GetRelative will be using the root node, looking
  407. // for it's children. This is when we want to do all of the work we've
  408. // deferred, like creating the provider and loading the template files.
  409. if (m_fDeferredPending)
  410. {
  411. m_dwDeferredError = GATH_ERR_NOERROR;
  412. // Call GetProvider to connect to WBEM.
  413. // if (GetProvider() == NULL)
  414. // m_dwDeferredError = m_dwLastError;
  415. m_fDeferredPending = FALSE;
  416. }
  417. if (m_dwDeferredError != GATH_ERR_NOERROR)
  418. return NULL;
  419. // Look up the internal representation for the specified category.
  420. pInternalCat = GetInternalRep(dwID);
  421. if (pInternalCat == NULL)
  422. {
  423. SetLastError(GATH_ERR_BADCATEGORYID);
  424. return NULL;
  425. }
  426. // Then try to return the relative category, if there is one.
  427. switch (relative)
  428. {
  429. case PARENT:
  430. dwRelativeID = pInternalCat->m_dwParentID;
  431. break;
  432. case CHILD:
  433. dwRelativeID = pInternalCat->m_dwChildID;
  434. break;
  435. case NEXT_SIBLING:
  436. dwRelativeID = pInternalCat->m_dwNextID;
  437. break;
  438. case PREV_SIBLING:
  439. dwRelativeID = pInternalCat->m_dwPrevID;
  440. break;
  441. }
  442. if (pInternalCat && dwRelativeID)
  443. return BuildDataCategory(dwRelativeID);
  444. else
  445. return NULL;
  446. }
  447. //-----------------------------------------------------------------------------
  448. // This method returns whether or not a specified category is dynamic. This
  449. // can be determined by checking a flag.
  450. //-----------------------------------------------------------------------------
  451. BOOL CDataGatherer::IsCategoryDynamic(DWORD dwID)
  452. {
  453. ASSERT(m_fInitialized);
  454. if (!m_fInitialized)
  455. return FALSE;
  456. INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
  457. if (pInternal)
  458. return pInternal->m_fDynamic;
  459. return FALSE;
  460. }
  461. //-----------------------------------------------------------------------------
  462. // This method returns whether or not a specified category has dynamic
  463. // children. We need to use recursion to look at all the children, making
  464. // this an expensive operation.
  465. //
  466. // TBD: compute this all in one pass and save it
  467. // NOTE: currently we don't have dynamic categories
  468. //-----------------------------------------------------------------------------
  469. BOOL CDataGatherer::HasDynamicChildren(DWORD dwID, BOOL /* fRecursive */)
  470. {
  471. if (dwID == 0)
  472. return FALSE;
  473. return FALSE;
  474. }
  475. //-----------------------------------------------------------------------------
  476. // Get the count of columns from the internal representation - only count
  477. // columns with the appropriate data complexity (BASIC or ADVANCED).
  478. //-----------------------------------------------------------------------------
  479. DWORD CDataGatherer::GetColumnCount(DWORD dwID)
  480. {
  481. DWORD dwCount = 0;
  482. ASSERT(m_fInitialized);
  483. if (!m_fInitialized)
  484. return 0;
  485. if (GetLastError() || GetLastError(dwID))
  486. return 1;
  487. INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
  488. ASSERT(pInternal && pInternal->m_fListView);
  489. if (pInternal && pInternal->m_fListView)
  490. {
  491. GATH_FIELD * pCol = pInternal->m_pColSpec;
  492. while (pCol)
  493. {
  494. if (m_complexity == ADVANCED || pCol->m_datacomplexity == BASIC)
  495. dwCount++;
  496. pCol = pCol->m_pNext;
  497. }
  498. }
  499. return dwCount;
  500. }
  501. //-----------------------------------------------------------------------------
  502. // Get the count of rows from the internal representation - taking into
  503. // account the data complexity.
  504. //-----------------------------------------------------------------------------
  505. DWORD CDataGatherer::GetRowCount(DWORD dwID)
  506. {
  507. DWORD dwCount = 0;
  508. ASSERT(m_fInitialized);
  509. if (!m_fInitialized)
  510. return 0;
  511. if (GetLastError() || GetLastError(dwID))
  512. return 1;
  513. INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
  514. ASSERT(pInternal && pInternal->m_fListView);
  515. if(NULL == pInternal)
  516. return 0;
  517. if (pInternal && pInternal->m_fListView)
  518. {
  519. for (int i = 0; i < (int)pInternal->m_dwLineCount; i++)
  520. if (m_complexity == ADVANCED || pInternal->m_apLines[i]->m_datacomplexity == BASIC)
  521. dwCount++;
  522. }
  523. // If the row count is zero, return a count of one instead. This allows us
  524. // to display a message for no data. This is not true if there are
  525. // sub-categories for this category.
  526. if (dwCount == 0 && pInternal->m_dwChildID == 0)
  527. dwCount = 1;
  528. return dwCount;
  529. }
  530. //-----------------------------------------------------------------------------
  531. // This method returns the data complexity (BASIC or ADVANCED) for a row.
  532. //-----------------------------------------------------------------------------
  533. BOOL CDataGatherer::GetRowDataComplexity(DWORD dwID, DWORD nRow, DataComplexity & complexity)
  534. {
  535. DWORD dwCount = nRow;
  536. ASSERT(m_fInitialized);
  537. if (!m_fInitialized)
  538. return 0;
  539. INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
  540. ASSERT(pInternal && pInternal->m_fListView);
  541. if (pInternal && pInternal->m_fListView)
  542. {
  543. // If there are no lines and we're be queried about the first line,
  544. // it means that information is being requested for the "no instances..."
  545. // message. Return as if it were BASIC information.
  546. if (nRow == 0 && pInternal->m_dwLineCount == 0)
  547. {
  548. complexity = BASIC;
  549. return TRUE;
  550. }
  551. for (int i = 0; i < (int)pInternal->m_dwLineCount; i++)
  552. if (m_complexity == ADVANCED || pInternal->m_apLines[i]->m_datacomplexity == BASIC)
  553. {
  554. if (dwCount == 0)
  555. {
  556. complexity = pInternal->m_apLines[i]->m_datacomplexity;
  557. return TRUE;
  558. }
  559. dwCount -= 1;
  560. }
  561. }
  562. return FALSE;
  563. }
  564. //-----------------------------------------------------------------------------
  565. // Get the caption for the specified column. Stored in internal representation.
  566. //-----------------------------------------------------------------------------
  567. BOOL CDataGatherer::GetColumnCaption(DWORD dwID, DWORD nColumn, CString & strCaption)
  568. {
  569. INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
  570. ASSERT(pInternal && pInternal->m_fListView);
  571. ASSERT(m_fInitialized);
  572. if (!m_fInitialized)
  573. return FALSE;
  574. if (GetLastError() || GetLastError(dwID))
  575. {
  576. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  577. strCaption.LoadString(IDS_DESCRIPTION);
  578. return TRUE;
  579. }
  580. // The column caption is a refreshed value (not something read from the
  581. // template file). We need to look it up in the array of refreshed
  582. // column headers.
  583. if (pInternal && pInternal->m_fListView)
  584. if (nColumn < pInternal->m_dwColCount)
  585. {
  586. // Get the actual index for the column information. This may be
  587. // higher than the index parameter, if we are currently showing
  588. // BASIC information (we need to skip over advanced columns).
  589. DWORD nActualColumn = 0;
  590. if (m_complexity == BASIC)
  591. {
  592. GATH_FIELD * pCol = pInternal->m_pColSpec;
  593. int iCount = (int) nColumn;
  594. do
  595. {
  596. // Skip over any advanced categories.
  597. while (pCol && pCol->m_datacomplexity == ADVANCED)
  598. {
  599. pCol = pCol->m_pNext;
  600. nActualColumn++;
  601. }
  602. iCount--;
  603. nActualColumn++;
  604. pCol = pCol->m_pNext;
  605. } while (pCol && (iCount >= 0));
  606. if (iCount >= 0)
  607. return FALSE;
  608. else
  609. nActualColumn--; // we went one too far
  610. }
  611. else
  612. nActualColumn = nColumn;
  613. strCaption = pInternal->m_aCols[nActualColumn].m_strText;
  614. return TRUE;
  615. }
  616. return FALSE;
  617. }
  618. //-----------------------------------------------------------------------------
  619. // Return the width of the specified column. Stored in internal representation.
  620. //-----------------------------------------------------------------------------
  621. BOOL CDataGatherer::GetColumnWidth(DWORD dwID, DWORD nColumn, DWORD &cxWidth)
  622. {
  623. ASSERT(m_fInitialized);
  624. if (!m_fInitialized)
  625. return FALSE;
  626. if (GetLastError() || GetLastError(dwID))
  627. {
  628. cxWidth = 600;
  629. return TRUE;
  630. }
  631. GATH_FIELD * pField = GetColumnField(dwID, nColumn);
  632. if (pField)
  633. {
  634. cxWidth = pField->m_usWidth;
  635. return TRUE;
  636. }
  637. return FALSE;
  638. }
  639. //-----------------------------------------------------------------------------
  640. // This method returns how a specified column should be sorted.
  641. //-----------------------------------------------------------------------------
  642. BOOL CDataGatherer::GetColumnSort(DWORD dwID, DWORD nColumn, MSIColumnSortType & sorttype)
  643. {
  644. ASSERT(m_fInitialized);
  645. if (!m_fInitialized)
  646. return FALSE;
  647. if (GetLastError() || GetLastError(dwID))
  648. {
  649. sorttype = NOSORT;
  650. return TRUE;
  651. }
  652. GATH_FIELD * pField = GetColumnField(dwID, nColumn);
  653. if (pField)
  654. {
  655. sorttype = pField->m_sort;
  656. return TRUE;
  657. }
  658. return FALSE;
  659. }
  660. //-----------------------------------------------------------------------------
  661. // This method returns the data complexity (BASIC or ADVANCED) for a column.
  662. //-----------------------------------------------------------------------------
  663. BOOL CDataGatherer::GetColumnDataComplexity(DWORD dwID, DWORD nColumn, DataComplexity & complexity)
  664. {
  665. ASSERT(m_fInitialized);
  666. if (!m_fInitialized)
  667. return FALSE;
  668. if (GetLastError() || GetLastError(dwID))
  669. {
  670. complexity = BASIC;
  671. return TRUE;
  672. }
  673. GATH_FIELD * pField = GetColumnField(dwID, nColumn);
  674. if (pField)
  675. {
  676. complexity = pField->m_datacomplexity;
  677. return TRUE;
  678. }
  679. return FALSE;
  680. }
  681. //-----------------------------------------------------------------------------
  682. // This (private) method is used to get the internal field representation
  683. // of the specified column.
  684. //-----------------------------------------------------------------------------
  685. GATH_FIELD * CDataGatherer::GetColumnField(DWORD dwID, DWORD nColumn)
  686. {
  687. INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
  688. ASSERT(pInternal && pInternal->m_fListView);
  689. if (pInternal && pInternal->m_fListView)
  690. {
  691. // We need to scan the collection of column fields to find the requested one.
  692. GATH_FIELD * pField = pInternal->m_pColSpec;
  693. DWORD dwIndex = nColumn;
  694. while (pField)
  695. {
  696. if (m_complexity == ADVANCED || pField->m_datacomplexity == BASIC)
  697. {
  698. if (dwIndex <= 0)
  699. return pField;
  700. dwIndex--;
  701. }
  702. pField = pField->m_pNext;
  703. }
  704. }
  705. return NULL;
  706. }
  707. //-----------------------------------------------------------------------------
  708. // This method returns the value for a specified row and column number,
  709. // in both string and DWORD format.
  710. //-----------------------------------------------------------------------------
  711. BOOL CDataGatherer::GetValue(DWORD dwID, DWORD nRow, DWORD nColumn, CString &strValue, DWORD &dwValue)
  712. {
  713. ASSERT(m_fInitialized);
  714. if (!m_fInitialized)
  715. return FALSE;
  716. if (GetLastError() || GetLastError(dwID))
  717. {
  718. if (nRow == 0 && nColumn == 0)
  719. {
  720. if (GetLastError())
  721. strValue = GetErrorText();
  722. else
  723. strValue = GetErrorText(dwID);
  724. }
  725. else
  726. strValue.Empty();
  727. return TRUE;
  728. }
  729. INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
  730. ASSERT(pInternal && pInternal->m_fListView);
  731. if (pInternal && pInternal->m_fListView)
  732. {
  733. if (nRow == 0 && pInternal->m_dwLineCount == 0)
  734. {
  735. // Return the string from the template file for "no instances",
  736. // for the first column.
  737. if (nColumn == 0)
  738. strValue = pInternal->m_strNoInstances;
  739. else
  740. strValue.Empty();
  741. return TRUE;
  742. }
  743. // Otherwise, look up the cached value. First get the real row and
  744. // column indices, which might be different from the specified
  745. // indices (because of the BASIC/ADVANCED issue).
  746. int iActualRow = 0;
  747. int iActualCol = 0;
  748. // Get the actual row index.
  749. if (m_complexity == BASIC)
  750. {
  751. int iCount = (int) nRow;
  752. do
  753. {
  754. // Skip over any advanced rows.
  755. while (iActualRow < (int)pInternal->m_dwLineCount && pInternal->m_apLines[iActualRow]->m_datacomplexity == ADVANCED)
  756. iActualRow++;
  757. iCount -= 1;
  758. iActualRow += 1;
  759. } while ((iActualRow < (int)pInternal->m_dwLineCount) && (iCount >= 0));
  760. if (iCount >= 0)
  761. return FALSE;
  762. else
  763. iActualRow -= 1; // we went one too far
  764. }
  765. else
  766. iActualRow = nRow;
  767. // Get the actual column index.
  768. if (m_complexity == BASIC)
  769. {
  770. GATH_FIELD * pCol = pInternal->m_pColSpec;
  771. int iCount = (int) nColumn;
  772. do
  773. {
  774. // Skip over any advanced columns.
  775. while (pCol && pCol->m_datacomplexity == ADVANCED)
  776. {
  777. pCol = pCol->m_pNext;
  778. iActualCol++;
  779. }
  780. iCount--;
  781. iActualCol++;
  782. pCol = pCol->m_pNext;
  783. } while (pCol && (iCount >= 0));
  784. if (iCount >= 0)
  785. return FALSE;
  786. else
  787. iActualCol--; // we went one too far
  788. }
  789. else
  790. iActualCol = nColumn;
  791. // Retrieve the data using the actual indices.
  792. if (iActualRow >= 0 && iActualRow < (int)pInternal->m_dwLineCount)
  793. if (iActualCol >= 0 && iActualCol < (int)pInternal->m_dwColCount)
  794. {
  795. strValue = pInternal->m_apLines[iActualRow]->m_aValue[iActualCol].m_strText;
  796. dwValue = pInternal->m_apLines[iActualRow]->m_aValue[iActualCol].m_dwValue;
  797. return TRUE;
  798. }
  799. }
  800. return FALSE;
  801. }
  802. //=============================================================================
  803. // Functions used internally to CDataGatherer, or by other friend classes.
  804. //=============================================================================
  805. //-----------------------------------------------------------------------------
  806. // This methods deletes all of the internal category representations
  807. // and empties the map.
  808. //-----------------------------------------------------------------------------
  809. void CDataGatherer::RemoveAllCategories()
  810. {
  811. INTERNAL_CATEGORY * pInternalCat;
  812. WORD key;
  813. ASSERT(m_fInitialized);
  814. if (!m_fInitialized)
  815. return;
  816. for (POSITION pos = m_mapCategories.GetStartPosition(); pos != NULL;)
  817. {
  818. m_mapCategories.GetNextAssoc(pos, key, (void * &) pInternalCat);
  819. ASSERT(pInternalCat);
  820. if (pInternalCat)
  821. delete pInternalCat;
  822. }
  823. m_mapCategories.RemoveAll();
  824. // Reset the root ID. Don't reset the next free ID, because we don't want
  825. // the IDs to overlap during the lifetime of the object (otherwise some
  826. // categories passed out might refer to the wrong internal category).
  827. m_dwRootID = 0;
  828. }
  829. //-----------------------------------------------------------------------------
  830. // This method is used to return a pointer to the internal representation of
  831. // the category.
  832. //-----------------------------------------------------------------------------
  833. INTERNAL_CATEGORY * CDataGatherer::GetInternalRep(DWORD dwID)
  834. {
  835. INTERNAL_CATEGORY * pInternalCat;
  836. ASSERT(m_fInitialized);
  837. if (!m_fInitialized)
  838. return NULL;
  839. if (m_mapCategories.Lookup((WORD) dwID, (void * &) pInternalCat))
  840. {
  841. ASSERT(pInternalCat && pInternalCat->m_dwID == dwID);
  842. return pInternalCat;
  843. }
  844. else
  845. {
  846. ASSERT(FALSE);
  847. return NULL;
  848. }
  849. }
  850. //-----------------------------------------------------------------------------
  851. // Return the CDataProvider object (well, a pointer to the object). If there
  852. // isn't one yet, then create one.
  853. //-----------------------------------------------------------------------------
  854. CDataProvider * CDataGatherer::GetProvider()
  855. {
  856. ASSERT(m_fInitialized);
  857. if (!m_fInitialized)
  858. return NULL;
  859. if (m_pProvider == NULL)
  860. {
  861. // If the m_pProvider member is NULL, then we need to create a new one.
  862. // Create the provider for the value stored in the deferred provider. If
  863. // it is set, then we are doing the deferred creation of a provider for
  864. // another machine. If it's empty, we're looking at the local machine.
  865. // If the Create fails, it will call SetLastError saying why.
  866. m_pProvider = new CDataProvider;
  867. if (m_pProvider == NULL)
  868. SetLastError(GATH_ERR_ALLOCATIONFAILED);
  869. if (!m_pProvider->Create(m_strDeferredProvider, this))
  870. {
  871. delete m_pProvider;
  872. m_pProvider = NULL;
  873. }
  874. }
  875. return m_pProvider;
  876. }
  877. //-----------------------------------------------------------------------------
  878. // This method resets the internal refreshed flag. It's called if the provider
  879. // is pointed to a different computer, for example, to make each category get
  880. // fresh data to display.
  881. //-----------------------------------------------------------------------------
  882. void CDataGatherer::ResetCategoryRefresh()
  883. {
  884. INTERNAL_CATEGORY * pInternalCat;
  885. WORD key;
  886. for (POSITION pos = m_mapCategories.GetStartPosition(); pos != NULL;)
  887. {
  888. m_mapCategories.GetNextAssoc(pos, key, (void * &) pInternalCat);
  889. ASSERT(pInternalCat);
  890. if (pInternalCat)
  891. pInternalCat->m_fRefreshed = FALSE;
  892. }
  893. }
  894. //-----------------------------------------------------------------------------
  895. // This method is used to refresh the contents of the internal category struct.
  896. // Doing this means refreshing the category name, its columns, and building
  897. // a list of lines based on the list of line specifiers in the category.
  898. //-----------------------------------------------------------------------------
  899. BOOL CDataGatherer::RefreshCategory(DWORD dwID, BOOL fRecursive, volatile BOOL *pfCancel, BOOL fSoftRefresh)
  900. {
  901. INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
  902. ASSERT(m_fInitialized);
  903. if (!m_fInitialized)
  904. return FALSE;
  905. if (pInternal == NULL)
  906. return FALSE;
  907. // Check to see if the refresh operation has been cancelled.
  908. if (pfCancel && *pfCancel == TRUE)
  909. {
  910. TRACE0("-- CDataGatherer::RefreshCategory() refresh cancelled by caller\n");
  911. return FALSE;
  912. }
  913. // If this is the first call to this recursive function, reset the
  914. // cache of WBEM enumerator pointers.
  915. if (m_cInRefresh == 0 && m_pProvider)
  916. m_pProvider->m_enumMap.Reset();
  917. m_cInRefresh++;
  918. if (m_pProvider)
  919. m_pProvider->m_dwRefreshingCategoryID = dwID;
  920. // Remove the cached items in the CDataProvider object.
  921. if (m_pProvider)
  922. m_pProvider->ClearCache();
  923. // If this is a soft refresh, and this category has been refreshed at least once,
  924. // skip the refresh operation. An example of a soft refresh would be the user
  925. // clicking on the category for the first time, where we would want to skip the
  926. // refresh if a global refresh had been done previously.
  927. if (!pInternal->m_fRefreshed || !fSoftRefresh)
  928. {
  929. if (!CRefreshFunctions::RefreshColumns(this, pInternal))
  930. {
  931. TRACE0("-- CDataGatherer::RefreshCategory() failed at RefreshColumns\n");
  932. m_cInRefresh--;
  933. if (m_pProvider)
  934. m_pProvider->m_dwRefreshingCategoryID = 0;
  935. return FALSE; // RefreshValue will set last error
  936. }
  937. // The RefreshLines function returns a CPtrList of pointers to line structures. These
  938. // pointers need to be copied to the pInternal->m_apLines array.
  939. CPtrList listLinePtrs;
  940. if (CRefreshFunctions::RefreshLines(this, pInternal->m_pLineSpec, pInternal->m_dwColCount, listLinePtrs, pfCancel))
  941. {
  942. if (pInternal->m_apLines && pInternal->m_dwLineCount)
  943. {
  944. for (DWORD dwIndex = 0; dwIndex < pInternal->m_dwLineCount; dwIndex++)
  945. delete pInternal->m_apLines[dwIndex];
  946. delete [] pInternal->m_apLines;
  947. }
  948. // Move the contents of listLinePtrs to the array of line pointers in the internal struct.
  949. pInternal->m_dwLineCount = (DWORD) listLinePtrs.GetCount();
  950. if (pInternal->m_dwLineCount)
  951. {
  952. pInternal->m_apLines = new GATH_LINE *[pInternal->m_dwLineCount];
  953. if (pInternal->m_apLines)
  954. {
  955. DWORD dwIndex = 0;
  956. for (POSITION pos = listLinePtrs.GetHeadPosition(); pos != NULL;)
  957. {
  958. ASSERT(dwIndex < (DWORD) listLinePtrs.GetCount());
  959. pInternal->m_apLines[dwIndex] = (GATH_LINE *) listLinePtrs.GetNext(pos);
  960. dwIndex++;
  961. }
  962. }
  963. else
  964. {
  965. // If there was an error, we need to deallocate the lines.
  966. GATH_LINE * pLine;
  967. for (POSITION pos = listLinePtrs.GetHeadPosition(); pos != NULL;)
  968. {
  969. pLine = (GATH_LINE *) listLinePtrs.GetNext(pos) ;
  970. if (pLine)
  971. delete pLine;
  972. }
  973. TRACE0("-- CDataGatherer::RefreshCategory() failed allocating m_apLines\n");
  974. SetLastError(GATH_ERR_ALLOCATIONFAILED);
  975. m_cInRefresh--;
  976. if (m_pProvider)
  977. m_pProvider->m_dwRefreshingCategoryID = 0;
  978. return FALSE;
  979. }
  980. }
  981. pInternal->m_fRefreshed = TRUE;
  982. }
  983. else
  984. {
  985. TRACE0("-- CDataGatherer::RefreshCategory() failed at RefreshLines\n");
  986. m_cInRefresh--;
  987. if (m_pProvider)
  988. m_pProvider->m_dwRefreshingCategoryID = 0;
  989. return FALSE; // RefreshLines will set last error
  990. }
  991. }
  992. if (fRecursive)
  993. {
  994. INTERNAL_CATEGORY * pChild;
  995. DWORD dwChildID = pInternal->m_dwChildID;
  996. while (dwChildID)
  997. {
  998. if (!RefreshCategory(dwChildID, TRUE, pfCancel))
  999. {
  1000. m_cInRefresh--;
  1001. if (m_pProvider)
  1002. m_pProvider->m_dwRefreshingCategoryID = 0;
  1003. return FALSE;
  1004. }
  1005. pChild = GetInternalRep(dwChildID);
  1006. if (pChild)
  1007. dwChildID = pChild->m_dwNextID;
  1008. else
  1009. break;
  1010. }
  1011. }
  1012. else
  1013. {
  1014. // Even if we aren't recursive, we should refresh the names of the sub
  1015. // categories, since they might be enumerated before they are refreshed.
  1016. INTERNAL_CATEGORY * pChild;
  1017. DWORD dwChildID = pInternal->m_dwChildID;
  1018. while (dwChildID)
  1019. {
  1020. pChild = GetInternalRep(dwChildID);
  1021. if (pChild)
  1022. {
  1023. if (!CRefreshFunctions::RefreshValue(this, &pChild->m_categoryName, &pChild->m_fieldName))
  1024. {
  1025. m_cInRefresh--;
  1026. if (m_pProvider)
  1027. m_pProvider->m_dwRefreshingCategoryID = 0;
  1028. return FALSE;
  1029. }
  1030. dwChildID = pChild->m_dwNextID;
  1031. }
  1032. else
  1033. break;
  1034. }
  1035. }
  1036. m_cInRefresh--;
  1037. if (m_pProvider)
  1038. m_pProvider->m_dwRefreshingCategoryID = 0;
  1039. return TRUE;
  1040. }
  1041. //-----------------------------------------------------------------------------
  1042. // Sets the last error (the value returned by GetLastError) to the specified
  1043. // DWORD value.
  1044. //-----------------------------------------------------------------------------
  1045. void CDataGatherer::SetLastError(DWORD dwError)
  1046. {
  1047. // Making a change - disable all of the error reset calls (a few will
  1048. // be done explicitly.
  1049. if (dwError != GATH_ERR_NOERROR)
  1050. m_dwLastError = dwError;
  1051. #ifdef _DEBUG
  1052. if (dwError)
  1053. TRACE1("-- SetLastError(0x%08x)\n", dwError);
  1054. #endif
  1055. }
  1056. void CDataGatherer::SetLastError(DWORD dwError, DWORD dwID)
  1057. {
  1058. if (dwError != GATH_ERR_NOERROR)
  1059. {
  1060. INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
  1061. ASSERT(pInternal);
  1062. if (pInternal)
  1063. pInternal->m_dwLastError = dwError;
  1064. }
  1065. #ifdef _DEBUG
  1066. if (dwError)
  1067. TRACE2("-- SetLastError(0x%08x, %d)\n", dwError, dwID);
  1068. #endif
  1069. }
  1070. //-----------------------------------------------------------------------------
  1071. // Sets the error flag based on an HRESULT returned value.
  1072. //-----------------------------------------------------------------------------
  1073. void CDataGatherer::SetLastErrorHR(HRESULT hrError)
  1074. {
  1075. DWORD dwError;
  1076. switch (hrError)
  1077. {
  1078. case WBEM_E_OUT_OF_MEMORY:
  1079. dwError = GATH_ERR_NOWBEMOUTOFMEM;
  1080. break;
  1081. case WBEM_E_ACCESS_DENIED:
  1082. dwError = GATH_ERR_NOWBEMACCESSDENIED;
  1083. break;
  1084. case WBEM_E_INVALID_NAMESPACE:
  1085. dwError = GATH_ERR_NOWBEMBADSERVER;
  1086. break;
  1087. case WBEM_E_TRANSPORT_FAILURE:
  1088. dwError = GATH_ERR_NOWBEMNETWORKFAILURE;
  1089. break;
  1090. case WBEM_E_FAILED:
  1091. case WBEM_E_INVALID_PARAMETER:
  1092. default:
  1093. dwError = GATH_ERR_NOWBEMCONNECT;
  1094. }
  1095. SetLastError(dwError);
  1096. }
  1097. void CDataGatherer::SetLastErrorHR(HRESULT hrError, DWORD dwID)
  1098. {
  1099. DWORD dwError;
  1100. switch (hrError)
  1101. {
  1102. case WBEM_E_OUT_OF_MEMORY:
  1103. dwError = GATH_ERR_NOWBEMOUTOFMEM;
  1104. break;
  1105. case WBEM_E_ACCESS_DENIED:
  1106. dwError = GATH_ERR_NOWBEMACCESSDENIED;
  1107. break;
  1108. case WBEM_E_INVALID_NAMESPACE:
  1109. dwError = GATH_ERR_NOWBEMBADSERVER;
  1110. break;
  1111. case WBEM_E_TRANSPORT_FAILURE:
  1112. dwError = GATH_ERR_NOWBEMNETWORKFAILURE;
  1113. break;
  1114. case WBEM_E_FAILED:
  1115. case WBEM_E_INVALID_PARAMETER:
  1116. default:
  1117. dwError = GATH_ERR_NOWBEMCONNECT;
  1118. }
  1119. SetLastError(dwError, dwID);
  1120. }
  1121. //-----------------------------------------------------------------------------
  1122. // Resets the error flag to a no error state (added because SetLastError no
  1123. // longer allows this).
  1124. //-----------------------------------------------------------------------------
  1125. void CDataGatherer::ResetLastError()
  1126. {
  1127. m_dwLastError = GATH_ERR_NOERROR;
  1128. }
  1129. //-----------------------------------------------------------------------------
  1130. // Return a text representation of the error for display.
  1131. //-----------------------------------------------------------------------------
  1132. CString CDataGatherer::GetErrorText()
  1133. {
  1134. CString strErrorText(_T(""));
  1135. CString strMachine(m_strDeferredProvider);
  1136. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  1137. switch (m_dwLastError)
  1138. {
  1139. case GATH_ERR_ALLOCATIONFAILED:
  1140. case GATH_ERR_NOWBEMOUTOFMEM:
  1141. strErrorText.LoadString(IDS_OUTOFMEMERROR);
  1142. break;
  1143. case GATH_ERR_NOWBEMLOCATOR:
  1144. strErrorText.LoadString(IDS_NOLOCATOR);
  1145. break;
  1146. case GATH_ERR_NOWBEMCONNECT:
  1147. strErrorText.Format(IDS_NOGATHERER, strMachine);
  1148. break;
  1149. case GATH_ERR_NOWBEMACCESSDENIED:
  1150. strErrorText.Format(IDS_GATHERACCESS, strMachine);
  1151. break;
  1152. case GATH_ERR_NOWBEMBADSERVER:
  1153. strErrorText.Format(IDS_BADSERVER, strMachine);
  1154. break;
  1155. case GATH_ERR_NOWBEMNETWORKFAILURE:
  1156. strErrorText.Format(IDS_NETWORKERROR, strMachine);
  1157. break;
  1158. default:
  1159. case GATH_ERR_BADCATEGORYID:
  1160. strErrorText.LoadString(IDS_UNEXPECTED);
  1161. break;
  1162. }
  1163. return strErrorText;
  1164. }
  1165. CString CDataGatherer::GetErrorText(DWORD dwID)
  1166. {
  1167. CString strErrorText(_T(""));
  1168. CString strMachine(m_strDeferredProvider);
  1169. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  1170. INTERNAL_CATEGORY * pInternal = GetInternalRep(dwID);
  1171. ASSERT(pInternal);
  1172. if (pInternal)
  1173. {
  1174. switch (pInternal->m_dwLastError)
  1175. {
  1176. case GATH_ERR_ALLOCATIONFAILED:
  1177. case GATH_ERR_NOWBEMOUTOFMEM:
  1178. strErrorText.LoadString(IDS_OUTOFMEMERROR);
  1179. break;
  1180. case GATH_ERR_NOWBEMLOCATOR:
  1181. strErrorText.LoadString(IDS_NOLOCATOR);
  1182. break;
  1183. case GATH_ERR_NOWBEMCONNECT:
  1184. strErrorText.Format(IDS_NOGATHERER, strMachine);
  1185. break;
  1186. case GATH_ERR_NOWBEMACCESSDENIED:
  1187. strErrorText.Format(IDS_GATHERACCESS, strMachine);
  1188. break;
  1189. case GATH_ERR_NOWBEMBADSERVER:
  1190. strErrorText.Format(IDS_BADSERVER, strMachine);
  1191. break;
  1192. case GATH_ERR_NOWBEMNETWORKFAILURE:
  1193. strErrorText.Format(IDS_NETWORKERROR, strMachine);
  1194. break;
  1195. default:
  1196. case GATH_ERR_BADCATEGORYID:
  1197. strErrorText.LoadString(IDS_UNEXPECTED);
  1198. break;
  1199. }
  1200. }
  1201. return strErrorText;
  1202. }
  1203. //-----------------------------------------------------------------------------
  1204. // This method is used to construct a CDataCategory object for the passed ID.
  1205. // The caller is responsible for ultimately deallocating the object. We use
  1206. // the m_mapCategories to retrieve an internal representation of the category,
  1207. // construct a CDataCategory object and set it up to refer the this category.
  1208. //-----------------------------------------------------------------------------
  1209. CDataCategory * CDataGatherer::BuildDataCategory(DWORD dwID)
  1210. {
  1211. CDataCategory * pReturnCategory;
  1212. INTERNAL_CATEGORY * pInternalCat;
  1213. ASSERT(m_fInitialized);
  1214. ASSERT(dwID != 0);
  1215. SetLastError(GATH_ERR_NOERROR);
  1216. if (!m_fInitialized)
  1217. {
  1218. SetLastError(GATH_ERR_NOTINITIALIZED);
  1219. return FALSE;
  1220. }
  1221. // First, try to look up an internal category representation of the category.
  1222. if (!m_mapCategories.Lookup((WORD) dwID, (void * &) pInternalCat))
  1223. {
  1224. SetLastError(GATH_ERR_BADCATEGORYID);
  1225. return NULL;
  1226. }
  1227. ASSERT(pInternalCat);
  1228. if (pInternalCat == NULL)
  1229. return NULL; // might be that this category was hidden
  1230. // Create the object to return (either a CDataCategory or CDataListCategory,
  1231. // depending on the information in pInternalCat).
  1232. if (pInternalCat->m_fListView)
  1233. pReturnCategory = (CDataCategory *) new CDataListCategory;
  1234. else
  1235. pReturnCategory = new CDataCategory;
  1236. if (pReturnCategory == NULL)
  1237. {
  1238. SetLastError(GATH_ERR_ALLOCATIONFAILED);
  1239. return NULL;
  1240. }
  1241. // All the external category theoretically needs is a pointer to this object
  1242. // and its ID number.
  1243. pReturnCategory->m_pGatherer = this;
  1244. pReturnCategory->m_dwID = dwID;
  1245. return pReturnCategory;
  1246. }
  1247. //-----------------------------------------------------------------------------
  1248. // Recursive method used internally to find a category path based on a
  1249. // category identifier.
  1250. //-----------------------------------------------------------------------------
  1251. BOOL CDataGatherer::FindCategoryByIdentifer(const CString & strIdentifier, CString & strPath, DWORD dwID)
  1252. {
  1253. INTERNAL_CATEGORY * pInternalCat;
  1254. // Look up the internal representation for the specified category.
  1255. pInternalCat = GetInternalRep(dwID);
  1256. // If this category is the one we're looking for, add the name to the
  1257. // path variable and return true.
  1258. if (strIdentifier.CompareNoCase(pInternalCat->m_strIdentifier) == 0)
  1259. {
  1260. if (!strPath.IsEmpty())
  1261. strPath += CString(_T("\\"));
  1262. strPath += pInternalCat->m_categoryName.m_strText;
  1263. return TRUE;
  1264. }
  1265. // Otherwise, look through the children.
  1266. DWORD dwChildID = pInternalCat->m_dwChildID;
  1267. while (dwChildID)
  1268. {
  1269. if (FindCategoryByIdentifer(strIdentifier, strPath, dwChildID))
  1270. return TRUE;
  1271. dwChildID = pInternalCat->m_dwNextID;
  1272. }
  1273. return FALSE;
  1274. }
  1275. //-----------------------------------------------------------------------------
  1276. // This method is used to convert a string path (category names, starting at
  1277. // the root category, delimited by backslashes) into the ID for the category.
  1278. //-----------------------------------------------------------------------------
  1279. DWORD CDataGatherer::FindCategoryByPath(const CString & strPath)
  1280. {
  1281. INTERNAL_CATEGORY * pInternalCat;
  1282. CString strWorkingPath(strPath), strNextCategory;
  1283. DWORD dwID = 0, dwSearchID, dwCurrentID = 0;
  1284. while (!strWorkingPath.IsEmpty())
  1285. {
  1286. GetToken(strNextCategory, strWorkingPath, _T('\\'));
  1287. // Look for the child of the current category to match the name. If the
  1288. // current category ID is zero, make sure the root category name matches.
  1289. if (dwCurrentID == 0)
  1290. {
  1291. pInternalCat = GetInternalRep(m_dwRootID);
  1292. if (pInternalCat == NULL || pInternalCat->m_categoryName.m_strText.CompareNoCase(strNextCategory))
  1293. return 0;
  1294. dwCurrentID = m_dwRootID;
  1295. }
  1296. else
  1297. {
  1298. // Start looking through the children of the current node.
  1299. ASSERT(pInternalCat && pInternalCat->m_dwID == dwCurrentID);
  1300. dwSearchID = pInternalCat->m_dwChildID;
  1301. while (dwSearchID)
  1302. {
  1303. pInternalCat = GetInternalRep(dwSearchID);
  1304. if (pInternalCat == NULL)
  1305. return 0;
  1306. if (pInternalCat->m_categoryName.m_strText.CompareNoCase(strNextCategory) == 0)
  1307. break;
  1308. dwSearchID = pInternalCat->m_dwNextID;
  1309. }
  1310. if (dwSearchID == 0)
  1311. return 0;
  1312. else
  1313. dwCurrentID = dwSearchID;
  1314. }
  1315. }
  1316. return dwCurrentID;
  1317. }
  1318. //-----------------------------------------------------------------------------
  1319. // This method searches the specified category and all of it's children
  1320. // for a string. It starts from the iLineth line. If a match is found, the
  1321. // line number and path to the category where the match was made are set,
  1322. // and we return TRUE.
  1323. //-----------------------------------------------------------------------------
  1324. BOOL CDataGatherer::RecursiveFind(INTERNAL_CATEGORY * pCat, MSI_FIND_STRUCT *pFind, int & iLine, CString & strPath)
  1325. {
  1326. ASSERT(pCat);
  1327. ASSERT(pFind);
  1328. // Look through the lines in the current category for a match with strSearch.
  1329. // Note: only look through lines and columns with the appropriate complexity
  1330. // (BASIC or ADVANCED). The line number should take this into account as well.
  1331. int iResultLine = 0, iCurrentLine = 0;
  1332. CString strValue;
  1333. // A line number of -1 indicates that we should check the category name.
  1334. if (iLine == -1)
  1335. {
  1336. strValue = pCat->m_categoryName.m_strText;
  1337. if (!pFind->m_fCaseSensitive)
  1338. strValue.MakeUpper();
  1339. if (strValue.Find(pFind->m_strSearch) != -1)
  1340. {
  1341. GetCategoryPath(pCat, strPath);
  1342. return TRUE;
  1343. }
  1344. iLine = 0;
  1345. }
  1346. // Otherwise, look through lines for this category.
  1347. if (pFind->m_fSearchData && pCat->m_fListView)
  1348. {
  1349. // We need to search the message which is displayed when there is no
  1350. // data as well (searching a saved file does this).
  1351. if (pCat->m_dwLineCount == 0)
  1352. {
  1353. if (iLine == 0)
  1354. {
  1355. strValue = pCat->m_strNoInstances;
  1356. if (!pFind->m_fCaseSensitive)
  1357. strValue.MakeUpper();
  1358. if (strValue.Find(pFind->m_strSearch) != -1)
  1359. {
  1360. iLine = 0;
  1361. GetCategoryPath(pCat, strPath);
  1362. return TRUE;
  1363. }
  1364. }
  1365. }
  1366. else
  1367. {
  1368. // This category does have data - search through it.
  1369. while (iCurrentLine < (int)pCat->m_dwLineCount)
  1370. {
  1371. // Check to see if the Find has been cancelled.
  1372. if (pFind->m_pfCancel && *pFind->m_pfCancel)
  1373. return FALSE;
  1374. if (m_complexity == ADVANCED || pCat->m_apLines[iCurrentLine]->m_datacomplexity == BASIC)
  1375. {
  1376. // Search through the columns of data for a match. Start looking only after
  1377. // we've skipped any lines indicated by the iLine parameter.
  1378. if (iResultLine >= iLine)
  1379. {
  1380. GATH_FIELD * pCol = pCat->m_pColSpec;
  1381. int iCurrentCol = 0;
  1382. while (pCol)
  1383. {
  1384. if (m_complexity == ADVANCED || pCol->m_datacomplexity == BASIC)
  1385. {
  1386. strValue = pCat->m_apLines[iCurrentLine]->m_aValue[iCurrentCol].m_strText;
  1387. if (!pFind->m_fCaseSensitive)
  1388. strValue.MakeUpper();
  1389. if (strValue.Find(pFind->m_strSearch) != -1)
  1390. {
  1391. iLine = iResultLine;
  1392. GetCategoryPath(pCat, strPath);
  1393. return TRUE;
  1394. }
  1395. }
  1396. pCol = pCol->m_pNext;
  1397. iCurrentCol++;
  1398. }
  1399. }
  1400. iResultLine++;
  1401. }
  1402. iCurrentLine++;
  1403. }
  1404. }
  1405. }
  1406. // No matches were found. Search through the children of this category for a
  1407. // match. Create a temporary path and line variable to pass to the children.
  1408. CString strTempPath;
  1409. int iTempLine;
  1410. INTERNAL_CATEGORY * pChildCat;
  1411. DWORD dwChildID = pCat->m_dwChildID;
  1412. while (dwChildID)
  1413. {
  1414. pChildCat = GetInternalRep(dwChildID);
  1415. if (pChildCat)
  1416. {
  1417. iTempLine = (pFind->m_fSearchCategories) ? -1 : 0;
  1418. if (RecursiveFind(pChildCat, pFind, iTempLine, strTempPath))
  1419. {
  1420. strPath = strTempPath;
  1421. iLine = iTempLine;
  1422. return TRUE;
  1423. }
  1424. dwChildID = pChildCat->m_dwNextID;
  1425. }
  1426. else
  1427. dwChildID = 0;
  1428. }
  1429. return FALSE;
  1430. }
  1431. //-----------------------------------------------------------------------------
  1432. // Return the path of category names to get from the root category to this
  1433. // category, separated by backslashes.
  1434. //-----------------------------------------------------------------------------
  1435. void CDataGatherer::GetCategoryPath(INTERNAL_CATEGORY * pCat, CString & strPath)
  1436. {
  1437. INTERNAL_CATEGORY * pParent;
  1438. CString strWorking;
  1439. pParent = pCat;
  1440. while (pParent)
  1441. {
  1442. if (!strWorking.IsEmpty())
  1443. strWorking = CString(_T("\\")) + strWorking;
  1444. strWorking = pParent->m_categoryName.m_strText + strWorking;
  1445. if (pParent->m_dwParentID)
  1446. pParent = GetInternalRep(pParent->m_dwParentID);
  1447. else
  1448. break;
  1449. }
  1450. ASSERT(pParent); // if not set we tried to get a ptr for a bad ID, or bad param
  1451. strPath = strWorking;
  1452. }
  1453. //-----------------------------------------------------------------------------
  1454. // Determine is the passed category pointer is a child category of the passed
  1455. // category path. Do this by using the pointer to get a category path, and
  1456. // doing a string search for the parent (the parent path will be in any child's
  1457. // path).
  1458. //-----------------------------------------------------------------------------
  1459. BOOL CDataGatherer::IsChildPath(INTERNAL_CATEGORY * pInternalCat, const CString & strParentPath)
  1460. {
  1461. CString strParent, strChild;
  1462. GetCategoryPath(pInternalCat, strChild);
  1463. strParent = strParentPath;
  1464. strParent.MakeUpper();
  1465. strChild.MakeUpper();
  1466. return (strChild.Find(strParent) != -1 && strChild.Compare(strParent) != 0);
  1467. }
  1468. //-----------------------------------------------------------------------------
  1469. // Dump out the contents of the CDataGatherer object (DEBUG only).
  1470. //-----------------------------------------------------------------------------
  1471. #ifdef _DEBUG
  1472. void CDataGatherer::Dump()
  1473. {
  1474. ASSERT(m_fInitialized);
  1475. if (!m_fInitialized)
  1476. return;
  1477. TRACE0("Dumping CDataGatherer object...\n");
  1478. TRACE1(" m_fInitialized = %s\n", (m_fInitialized ? "TRUE" : "FALSE"));
  1479. TRACE1(" m_dwNextFreeID = %ld\n", m_dwNextFreeID);
  1480. TRACE1(" m_dwRootID = %ld\n", m_dwRootID);
  1481. INTERNAL_CATEGORY * pCat = GetInternalRep(m_dwRootID);
  1482. if (pCat)
  1483. pCat->DumpCategoryRecursive(4, this);
  1484. }
  1485. #endif