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.

3444 lines
96 KiB

  1. //=============================================================================
  2. // Code for loading and refreshing version 5.0 extensions.
  3. //=============================================================================
  4. #include "stdafx.h"
  5. #include "category.h"
  6. #include "dataset.h"
  7. #include "wmiabstraction.h"
  8. #include "version5extension.h"
  9. CMapExtensionRefreshData gmapExtensionRefreshData;
  10. extern HRESULT ChangeWBEMSecurity(IUnknown * pUnknown);
  11. //-----------------------------------------------------------------------------
  12. // Load the specified template from the named extension. This will involve
  13. // loading the DLL and using the entry point to retrieve the text for the
  14. // extension's template.
  15. //
  16. // Once the data is loaded, it's parsed into version 5 format structures.
  17. //-----------------------------------------------------------------------------
  18. typedef DWORD (__cdecl *pfuncGetTemplate)(void ** ppBuffer);
  19. DWORD CTemplateFileFunctions::ParseTemplateIntoVersion5Categories(const CString & strExtension, CMapWordToPtr & mapVersion5Categories)
  20. {
  21. DWORD dwRootID = 0;
  22. HINSTANCE hinst = LoadLibrary(strExtension);
  23. if (hinst == NULL)
  24. return dwRootID;
  25. pfuncGetTemplate pfunc = (pfuncGetTemplate) GetProcAddress(hinst, "GetTemplate");
  26. if (pfunc == NULL)
  27. {
  28. FreeLibrary(hinst);
  29. return dwRootID;
  30. }
  31. // Call the DLL function with a NULL parameter to get the size of the buffer.
  32. void * pBuffer;
  33. CMemFile memfile;
  34. DWORD dwBufferSize = (*pfunc)((void **)&pBuffer);
  35. if (dwBufferSize && pBuffer)
  36. {
  37. memfile.Attach((BYTE *)pBuffer, dwBufferSize, 0);
  38. dwRootID = ReadTemplateFile(&memfile, mapVersion5Categories);
  39. memfile.Detach();
  40. (void)(*pfunc)(NULL); // calling the exported DLL function with NULL frees its buffers
  41. }
  42. if (hinst != NULL)
  43. FreeLibrary(hinst);
  44. return dwRootID;
  45. }
  46. //-----------------------------------------------------------------------------
  47. // This function reads the contents of a template file (in this case, a memory
  48. // file) and produces a map of ID, INTERNAL_CATEGORY pointer pairs. It returns
  49. // the ID for the root node in the tree.
  50. //-----------------------------------------------------------------------------
  51. DWORD CTemplateFileFunctions::ReadTemplateFile(CFile * pFile, CMapWordToPtr & mapVersion5Categories)
  52. {
  53. ASSERT(pFile);
  54. if (pFile == NULL || !VerifyUNICODEFile(pFile) || !ReadHeaderInfo(pFile))
  55. return 0;
  56. return (ReadNodeRecursive(pFile, mapVersion5Categories, 0, 0));
  57. }
  58. //-----------------------------------------------------------------------------
  59. // Make sure this is an MSInfo template file.
  60. //-----------------------------------------------------------------------------
  61. BOOL CTemplateFileFunctions::ReadHeaderInfo(CFile * pFile)
  62. {
  63. return VerifyAndAdvanceFile(pFile, CString(_T(TEMPLATE_FILE_TAG)));
  64. }
  65. //-----------------------------------------------------------------------------
  66. // This method verifies that the passed file is a UNICODE file, by reading the
  67. // value 0xFEFF from the file. It also leaves the file pointer past this word.
  68. //-----------------------------------------------------------------------------
  69. BOOL CTemplateFileFunctions::VerifyUNICODEFile(CFile * pFile)
  70. {
  71. WORD verify;
  72. if (pFile->Read((void *) &verify, sizeof(WORD)) != sizeof(WORD))
  73. return FALSE;
  74. return (verify == 0xFEFF);
  75. }
  76. //-----------------------------------------------------------------------------
  77. // This method verifies that the text in strVerify comes next in the file (not
  78. // including case or whitespace differences) and advances the file past that
  79. // text. If the text was the next content in the file, TRUE is returned,
  80. // otherwise FALSE. If FALSE is returned, the file is backed up to where it
  81. // was when this method was called.
  82. //-----------------------------------------------------------------------------
  83. BOOL CTemplateFileFunctions::VerifyAndAdvanceFile(CFile * pFile, const CString & strVerify)
  84. {
  85. DWORD dwPosition = pFile->GetPosition();
  86. WCHAR cLastChar, cCurrentChar = L'\0';
  87. BOOL fInComment = FALSE;
  88. int iCharIndex = 0, iStringLen = strVerify.GetLength();
  89. while (iCharIndex < iStringLen)
  90. {
  91. // Save the last character read, since the comment token ("//") is
  92. // two characters long.
  93. cLastChar = cCurrentChar;
  94. // Read the next character in the file.
  95. if (pFile->Read((void *) &cCurrentChar, sizeof(WCHAR)) != sizeof(WCHAR))
  96. return FALSE;
  97. // If we're in a comment, and the character we just read isn't a new line,
  98. // we want to ignore it.
  99. if (fInComment)
  100. {
  101. if (cCurrentChar == L'\n')
  102. fInComment = FALSE;
  103. continue;
  104. }
  105. // Check to see if we've started into a comment. Note that we ignore
  106. // the first '/' also by continuing.
  107. if (cCurrentChar == L'/')
  108. {
  109. if (cLastChar == L'/')
  110. fInComment = TRUE;
  111. continue;
  112. }
  113. // Skip whitespace, and also leading commas.
  114. if (iswspace(cCurrentChar) || (cCurrentChar == L',' && iCharIndex == 0))
  115. continue;
  116. if (cCurrentChar != (WCHAR)strVerify[iCharIndex])
  117. {
  118. pFile->Seek((LONG)dwPosition, CFile::begin);
  119. return FALSE;
  120. }
  121. iCharIndex++;
  122. }
  123. return TRUE;
  124. }
  125. //-----------------------------------------------------------------------------
  126. // This is the recursive function to read a node. It reads the information
  127. // from the node parameters, creates the node, and processes the contents of
  128. // the block following the node (contained within "{}"'s). It's called
  129. // recursively if there are any nodes in that block.
  130. //
  131. // In this version (for 6.0), it returns the ID of the node it's read.
  132. //-----------------------------------------------------------------------------
  133. DWORD CTemplateFileFunctions::ReadNodeRecursive(CFile * pFile, CMapWordToPtr & mapCategories, DWORD dwParentID, DWORD dwPrevID)
  134. {
  135. // Determine if we need to create a new category for this node. Read the
  136. // information from the file to determine the identifier for the new category.
  137. CString strEnumerateClass, strIdentifier;
  138. if (!VerifyAndAdvanceFile(pFile, CString(NODE_KEYWORD) + CString("(")))
  139. return 0;
  140. if (!ReadArgument(pFile, strEnumerateClass))
  141. return 0;
  142. if (!ReadArgument(pFile, strIdentifier))
  143. return 0;
  144. // Generate the ID for this new node. This should be one greater than the max in the
  145. // map (or one, if the map is empty).
  146. DWORD dwID = 0;
  147. WORD wMapID;
  148. void * pDontCare;
  149. for (POSITION pos = mapCategories.GetStartPosition(); pos != NULL;)
  150. {
  151. mapCategories.GetNextAssoc(pos, wMapID, pDontCare);
  152. if ((DWORD) wMapID > dwID)
  153. dwID = (DWORD) wMapID;
  154. }
  155. dwID += 1;
  156. // Create the category for the node.
  157. INTERNAL_CATEGORY * pCategory = CreateCategory(mapCategories, dwID, dwParentID, dwPrevID);
  158. // Read the contents of the node argument list ("node(enum, identifier, field(source, formatstr, arg...))")
  159. // We've already read up to and including the identifier.
  160. pCategory->m_strEnumerateClass = strEnumerateClass;
  161. pCategory->m_strIdentifier = strIdentifier;
  162. if (!ReadField(pFile, pCategory->m_fieldName))
  163. return 0;
  164. // Copy the field name to the name of the category (they are two different
  165. // member variables to allow for dynamically refreshed names, which turns
  166. // out to be unnecessary in this version).
  167. pCategory->m_categoryName.m_strText = pCategory->m_fieldName.m_strFormat;
  168. if (!ReadArgument(pFile, pCategory->m_strNoInstances))
  169. return 0;
  170. if (!VerifyAndAdvanceFile(pFile, CString("){")))
  171. return 0;
  172. // Process the contents of the block (enclosed in "{}") for this node.
  173. DWORD dwSubNodePrev = 0, dwNewNode = 0;
  174. CString strKeyword;
  175. // If this new category isn't actually new (i.e. it is being read from a
  176. // template and overlaps an existing category) see if there are any
  177. // existing children.
  178. //
  179. // Version 6.0: these are being read into distinct trees, so there should
  180. // be no overlap (it would be resolved later).
  181. while (GetKeyword(pFile, strKeyword))
  182. {
  183. if (strKeyword.CompareNoCase(CString(NODE_KEYWORD)) == 0)
  184. {
  185. dwNewNode = ReadNodeRecursive(pFile, mapCategories, dwID, dwSubNodePrev);
  186. if (dwNewNode == 0)
  187. return 0;
  188. // If this is the first child node we've read, save its ID.
  189. if (pCategory->m_dwChildID == 0)
  190. pCategory->m_dwChildID = dwNewNode;
  191. // If we've read another child node, set its next field appropriately.
  192. if (dwSubNodePrev)
  193. {
  194. INTERNAL_CATEGORY * pPrevCategory = GetInternalRep(mapCategories, dwSubNodePrev);
  195. if (pPrevCategory)
  196. pPrevCategory->m_dwNextID = dwNewNode;
  197. }
  198. dwSubNodePrev = dwNewNode;
  199. }
  200. else if (strKeyword.CompareNoCase(CString(COLUMN_KEYWORD)) == 0)
  201. {
  202. if (!ReadColumnInfo(pFile, mapCategories, dwID))
  203. return 0;
  204. }
  205. else if (strKeyword.CompareNoCase(CString(LINE_KEYWORD)) == 0)
  206. {
  207. GATH_LINESPEC * pNewLineSpec = ReadLineInfo(pFile);
  208. if (pNewLineSpec == NULL)
  209. return 0;
  210. // Add the line we just read in to the end of the list of line specs for this
  211. // internal category.
  212. if (pCategory->m_pLineSpec == NULL)
  213. pCategory->m_pLineSpec = pNewLineSpec;
  214. else
  215. {
  216. GATH_LINESPEC * pLineSpec = pCategory->m_pLineSpec;
  217. while (pLineSpec->m_pNext)
  218. pLineSpec = pLineSpec->m_pNext;
  219. pLineSpec->m_pNext = pNewLineSpec;
  220. }
  221. }
  222. else if (strKeyword.CompareNoCase(CString(ENUMLINE_KEYWORD)) == 0)
  223. {
  224. GATH_LINESPEC * pNewLineSpec = ReadLineEnumRecursive(pFile, mapCategories);
  225. if (pNewLineSpec == NULL)
  226. return 0;
  227. // Add the line we just read in to the end of the list of line specs for this
  228. // internal category.
  229. if (pCategory->m_pLineSpec == NULL)
  230. pCategory->m_pLineSpec = pNewLineSpec;
  231. else
  232. {
  233. GATH_LINESPEC * pLineSpec = pCategory->m_pLineSpec;
  234. while (pLineSpec->m_pNext)
  235. pLineSpec = pLineSpec->m_pNext;
  236. pLineSpec->m_pNext = pNewLineSpec;
  237. }
  238. }
  239. else
  240. {
  241. ASSERT(FALSE);
  242. VerifyAndAdvanceFile(pFile, strKeyword);
  243. }
  244. }
  245. if (!VerifyAndAdvanceFile(pFile, CString("}")))
  246. return 0;
  247. return dwID;
  248. }
  249. //-----------------------------------------------------------------------------
  250. // Get the category structure, given a DWORD ID.
  251. //-----------------------------------------------------------------------------
  252. INTERNAL_CATEGORY * CTemplateFileFunctions::GetInternalRep(CMapWordToPtr & mapCategories, DWORD dwID)
  253. {
  254. INTERNAL_CATEGORY * pReturn;
  255. if (mapCategories.Lookup((WORD) dwID, (void * &) pReturn))
  256. return pReturn;
  257. return NULL;
  258. }
  259. //-----------------------------------------------------------------------------
  260. // Create the internal category structure.
  261. //
  262. // Version 6.0: this doesn't set the category ID.
  263. //-----------------------------------------------------------------------------
  264. INTERNAL_CATEGORY * CTemplateFileFunctions::CreateCategory(CMapWordToPtr & mapCategories, DWORD dwNewID, DWORD dwParentID, DWORD dwPrevID)
  265. {
  266. INTERNAL_CATEGORY * pInternalCat;
  267. INTERNAL_CATEGORY * pPreviousCat;
  268. CString strName;
  269. pInternalCat = new INTERNAL_CATEGORY;
  270. if (!pInternalCat)
  271. return NULL;
  272. pInternalCat->m_dwID = dwNewID;
  273. pInternalCat->m_fListView = TRUE;
  274. pInternalCat->m_dwParentID = dwParentID;
  275. pInternalCat->m_dwPrevID = dwPrevID;
  276. if (dwPrevID)
  277. {
  278. pPreviousCat = GetInternalRep(mapCategories, dwPrevID);
  279. if (pPreviousCat)
  280. pPreviousCat->m_dwNextID = dwNewID;
  281. }
  282. mapCategories.SetAt((WORD)dwNewID, (void *) pInternalCat);
  283. return pInternalCat;
  284. }
  285. //-----------------------------------------------------------------------------
  286. // This method simply reads an argument (as string) from the file, until a
  287. // punctuation or whitespace character is found. If a quote mark is found,
  288. // all characters are included in the string until another quote is found.
  289. // NOTE: currently no way to have a quote mark in the string.
  290. //-----------------------------------------------------------------------------
  291. BOOL CTemplateFileFunctions::ReadArgument(CFile * pFile, CString & strSource)
  292. {
  293. BOOL fInQuote = FALSE, fInComment = FALSE;
  294. CString strTemp;
  295. WCHAR cLastChar, cCurrentChar = L'\0';
  296. // Skip over characters until we reach an alphanumeric char. If we find
  297. // a close paren, then we've reached the end of the argument list and
  298. // should return FALSE.
  299. do
  300. {
  301. // Save the last character read, since the comment token ("//") is
  302. // two characters long.
  303. cLastChar = cCurrentChar;
  304. // Read the next character in the file.
  305. if (pFile->Read((void *) &cCurrentChar, sizeof(WCHAR)) != sizeof(WCHAR))
  306. return FALSE;
  307. // If we're in a comment, and the character we just read isn't a new line,
  308. // we want to ignore it.
  309. if (fInComment)
  310. {
  311. if (cCurrentChar == L'\n')
  312. fInComment = FALSE;
  313. continue;
  314. }
  315. // Check to see if we've started into a comment.
  316. if (cCurrentChar == L'/')
  317. {
  318. if (cLastChar == L'/')
  319. fInComment = TRUE;
  320. continue;
  321. }
  322. if (cCurrentChar == L')')
  323. return FALSE;
  324. } while (!iswalnum(cCurrentChar) && cCurrentChar != L'"');
  325. // Read characters into the string until we find whitespace or punctuation.
  326. do
  327. {
  328. if (cCurrentChar == L'"')
  329. {
  330. fInQuote = !fInQuote;
  331. continue;
  332. }
  333. if (iswalnum(cCurrentChar) || fInQuote)
  334. {
  335. char strt[5] = "";
  336. BOOL used = FALSE;
  337. #ifdef UNICODE
  338. strTemp += (TCHAR) cCurrentChar;
  339. #else
  340. WideCharToMultiByte(CP_ACP, 0, &cCurrentChar, 1, strt, 2, "?", &used);
  341. strTemp += strt;
  342. #endif
  343. }
  344. else
  345. {
  346. break;
  347. }
  348. } while (pFile->Read((void *) &cCurrentChar, sizeof(WCHAR)) == sizeof(WCHAR));
  349. // If the last character read (the one which terminated this argument) was
  350. // not a comma, then back the file up so that the character can be re-read
  351. // and interpreted.
  352. if (cCurrentChar != L',')
  353. pFile->Seek(-(LONG)sizeof(WCHAR), CFile::current);
  354. strSource = strTemp;
  355. return TRUE;
  356. }
  357. //-----------------------------------------------------------------------------
  358. // A field consists of a source string, followed by a format string, followed
  359. // by a list of zero or more arguments.
  360. //-----------------------------------------------------------------------------
  361. BOOL CTemplateFileFunctions::ReadField(CFile * pFile, GATH_FIELD & field)
  362. {
  363. // Advance past the field keyword and read the two source and format strings.
  364. if (!VerifyAndAdvanceFile(pFile, CString(FIELD_KEYWORD) + CString("(")))
  365. return FALSE;
  366. if (!ReadArgument(pFile, field.m_strSource))
  367. return FALSE;
  368. if (!ReadArgument(pFile, field.m_strFormat))
  369. return FALSE;
  370. // Read arguments until there are no more, building them into a list of
  371. // arguments stored by the FIELD struct.
  372. GATH_VALUE arg;
  373. GATH_VALUE * pArg = NULL;
  374. while (ReadArgument(pFile, arg.m_strText))
  375. {
  376. if (pArg == NULL)
  377. {
  378. field.m_pArgs = new GATH_VALUE;
  379. if (field.m_pArgs == NULL)
  380. return FALSE;
  381. *field.m_pArgs = arg;
  382. pArg = field.m_pArgs;
  383. }
  384. else
  385. {
  386. pArg->m_pNext = new GATH_VALUE;
  387. if (pArg->m_pNext == NULL)
  388. return FALSE;
  389. *pArg->m_pNext = arg;
  390. pArg = pArg->m_pNext;
  391. }
  392. }
  393. return TRUE;
  394. }
  395. //-----------------------------------------------------------------------------
  396. // Read an enumline(){} block. This construct is used to group lines together
  397. // which are enumerated for each instance of a class. A line is added to
  398. // the parent node's list of lines with a m_strEnumerateClass equal to the
  399. // class to be enumerated. The added line structure will have children lines
  400. // (the lines to be enumerated) referenced by m_pEnumeratedGroup.
  401. //-----------------------------------------------------------------------------
  402. GATH_LINESPEC * CTemplateFileFunctions::ReadLineEnumRecursive(CFile * pFile, CMapWordToPtr & mapCategories)
  403. {
  404. if (!VerifyAndAdvanceFile(pFile, CString(ENUMLINE_KEYWORD) + CString("(")))
  405. return NULL;
  406. // Declare a line specification variable to store the line info.
  407. GATH_LINESPEC * pNewLineSpec = new GATH_LINESPEC;
  408. if (pNewLineSpec == NULL)
  409. return NULL;
  410. // Read in the enumerated class variable.
  411. if (!ReadArgument(pFile, pNewLineSpec->m_strEnumerateClass))
  412. {
  413. delete pNewLineSpec;
  414. return NULL;
  415. }
  416. // Read in the variable (zero or more) number of fields for the constraints.
  417. GATH_FIELD * pNewField = new GATH_FIELD;
  418. if (pNewField == NULL)
  419. return NULL;
  420. while (ReadField(pFile, *pNewField))
  421. {
  422. if (pNewLineSpec->m_pConstraintFields == NULL)
  423. pNewLineSpec->m_pConstraintFields = pNewField;
  424. else
  425. {
  426. // Add the newly read field to the end of the field list. Note,
  427. // this is inefficient, and should be fixed. (NOTE)
  428. GATH_FIELD * pFieldScan = pNewLineSpec->m_pConstraintFields;
  429. while (pFieldScan->m_pNext)
  430. pFieldScan = pFieldScan->m_pNext;
  431. pFieldScan->m_pNext = pNewField;
  432. }
  433. pNewField = new GATH_FIELD;
  434. if (pNewField == NULL)
  435. return NULL;
  436. }
  437. delete pNewField;
  438. // Advance past the close paren and the (necessary) open bracket.
  439. if (!VerifyAndAdvanceFile(pFile, CString("){")))
  440. {
  441. delete pNewLineSpec;
  442. return NULL;
  443. }
  444. // Read the contents of the block (should be all lines or enumlines).
  445. CString strKeyword;
  446. while (GetKeyword(pFile, strKeyword))
  447. {
  448. if (strKeyword.CompareNoCase(CString(LINE_KEYWORD)) == 0)
  449. {
  450. GATH_LINESPEC * pNewSubLine = ReadLineInfo(pFile);
  451. if (pNewSubLine == NULL)
  452. {
  453. delete pNewLineSpec;
  454. return NULL;
  455. }
  456. if (pNewLineSpec->m_pEnumeratedGroup == NULL)
  457. pNewLineSpec->m_pEnumeratedGroup = pNewSubLine;
  458. else
  459. {
  460. GATH_LINESPEC * pLineSpec = pNewLineSpec->m_pEnumeratedGroup;
  461. while (pLineSpec->m_pNext)
  462. pLineSpec = pLineSpec->m_pNext;
  463. pLineSpec->m_pNext = pNewSubLine;
  464. }
  465. }
  466. else if (strKeyword.CompareNoCase(CString(ENUMLINE_KEYWORD)) == 0)
  467. {
  468. GATH_LINESPEC * pNewSubLine = ReadLineEnumRecursive(pFile, mapCategories);
  469. if (pNewSubLine == NULL)
  470. {
  471. delete pNewLineSpec;
  472. return NULL;
  473. }
  474. if (pNewLineSpec->m_pEnumeratedGroup == NULL)
  475. pNewLineSpec->m_pEnumeratedGroup = pNewSubLine;
  476. else
  477. {
  478. GATH_LINESPEC * pLineSpec = pNewLineSpec->m_pEnumeratedGroup;
  479. while (pLineSpec->m_pNext)
  480. pLineSpec = pLineSpec->m_pNext;
  481. pLineSpec->m_pNext = pNewSubLine;
  482. }
  483. }
  484. else
  485. {
  486. delete pNewLineSpec;
  487. return NULL;
  488. }
  489. }
  490. if (!VerifyAndAdvanceFile(pFile, CString("}")))
  491. {
  492. delete pNewLineSpec;
  493. return NULL;
  494. }
  495. return pNewLineSpec;
  496. }
  497. //-----------------------------------------------------------------------------
  498. // This method reads in a "column" line from the file, adding the appropriate
  499. // entries for the columns into the category referenced by dwID. The column
  500. // line contains a bunch of fields in a list.
  501. //-----------------------------------------------------------------------------
  502. BOOL CTemplateFileFunctions::ReadColumnInfo(CFile * pFile, CMapWordToPtr & mapCategories, DWORD dwID)
  503. {
  504. CString strTemp;
  505. if (!VerifyAndAdvanceFile(pFile, CString(COLUMN_KEYWORD) + CString("(")))
  506. return FALSE;
  507. // Get the internal category referenced by dwID.
  508. INTERNAL_CATEGORY * pCategory = GetInternalRep(mapCategories, dwID);
  509. if (!pCategory)
  510. return FALSE;
  511. // We only allow one column specifier list per node.
  512. if (pCategory->m_pColSpec)
  513. return FALSE;
  514. // While we are still reading fields from the file, keep adding to the column list.
  515. GATH_FIELD * pNewField = new GATH_FIELD;
  516. if (pNewField == NULL)
  517. return FALSE;
  518. while (ReadField(pFile, *pNewField))
  519. {
  520. if (pCategory->m_pColSpec == NULL)
  521. pCategory->m_pColSpec = pNewField;
  522. else
  523. {
  524. // Scan to the last field in the linespec.m_pFields list, and insert the new field.
  525. GATH_FIELD * pFieldScan = pCategory->m_pColSpec;
  526. while (pFieldScan->m_pNext)
  527. pFieldScan = pFieldScan->m_pNext;
  528. pFieldScan->m_pNext = pNewField;
  529. }
  530. // Parse the width out of the column caption.
  531. if (pNewField->m_strFormat.ReverseFind(_T(',')) != -1)
  532. {
  533. strTemp = pNewField->m_strFormat.Right(pNewField->m_strFormat.GetLength() - pNewField->m_strFormat.ReverseFind(_T(',')) - 1);
  534. pNewField->m_usWidth = (unsigned short) _ttoi(strTemp);
  535. pNewField->m_strFormat = pNewField->m_strFormat.Left(pNewField->m_strFormat.GetLength() - strTemp.GetLength() - 1);
  536. }
  537. else
  538. {
  539. ASSERT(FALSE);
  540. pNewField->m_usWidth = (unsigned short) 80;
  541. }
  542. // Parse off any remaining information in the column label (the label ends
  543. // with [name, n], when n is the width, and name is the ID for the column
  544. // which should not be displayed).
  545. if (pNewField->m_strFormat.ReverseFind(_T('[')) != -1)
  546. pNewField->m_strFormat = pNewField->m_strFormat.Left(pNewField->m_strFormat.ReverseFind(_T('[')) - 1);
  547. // Read the sorting type from the file.
  548. if (ReadArgument(pFile, strTemp))
  549. {
  550. if (strTemp.CompareNoCase(CString(_T(SORT_LEXICAL))) == 0)
  551. pNewField->m_sort = LEXICAL;
  552. else if (strTemp.CompareNoCase(CString(_T(SORT_VALUE))) == 0)
  553. pNewField->m_sort = BYVALUE;
  554. else
  555. pNewField->m_sort = NOSORT;
  556. }
  557. else
  558. return FALSE;
  559. // Read the complexity (BASIC or ADVANCED) from the file.
  560. if (ReadArgument(pFile, strTemp))
  561. {
  562. if (strTemp.CompareNoCase(CString(_T(COMPLEXITY_ADVANCED))) == 0)
  563. pNewField->m_datacomplexity = ADVANCED;
  564. else
  565. pNewField->m_datacomplexity = BASIC;
  566. }
  567. else
  568. return FALSE;
  569. pNewField = new GATH_FIELD;
  570. if (pNewField == NULL)
  571. return FALSE;
  572. }
  573. delete pNewField;
  574. if (!VerifyAndAdvanceFile(pFile, CString(")")))
  575. return FALSE;
  576. return TRUE;
  577. }
  578. //-----------------------------------------------------------------------------
  579. // Read in the information for a single line. Add the line to the internal
  580. // representation of the category. NOTE: inefficient, since this will be
  581. // called multiple times and the line list will need to be scanned to the
  582. // end each time.
  583. //-----------------------------------------------------------------------------
  584. GATH_LINESPEC * CTemplateFileFunctions::ReadLineInfo(CFile * pFile)
  585. {
  586. if (!VerifyAndAdvanceFile(pFile, CString(LINE_KEYWORD) + CString("(")))
  587. return NULL;
  588. // Declare a line specification variable to store the line info.
  589. GATH_LINESPEC * pNewLineSpec = new GATH_LINESPEC;
  590. if (pNewLineSpec == NULL)
  591. return NULL;
  592. // While we are still reading fields from the file, keep adding to the column list.
  593. // NOTE: inefficient, repeated scans through linespec.m_pFields list.
  594. GATH_FIELD * pNewField = new GATH_FIELD;
  595. if (pNewField == NULL)
  596. {
  597. delete pNewLineSpec;
  598. return NULL;
  599. }
  600. // Read in the complexity (BASIC or ADVANCED) for this line.
  601. CString strTemp;
  602. if (ReadArgument(pFile, strTemp))
  603. {
  604. if (strTemp.CompareNoCase(CString(_T(COMPLEXITY_ADVANCED))) == 0)
  605. pNewLineSpec->m_datacomplexity = ADVANCED;
  606. else
  607. pNewLineSpec->m_datacomplexity = BASIC;
  608. }
  609. else
  610. return FALSE;
  611. while (ReadField(pFile, *pNewField))
  612. {
  613. if (pNewLineSpec->m_pFields == NULL)
  614. pNewLineSpec->m_pFields = pNewField;
  615. else
  616. {
  617. // Scan to the last field in the linespec.m_pFields list, and insert the new field.
  618. GATH_FIELD * pFieldScan = pNewLineSpec->m_pFields;
  619. while (pFieldScan->m_pNext)
  620. pFieldScan = pFieldScan->m_pNext;
  621. pFieldScan->m_pNext = pNewField;
  622. }
  623. pNewField = new GATH_FIELD;
  624. if (pNewField == NULL)
  625. {
  626. delete pNewLineSpec;
  627. return NULL;
  628. }
  629. }
  630. delete pNewField;
  631. if (!VerifyAndAdvanceFile(pFile, CString(")")))
  632. {
  633. delete pNewLineSpec;
  634. return NULL;
  635. }
  636. return pNewLineSpec;
  637. }
  638. //-----------------------------------------------------------------------------
  639. // This method returns the next keyword in the file. Any whitespace or
  640. // punctuation is skipped until an alphanumeric character is read. The keyword
  641. // returned is the string starting with this character until whitespace or
  642. // punctuation is encountered. Note: it is very important that this function
  643. // returns the file to the state it was in when the function started, with
  644. // the current position restored.
  645. //
  646. // NOTE: inefficient
  647. //-----------------------------------------------------------------------------
  648. BOOL CTemplateFileFunctions::GetKeyword(CFile * pFile, CString & strKeyword)
  649. {
  650. CString strTemp = CString("");
  651. DWORD dwPosition = pFile->GetPosition();
  652. WCHAR cLastChar, cCurrentChar = L'\0';
  653. BOOL fInComment = FALSE;
  654. // Skip over whitespace characters until we reach an alphanumeric char.
  655. do
  656. {
  657. // Save the last character read, since the comment token ("//") is
  658. // two characters long.
  659. cLastChar = cCurrentChar;
  660. // Read the next character in the file.
  661. if (pFile->Read((void *) &cCurrentChar, sizeof(WCHAR)) != sizeof(WCHAR))
  662. return FALSE;
  663. // If we're in a comment, and the character we just read isn't a new line,
  664. // we want to ignore it.
  665. if (fInComment)
  666. {
  667. if (cCurrentChar == L'\n')
  668. fInComment = FALSE;
  669. continue;
  670. }
  671. // Check to see if we've started into a comment.
  672. if (cCurrentChar == _T('/'))
  673. {
  674. if (cLastChar == _T('/'))
  675. fInComment = TRUE;
  676. continue;
  677. }
  678. } while (iswspace(cCurrentChar) || cCurrentChar == L'/' || fInComment);
  679. // Read the keyword while it's alphanumeric.
  680. if (iswalnum(cCurrentChar))
  681. do
  682. {
  683. strTemp += (TCHAR) cCurrentChar;
  684. if (pFile->Read((void *) &cCurrentChar, sizeof(WCHAR)) != sizeof(WCHAR))
  685. return FALSE;
  686. } while (iswalnum(cCurrentChar));
  687. // Reset the file, set the keyword and return.
  688. pFile->Seek((LONG)dwPosition, CFile::begin);
  689. strKeyword = strTemp;
  690. return !strTemp.IsEmpty();
  691. }
  692. //-----------------------------------------------------------------------------
  693. // INTERNAL_CATEGORY constructor and destructor.
  694. //-----------------------------------------------------------------------------
  695. INTERNAL_CATEGORY::INTERNAL_CATEGORY()
  696. {
  697. m_categoryName.m_strText = CString(" ");
  698. m_fieldName.m_strFormat = CString(" ");
  699. m_strEnumerateClass = CString("");
  700. m_strIdentifier = CString("");
  701. m_strNoInstances = CString("");
  702. m_fListView = FALSE;
  703. m_fDynamic = FALSE;
  704. m_dwID = 0;
  705. m_dwParentID = 0;
  706. m_dwChildID = 0;
  707. m_dwPrevID = 0;
  708. m_dwNextID = 0;
  709. m_dwDynamicChildID = 0;
  710. m_dwColCount = 0;
  711. m_pColSpec = NULL;
  712. m_aCols = NULL;
  713. m_pLineSpec = NULL;
  714. m_dwLineCount = 0;
  715. m_apLines = NULL;
  716. m_fIncluded = TRUE;
  717. m_fRefreshed = FALSE;
  718. m_dwLastError = S_OK; // GATH_ERR_NOERROR;
  719. }
  720. INTERNAL_CATEGORY::~INTERNAL_CATEGORY()
  721. {
  722. if (m_pColSpec)
  723. delete m_pColSpec;
  724. if (m_aCols)
  725. delete [] m_aCols;
  726. if (m_pLineSpec)
  727. delete m_pLineSpec;
  728. if (m_apLines)
  729. {
  730. for (DWORD dwIndex = 0; dwIndex < m_dwLineCount; dwIndex++)
  731. delete m_apLines[dwIndex];
  732. delete [] m_apLines;
  733. }
  734. }
  735. //-----------------------------------------------------------------------------
  736. // GATH_FIELD constructor and destructor.
  737. //-----------------------------------------------------------------------------
  738. GATH_FIELD::GATH_FIELD()
  739. {
  740. m_pArgs = NULL;
  741. m_pNext = NULL;
  742. m_usWidth = 0;
  743. m_sort = NOSORT;
  744. m_datacomplexity = BASIC;
  745. }
  746. GATH_FIELD::~GATH_FIELD()
  747. {
  748. if (m_pArgs) delete m_pArgs;
  749. if (m_pNext) delete m_pNext;
  750. }
  751. //-----------------------------------------------------------------------------
  752. // GATH_VALUE constructor and destructor.
  753. //-----------------------------------------------------------------------------
  754. GATH_VALUE::GATH_VALUE()
  755. {
  756. m_pNext = NULL;
  757. m_dwValue = 0L;
  758. }
  759. GATH_VALUE::~GATH_VALUE()
  760. {
  761. if (m_pNext) delete m_pNext;
  762. }
  763. //-----------------------------------------------------------------------------
  764. // GATH_LINESPEC constructor and destructor.
  765. //-----------------------------------------------------------------------------
  766. GATH_LINESPEC::GATH_LINESPEC()
  767. {
  768. m_pFields = NULL;
  769. m_pEnumeratedGroup = NULL;
  770. m_pConstraintFields = NULL;
  771. m_pNext = NULL;
  772. m_datacomplexity = BASIC;
  773. }
  774. GATH_LINESPEC::~GATH_LINESPEC()
  775. {
  776. if (m_pFields)
  777. delete m_pFields;
  778. if (m_pEnumeratedGroup)
  779. delete m_pEnumeratedGroup;
  780. if (m_pConstraintFields)
  781. delete m_pConstraintFields;
  782. if (m_pNext)
  783. delete m_pNext;
  784. }
  785. //-----------------------------------------------------------------------------
  786. // GATH_LINE constructor and destructor.
  787. //-----------------------------------------------------------------------------
  788. GATH_LINE::GATH_LINE()
  789. {
  790. m_datacomplexity = BASIC;
  791. m_aValue = NULL;
  792. }
  793. GATH_LINE::~GATH_LINE()
  794. {
  795. if (m_aValue)
  796. delete [] m_aValue;
  797. }
  798. //-----------------------------------------------------------------------------
  799. // This function is called to refresh the data for all of the extensions. It
  800. // will use the refresh index to look up the line spec, and then call some
  801. // version 5.0 functions to do the refresh. Finally, it will convert the
  802. // data generated by those functions into our new format.
  803. //-----------------------------------------------------------------------------
  804. HRESULT RefreshExtensions(CWMIHelper * pWMI, DWORD dwIndex, volatile BOOL * pfCancel, CPtrList * aColValues, int iColCount, void ** ppCache)
  805. {
  806. HRESULT hr = S_OK;
  807. if (pWMI == NULL)
  808. return hr;
  809. pWMI->m_hrLastVersion5Error = S_OK;
  810. // Reset the caches so the data is actually refreshed (140535).
  811. pWMI->Version5ClearCache();
  812. pWMI->m_enumMap.Reset();
  813. // Get the line spec pointer for this index.
  814. GATH_LINESPEC * pLineSpec = gmapExtensionRefreshData.Lookup(dwIndex);
  815. if (pLineSpec == NULL)
  816. return hr;
  817. // Here's some code from 5.0 for refreshing a list of line pointers for a line spec.
  818. CPtrList listLinePtrs;
  819. if (CRefreshFunctions::RefreshLines(pWMI, pLineSpec, (DWORD) iColCount, listLinePtrs, pfCancel))
  820. {
  821. // Move the contents of the list of lines to our internal structures.
  822. if (listLinePtrs.GetCount() > 0)
  823. {
  824. GATH_LINE * pLine;
  825. for (POSITION pos = listLinePtrs.GetHeadPosition(); pos != NULL;)
  826. {
  827. pLine = (GATH_LINE *) listLinePtrs.GetNext(pos);
  828. if (pLine == NULL || pLine->m_aValue == NULL)
  829. continue;
  830. for (int iCol = 0; iCol < iColCount; iCol++)
  831. {
  832. CString strValue = pLine->m_aValue[iCol].m_strText;
  833. DWORD dwValue = pLine->m_aValue[iCol].m_dwValue;
  834. BOOL fAdvanced = (pLine->m_datacomplexity == ADVANCED);
  835. pWMI->AppendCell(aColValues[iCol], strValue, dwValue, fAdvanced);
  836. }
  837. delete pLine;
  838. }
  839. }
  840. else
  841. {
  842. CString * pstrNoData = gmapExtensionRefreshData.LookupString(dwIndex);
  843. if (pstrNoData && !pstrNoData->IsEmpty() && iColCount > 0)
  844. {
  845. pWMI->AppendCell(aColValues[0], *pstrNoData, 0, FALSE);
  846. for (int iCol = 1; iCol < iColCount; iCol++)
  847. pWMI->AppendCell(aColValues[iCol], _T(""), 0, FALSE);
  848. }
  849. }
  850. }
  851. return pWMI->m_hrLastVersion5Error;
  852. }
  853. //-----------------------------------------------------------------------------
  854. // Refresh the list of lines based on the list of line fields. We'll also
  855. // need to set the number of lines. The list of lines is generated based on
  856. // the pLineSpec pointer and dwColumns variables. The generated lines are
  857. // returned in the listLinePtrs parameter.
  858. //-----------------------------------------------------------------------------
  859. BOOL CRefreshFunctions::RefreshLines(CWMIHelper * pWMI, GATH_LINESPEC * pLineSpec, DWORD dwColumns, CPtrList & listLinePtrs, volatile BOOL * pfCancel)
  860. {
  861. BOOL bReturn = TRUE;
  862. // Traverse the list of line specifiers to generate the list of lines.
  863. GATH_LINESPEC * pCurrentLineSpec = pLineSpec;
  864. GATH_LINE * pLine = NULL;
  865. while (pCurrentLineSpec && (pfCancel == NULL || *pfCancel == FALSE))
  866. {
  867. // Check if the current line spec is for a single line or an enumerated group.
  868. if (pCurrentLineSpec->m_strEnumerateClass.IsEmpty() || pCurrentLineSpec->m_strEnumerateClass.CompareNoCase(CString(STATIC_SOURCE)) == 0)
  869. {
  870. // This is for a single line. Allocate a new line structure and fill it
  871. // in with the data generated from the line spec.
  872. pLine = new GATH_LINE;
  873. if (pLine == NULL)
  874. {
  875. bReturn = FALSE;
  876. break;
  877. }
  878. if (RefreshOneLine(pWMI, pLine, pCurrentLineSpec, dwColumns))
  879. listLinePtrs.AddTail((void *) pLine);
  880. else
  881. {
  882. bReturn = FALSE;
  883. break;
  884. }
  885. }
  886. else
  887. {
  888. // This line represents an enumerated group of lines. We need to enumerate
  889. // the class and call RefreshLines for the group of enumerated lines, once
  890. // for each class instance.
  891. if (pWMI->Version5ResetClass(pCurrentLineSpec->m_strEnumerateClass, pCurrentLineSpec->m_pConstraintFields))
  892. do
  893. {
  894. if (!RefreshLines(pWMI, pCurrentLineSpec->m_pEnumeratedGroup, dwColumns, listLinePtrs, pfCancel))
  895. break;
  896. } while (pWMI->Version5EnumClass(pCurrentLineSpec->m_strEnumerateClass, pCurrentLineSpec->m_pConstraintFields));
  897. }
  898. pCurrentLineSpec = pCurrentLineSpec->m_pNext;
  899. }
  900. if (pfCancel && *pfCancel)
  901. return FALSE;
  902. // If there was a failure generating the lines, clean up after ourselves.
  903. if (!bReturn)
  904. {
  905. if (pLine)
  906. delete pLine;
  907. for (POSITION pos = listLinePtrs.GetHeadPosition(); pos != NULL;)
  908. {
  909. pLine = (GATH_LINE *) listLinePtrs.GetNext(pos) ;
  910. if (pLine)
  911. delete pLine;
  912. }
  913. listLinePtrs.RemoveAll();
  914. return FALSE;
  915. }
  916. return TRUE;
  917. }
  918. //-----------------------------------------------------------------------------
  919. // Refresh a line based on a line spec.
  920. //-----------------------------------------------------------------------------
  921. BOOL CRefreshFunctions::RefreshOneLine(CWMIHelper * pWMI, GATH_LINE * pLine, GATH_LINESPEC * pLineSpec, DWORD dwColCount)
  922. {
  923. // Allocate the new array of values.
  924. if (pLine->m_aValue)
  925. delete [] pLine->m_aValue;
  926. pLine->m_aValue = new GATH_VALUE[dwColCount];
  927. if (pLine->m_aValue == NULL)
  928. return FALSE;
  929. // Set the data complexity for the line based on the line spec.
  930. pLine->m_datacomplexity = pLineSpec->m_datacomplexity;
  931. // Compute each of the values for the fields.
  932. GATH_FIELD * pField = pLineSpec->m_pFields;
  933. for (DWORD dwIndex = 0; dwIndex < dwColCount; dwIndex++)
  934. {
  935. if (pField == NULL)
  936. return FALSE;
  937. if (!RefreshValue(pWMI, &pLine->m_aValue[dwIndex], pField))
  938. return FALSE;
  939. pField = pField->m_pNext;
  940. }
  941. return TRUE;
  942. }
  943. //-----------------------------------------------------------------------------
  944. // This method takes the information in a GATH_FIELD struct and uses it to
  945. // generate a current GATH_VALUE struct.
  946. //-----------------------------------------------------------------------------
  947. BOOL CRefreshFunctions::RefreshValue(CWMIHelper * pWMI, GATH_VALUE * pVal, GATH_FIELD * pField)
  948. {
  949. TCHAR szFormatFragment[MAX_PATH];
  950. const TCHAR *pSourceChar;
  951. TCHAR *pDestinationChar;
  952. TCHAR cFormat = _T('\0');
  953. BOOL fReadPercent = FALSE;
  954. BOOL fReturnValue = TRUE;
  955. CString strResult, strTemp;
  956. int iArgNumber = 0;
  957. DWORD dwValue = 0L;
  958. // Process the format string. Because of the difficulty caused by having
  959. // variable number of arguments to be inserted (like printf), we'll need
  960. // to break the format string into chunks and do the sprintf function
  961. // for each format flag we come across.
  962. pSourceChar = (LPCTSTR) pField->m_strFormat;
  963. pDestinationChar = szFormatFragment;
  964. while (*pSourceChar)
  965. {
  966. if (fReadPercent)
  967. {
  968. // If we read a percent sign, we should be looking for a valid flag.
  969. // We are using some additional flags to printf (and not supporting
  970. // others). If we read another percent, just insert a single percent.
  971. switch (*pSourceChar)
  972. {
  973. case _T('%'):
  974. fReadPercent = FALSE;
  975. break;
  976. case _T('b'): case _T('B'):
  977. case _T('l'): case _T('L'):
  978. case _T('u'): case _T('U'):
  979. case _T('s'): case _T('S'):
  980. fReadPercent = FALSE;
  981. cFormat = *pSourceChar;
  982. *pDestinationChar = _T('s');
  983. break;
  984. case _T('t'): case _T('T'):
  985. fReadPercent = FALSE;
  986. cFormat = *pSourceChar;
  987. *pDestinationChar = _T('s');
  988. break;
  989. case _T('x'): case _T('X'):
  990. case _T('d'): case _T('D'):
  991. fReadPercent = FALSE;
  992. cFormat = _T('d');
  993. *pDestinationChar = *pSourceChar;
  994. break;
  995. case _T('q'): case _T('Q'):
  996. fReadPercent = FALSE;
  997. cFormat = _T('q');
  998. *pDestinationChar = _T('s');
  999. break;
  1000. case _T('z'): case _T('Z'):
  1001. fReadPercent = FALSE;
  1002. cFormat = _T('z');
  1003. *pDestinationChar = _T('s');
  1004. break;
  1005. case _T('y'): case _T('Y'):
  1006. fReadPercent = FALSE;
  1007. cFormat = _T('y');
  1008. *pDestinationChar = _T('s');
  1009. break;
  1010. case _T('v'): case _T('V'):
  1011. fReadPercent = FALSE;
  1012. cFormat = _T('v');
  1013. *pDestinationChar = _T('s');
  1014. break;
  1015. case _T('f'): case _T('F'):
  1016. fReadPercent = FALSE;
  1017. cFormat = *pSourceChar;
  1018. *pDestinationChar = *pSourceChar;
  1019. break;
  1020. default:
  1021. *pDestinationChar = *pSourceChar;
  1022. }
  1023. }
  1024. else if (*pSourceChar == _T('%'))
  1025. {
  1026. *pDestinationChar = _T('%');
  1027. fReadPercent = TRUE;
  1028. }
  1029. else
  1030. *pDestinationChar = *pSourceChar;
  1031. pSourceChar++;
  1032. pDestinationChar++;
  1033. // If a format flag is set or we are at the end of the source string,
  1034. // then we have a complete fragment and we should produce some output,
  1035. // which will be concatenated to the strResult string.
  1036. if (cFormat || *pSourceChar == _T('\0'))
  1037. {
  1038. *pDestinationChar = _T('\0');
  1039. if (cFormat)
  1040. {
  1041. // Based on the format type, get a value from the provider for
  1042. // the next argument. Format the result using the formatting
  1043. // fragment we extracted, and concatenate it.
  1044. if (GetValue(pWMI, cFormat, szFormatFragment, strTemp, dwValue, pField, iArgNumber++))
  1045. {
  1046. strResult += strTemp;
  1047. cFormat = _T('\0');
  1048. }
  1049. else
  1050. {
  1051. strResult = strTemp;
  1052. break;
  1053. }
  1054. }
  1055. else
  1056. {
  1057. // There was no format flag, but we are at the end of the string.
  1058. // Add the fragment we got to the result string.
  1059. strResult += CString(szFormatFragment);
  1060. }
  1061. pDestinationChar = szFormatFragment;
  1062. }
  1063. }
  1064. // Assign the values we generated to the GATH_VALUE structure. Important note:
  1065. // the dwValue variable will only have ONE value, even though multiple values
  1066. // might have been generated to build the strResult string. Only the last
  1067. // value will be saved in dwValue. This is OK, because this value is only
  1068. // used for sorting a column when the column is marked for non-lexical sorting.
  1069. // In that case, there should be only one value used to generat the string.
  1070. pVal->m_strText = strResult;
  1071. pVal->m_dwValue = dwValue;
  1072. return fReturnValue;
  1073. }
  1074. //-----------------------------------------------------------------------------
  1075. // Return a string with delimiters added for the number.
  1076. //-----------------------------------------------------------------------------
  1077. CString DelimitNumber(double dblValue)
  1078. {
  1079. NUMBERFMT fmt;
  1080. TCHAR szResult[MAX_PATH] = _T("");
  1081. TCHAR szDelimiter[4] = _T(",");
  1082. GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szDelimiter, 4);
  1083. memset(&fmt, 0, sizeof(NUMBERFMT));
  1084. fmt.Grouping = 3;
  1085. fmt.lpDecimalSep = _T(""); // doesn't matter - there aren't decimal digits
  1086. fmt.lpThousandSep = szDelimiter;
  1087. CString strValue;
  1088. strValue.Format(_T("%.0f"), dblValue);
  1089. GetNumberFormat(LOCALE_USER_DEFAULT, 0, strValue, &fmt, szResult, MAX_PATH);
  1090. return CString(szResult);
  1091. }
  1092. //-----------------------------------------------------------------------------
  1093. // This method gets a single value from the provider, based on the format
  1094. // character from the template file. It formats the results using the
  1095. // format string szFormatFragment, which should only take one argument.
  1096. //-----------------------------------------------------------------------------
  1097. BOOL CRefreshFunctions::GetValue(CWMIHelper * pWMI, TCHAR cFormat, TCHAR *szFormatFragment, CString &strResult, DWORD &dwResult, GATH_FIELD *pField, int iArgNumber)
  1098. {
  1099. CString strTemp;
  1100. COleDateTime datetimeTemp;
  1101. double dblValue;
  1102. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  1103. strResult.Empty();
  1104. dwResult = 0L;
  1105. if (!pField->m_strSource.IsEmpty() && pField->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) != 0)
  1106. {
  1107. // Find the right argument for this formatting (indicated by the iArgNumber
  1108. // parameter.
  1109. GATH_VALUE * pArg = pField->m_pArgs;
  1110. while (iArgNumber && pArg)
  1111. {
  1112. pArg = pArg->m_pNext;
  1113. iArgNumber--;
  1114. }
  1115. if (pArg == NULL)
  1116. return FALSE;
  1117. switch (cFormat)
  1118. {
  1119. case 'b': case 'B':
  1120. // This is a boolean type. Show either true or false, depending on
  1121. // the numeric value.
  1122. if (pWMI->Version5QueryValueDWORD(pField->m_strSource, pArg->m_strText, dwResult, strTemp))
  1123. {
  1124. strTemp = (dwResult) ? pWMI->m_strTrue : pWMI->m_strFalse;
  1125. strResult.Format(szFormatFragment, strTemp);
  1126. return TRUE;
  1127. }
  1128. else
  1129. {
  1130. strResult = strTemp;
  1131. return FALSE;
  1132. }
  1133. break;
  1134. case 'd': case 'D':
  1135. // This is the numeric type.
  1136. if (pWMI->Version5QueryValueDWORD(pField->m_strSource, pArg->m_strText, dwResult, strTemp))
  1137. {
  1138. strResult.Format(szFormatFragment, dwResult);
  1139. return TRUE;
  1140. }
  1141. else
  1142. {
  1143. strResult = strTemp;
  1144. return FALSE;
  1145. }
  1146. break;
  1147. case 'f': case 'F':
  1148. // This is the double floating point type.
  1149. if (pWMI->Version5QueryValueDoubleFloat(pField->m_strSource, pArg->m_strText, dblValue, strTemp))
  1150. {
  1151. strResult.Format(szFormatFragment, dblValue);
  1152. return TRUE;
  1153. }
  1154. else
  1155. {
  1156. strResult = strTemp;
  1157. return FALSE;
  1158. }
  1159. break;
  1160. case 't': case 'T':
  1161. // This is the OLE date and time type. Format the date and time into the
  1162. // string result, and return the date part in the DWORD (the day number is
  1163. // to the left of the decimal in the DATE type).
  1164. if (pWMI->Version5QueryValueDateTime(pField->m_strSource, pArg->m_strText, datetimeTemp, strTemp))
  1165. {
  1166. strResult = datetimeTemp.Format();
  1167. dwResult = (DWORD)(DATE)datetimeTemp;
  1168. return TRUE;
  1169. }
  1170. else
  1171. {
  1172. strResult = strTemp;
  1173. return FALSE;
  1174. }
  1175. break;
  1176. case 'l': case 'L':
  1177. // This is a string type, with the string converted to lower case.
  1178. if (pWMI->Version5QueryValue(pField->m_strSource, pArg->m_strText, strTemp))
  1179. {
  1180. strTemp.MakeLower();
  1181. strResult.Format(szFormatFragment, strTemp);
  1182. return TRUE;
  1183. }
  1184. else
  1185. {
  1186. strResult = strTemp;
  1187. return FALSE;
  1188. }
  1189. break;
  1190. case 'u': case 'U':
  1191. // This is a string type, with the string converted to upper case.
  1192. if (pWMI->Version5QueryValue(pField->m_strSource, pArg->m_strText, strTemp))
  1193. {
  1194. strTemp.MakeUpper();
  1195. strResult.Format(szFormatFragment, strTemp);
  1196. return TRUE;
  1197. }
  1198. else
  1199. {
  1200. strResult = strTemp;
  1201. return FALSE;
  1202. }
  1203. break;
  1204. case 's': case 'S':
  1205. // This is the string type (string is the default type).
  1206. if (pWMI->Version5QueryValue(pField->m_strSource, pArg->m_strText, strTemp))
  1207. {
  1208. strResult.Format(szFormatFragment, strTemp);
  1209. // We only need to do this when the value returned is a number
  1210. // and is going in a column that we want to sort numerically.
  1211. // This won't break the case where a numeric string is to be
  1212. // sorted as a string because dwResult will be ignored.
  1213. if (!strTemp.IsEmpty() && iswdigit( strTemp[0]))
  1214. dwResult = _ttol( (LPCTSTR)strTemp);
  1215. return TRUE;
  1216. }
  1217. else
  1218. {
  1219. strResult = strTemp;
  1220. return FALSE;
  1221. }
  1222. break;
  1223. case 'q': case 'Q':
  1224. // This is a specialized type for the Win32_BIOS class. We want to show
  1225. // the "Version" property - if it isn't there, then we want to show
  1226. // the "Name" property and "ReleaseDate" properties concatenated
  1227. // together.
  1228. if (pWMI->Version5QueryValue(pField->m_strSource, CString(_T("Version")), strTemp))
  1229. {
  1230. strResult = strTemp;
  1231. return TRUE;
  1232. }
  1233. else
  1234. {
  1235. if (pWMI->Version5QueryValue(pField->m_strSource, CString(_T("Name")), strTemp))
  1236. strResult = strTemp;
  1237. if (pWMI->Version5QueryValueDateTime(pField->m_strSource, CString(_T("ReleaseDate")), datetimeTemp, strTemp))
  1238. strResult += CString(_T(" ")) + datetimeTemp.Format();
  1239. return TRUE;
  1240. }
  1241. break;
  1242. case 'z': case 'Z':
  1243. // This is a specialized size type, where the value is a numeric count
  1244. // of bytes. We want to convert it into the best possible units for
  1245. // display (for example, display "4.20 MB (4,406,292 bytes)").
  1246. if (pWMI->Version5QueryValueDoubleFloat(pField->m_strSource, pArg->m_strText, dblValue, strTemp))
  1247. {
  1248. double dValue = (double) dblValue;
  1249. DWORD dwDivisor = 1;
  1250. // Reduce the dValue to the smallest possible number (with a larger unit).
  1251. while (dValue > 1024.0 && dwDivisor < (1024 * 1024 * 1024))
  1252. {
  1253. dwDivisor *= 1024;
  1254. dValue /= 1024.0;
  1255. }
  1256. if (dwDivisor == 1)
  1257. strResult.Format(IDS_SIZEBYTES, DelimitNumber(dblValue));
  1258. else if (dwDivisor == (1024))
  1259. strResult.Format(IDS_SIZEKB_BYTES, dValue, DelimitNumber(dblValue));
  1260. else if (dwDivisor == (1024 * 1024))
  1261. strResult.Format(IDS_SIZEMB_BYTES, dValue, DelimitNumber(dblValue));
  1262. else if (dwDivisor == (1024 * 1024 * 1024))
  1263. strResult.Format(IDS_SIZEGB_BYTES, dValue, DelimitNumber(dblValue));
  1264. dwResult = (DWORD) dblValue; // So we can sort on this value (bug 391127).
  1265. }
  1266. else
  1267. {
  1268. strResult = strTemp;
  1269. return FALSE;
  1270. }
  1271. break;
  1272. case 'y': case 'Y':
  1273. // This is a specialized size type, where the value is a numeric count
  1274. // of bytes, already in KB. If it's big enough, show it in MB or GB.
  1275. if (pWMI->Version5QueryValueDoubleFloat(pField->m_strSource, pArg->m_strText, dblValue, strTemp))
  1276. {
  1277. strResult.Format(IDS_SIZEKB, DelimitNumber(dblValue));
  1278. dwResult = (DWORD) dblValue; // So we can sort on this value (bug 391127).
  1279. }
  1280. else
  1281. {
  1282. strResult = strTemp;
  1283. return FALSE;
  1284. }
  1285. break;
  1286. case 'v': case 'V':
  1287. // This is a specialized type, assumed to be an LCID (locale ID). Show the
  1288. // locale.
  1289. if (pWMI->Version5QueryValue(pField->m_strSource, pArg->m_strText, strTemp))
  1290. {
  1291. // strTemp contains a string locale ID (like "0409"). Convert it into
  1292. // and actual LCID.
  1293. LCID lcid = (LCID) _tcstoul(strTemp, NULL, 16);
  1294. TCHAR szCountry[MAX_PATH];
  1295. if (GetLocaleInfo(lcid, LOCALE_SCOUNTRY, szCountry, MAX_PATH))
  1296. strResult = szCountry;
  1297. else
  1298. strResult = strTemp;
  1299. }
  1300. else
  1301. {
  1302. strResult = strTemp;
  1303. return FALSE;
  1304. }
  1305. break;
  1306. default:
  1307. ASSERT(FALSE); // unknown formatting flag
  1308. return TRUE;
  1309. }
  1310. }
  1311. return FALSE;
  1312. }
  1313. //=============================================================================
  1314. // Functions extending the CWMILiveHelper to support version 5 style refreshes.
  1315. //=============================================================================
  1316. #include "wmilive.h"
  1317. //-----------------------------------------------------------------------------
  1318. // Reset the CMSIEnumerator pointer to the start of the enumeration (and
  1319. // make sure there is one). Remove the object pointer, so the first call
  1320. // to GetObject will return the first item in the enumerator.
  1321. //-----------------------------------------------------------------------------
  1322. BOOL CWMILiveHelper::Version5ResetClass(const CString & strClass, GATH_FIELD * pConstraints)
  1323. {
  1324. CMSIEnumerator * pMSIEnumerator = Version5GetEnumObject(strClass, pConstraints);
  1325. if (pMSIEnumerator == NULL)
  1326. return FALSE;
  1327. // Reset the enumerator, and remove the cached object pointer if there is one.
  1328. pMSIEnumerator->Reset(pConstraints);
  1329. Version5RemoveObject(strClass);
  1330. CMSIObject * pObject = Version5GetObject(strClass, pConstraints);
  1331. if (pObject == NULL || pObject->IsValid() == MOS_NO_INSTANCES)
  1332. return FALSE;
  1333. return TRUE;
  1334. }
  1335. //-----------------------------------------------------------------------------
  1336. // Move the cached IWbemClassObject pointer to the next instance.
  1337. //-----------------------------------------------------------------------------
  1338. BOOL CWMILiveHelper::Version5EnumClass(const CString & strClass, GATH_FIELD * pConstraints)
  1339. {
  1340. // Verify that there is an object enumerator in place.
  1341. if (Version5GetEnumObject(strClass, pConstraints) == NULL)
  1342. return FALSE;
  1343. // If there is an object interface, remove it, then make a new one.
  1344. // Then retrieve the object pointer (this will do the Next on the
  1345. // enumerator to get the next instance).
  1346. Version5RemoveObject(strClass);
  1347. CMSIObject * pObject = Version5GetObject(strClass, pConstraints);
  1348. if (pObject && (pObject->IsValid() == MOS_INSTANCE))
  1349. return TRUE;
  1350. return FALSE;
  1351. }
  1352. //-----------------------------------------------------------------------------
  1353. // Retrieve the interface pointer for the specified IEnumWbemClassObject.
  1354. // If there isn't one cached, create one and cache it. It's possible for the
  1355. // pConstraints parameter to contain a field specify a WBEM SQL condition for
  1356. // this enumerator.
  1357. //-----------------------------------------------------------------------------
  1358. CMSIEnumerator * CWMILiveHelper::Version5GetEnumObject(const CString & strClass, const GATH_FIELD * pConstraints)
  1359. {
  1360. // See if we've cached this enumerator object.
  1361. CMSIEnumerator * pReturn = NULL;
  1362. if (m_mapClassToEnumInterface.Lookup(strClass, (void * &) pReturn))
  1363. return pReturn;
  1364. // We'll need to create this enumerator here, and save it in the cache.
  1365. pReturn = new CMSIEnumerator;
  1366. if (pReturn == NULL)
  1367. return NULL;
  1368. if (FAILED(pReturn->Create(strClass, pConstraints, this)))
  1369. {
  1370. delete pReturn;
  1371. return NULL;
  1372. }
  1373. m_mapClassToEnumInterface.SetAt(strClass, (void *) pReturn);
  1374. return pReturn;
  1375. }
  1376. //-----------------------------------------------------------------------------
  1377. // Remove the specified IWbemClassObject pointer from the cache.
  1378. //-----------------------------------------------------------------------------
  1379. void CWMILiveHelper::Version5RemoveObject(const CString & strClass)
  1380. {
  1381. CMSIObject * pObject = NULL;
  1382. if (m_mapClassToInterface.Lookup(strClass, (void * &) pObject) && pObject)
  1383. delete pObject;
  1384. m_mapClassToInterface.RemoveKey(strClass);
  1385. }
  1386. //-----------------------------------------------------------------------------
  1387. // Retrieve the interface pointer for the specified IWbemClassObject.
  1388. // If there isn't one cached, create one and cache it.
  1389. //-----------------------------------------------------------------------------
  1390. CMSIObject * CWMILiveHelper::Version5GetObject(const CString & strClass, const GATH_FIELD * pConstraints, CString * pstrLabel)
  1391. {
  1392. CMSIObject * pReturn = NULL;
  1393. if (m_mapClassToInterface.Lookup(strClass, (void * &) pReturn))
  1394. return pReturn;
  1395. // We don't have one of these objects cached. Get one from the enumerator.
  1396. CMSIEnumerator * pEnumerator = Version5GetEnumObject(strClass);
  1397. if (pEnumerator)
  1398. {
  1399. HRESULT hr = pEnumerator->Next(&pReturn);
  1400. if (S_OK != hr)
  1401. {
  1402. if (pReturn)
  1403. delete pReturn;
  1404. pReturn = NULL;
  1405. m_hrError = hr;
  1406. }
  1407. }
  1408. if (pReturn)
  1409. m_mapClassToInterface.SetAt(strClass, (void *) pReturn);
  1410. return pReturn;
  1411. }
  1412. //-----------------------------------------------------------------------------
  1413. // This method is used to get the current value for a given class and property
  1414. // string. Starting with the IWbemServices interface, it gets an interface
  1415. // for the requested class enums the first instance. Performance is improved
  1416. // by caching the instance interfaces in m_mapClassToInterface.
  1417. //-----------------------------------------------------------------------------
  1418. BOOL CWMILiveHelper::Version5QueryValue(const CString & strClass, const CString & strProperty, CString & strResult)
  1419. {
  1420. strResult.Empty();
  1421. CMSIObject * pObject = Version5GetObject(strClass, NULL);
  1422. ASSERT(pObject);
  1423. if (!pObject)
  1424. return FALSE;
  1425. switch (pObject->IsValid())
  1426. {
  1427. case MOS_INSTANCE:
  1428. {
  1429. BOOL fUseValueMap = FALSE;
  1430. CString strProp(strProperty);
  1431. if (strProp.Left(8) == CString(_T("ValueMap")))
  1432. {
  1433. strProp = strProp.Right(strProp.GetLength() - 8);
  1434. fUseValueMap = TRUE;
  1435. }
  1436. VARIANT variant;
  1437. BSTR propName = strProp.AllocSysString();
  1438. VariantInit(&variant);
  1439. VariantClear(&variant);
  1440. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  1441. {
  1442. // If the property we just got is an array, we should convert it to string
  1443. // containing a list of the items in the array.
  1444. if ((variant.vt & VT_ARRAY) && (variant.vt & VT_BSTR) && variant.parray)
  1445. {
  1446. if (SafeArrayGetDim(variant.parray) == 1)
  1447. {
  1448. long lLower = 0, lUpper = 0;
  1449. SafeArrayGetLBound(variant.parray, 0, &lLower);
  1450. SafeArrayGetUBound(variant.parray, 0, &lUpper);
  1451. CString strWorking;
  1452. BSTR bstr = NULL;
  1453. for (long i = lLower; i <= lUpper; i++)
  1454. if (SUCCEEDED(SafeArrayGetElement(variant.parray, &i, (wchar_t*)&bstr)))
  1455. {
  1456. if (i != lLower)
  1457. strWorking += _T(", ");
  1458. strWorking += bstr;
  1459. }
  1460. strResult = strWorking;
  1461. return TRUE;
  1462. }
  1463. }
  1464. else if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  1465. {
  1466. strResult = V_BSTR(&variant);
  1467. CString strFound;
  1468. if (fUseValueMap && SUCCEEDED(Version5CheckValueMap(strClass, strProp, strResult, strFound)))
  1469. strResult = strFound;
  1470. return TRUE;
  1471. }
  1472. else
  1473. strResult = m_strPropertyUnavail;
  1474. }
  1475. else
  1476. strResult = m_strBadProperty;
  1477. }
  1478. break;
  1479. case MOS_MSG_INSTANCE:
  1480. pObject->GetErrorLabel(strResult);
  1481. break;
  1482. case MOS_NO_INSTANCES:
  1483. default:
  1484. ASSERT(FALSE);
  1485. break;
  1486. }
  1487. return FALSE;
  1488. }
  1489. //-----------------------------------------------------------------------------
  1490. // This method is equivalent to QueryValue, except it returns a DWORD value.
  1491. // If FALSE is returned, then the string in strMessage should be displayed.
  1492. //-----------------------------------------------------------------------------
  1493. BOOL CWMILiveHelper::Version5QueryValueDWORD(const CString & strClass, const CString & strProperty, DWORD & dwResult, CString & strMessage)
  1494. {
  1495. dwResult = 0L;
  1496. strMessage.Empty();
  1497. CMSIObject * pObject = Version5GetObject(strClass, NULL);
  1498. ASSERT(pObject);
  1499. if (!pObject)
  1500. return FALSE;
  1501. switch (pObject->IsValid())
  1502. {
  1503. case MOS_INSTANCE:
  1504. {
  1505. VARIANT variant;
  1506. BSTR propName = strProperty.AllocSysString();
  1507. VariantInit(&variant);
  1508. VariantClear(&variant);
  1509. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  1510. {
  1511. if (VariantChangeType(&variant, &variant, 0, VT_I4) == S_OK)
  1512. {
  1513. dwResult = V_I4(&variant);
  1514. return TRUE;
  1515. }
  1516. else
  1517. strMessage = m_strPropertyUnavail;
  1518. }
  1519. else
  1520. strMessage = m_strBadProperty;
  1521. }
  1522. break;
  1523. case MOS_MSG_INSTANCE:
  1524. pObject->GetErrorLabel(strMessage);
  1525. break;
  1526. case MOS_NO_INSTANCES:
  1527. default:
  1528. ASSERT(FALSE);
  1529. break;
  1530. }
  1531. return FALSE;
  1532. }
  1533. //-----------------------------------------------------------------------------
  1534. // This method is equivalent to QueryValue, except it returns a double float
  1535. // value. If FALSE is returned, then the string in strMessage should
  1536. // be displayed.
  1537. //-----------------------------------------------------------------------------
  1538. BOOL CWMILiveHelper::Version5QueryValueDoubleFloat(const CString & strClass, const CString & strProperty, double & dblResult, CString & strMessage)
  1539. {
  1540. dblResult = 0L;
  1541. strMessage.Empty();
  1542. CMSIObject * pObject = Version5GetObject(strClass, NULL);
  1543. ASSERT(pObject);
  1544. if (!pObject)
  1545. return FALSE;
  1546. switch (pObject->IsValid())
  1547. {
  1548. case MOS_INSTANCE:
  1549. {
  1550. VARIANT variant;
  1551. BSTR propName = strProperty.AllocSysString();
  1552. VariantInit(&variant);
  1553. VariantClear(&variant);
  1554. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  1555. {
  1556. if (VariantChangeType(&variant, &variant, 0, VT_R8) == S_OK)
  1557. {
  1558. dblResult = V_R8(&variant);
  1559. return TRUE;
  1560. }
  1561. else
  1562. strMessage = m_strPropertyUnavail;
  1563. }
  1564. else
  1565. strMessage = m_strBadProperty;
  1566. }
  1567. break;
  1568. case MOS_MSG_INSTANCE:
  1569. pObject->GetErrorLabel(strMessage);
  1570. break;
  1571. case MOS_NO_INSTANCES:
  1572. default:
  1573. ASSERT(FALSE);
  1574. break;
  1575. }
  1576. return FALSE;
  1577. }
  1578. //-----------------------------------------------------------------------------
  1579. // This method is equivalent to QueryValue, except it returns an OLE date
  1580. // & time object. If FALSE is returned, then the string in strMessage should
  1581. // be displayed.
  1582. //-----------------------------------------------------------------------------
  1583. BOOL CWMILiveHelper::Version5QueryValueDateTime(const CString & strClass, const CString & strProperty, COleDateTime & datetime, CString & strMessage)
  1584. {
  1585. datetime.SetDateTime(0, 1, 1, 0, 0, 0);
  1586. strMessage.Empty();
  1587. CMSIObject * pObject = Version5GetObject(strClass, NULL);
  1588. ASSERT(pObject);
  1589. if (!pObject)
  1590. return FALSE;
  1591. switch (pObject->IsValid())
  1592. {
  1593. case MOS_INSTANCE:
  1594. {
  1595. VARIANT variant;
  1596. BSTR propName = strProperty.AllocSysString();
  1597. VariantInit(&variant);
  1598. VariantClear(&variant);
  1599. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  1600. {
  1601. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  1602. {
  1603. // Parse the date and time into an COleDateTime object. Note: we should
  1604. // be able to get an OLE date from WBEM, but for now we need to just
  1605. // deal with the string returned.
  1606. int nYear, nMonth, nDay, nHour, nMin, nSec;
  1607. CString strTemp = V_BSTR(&variant);
  1608. nYear = _ttoi(strTemp.Mid(0, 4));
  1609. nMonth = _ttoi(strTemp.Mid(4, 2));
  1610. nDay = _ttoi(strTemp.Mid(6, 2));
  1611. nHour = _ttoi(strTemp.Mid(8, 2));
  1612. nMin = _ttoi(strTemp.Mid(10, 2));
  1613. nSec = _ttoi(strTemp.Mid(12, 2));
  1614. datetime.SetDateTime(nYear, nMonth, nDay, nHour, nMin, nSec);
  1615. return TRUE;
  1616. }
  1617. else
  1618. strMessage = m_strPropertyUnavail;
  1619. }
  1620. else
  1621. strMessage = m_strBadProperty;
  1622. }
  1623. break;
  1624. case MOS_MSG_INSTANCE:
  1625. pObject->GetErrorLabel(strMessage);
  1626. break;
  1627. case MOS_NO_INSTANCES:
  1628. default:
  1629. ASSERT(FALSE);
  1630. break;
  1631. }
  1632. return FALSE;
  1633. }
  1634. //-----------------------------------------------------------------------------
  1635. // Evaluate whether or not a specific object meets the filtering requirements
  1636. // set by the constraints (filtering are the constraints where one half is
  1637. // a static value).
  1638. //-----------------------------------------------------------------------------
  1639. BOOL CWMILiveHelper::Version5EvaluateFilter(IWbemClassObject * pObject, const GATH_FIELD * pConstraints)
  1640. {
  1641. const GATH_FIELD * pLHS = pConstraints, * pRHS = NULL;
  1642. VARIANT variant;
  1643. CString strValue;
  1644. BSTR propName;
  1645. ASSERT(pObject);
  1646. if (pObject == NULL)
  1647. return FALSE;
  1648. while (pLHS && pLHS->m_pNext)
  1649. {
  1650. pRHS = pLHS->m_pNext;
  1651. VariantInit(&variant);
  1652. // If either the left or right hand side is static, we need to do the check.
  1653. // First check out if the left side is static.
  1654. if (pLHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0 && pRHS->m_pArgs)
  1655. {
  1656. propName = pRHS->m_pArgs->m_strText.AllocSysString();
  1657. strValue.Empty();
  1658. VariantClear(&variant);
  1659. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  1660. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  1661. {
  1662. strValue = V_BSTR(&variant);
  1663. if (strValue.CompareNoCase(pLHS->m_strFormat) != 0)
  1664. return FALSE;
  1665. }
  1666. }
  1667. // Next check out if the right side is static.
  1668. if (pRHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0 && pLHS->m_pArgs)
  1669. {
  1670. propName = pLHS->m_pArgs->m_strText.AllocSysString();
  1671. strValue.Empty();
  1672. VariantClear(&variant);
  1673. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  1674. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  1675. {
  1676. strValue = V_BSTR(&variant);
  1677. if (strValue.CompareNoCase(pRHS->m_strFormat) != 0)
  1678. return FALSE;
  1679. }
  1680. }
  1681. // Advance our pointer to the left hand side by two.
  1682. pLHS = pRHS->m_pNext;
  1683. }
  1684. return TRUE;
  1685. }
  1686. //-----------------------------------------------------------------------------
  1687. // This method uses an object interface and the constraint fields to advance
  1688. // any joined classes to the correct instances.
  1689. //-----------------------------------------------------------------------------
  1690. void CWMILiveHelper::Version5EvaluateJoin(const CString & strClass, IWbemClassObject * pObject, const GATH_FIELD * pConstraints)
  1691. {
  1692. const GATH_FIELD *pLHS = pConstraints, *pRHS = NULL;
  1693. const GATH_FIELD *pEnumerated, *pJoinedTo;
  1694. GATH_FIELD fieldEnumerated, fieldJoinedTo;
  1695. VARIANT variant;
  1696. CString strValue;
  1697. BSTR propName;
  1698. ASSERT(pObject);
  1699. if (pObject == NULL)
  1700. return;
  1701. while (pLHS && pLHS->m_pNext)
  1702. {
  1703. pRHS = pLHS->m_pNext;
  1704. // If either side is static, this is a filter, rather than a join.
  1705. if ((pRHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0) ||
  1706. (pLHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0))
  1707. {
  1708. pLHS = pRHS->m_pNext;
  1709. continue;
  1710. }
  1711. // Find out which side refers to the class we're enumerating.
  1712. if (pRHS->m_strSource.CompareNoCase(strClass) == 0)
  1713. {
  1714. pEnumerated = pRHS;
  1715. pJoinedTo = pLHS;
  1716. }
  1717. else if (pLHS->m_strSource.CompareNoCase(strClass) == 0)
  1718. {
  1719. pEnumerated = pLHS;
  1720. pJoinedTo = pRHS;
  1721. }
  1722. else
  1723. {
  1724. pLHS = pRHS->m_pNext;
  1725. continue;
  1726. }
  1727. // Next, enumerate through the instances of the joined to class until
  1728. // we find one which satisfies the constraint. We can use the EvaluateFilter
  1729. // method to find out when the constraint is met. Set up a field pointer
  1730. // for the constraint (get the value from the enumerated class and use it
  1731. // as a static.
  1732. fieldJoinedTo = *pJoinedTo;
  1733. fieldJoinedTo.m_pNext = NULL;
  1734. VariantInit(&variant);
  1735. strValue.Empty();
  1736. if (pEnumerated->m_pArgs)
  1737. {
  1738. propName = pEnumerated->m_pArgs->m_strText.AllocSysString();
  1739. VariantClear(&variant);
  1740. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  1741. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  1742. {
  1743. strValue = V_BSTR(&variant);
  1744. }
  1745. }
  1746. fieldEnumerated.m_strSource = CString(STATIC_SOURCE);
  1747. fieldEnumerated.m_pNext = &fieldJoinedTo;
  1748. fieldEnumerated.m_strFormat = strValue;
  1749. // Now, enumerate the joined to class until it meets the constraints.
  1750. Version5RemoveObject(pJoinedTo->m_strSource);
  1751. Version5ResetClass(pJoinedTo->m_strSource, &fieldEnumerated);
  1752. Version5GetObject(pJoinedTo->m_strSource, &fieldEnumerated);
  1753. // Advance our pointer to the left hand side by two.
  1754. pLHS = pRHS->m_pNext;
  1755. }
  1756. // Because the GATH_FIELD destructor follows next pointers, we want
  1757. // to unlink our two GATH_FIELD locals. Also, we don't want the
  1758. // destructor for fieldJoinedTo to delete the arguments.
  1759. fieldEnumerated.m_pNext = NULL;
  1760. fieldJoinedTo.m_pArgs = NULL;
  1761. }
  1762. //-----------------------------------------------------------------------------
  1763. // Evaluate whether or not the constraints indicate that a class is being
  1764. // enumerated as a dependency class. This is currently indicated by a single
  1765. // field structure with a static value of "dependency".
  1766. //-----------------------------------------------------------------------------
  1767. BOOL CWMILiveHelper::Version5IsDependencyJoin(const GATH_FIELD * pConstraints)
  1768. {
  1769. if (pConstraints != NULL && pConstraints->m_pNext == NULL)
  1770. if (pConstraints->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0)
  1771. if (pConstraints->m_strFormat.CompareNoCase(CString(DEPENDENCY_JOIN)) == 0)
  1772. return TRUE;
  1773. return FALSE;
  1774. }
  1775. //-----------------------------------------------------------------------------
  1776. // This method is used when a dependency class is being enumerated. In a
  1777. // dependency class, each property of a class instance contains a reference
  1778. // to an instance in another class. This method will cache eache of the
  1779. // instances specified by the dependency class so properties of those instances
  1780. // can be referred to in the line structures.
  1781. //-----------------------------------------------------------------------------
  1782. void CWMILiveHelper::Version5EvaluateDependencyJoin(IWbemClassObject * pObject)
  1783. {
  1784. VARIANT variant, varClassName;
  1785. IWbemClassObject * pNewObject = NULL;
  1786. //if (pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_LOCAL_ONLY) == S_OK)
  1787. //while (pObject->Next(0, NULL, &variant, NULL, NULL) == S_OK)
  1788. VariantInit(&variant);
  1789. VariantClear(&variant);
  1790. if (pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_NONSYSTEM_ONLY) == WBEM_S_NO_ERROR)
  1791. while (pObject->Next(0, NULL, &variant, NULL, NULL) == WBEM_S_NO_ERROR)
  1792. {
  1793. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  1794. {
  1795. // Use the object path to create a pointer to the specified
  1796. // object and store it in the cache.
  1797. CString strObjectPath = V_BSTR(&variant);
  1798. BSTR bstrObjectPath = strObjectPath.AllocSysString();
  1799. HRESULT hRes = m_pIWbemServices->GetObject(bstrObjectPath, 0, NULL, &pNewObject, NULL);
  1800. if (SUCCEEDED(hRes))
  1801. {
  1802. // We need to get the class name of the new object so we know
  1803. // where to cache it. We could parse it out of the object path,
  1804. // but it will be better in the long run to get it by querying
  1805. // the new object.
  1806. if (pNewObject)
  1807. {
  1808. CString strClassName, strClassNameProp(_T("__CLASS"));
  1809. BSTR classNameProp = strClassNameProp.AllocSysString();
  1810. VariantInit(&varClassName);
  1811. VariantClear(&varClassName);
  1812. if (pNewObject->Get(classNameProp, 0L, &varClassName, NULL, NULL) == S_OK)
  1813. if (VariantChangeType(&varClassName, &varClassName, 0, VT_BSTR) == S_OK)
  1814. strClassName = V_BSTR(&varClassName);
  1815. if (!strClassName.IsEmpty())
  1816. {
  1817. CMSIObject * pNewMSIObject = new CMSIObject(pNewObject, CString(_T("")), S_OK, this, MOS_INSTANCE);
  1818. if (pNewMSIObject)
  1819. {
  1820. CMSIObject * pOldObject = NULL;
  1821. if (m_mapClassToInterface.Lookup(strClassName, (void * &) pOldObject) && pOldObject)
  1822. delete pOldObject;
  1823. m_mapClassToInterface.SetAt(strClassName, (void *) pNewMSIObject);
  1824. }
  1825. }
  1826. else
  1827. {
  1828. delete pNewObject;
  1829. pNewObject = NULL;
  1830. }
  1831. }
  1832. }
  1833. }
  1834. VariantClear(&variant);
  1835. }
  1836. }
  1837. //-----------------------------------------------------------------------------
  1838. // Remove the specified IEnumWbemClassObject pointer from the cache.
  1839. //-----------------------------------------------------------------------------
  1840. void CWMILiveHelper::Version5RemoveEnumObject(const CString & strClass)
  1841. {
  1842. CMSIEnumerator * pEnumObject = NULL;
  1843. if (m_mapClassToEnumInterface.Lookup(strClass, (void * &) pEnumObject) && pEnumObject)
  1844. delete pEnumObject;
  1845. m_mapClassToEnumInterface.RemoveKey(strClass);
  1846. }
  1847. //-----------------------------------------------------------------------------
  1848. // Clear out the contents of the caches (forcing the interfaces to be
  1849. // retrieved again).
  1850. //-----------------------------------------------------------------------------
  1851. void CWMILiveHelper::Version5ClearCache()
  1852. {
  1853. CMSIObject * pObject = NULL;
  1854. CMSIEnumerator * pEnumObject = NULL;
  1855. POSITION pos;
  1856. CString strClass;
  1857. for (pos = m_mapClassToInterface.GetStartPosition(); pos != NULL;)
  1858. {
  1859. m_mapClassToInterface.GetNextAssoc(pos, strClass, (void * &) pObject);
  1860. if (pObject)
  1861. delete pObject;
  1862. }
  1863. m_mapClassToInterface.RemoveAll();
  1864. for (pos = m_mapClassToEnumInterface.GetStartPosition(); pos != NULL;)
  1865. {
  1866. m_mapClassToEnumInterface.GetNextAssoc(pos, strClass, (void * &) pEnumObject);
  1867. if (pEnumObject)
  1868. delete pEnumObject;
  1869. }
  1870. m_mapClassToEnumInterface.RemoveAll();
  1871. }
  1872. //-----------------------------------------------------------------------------
  1873. // This function is used to retrieve a pointer to IWbemServices for a
  1874. // particular namespace. The default namespace is cimv2.
  1875. //-----------------------------------------------------------------------------
  1876. IWbemServices * CWMILiveHelper::Version5GetWBEMService(CString * pstrNamespace)
  1877. {
  1878. if (pstrNamespace == NULL || pstrNamespace->IsEmpty())
  1879. return m_pServices;
  1880. // Something like the following is useful for forcing a provider error when
  1881. // testing the error containment:
  1882. //
  1883. // if (*pstrNamespace == _T("MSAPPS")) *pstrNamespace += _T("X");
  1884. IWbemServices * pServices;
  1885. // In 5.0 we kept a map, but we probably won't do it here...
  1886. //
  1887. // if (m_mapNamespaceToService.Lookup(*pstrNamespace, (void * &) pServices) && pServices)
  1888. // return pServices;
  1889. // There is no WBEM services pointer for that namespace, we need to create one.
  1890. CString strNamespace(_T(""));
  1891. if (m_strMachine.IsEmpty())
  1892. strNamespace = CString(_T("\\\\.\\root\\")) + *pstrNamespace;
  1893. else
  1894. {
  1895. if (m_strMachine.Right(1) == CString(_T("\\")))
  1896. strNamespace = m_strMachine + CString(_T("root\\")) + *pstrNamespace;
  1897. else
  1898. strNamespace = m_strMachine + CString(_T("\\root\\")) + *pstrNamespace;
  1899. if (strNamespace.Left(2).Compare(CString(_T("\\\\"))) != 0)
  1900. strNamespace = CString(_T("\\\\")) + strNamespace;
  1901. }
  1902. IWbemLocator * pIWbemLocator = NULL;
  1903. if (CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pIWbemLocator) == S_OK)
  1904. {
  1905. BSTR pNamespace = strNamespace.AllocSysString();
  1906. HRESULT hrServer = pIWbemLocator->ConnectServer(pNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pServices);
  1907. if (pNamespace)
  1908. SysFreeString(pNamespace);
  1909. if (pIWbemLocator)
  1910. {
  1911. pIWbemLocator->Release();
  1912. pIWbemLocator = NULL;
  1913. }
  1914. if (SUCCEEDED(hrServer) && pServices)
  1915. {
  1916. ChangeWBEMSecurity(pServices);
  1917. // In 5.0 we kept a map, but we probably won't do it here...
  1918. //
  1919. // m_mapNamespaceToService.SetAt(*pstrNamespace, (void *) pServices);
  1920. if (m_pIWbemServices)
  1921. m_pIWbemServices->Release();
  1922. m_pIWbemServices = pServices;
  1923. m_pIWbemServices->AddRef();
  1924. return pServices;
  1925. }
  1926. m_hrLastVersion5Error = hrServer;
  1927. }
  1928. return NULL;
  1929. }
  1930. //-----------------------------------------------------------------------------
  1931. // The CMSIEnumerator class encapsulates the WBEM enumerator interface, or
  1932. // implements our own form of enumerator (such as for the LNK command in the
  1933. // template file).
  1934. //
  1935. // Nothing particularly interesting about the constructor or destructor.
  1936. //-----------------------------------------------------------------------------
  1937. CMSIEnumerator::CMSIEnumerator()
  1938. {
  1939. m_enumtype = CMSIEnumerator::CLASS;
  1940. m_fOnlyDups = FALSE;
  1941. m_fGotDuplicate = FALSE;
  1942. m_fMinOfOne = FALSE;
  1943. m_iMinOfOneCount = 0;
  1944. m_pEnum = NULL;
  1945. m_pWMI = NULL;
  1946. m_pConstraints = NULL;
  1947. m_pSavedDup = NULL;
  1948. m_pstrList = NULL;
  1949. m_hresCreation = S_OK;
  1950. }
  1951. CMSIEnumerator::~CMSIEnumerator()
  1952. {
  1953. if (m_pEnum)
  1954. {
  1955. switch (m_enumtype)
  1956. {
  1957. case CMSIEnumerator::WQL:
  1958. break;
  1959. case CMSIEnumerator::LNK:
  1960. m_pWMI->m_enumMap.SetEnumerator(m_strAssocClass, m_pEnum);
  1961. break;
  1962. case CMSIEnumerator::INTERNAL:
  1963. if (m_pstrList)
  1964. {
  1965. delete m_pstrList;
  1966. m_pstrList = NULL;
  1967. }
  1968. break;
  1969. case CMSIEnumerator::CLASS:
  1970. default:
  1971. m_pWMI->m_enumMap.SetEnumerator(m_strClass, m_pEnum);
  1972. break;
  1973. }
  1974. m_pEnum->Release();
  1975. m_pEnum = NULL;
  1976. }
  1977. }
  1978. //-----------------------------------------------------------------------------
  1979. // Creating the CMSIEnumerator object involves determining what sort of
  1980. // enumerator is required. We support the following types:
  1981. //
  1982. // 1. Straight enumeration of a class
  1983. // 2. Enumerate class, with applied constraints
  1984. // 3. Enumerate the results of a WQL statement.
  1985. // 4. Interprete a LNK command to enumerate associated classes.
  1986. // 5. Do internal processing on an INTERNAL type.
  1987. //-----------------------------------------------------------------------------
  1988. HRESULT CMSIEnumerator::Create(const CString & strClass, const GATH_FIELD * pConstraints, CWMIHelper * pWMI)
  1989. {
  1990. if (strClass.IsEmpty() || !pWMI)
  1991. return E_INVALIDARG;
  1992. // Create may be called multiple times (to reset the enumerator). So we may need to
  1993. // release the enumerator pointer.
  1994. if (m_pEnum)
  1995. {
  1996. m_pEnum->Release();
  1997. m_pEnum = NULL;
  1998. }
  1999. // Divide the specified class into class and namespace parts, get the WBEM service.
  2000. CString strNamespacePart(_T("")), strClassPart(strClass);
  2001. int i = strClass.Find(_T(":"));
  2002. if (i != -1)
  2003. {
  2004. strNamespacePart = strClass.Left(i);
  2005. strClassPart = strClass.Right(strClass.GetLength() - i - 1);
  2006. }
  2007. IWbemServices * pServices = pWMI->Version5GetWBEMService(&strNamespacePart);
  2008. if (pServices == NULL)
  2009. return NULL;
  2010. // First, we need to determine what type of enumerator this is. Scan through
  2011. // the constraints - if we see one which has a string starting with "WQL:" or
  2012. // "LNK:", then we know what type this enumerator is.
  2013. CString strStatement;
  2014. const GATH_FIELD * pScanConstraint = pConstraints;
  2015. while (pScanConstraint)
  2016. {
  2017. if (pScanConstraint->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0)
  2018. {
  2019. if (pScanConstraint->m_strFormat.Left(4).CompareNoCase(CString(_T("WQL:"))) == 0)
  2020. m_enumtype = CMSIEnumerator::WQL;
  2021. else if (pScanConstraint->m_strFormat.Left(4).CompareNoCase(CString(_T("LNK:"))) == 0)
  2022. m_enumtype = CMSIEnumerator::LNK;
  2023. else if (pScanConstraint->m_strFormat.Left(4).CompareNoCase(CString(_T("INT:"))) == 0)
  2024. m_enumtype = CMSIEnumerator::INTERNAL;
  2025. if (m_enumtype != CMSIEnumerator::CLASS)
  2026. {
  2027. strStatement = pScanConstraint->m_strFormat;
  2028. strStatement = strStatement.Right(strStatement.GetLength() - 4);
  2029. break;
  2030. }
  2031. }
  2032. pScanConstraint = pScanConstraint->m_pNext;
  2033. }
  2034. // If this is a WQL or a LNK enumerator, processes the statement by replacing
  2035. // [class.property] with the actual value from WBEM. If we find the string
  2036. // "[min-of-one]", make a note of it for later.
  2037. if (m_enumtype == CMSIEnumerator::WQL)
  2038. ProcessEnumString(strStatement, m_fMinOfOne, m_fOnlyDups, pWMI, m_strNoInstanceLabel, TRUE);
  2039. else if (m_enumtype == CMSIEnumerator::LNK)
  2040. if (SUCCEEDED(ParseLNKCommand(strStatement, m_strObjPath, m_strAssocClass, m_strResultClass)))
  2041. {
  2042. // Save the object path for later - so we can change the object without
  2043. // completely reprocessing the statement. Then replace the keywords in
  2044. // the statement and break out the pieces again.
  2045. m_strLNKObject = m_strObjPath;
  2046. ProcessEnumString(strStatement, m_fMinOfOne, m_fOnlyDups, pWMI, m_strNoInstanceLabel);
  2047. ParseLNKCommand(strStatement, m_strObjPath, m_strAssocClass, m_strResultClass);
  2048. }
  2049. // Now, based on the enumerator type, create the WBEM enumerator object.
  2050. switch (m_enumtype)
  2051. {
  2052. case CMSIEnumerator::WQL:
  2053. {
  2054. BSTR language = SysAllocString(L"WQL");
  2055. BSTR query = strStatement.AllocSysString();
  2056. m_hresCreation = pServices->ExecQuery(language, query, WBEM_FLAG_RETURN_IMMEDIATELY, 0, &m_pEnum);
  2057. SysFreeString(query);
  2058. SysFreeString(language);
  2059. }
  2060. break;
  2061. case CMSIEnumerator::LNK:
  2062. {
  2063. m_hresCreation = ParseLNKCommand(strStatement, m_strObjPath, m_strAssocClass, m_strResultClass);
  2064. if (SUCCEEDED(m_hresCreation))
  2065. {
  2066. BSTR className = m_strAssocClass.AllocSysString();
  2067. m_pEnum = pWMI->m_enumMap.GetEnumerator(m_strAssocClass);
  2068. if (m_pEnum)
  2069. m_hresCreation = S_OK;
  2070. else
  2071. m_hresCreation = pServices->CreateInstanceEnum(className, WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &m_pEnum);
  2072. SysFreeString(className);
  2073. }
  2074. }
  2075. break;
  2076. case CMSIEnumerator::INTERNAL:
  2077. // We'll call a function here so we can do whatever processing is required
  2078. // to create this internal enumeration.
  2079. m_hresCreation = CreateInternalEnum(strStatement, pWMI);
  2080. break;
  2081. case CMSIEnumerator::CLASS:
  2082. default:
  2083. {
  2084. BSTR className = strClassPart.AllocSysString();
  2085. m_pEnum = pWMI->m_enumMap.GetEnumerator(strClassPart);
  2086. if (m_pEnum)
  2087. m_hresCreation = S_OK;
  2088. else
  2089. m_hresCreation = pServices->CreateInstanceEnum(className, WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &m_pEnum);
  2090. SysFreeString(className);
  2091. }
  2092. }
  2093. // Set some of the other member variables.
  2094. m_strClass = strClass;
  2095. m_pWMI = pWMI;
  2096. m_iMinOfOneCount = (m_fMinOfOne) ? 1 : 0;
  2097. m_pConstraints = pConstraints;
  2098. if (m_pEnum)
  2099. ChangeWBEMSecurity(m_pEnum);
  2100. // Based on the HRESULT from creating the enumeration, determine what to return.
  2101. // For certain errors, we want to act like the creation succeeded, then supply
  2102. // objects which return the error text.
  2103. if (FAILED(m_hresCreation))
  2104. {
  2105. m_fMinOfOne = TRUE;
  2106. m_iMinOfOneCount = 1;
  2107. }
  2108. pServices->Release();
  2109. return S_OK;
  2110. }
  2111. //-----------------------------------------------------------------------------
  2112. // This function is used to create internal enumeration types (enumerations
  2113. // which are beyond the template file syntax). Basically a bunch of special
  2114. // cases.
  2115. //-----------------------------------------------------------------------------
  2116. HRESULT CMSIEnumerator::CreateInternalEnum(const CString & strInternal, CWMIHelper * pWMI)
  2117. {
  2118. if (strInternal.CompareNoCase(CString(_T("dlls"))) == 0)
  2119. {
  2120. // We want to enumerate all the loaded dlls and exes on the system.
  2121. // This can be done by enumerating the CIM_ProcessExecutable class
  2122. // and removing duplicate file names. We'll keep the filenames (with
  2123. // path information) in a string list.
  2124. if (m_pstrList == NULL)
  2125. {
  2126. m_pstrList = new CStringList;
  2127. if (m_pstrList == NULL)
  2128. return E_FAIL;
  2129. }
  2130. else
  2131. m_pstrList->RemoveAll();
  2132. HRESULT hr = S_OK;
  2133. IWbemServices * pServices = pWMI->Version5GetWBEMService();
  2134. if (pServices)
  2135. {
  2136. BSTR className = SysAllocString(L"CIM_ProcessExecutable");
  2137. IEnumWbemClassObject * pEnum = NULL;
  2138. hr = pServices->CreateInstanceEnum(className, WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnum);
  2139. if (SUCCEEDED(hr))
  2140. {
  2141. IWbemClassObject * pWBEMObject = NULL;
  2142. ULONG uReturned;
  2143. VARIANT variant;
  2144. BSTR propName = SysAllocString(L"Antecedent");
  2145. VariantInit(&variant);
  2146. do
  2147. {
  2148. uReturned = 0;
  2149. hr = pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned);
  2150. if (SUCCEEDED(hr) && pWBEMObject && uReturned)
  2151. {
  2152. // For each instance of CIM_ProcessExecutable, get the
  2153. // Antecedent property (which contains the file path).
  2154. // If it is unique, save it in the list.
  2155. VariantClear(&variant);
  2156. if (pWBEMObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  2157. {
  2158. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  2159. {
  2160. CString strResult = V_BSTR(&variant);
  2161. strResult.MakeLower();
  2162. if (m_pstrList->Find(strResult) == NULL)
  2163. m_pstrList->AddHead(strResult);
  2164. }
  2165. }
  2166. }
  2167. } while (SUCCEEDED(hr) && pWBEMObject && uReturned);
  2168. ::SysFreeString(propName);
  2169. pEnum->Release();
  2170. }
  2171. ::SysFreeString(className);
  2172. pServices->Release();
  2173. }
  2174. return hr;
  2175. }
  2176. return S_OK;
  2177. }
  2178. //-----------------------------------------------------------------------------
  2179. // Help function for ProcessEnumString, used to convert single backslashes
  2180. // into double backslashes (required for WQL statements).
  2181. //-----------------------------------------------------------------------------
  2182. void MakeDoubleBackslashes(CString & strValue)
  2183. {
  2184. CString strTemp(strValue);
  2185. CString strResults;
  2186. while (!strTemp.IsEmpty())
  2187. {
  2188. if (strTemp[0] != _T('\\'))
  2189. {
  2190. int index = strTemp.Find(_T('\\'));
  2191. if (index < 0)
  2192. index = strTemp.GetLength();
  2193. strResults += strTemp.Left(index);
  2194. strTemp = strTemp.Right(strTemp.GetLength() - index);
  2195. }
  2196. else
  2197. {
  2198. strResults += CString("\\\\");
  2199. strTemp = strTemp.Mid(1);
  2200. }
  2201. }
  2202. strValue = strResults;
  2203. }
  2204. //-----------------------------------------------------------------------------
  2205. // This function replaces [class.property] with the actual value of the
  2206. // property, and strings out [min-of-one], indicating if it was present in
  2207. // the fMinOfOne parameter.
  2208. //-----------------------------------------------------------------------------
  2209. void CMSIEnumerator::ProcessEnumString(CString & strStatement, BOOL & fMinOfOne, BOOL & fOnlyDups, CWMIHelper * pWMI, CString & strNoInstanceLabel, BOOL fMakeDoubleBackslashes)
  2210. {
  2211. CString strMinOfOne(_T("min-of-one"));
  2212. CString strOnlyDups(_T("more-than-one"));
  2213. CString strResults;
  2214. fMinOfOne = FALSE;
  2215. fOnlyDups = FALSE;
  2216. while (!strStatement.IsEmpty())
  2217. {
  2218. if (strStatement[0] != _T('['))
  2219. {
  2220. int index = strStatement.Find(_T('['));
  2221. if (index < 0)
  2222. index = strStatement.GetLength();
  2223. strResults += strStatement.Left(index);
  2224. strStatement = strStatement.Right(strStatement.GetLength() - index);
  2225. }
  2226. else
  2227. {
  2228. CString strKeyword;
  2229. strStatement = strStatement.Right(strStatement.GetLength() - 1);
  2230. int index = strStatement.Find(_T(']'));
  2231. if (index < 0)
  2232. break;
  2233. strKeyword = strStatement.Left(index);
  2234. if (strKeyword.Left(strMinOfOne.GetLength()).CompareNoCase(strMinOfOne) == 0)
  2235. {
  2236. fMinOfOne = TRUE;
  2237. int iEqualsIndex = strKeyword.Find(_T('='));
  2238. if (iEqualsIndex > 0)
  2239. strNoInstanceLabel = strKeyword.Right(strKeyword.GetLength() - iEqualsIndex - 1);
  2240. }
  2241. else if (strKeyword.Left(strOnlyDups.GetLength()).CompareNoCase(strOnlyDups) == 0)
  2242. {
  2243. fOnlyDups = TRUE;
  2244. int iEqualsIndex = strKeyword.Find(_T('='));
  2245. if (iEqualsIndex > 0)
  2246. strNoInstanceLabel = strKeyword.Right(strKeyword.GetLength() - iEqualsIndex - 1);
  2247. }
  2248. else if (!strKeyword.IsEmpty())
  2249. {
  2250. int iDotIndex = strKeyword.Find(_T('.'));
  2251. if (iDotIndex >= 0)
  2252. {
  2253. CString strValue;
  2254. if (pWMI->Version5QueryValue(strKeyword.Left(iDotIndex), strKeyword.Right(strKeyword.GetLength() - iDotIndex - 1), strValue))
  2255. {
  2256. if (fMakeDoubleBackslashes)
  2257. MakeDoubleBackslashes(strValue);
  2258. strResults += strValue;
  2259. }
  2260. }
  2261. }
  2262. strStatement = strStatement.Right(strStatement.GetLength() - (index + 1));
  2263. }
  2264. }
  2265. strStatement = strResults;
  2266. }
  2267. //-----------------------------------------------------------------------------
  2268. // Parse the component classes from the LNK command.
  2269. //-----------------------------------------------------------------------------
  2270. HRESULT CMSIEnumerator::ParseLNKCommand(const CString & strStatement, CString & strObjPath, CString & strAssocClass, CString & strResultClass)
  2271. {
  2272. // We need to parse out the LNK statement into two or three components,
  2273. // from the form "objPath->assocClass[->resultClass]", with the
  2274. // brackets indicating that the resultClass is optional.
  2275. CString strWorking(strStatement);
  2276. int iArrowIndex = strWorking.Find(_T("->"));
  2277. if (iArrowIndex == -1)
  2278. return E_INVALIDARG;
  2279. strObjPath = strWorking.Left(iArrowIndex);
  2280. strWorking = strWorking.Right(strWorking.GetLength() - (iArrowIndex + 2));
  2281. iArrowIndex = strWorking.Find(_T("->"));
  2282. if (iArrowIndex == -1)
  2283. strAssocClass = strWorking;
  2284. else
  2285. {
  2286. strAssocClass = strWorking.Left(iArrowIndex);
  2287. strWorking = strWorking.Right(strWorking.GetLength() - (iArrowIndex + 2));
  2288. strResultClass = strWorking;
  2289. strResultClass.MakeLower();
  2290. }
  2291. strAssocClass.TrimRight(); strAssocClass.TrimLeft();
  2292. strObjPath.TrimRight(); strObjPath.TrimLeft();
  2293. strResultClass.TrimRight(); strResultClass.TrimLeft();
  2294. return S_OK;
  2295. }
  2296. //-----------------------------------------------------------------------------
  2297. // The Next method will advance the enumerator based on the type of this
  2298. // enumerator.
  2299. //-----------------------------------------------------------------------------
  2300. HRESULT CMSIEnumerator::Next(CMSIObject ** ppObject)
  2301. {
  2302. if (!ppObject)
  2303. return E_INVALIDARG;
  2304. *ppObject = NULL;
  2305. // If there was an error creating the enumeration, return the error code.
  2306. if (FAILED(m_hresCreation))
  2307. return m_hresCreation;
  2308. if (m_pEnum == NULL && m_enumtype != CMSIEnumerator::INTERNAL)
  2309. return E_UNEXPECTED;
  2310. HRESULT hRes = S_OK;
  2311. IWbemClassObject * pWBEMObject = NULL;
  2312. switch (m_enumtype)
  2313. {
  2314. case CMSIEnumerator::LNK:
  2315. {
  2316. // Scan through the enumerated associate class. Look for one which
  2317. // satisfies our requirements.
  2318. CString strTemp, strAssociatedObject(_T(""));
  2319. ULONG uReturned;
  2320. IWbemClassObject * pAssocObj;
  2321. do
  2322. {
  2323. pAssocObj = NULL;
  2324. uReturned = 0;
  2325. hRes = m_pEnum->Next(TIMEOUT, 1, &pAssocObj, &uReturned);
  2326. if (!pAssocObj || FAILED(hRes) || uReturned != 1)
  2327. {
  2328. // Even if we didn't succeed in getting a new object,
  2329. // we might have a saved one if we're only showing
  2330. // "more-than-one" objects.
  2331. if (m_fOnlyDups && m_pSavedDup && m_fGotDuplicate)
  2332. {
  2333. // We have found one previously, so return it.
  2334. // Make it look like the Next call was successful.
  2335. m_pSavedDup = NULL;
  2336. hRes = S_OK;
  2337. uReturned = 1;
  2338. strAssociatedObject = m_strSavedDup;
  2339. break;
  2340. }
  2341. else
  2342. {
  2343. if (m_pSavedDup)
  2344. {
  2345. // We only got one object instance, so get rid of it.
  2346. m_pSavedDup->Release();
  2347. m_pSavedDup = NULL;
  2348. }
  2349. break;
  2350. }
  2351. }
  2352. if (AssocObjectOK(pAssocObj, strTemp))
  2353. {
  2354. // This object passed the filter - but if we're showing
  2355. // only "more-than-one" objects, save this one and return
  2356. // the saved one.
  2357. if (m_fOnlyDups)
  2358. {
  2359. if (m_pSavedDup)
  2360. {
  2361. // We have found one previously, so return it and
  2362. // save the current.
  2363. IWbemClassObject * pSwap = pAssocObj;
  2364. CString strSwap = strTemp;
  2365. pAssocObj = m_pSavedDup;
  2366. m_pSavedDup = pSwap;
  2367. strTemp = m_strSavedDup;
  2368. m_strSavedDup = strSwap;
  2369. m_fGotDuplicate = TRUE;
  2370. }
  2371. else
  2372. {
  2373. // This is the first one we've found - don't
  2374. // return it until we find another.
  2375. m_pSavedDup = pAssocObj;
  2376. m_strSavedDup = strTemp;
  2377. m_fGotDuplicate = FALSE;
  2378. continue;
  2379. }
  2380. }
  2381. strAssociatedObject = strTemp;
  2382. pAssocObj->Release();
  2383. break;
  2384. }
  2385. pAssocObj->Release();
  2386. } while (pAssocObj);
  2387. // If there is an associated object path, get the object.
  2388. if (!strAssociatedObject.IsEmpty())
  2389. {
  2390. BSTR path = strAssociatedObject.AllocSysString();
  2391. if (m_pWMI->m_pIWbemServices != NULL)
  2392. hRes = m_pWMI->m_pIWbemServices->GetObject(path, 0, NULL, &pWBEMObject, NULL);
  2393. else
  2394. hRes = E_FAIL;
  2395. SysFreeString(path);
  2396. }
  2397. }
  2398. break;
  2399. case CMSIEnumerator::WQL:
  2400. {
  2401. ULONG uReturned;
  2402. hRes = m_pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned);
  2403. }
  2404. break;
  2405. case CMSIEnumerator::INTERNAL:
  2406. hRes = InternalNext(&pWBEMObject);
  2407. break;
  2408. case CMSIEnumerator::CLASS:
  2409. default:
  2410. {
  2411. ULONG uReturned;
  2412. // EvaluateFilter and IsDependencyJoin handle a NULL pConstraints parameter,
  2413. // but for efficiency we're going to have a distinct branch for a non-NULL
  2414. // value (since it will usually be NULL).
  2415. if (m_pConstraints)
  2416. {
  2417. // Keep enumerating the instances of this class until we've
  2418. // found one which satisfies all of the filters.
  2419. do
  2420. {
  2421. pWBEMObject = NULL;
  2422. hRes = m_pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned);
  2423. if (!pWBEMObject || hRes != S_OK || uReturned != 1)
  2424. break;
  2425. else if (m_pWMI->Version5EvaluateFilter(pWBEMObject, m_pConstraints))
  2426. break;
  2427. pWBEMObject->Release();
  2428. } while (pWBEMObject);
  2429. // If this class is being enumerated as a dependency class, then
  2430. // locate all the objects it references. If it isn't, we still
  2431. // need to check for any joins to other classes formed by the constraints.
  2432. if (pWBEMObject)
  2433. if (m_pWMI->Version5IsDependencyJoin(m_pConstraints))
  2434. m_pWMI->Version5EvaluateDependencyJoin(pWBEMObject);
  2435. else
  2436. m_pWMI->Version5EvaluateJoin(m_strClass, pWBEMObject, m_pConstraints);
  2437. }
  2438. else
  2439. hRes = m_pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned);
  2440. }
  2441. break;
  2442. }
  2443. if (pWBEMObject == NULL)
  2444. {
  2445. // There was no object to get. We'll still create a CMSIObject, but
  2446. // we'll set its state to indicate either that there are no instances,
  2447. // or one instance with an error message.
  2448. if (SUCCEEDED(hRes) && (m_iMinOfOneCount == 0))
  2449. *ppObject = new CMSIObject(pWBEMObject, m_strNoInstanceLabel, hRes, m_pWMI, MOS_NO_INSTANCES);
  2450. else
  2451. *ppObject = new CMSIObject(pWBEMObject, m_strNoInstanceLabel, hRes, m_pWMI, MOS_MSG_INSTANCE);
  2452. }
  2453. else
  2454. *ppObject = new CMSIObject(pWBEMObject, m_strNoInstanceLabel, hRes, m_pWMI, MOS_INSTANCE);
  2455. if (m_iMinOfOneCount)
  2456. m_iMinOfOneCount--;
  2457. return S_OK;
  2458. }
  2459. //-----------------------------------------------------------------------------
  2460. // InternalNext is used to return a WBEM object for an internal enumeration
  2461. // (one that requires processing beyond the template file). Basically a
  2462. // set of special cases.
  2463. //-----------------------------------------------------------------------------
  2464. HRESULT CMSIEnumerator::InternalNext(IWbemClassObject ** ppWBEMObject)
  2465. {
  2466. if (m_pstrList && !m_pstrList->IsEmpty())
  2467. {
  2468. CString strNextObject = m_pstrList->RemoveHead();
  2469. if (!strNextObject.IsEmpty())
  2470. {
  2471. IWbemServices * pServices = m_pWMI->Version5GetWBEMService();
  2472. if (pServices)
  2473. {
  2474. BSTR objectpath = strNextObject.AllocSysString();
  2475. HRESULT hr = S_OK;
  2476. if (FAILED(pServices->GetObject(objectpath, 0, NULL, ppWBEMObject, NULL)))
  2477. hr = E_FAIL;
  2478. ::SysFreeString(objectpath);
  2479. pServices->Release();
  2480. return hr;
  2481. }
  2482. }
  2483. }
  2484. return S_OK;
  2485. }
  2486. //-----------------------------------------------------------------------------
  2487. // Reset should just reset the enumerator pointer.
  2488. //-----------------------------------------------------------------------------
  2489. HRESULT CMSIEnumerator::Reset(const GATH_FIELD * pConstraints)
  2490. {
  2491. HRESULT hRes = S_OK;
  2492. if (m_pEnum)
  2493. {
  2494. switch (m_enumtype)
  2495. {
  2496. case CMSIEnumerator::WQL:
  2497. hRes = Create(m_strClass, pConstraints, m_pWMI);
  2498. break;
  2499. case CMSIEnumerator::LNK:
  2500. {
  2501. BOOL fDummy, fDummy2;
  2502. CString strDummy;
  2503. m_strObjPath = m_strLNKObject;
  2504. ProcessEnumString(m_strObjPath, fDummy, fDummy2, m_pWMI, strDummy);
  2505. m_iMinOfOneCount = (m_fMinOfOne) ? 1 : 0;
  2506. hRes = m_pEnum->Reset();
  2507. }
  2508. break;
  2509. case CMSIEnumerator::INTERNAL:
  2510. hRes = Create(m_strClass, pConstraints, m_pWMI);
  2511. break;
  2512. case CMSIEnumerator::CLASS:
  2513. default:
  2514. m_iMinOfOneCount = (m_fMinOfOne) ? 1 : 0;
  2515. hRes = m_pEnum->Reset();
  2516. break;
  2517. }
  2518. }
  2519. else
  2520. hRes = E_UNEXPECTED;
  2521. return hRes;
  2522. }
  2523. //-----------------------------------------------------------------------------
  2524. // Evaluate if the pObject parameter is valid for this LNK enumerator. In
  2525. // particular, we must find the m_strObjPath in one of its properties, and
  2526. // possibly finding another property containing the m_strResultClass string.
  2527. //-----------------------------------------------------------------------------
  2528. BOOL CMSIEnumerator::AssocObjectOK(IWbemClassObject * pObject, CString & strAssociatedObject)
  2529. {
  2530. strAssociatedObject.Empty();
  2531. ASSERT(pObject);
  2532. if (pObject == NULL)
  2533. return FALSE;
  2534. VARIANT variant;
  2535. CString strReturn(_T("")), strValue;
  2536. // Traverse the set of non-system properties. Look for one the is the same
  2537. // as the object path.
  2538. pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_NONSYSTEM_ONLY);
  2539. VariantInit(&variant);
  2540. while (pObject->Next(0, NULL, &variant, NULL, NULL) == WBEM_S_NO_ERROR)
  2541. {
  2542. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  2543. strValue = V_BSTR(&variant);
  2544. VariantClear(&variant);
  2545. if (strValue.CompareNoCase(m_strObjPath) == 0)
  2546. break;
  2547. }
  2548. pObject->EndEnumeration();
  2549. // If we found a property containing the object path, look through for other
  2550. // paths which might be to objects we're insterested in.
  2551. if (strValue.CompareNoCase(m_strObjPath) == 0)
  2552. {
  2553. pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_NONSYSTEM_ONLY);
  2554. while (strReturn.IsEmpty() && (pObject->Next(0, NULL, &variant, NULL, NULL) == WBEM_S_NO_ERROR))
  2555. {
  2556. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  2557. strValue = V_BSTR(&variant);
  2558. if (strValue.CompareNoCase(m_strObjPath) != 0)
  2559. {
  2560. if (m_strResultClass.IsEmpty())
  2561. strReturn = strValue;
  2562. else
  2563. {
  2564. CString strSearch(strValue);
  2565. strSearch.MakeLower();
  2566. if (strSearch.Find(m_strResultClass) != -1)
  2567. strReturn = strValue;
  2568. }
  2569. }
  2570. VariantClear(&variant);
  2571. }
  2572. pObject->EndEnumeration();
  2573. }
  2574. if (!strReturn.IsEmpty())
  2575. {
  2576. strAssociatedObject = strReturn;
  2577. return TRUE;
  2578. }
  2579. return FALSE;
  2580. }
  2581. //-----------------------------------------------------------------------------
  2582. // Implement the CMSIObject class. This is just a thin wrapper for the
  2583. // IWbemClassObject interface.
  2584. //-----------------------------------------------------------------------------
  2585. CMSIObject::CMSIObject(IWbemClassObject * pObject, const CString & strLabel, HRESULT hres, CWMIHelper * pWMI, MSIObjectState objState)
  2586. {
  2587. m_pObject = pObject;
  2588. m_strLabel = strLabel;
  2589. m_hresCreation = hres;
  2590. m_pWMI = pWMI;
  2591. m_objState = objState;
  2592. }
  2593. CMSIObject::~CMSIObject()
  2594. {
  2595. if (m_pObject)
  2596. {
  2597. m_pObject->Release();
  2598. m_pObject = NULL;
  2599. }
  2600. }
  2601. HRESULT CMSIObject::Get(BSTR property, LONG lFlags, VARIANT *pVal, VARTYPE *pvtType, LONG *plFlavor)
  2602. {
  2603. ASSERT(m_objState != MOS_NO_INSTANCES);
  2604. // If there is an object interface, just pass the request on through.
  2605. if (m_pObject)
  2606. return m_pObject->Get(property, lFlags, pVal, NULL /* pvtType */, plFlavor);
  2607. // Otherwise, we need to return the appropriate string.
  2608. CString strReturn;
  2609. GetErrorLabel(strReturn);
  2610. V_BSTR(pVal) = strReturn.AllocSysString();
  2611. pVal->vt = VT_BSTR;
  2612. return S_OK;
  2613. }
  2614. MSIObjectState CMSIObject::IsValid()
  2615. {
  2616. return m_objState;
  2617. }
  2618. HRESULT CMSIObject::GetErrorLabel(CString & strError)
  2619. {
  2620. switch (m_hresCreation)
  2621. {
  2622. case WBEM_E_ACCESS_DENIED:
  2623. strError = m_pWMI->m_strBadProperty; // shouldn't be showing errors this way in 6.0
  2624. break;
  2625. case WBEM_E_TRANSPORT_FAILURE:
  2626. strError = m_pWMI->m_strBadProperty;
  2627. break;
  2628. case S_OK:
  2629. case WBEM_S_FALSE:
  2630. default:
  2631. // This object was created from an enumeration that was marked as "min-of-one",
  2632. // meaning that at least one object, even if it's not valid, needed to be
  2633. // returned from the enumeration. Return the string we saved at object creation.
  2634. if (!m_strLabel.IsEmpty())
  2635. strError = m_strLabel;
  2636. else
  2637. strError = m_pWMI->m_strBadProperty;
  2638. break;
  2639. }
  2640. return S_OK;
  2641. }
  2642. //-----------------------------------------------------------------------------
  2643. // Look up strVal in the ValueMap (if it exists) for strClass.strProperty
  2644. // If the value or the ValueMap is not found, return E_Something.
  2645. //
  2646. // Useful code snippet - this will dump the contents of the cache of
  2647. // saved values. To find all value mapped properties, but this code
  2648. // someplace where it will execute when MSInfo exits, change QueryValue
  2649. // to call CheckValueMap for all properties, then run MSInfo and do a global
  2650. // refresh (like to save an NFO).
  2651. //
  2652. // msiLog.WriteLog(CMSInfoLog::BASIC, _T("BEGIN Dump of ValueMap Cache\r\n"));
  2653. // CString key, val, log;
  2654. // for (POSITION pos = g_mapValueMap.GetStartPosition(); pos != NULL;)
  2655. // {
  2656. // g_mapValueMap.GetNextAssoc(pos, key, val);
  2657. // log.Format(_T(" %s = %s\r\n", key, val);
  2658. // msiLog.WriteLog(CMSInfoLog::BASIC, log);
  2659. // }
  2660. // msiLog.WriteLog(CMSInfoLog::BASIC, _T("END Dump of ValueMap Cache\r\n"));
  2661. //-----------------------------------------------------------------------------
  2662. CMapStringToString g_mapValueMap;
  2663. HRESULT CWMILiveHelper::Version5CheckValueMap(const CString& strClass, const CString& strProperty, const CString& strVal, CString &strResult)
  2664. {
  2665. IWbemClassObject * pWBEMClassObject = NULL;
  2666. HRESULT hrMap = S_OK, hr = S_OK;
  2667. VARIANT vArray, vMapArray;
  2668. IWbemQualifierSet * qual = NULL;
  2669. // Check the cache of saved values.
  2670. CString strLookup = strClass + CString(_T(".")) + strProperty + CString(_T(":")) + strVal;
  2671. if (g_mapValueMap.Lookup(strLookup, strResult))
  2672. return S_OK;
  2673. // Get the class object (not instance) for this class.
  2674. IWbemServices * pServices = Version5GetWBEMService();
  2675. if (!pServices)
  2676. return E_FAIL;
  2677. CString strFullClass(_T("\\\\.\\root\\cimv2:"));
  2678. strFullClass += strClass;
  2679. BSTR bstrObjectPath = strFullClass.AllocSysString();
  2680. hr = pServices->GetObject(bstrObjectPath, WBEM_FLAG_USE_AMENDED_QUALIFIERS, NULL, &pWBEMClassObject, NULL);
  2681. ::SysFreeString(bstrObjectPath);
  2682. pServices->Release();
  2683. if (FAILED(hr))
  2684. return hr;
  2685. // Get the qualifiers from the class object.
  2686. BSTR bstrProperty = strProperty.AllocSysString();
  2687. hr = pWBEMClassObject->GetPropertyQualifierSet(bstrProperty, &qual);
  2688. ::SysFreeString(bstrProperty);
  2689. if (SUCCEEDED(hr) && qual)
  2690. {
  2691. // Get the ValueMap and Value arrays.
  2692. hrMap = qual->Get(L"ValueMap", 0, &vMapArray, NULL);
  2693. hr = qual->Get(L"Values", 0, &vArray, NULL);
  2694. if (SUCCEEDED(hr) && vArray.vt == (VT_BSTR | VT_ARRAY))
  2695. {
  2696. // Get the property value we're mapping.
  2697. long index;
  2698. if (SUCCEEDED(hrMap))
  2699. {
  2700. SAFEARRAY * pma = V_ARRAY(&vMapArray);
  2701. long lLowerBound = 0, lUpperBound = 0 ;
  2702. SafeArrayGetLBound(pma, 1, &lLowerBound);
  2703. SafeArrayGetUBound(pma, 1, &lUpperBound);
  2704. BSTR vMap;
  2705. for (long x = lLowerBound; x <= lUpperBound; x++)
  2706. {
  2707. SafeArrayGetElement(pma, &x, &vMap);
  2708. if (0 == strVal.CompareNoCase((LPCTSTR)vMap))
  2709. {
  2710. index = x;
  2711. break; // found it
  2712. }
  2713. }
  2714. }
  2715. else
  2716. {
  2717. // Shouldn't hit this case - if mof is well formed
  2718. // means there is no value map where we are expecting one.
  2719. // If the strVal we are looking for is a number, treat it
  2720. // as an index for the Values array. If it's a string,
  2721. // then this is an error.
  2722. TCHAR * szTest = NULL;
  2723. index = _tcstol((LPCTSTR)strVal, &szTest, 10);
  2724. if (szTest == NULL || (index == 0 && *szTest != 0) || strVal.IsEmpty())
  2725. hr = E_FAIL;
  2726. }
  2727. // Lookup the string.
  2728. if (SUCCEEDED(hr))
  2729. {
  2730. SAFEARRAY * psa = V_ARRAY(&vArray);
  2731. long ix[1] = {index};
  2732. BSTR str2;
  2733. hr = SafeArrayGetElement(psa, ix, &str2);
  2734. if (SUCCEEDED(hr))
  2735. {
  2736. strResult = str2;
  2737. SysFreeString(str2);
  2738. hr = S_OK;
  2739. }
  2740. else
  2741. {
  2742. hr = WBEM_E_VALUE_OUT_OF_RANGE;
  2743. }
  2744. }
  2745. }
  2746. qual->Release();
  2747. }
  2748. if (SUCCEEDED(hr))
  2749. g_mapValueMap.SetAt(strLookup, strResult);
  2750. return hr;
  2751. }
  2752. //-----------------------------------------------------------------------------
  2753. // The CEnumMap is a utility class to cache IEnumWbemClassObject pointers.
  2754. // There will be one instance of this class used to improve performance
  2755. // by avoiding the high overhead associated with creating enumerators for
  2756. // certain classes.
  2757. //-----------------------------------------------------------------------------
  2758. IEnumWbemClassObject * CEnumMap::GetEnumerator(const CString & strClass)
  2759. {
  2760. IEnumWbemClassObject * pEnum = NULL;
  2761. IEnumWbemClassObject * pNewEnum = NULL;
  2762. if (m_mapEnum.Lookup(strClass, (void * &) pEnum))
  2763. {
  2764. if (pEnum && SUCCEEDED(pEnum->Clone(&pNewEnum)) && pNewEnum)
  2765. pNewEnum->Reset();
  2766. else
  2767. pNewEnum = NULL;
  2768. }
  2769. return pNewEnum;
  2770. }
  2771. void CEnumMap::SetEnumerator(const CString & strClass, IEnumWbemClassObject * pEnum)
  2772. {
  2773. if (pEnum)
  2774. {
  2775. IEnumWbemClassObject * pEnumExisting = NULL;
  2776. if (m_mapEnum.Lookup(strClass, (void * &) pEnumExisting))
  2777. {
  2778. ; //WRITE(_T("SetEnumerator for %s, already exists, ignoring.\r\n"), strClass);
  2779. }
  2780. else
  2781. {
  2782. pEnum->AddRef();
  2783. m_mapEnum.SetAt(strClass, pEnum);
  2784. }
  2785. }
  2786. }
  2787. void CEnumMap::Reset()
  2788. {
  2789. IEnumWbemClassObject * pEnum = NULL;
  2790. CString key;
  2791. for (POSITION pos = m_mapEnum.GetStartPosition(); pos != NULL;)
  2792. {
  2793. m_mapEnum.GetNextAssoc(pos, key, (void * &) pEnum);
  2794. if (pEnum)
  2795. pEnum->Release();
  2796. }
  2797. m_mapEnum.RemoveAll();
  2798. }