Leaked source code of windows server 2003
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.

3455 lines
99 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. try
  879. {
  880. if (RefreshOneLine(pWMI, pLine, pCurrentLineSpec, dwColumns))
  881. listLinePtrs.AddTail((void *) pLine);
  882. else
  883. {
  884. bReturn = FALSE;
  885. break;
  886. }
  887. }
  888. catch (...)
  889. {
  890. if (pLine)
  891. delete pLine;
  892. pLine = NULL;
  893. bReturn = FALSE;
  894. break;
  895. }
  896. }
  897. else
  898. {
  899. // This line represents an enumerated group of lines. We need to enumerate
  900. // the class and call RefreshLines for the group of enumerated lines, once
  901. // for each class instance.
  902. if (pWMI->Version5ResetClass(pCurrentLineSpec->m_strEnumerateClass, pCurrentLineSpec->m_pConstraintFields))
  903. do
  904. {
  905. if (!RefreshLines(pWMI, pCurrentLineSpec->m_pEnumeratedGroup, dwColumns, listLinePtrs, pfCancel))
  906. break;
  907. } while (pWMI->Version5EnumClass(pCurrentLineSpec->m_strEnumerateClass, pCurrentLineSpec->m_pConstraintFields));
  908. }
  909. pCurrentLineSpec = pCurrentLineSpec->m_pNext;
  910. }
  911. if (pfCancel && *pfCancel)
  912. return FALSE;
  913. // If there was a failure generating the lines, clean up after ourselves.
  914. if (!bReturn)
  915. {
  916. if (pLine)
  917. delete pLine;
  918. for (POSITION pos = listLinePtrs.GetHeadPosition(); pos != NULL;)
  919. {
  920. pLine = (GATH_LINE *) listLinePtrs.GetNext(pos) ;
  921. if (pLine)
  922. delete pLine;
  923. }
  924. listLinePtrs.RemoveAll();
  925. return FALSE;
  926. }
  927. return TRUE;
  928. }
  929. //-----------------------------------------------------------------------------
  930. // Refresh a line based on a line spec.
  931. //-----------------------------------------------------------------------------
  932. BOOL CRefreshFunctions::RefreshOneLine(CWMIHelper * pWMI, GATH_LINE * pLine, GATH_LINESPEC * pLineSpec, DWORD dwColCount)
  933. {
  934. // Allocate the new array of values.
  935. if (pLine->m_aValue)
  936. delete [] pLine->m_aValue;
  937. pLine->m_aValue = new GATH_VALUE[dwColCount];
  938. if (pLine->m_aValue == NULL)
  939. return FALSE;
  940. // Set the data complexity for the line based on the line spec.
  941. pLine->m_datacomplexity = pLineSpec->m_datacomplexity;
  942. // Compute each of the values for the fields.
  943. GATH_FIELD * pField = pLineSpec->m_pFields;
  944. for (DWORD dwIndex = 0; dwIndex < dwColCount; dwIndex++)
  945. {
  946. if (pField == NULL)
  947. return FALSE;
  948. if (!RefreshValue(pWMI, &pLine->m_aValue[dwIndex], pField))
  949. return FALSE;
  950. pField = pField->m_pNext;
  951. }
  952. return TRUE;
  953. }
  954. //-----------------------------------------------------------------------------
  955. // This method takes the information in a GATH_FIELD struct and uses it to
  956. // generate a current GATH_VALUE struct.
  957. //-----------------------------------------------------------------------------
  958. BOOL CRefreshFunctions::RefreshValue(CWMIHelper * pWMI, GATH_VALUE * pVal, GATH_FIELD * pField)
  959. {
  960. TCHAR szFormatFragment[MAX_PATH];
  961. const TCHAR *pSourceChar;
  962. TCHAR *pDestinationChar;
  963. TCHAR cFormat = _T('\0');
  964. BOOL fReadPercent = FALSE;
  965. BOOL fReturnValue = TRUE;
  966. CString strResult, strTemp;
  967. int iArgNumber = 0;
  968. DWORD dwValue = 0L;
  969. // Process the format string. Because of the difficulty caused by having
  970. // variable number of arguments to be inserted (like printf), we'll need
  971. // to break the format string into chunks and do the sprintf function
  972. // for each format flag we come across.
  973. pSourceChar = (LPCTSTR) pField->m_strFormat;
  974. pDestinationChar = szFormatFragment;
  975. while (*pSourceChar)
  976. {
  977. if (fReadPercent)
  978. {
  979. // If we read a percent sign, we should be looking for a valid flag.
  980. // We are using some additional flags to printf (and not supporting
  981. // others). If we read another percent, just insert a single percent.
  982. switch (*pSourceChar)
  983. {
  984. case _T('%'):
  985. fReadPercent = FALSE;
  986. break;
  987. case _T('b'): case _T('B'):
  988. case _T('l'): case _T('L'):
  989. case _T('u'): case _T('U'):
  990. case _T('s'): case _T('S'):
  991. fReadPercent = FALSE;
  992. cFormat = *pSourceChar;
  993. *pDestinationChar = _T('s');
  994. break;
  995. case _T('t'): case _T('T'):
  996. fReadPercent = FALSE;
  997. cFormat = *pSourceChar;
  998. *pDestinationChar = _T('s');
  999. break;
  1000. case _T('x'): case _T('X'):
  1001. case _T('d'): case _T('D'):
  1002. fReadPercent = FALSE;
  1003. cFormat = _T('d');
  1004. *pDestinationChar = *pSourceChar;
  1005. break;
  1006. case _T('q'): case _T('Q'):
  1007. fReadPercent = FALSE;
  1008. cFormat = _T('q');
  1009. *pDestinationChar = _T('s');
  1010. break;
  1011. case _T('z'): case _T('Z'):
  1012. fReadPercent = FALSE;
  1013. cFormat = _T('z');
  1014. *pDestinationChar = _T('s');
  1015. break;
  1016. case _T('y'): case _T('Y'):
  1017. fReadPercent = FALSE;
  1018. cFormat = _T('y');
  1019. *pDestinationChar = _T('s');
  1020. break;
  1021. case _T('v'): case _T('V'):
  1022. fReadPercent = FALSE;
  1023. cFormat = _T('v');
  1024. *pDestinationChar = _T('s');
  1025. break;
  1026. case _T('f'): case _T('F'):
  1027. fReadPercent = FALSE;
  1028. cFormat = *pSourceChar;
  1029. *pDestinationChar = *pSourceChar;
  1030. break;
  1031. default:
  1032. *pDestinationChar = *pSourceChar;
  1033. }
  1034. }
  1035. else if (*pSourceChar == _T('%'))
  1036. {
  1037. *pDestinationChar = _T('%');
  1038. fReadPercent = TRUE;
  1039. }
  1040. else
  1041. *pDestinationChar = *pSourceChar;
  1042. pSourceChar++;
  1043. pDestinationChar++;
  1044. // If a format flag is set or we are at the end of the source string,
  1045. // then we have a complete fragment and we should produce some output,
  1046. // which will be concatenated to the strResult string.
  1047. if (cFormat || *pSourceChar == _T('\0'))
  1048. {
  1049. *pDestinationChar = _T('\0');
  1050. if (cFormat)
  1051. {
  1052. // Based on the format type, get a value from the provider for
  1053. // the next argument. Format the result using the formatting
  1054. // fragment we extracted, and concatenate it.
  1055. if (GetValue(pWMI, cFormat, szFormatFragment, strTemp, dwValue, pField, iArgNumber++))
  1056. {
  1057. strResult += strTemp;
  1058. cFormat = _T('\0');
  1059. }
  1060. else
  1061. {
  1062. strResult = strTemp;
  1063. break;
  1064. }
  1065. }
  1066. else
  1067. {
  1068. // There was no format flag, but we are at the end of the string.
  1069. // Add the fragment we got to the result string.
  1070. strResult += CString(szFormatFragment);
  1071. }
  1072. pDestinationChar = szFormatFragment;
  1073. }
  1074. }
  1075. // Assign the values we generated to the GATH_VALUE structure. Important note:
  1076. // the dwValue variable will only have ONE value, even though multiple values
  1077. // might have been generated to build the strResult string. Only the last
  1078. // value will be saved in dwValue. This is OK, because this value is only
  1079. // used for sorting a column when the column is marked for non-lexical sorting.
  1080. // In that case, there should be only one value used to generat the string.
  1081. pVal->m_strText = strResult;
  1082. pVal->m_dwValue = dwValue;
  1083. return fReturnValue;
  1084. }
  1085. //-----------------------------------------------------------------------------
  1086. // Return a string with delimiters added for the number.
  1087. //-----------------------------------------------------------------------------
  1088. CString DelimitNumber(double dblValue)
  1089. {
  1090. NUMBERFMT fmt;
  1091. TCHAR szResult[MAX_PATH] = _T("");
  1092. TCHAR szDelimiter[4] = _T(",");
  1093. GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szDelimiter, 4);
  1094. memset(&fmt, 0, sizeof(NUMBERFMT));
  1095. fmt.Grouping = 3;
  1096. fmt.lpDecimalSep = _T(""); // doesn't matter - there aren't decimal digits
  1097. fmt.lpThousandSep = szDelimiter;
  1098. CString strValue;
  1099. strValue.Format(_T("%.0f"), dblValue);
  1100. GetNumberFormat(LOCALE_USER_DEFAULT, 0, strValue, &fmt, szResult, MAX_PATH);
  1101. return CString(szResult);
  1102. }
  1103. //-----------------------------------------------------------------------------
  1104. // This method gets a single value from the provider, based on the format
  1105. // character from the template file. It formats the results using the
  1106. // format string szFormatFragment, which should only take one argument.
  1107. //-----------------------------------------------------------------------------
  1108. BOOL CRefreshFunctions::GetValue(CWMIHelper * pWMI, TCHAR cFormat, TCHAR *szFormatFragment, CString &strResult, DWORD &dwResult, GATH_FIELD *pField, int iArgNumber)
  1109. {
  1110. CString strTemp;
  1111. COleDateTime datetimeTemp;
  1112. double dblValue;
  1113. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  1114. strResult.Empty();
  1115. dwResult = 0L;
  1116. if (!pField->m_strSource.IsEmpty() && pField->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) != 0)
  1117. {
  1118. // Find the right argument for this formatting (indicated by the iArgNumber
  1119. // parameter.
  1120. GATH_VALUE * pArg = pField->m_pArgs;
  1121. while (iArgNumber && pArg)
  1122. {
  1123. pArg = pArg->m_pNext;
  1124. iArgNumber--;
  1125. }
  1126. if (pArg == NULL)
  1127. return FALSE;
  1128. switch (cFormat)
  1129. {
  1130. case 'b': case 'B':
  1131. // This is a boolean type. Show either true or false, depending on
  1132. // the numeric value.
  1133. if (pWMI->Version5QueryValueDWORD(pField->m_strSource, pArg->m_strText, dwResult, strTemp))
  1134. {
  1135. strTemp = (dwResult) ? pWMI->m_strTrue : pWMI->m_strFalse;
  1136. strResult.Format(szFormatFragment, strTemp);
  1137. return TRUE;
  1138. }
  1139. else
  1140. {
  1141. strResult = strTemp;
  1142. return FALSE;
  1143. }
  1144. break;
  1145. case 'd': case 'D':
  1146. // This is the numeric type.
  1147. if (pWMI->Version5QueryValueDWORD(pField->m_strSource, pArg->m_strText, dwResult, strTemp))
  1148. {
  1149. strResult.Format(szFormatFragment, dwResult);
  1150. return TRUE;
  1151. }
  1152. else
  1153. {
  1154. strResult = strTemp;
  1155. return FALSE;
  1156. }
  1157. break;
  1158. case 'f': case 'F':
  1159. // This is the double floating point type.
  1160. if (pWMI->Version5QueryValueDoubleFloat(pField->m_strSource, pArg->m_strText, dblValue, strTemp))
  1161. {
  1162. strResult.Format(szFormatFragment, dblValue);
  1163. return TRUE;
  1164. }
  1165. else
  1166. {
  1167. strResult = strTemp;
  1168. return FALSE;
  1169. }
  1170. break;
  1171. case 't': case 'T':
  1172. // This is the OLE date and time type. Format the date and time into the
  1173. // string result, and return the date part in the DWORD (the day number is
  1174. // to the left of the decimal in the DATE type).
  1175. if (pWMI->Version5QueryValueDateTime(pField->m_strSource, pArg->m_strText, datetimeTemp, strTemp))
  1176. {
  1177. strResult = datetimeTemp.Format();
  1178. dwResult = (DWORD)(DATE)datetimeTemp;
  1179. return TRUE;
  1180. }
  1181. else
  1182. {
  1183. strResult = strTemp;
  1184. return FALSE;
  1185. }
  1186. break;
  1187. case 'l': case 'L':
  1188. // This is a string type, with the string converted to lower case.
  1189. if (pWMI->Version5QueryValue(pField->m_strSource, pArg->m_strText, strTemp))
  1190. {
  1191. strTemp.MakeLower();
  1192. strResult.Format(szFormatFragment, strTemp);
  1193. return TRUE;
  1194. }
  1195. else
  1196. {
  1197. strResult = strTemp;
  1198. return FALSE;
  1199. }
  1200. break;
  1201. case 'u': case 'U':
  1202. // This is a string type, with the string converted to upper case.
  1203. if (pWMI->Version5QueryValue(pField->m_strSource, pArg->m_strText, strTemp))
  1204. {
  1205. strTemp.MakeUpper();
  1206. strResult.Format(szFormatFragment, strTemp);
  1207. return TRUE;
  1208. }
  1209. else
  1210. {
  1211. strResult = strTemp;
  1212. return FALSE;
  1213. }
  1214. break;
  1215. case 's': case 'S':
  1216. // This is the string type (string is the default type).
  1217. if (pWMI->Version5QueryValue(pField->m_strSource, pArg->m_strText, strTemp))
  1218. {
  1219. strResult.Format(szFormatFragment, strTemp);
  1220. // We only need to do this when the value returned is a number
  1221. // and is going in a column that we want to sort numerically.
  1222. // This won't break the case where a numeric string is to be
  1223. // sorted as a string because dwResult will be ignored.
  1224. if (!strTemp.IsEmpty() && iswdigit( strTemp[0]))
  1225. dwResult = _ttol( (LPCTSTR)strTemp);
  1226. return TRUE;
  1227. }
  1228. else
  1229. {
  1230. strResult = strTemp;
  1231. return FALSE;
  1232. }
  1233. break;
  1234. case 'q': case 'Q':
  1235. // This is a specialized type for the Win32_BIOS class. We want to show
  1236. // the "Version" property - if it isn't there, then we want to show
  1237. // the "Name" property and "ReleaseDate" properties concatenated
  1238. // together.
  1239. if (pWMI->Version5QueryValue(pField->m_strSource, CString(_T("Version")), strTemp))
  1240. {
  1241. strResult = strTemp;
  1242. return TRUE;
  1243. }
  1244. else
  1245. {
  1246. if (pWMI->Version5QueryValue(pField->m_strSource, CString(_T("Name")), strTemp))
  1247. strResult = strTemp;
  1248. if (pWMI->Version5QueryValueDateTime(pField->m_strSource, CString(_T("ReleaseDate")), datetimeTemp, strTemp))
  1249. strResult += CString(_T(" ")) + datetimeTemp.Format();
  1250. return TRUE;
  1251. }
  1252. break;
  1253. case 'z': case 'Z':
  1254. // This is a specialized size type, where the value is a numeric count
  1255. // of bytes. We want to convert it into the best possible units for
  1256. // display (for example, display "4.20 MB (4,406,292 bytes)").
  1257. if (pWMI->Version5QueryValueDoubleFloat(pField->m_strSource, pArg->m_strText, dblValue, strTemp))
  1258. {
  1259. double dValue = (double) dblValue;
  1260. DWORD dwDivisor = 1;
  1261. // Reduce the dValue to the smallest possible number (with a larger unit).
  1262. while (dValue > 1024.0 && dwDivisor < (1024 * 1024 * 1024))
  1263. {
  1264. dwDivisor *= 1024;
  1265. dValue /= 1024.0;
  1266. }
  1267. if (dwDivisor == 1)
  1268. strResult.Format(IDS_SIZEBYTES, DelimitNumber(dblValue));
  1269. else if (dwDivisor == (1024))
  1270. strResult.Format(IDS_SIZEKB_BYTES, dValue, DelimitNumber(dblValue));
  1271. else if (dwDivisor == (1024 * 1024))
  1272. strResult.Format(IDS_SIZEMB_BYTES, dValue, DelimitNumber(dblValue));
  1273. else if (dwDivisor == (1024 * 1024 * 1024))
  1274. strResult.Format(IDS_SIZEGB_BYTES, dValue, DelimitNumber(dblValue));
  1275. dwResult = (DWORD) dblValue; // So we can sort on this value (bug 391127).
  1276. }
  1277. else
  1278. {
  1279. strResult = strTemp;
  1280. return FALSE;
  1281. }
  1282. break;
  1283. case 'y': case 'Y':
  1284. // This is a specialized size type, where the value is a numeric count
  1285. // of bytes, already in KB. If it's big enough, show it in MB or GB.
  1286. if (pWMI->Version5QueryValueDoubleFloat(pField->m_strSource, pArg->m_strText, dblValue, strTemp))
  1287. {
  1288. strResult.Format(IDS_SIZEKB, DelimitNumber(dblValue));
  1289. dwResult = (DWORD) dblValue; // So we can sort on this value (bug 391127).
  1290. }
  1291. else
  1292. {
  1293. strResult = strTemp;
  1294. return FALSE;
  1295. }
  1296. break;
  1297. case 'v': case 'V':
  1298. // This is a specialized type, assumed to be an LCID (locale ID). Show the
  1299. // locale.
  1300. if (pWMI->Version5QueryValue(pField->m_strSource, pArg->m_strText, strTemp))
  1301. {
  1302. // strTemp contains a string locale ID (like "0409"). Convert it into
  1303. // and actual LCID.
  1304. LCID lcid = (LCID) _tcstoul(strTemp, NULL, 16);
  1305. TCHAR szCountry[MAX_PATH];
  1306. if (GetLocaleInfo(lcid, LOCALE_SCOUNTRY, szCountry, MAX_PATH))
  1307. strResult = szCountry;
  1308. else
  1309. strResult = strTemp;
  1310. }
  1311. else
  1312. {
  1313. strResult = strTemp;
  1314. return FALSE;
  1315. }
  1316. break;
  1317. default:
  1318. ASSERT(FALSE); // unknown formatting flag
  1319. return TRUE;
  1320. }
  1321. }
  1322. return FALSE;
  1323. }
  1324. //=============================================================================
  1325. // Functions extending the CWMILiveHelper to support version 5 style refreshes.
  1326. //=============================================================================
  1327. #include "wmilive.h"
  1328. //-----------------------------------------------------------------------------
  1329. // Reset the CMSIEnumerator pointer to the start of the enumeration (and
  1330. // make sure there is one). Remove the object pointer, so the first call
  1331. // to GetObject will return the first item in the enumerator.
  1332. //-----------------------------------------------------------------------------
  1333. BOOL CWMILiveHelper::Version5ResetClass(const CString & strClass, GATH_FIELD * pConstraints)
  1334. {
  1335. CMSIEnumerator * pMSIEnumerator = Version5GetEnumObject(strClass, pConstraints);
  1336. if (pMSIEnumerator == NULL)
  1337. return FALSE;
  1338. // Reset the enumerator, and remove the cached object pointer if there is one.
  1339. pMSIEnumerator->Reset(pConstraints);
  1340. Version5RemoveObject(strClass);
  1341. CMSIObject * pObject = Version5GetObject(strClass, pConstraints);
  1342. if (pObject == NULL || pObject->IsValid() == MOS_NO_INSTANCES)
  1343. return FALSE;
  1344. return TRUE;
  1345. }
  1346. //-----------------------------------------------------------------------------
  1347. // Move the cached IWbemClassObject pointer to the next instance.
  1348. //-----------------------------------------------------------------------------
  1349. BOOL CWMILiveHelper::Version5EnumClass(const CString & strClass, GATH_FIELD * pConstraints)
  1350. {
  1351. // Verify that there is an object enumerator in place.
  1352. if (Version5GetEnumObject(strClass, pConstraints) == NULL)
  1353. return FALSE;
  1354. // If there is an object interface, remove it, then make a new one.
  1355. // Then retrieve the object pointer (this will do the Next on the
  1356. // enumerator to get the next instance).
  1357. Version5RemoveObject(strClass);
  1358. CMSIObject * pObject = Version5GetObject(strClass, pConstraints);
  1359. if (pObject && (pObject->IsValid() == MOS_INSTANCE))
  1360. return TRUE;
  1361. return FALSE;
  1362. }
  1363. //-----------------------------------------------------------------------------
  1364. // Retrieve the interface pointer for the specified IEnumWbemClassObject.
  1365. // If there isn't one cached, create one and cache it. It's possible for the
  1366. // pConstraints parameter to contain a field specify a WBEM SQL condition for
  1367. // this enumerator.
  1368. //-----------------------------------------------------------------------------
  1369. CMSIEnumerator * CWMILiveHelper::Version5GetEnumObject(const CString & strClass, const GATH_FIELD * pConstraints)
  1370. {
  1371. // See if we've cached this enumerator object.
  1372. CMSIEnumerator * pReturn = NULL;
  1373. if (m_mapClassToEnumInterface.Lookup(strClass, (void * &) pReturn))
  1374. return pReturn;
  1375. // We'll need to create this enumerator here, and save it in the cache.
  1376. pReturn = new CMSIEnumerator;
  1377. if (pReturn == NULL)
  1378. return NULL;
  1379. if (FAILED(pReturn->Create(strClass, pConstraints, this)))
  1380. {
  1381. delete pReturn;
  1382. return NULL;
  1383. }
  1384. m_mapClassToEnumInterface.SetAt(strClass, (void *) pReturn);
  1385. return pReturn;
  1386. }
  1387. //-----------------------------------------------------------------------------
  1388. // Remove the specified IWbemClassObject pointer from the cache.
  1389. //-----------------------------------------------------------------------------
  1390. void CWMILiveHelper::Version5RemoveObject(const CString & strClass)
  1391. {
  1392. CMSIObject * pObject = NULL;
  1393. if (m_mapClassToInterface.Lookup(strClass, (void * &) pObject) && pObject)
  1394. delete pObject;
  1395. m_mapClassToInterface.RemoveKey(strClass);
  1396. }
  1397. //-----------------------------------------------------------------------------
  1398. // Retrieve the interface pointer for the specified IWbemClassObject.
  1399. // If there isn't one cached, create one and cache it.
  1400. //-----------------------------------------------------------------------------
  1401. CMSIObject * CWMILiveHelper::Version5GetObject(const CString & strClass, const GATH_FIELD * pConstraints, CString * pstrLabel)
  1402. {
  1403. CMSIObject * pReturn = NULL;
  1404. if (m_mapClassToInterface.Lookup(strClass, (void * &) pReturn))
  1405. return pReturn;
  1406. // We don't have one of these objects cached. Get one from the enumerator.
  1407. CMSIEnumerator * pEnumerator = Version5GetEnumObject(strClass);
  1408. if (pEnumerator)
  1409. {
  1410. HRESULT hr = pEnumerator->Next(&pReturn);
  1411. if (S_OK != hr)
  1412. {
  1413. if (pReturn)
  1414. delete pReturn;
  1415. pReturn = NULL;
  1416. m_hrError = hr;
  1417. }
  1418. }
  1419. if (pReturn)
  1420. m_mapClassToInterface.SetAt(strClass, (void *) pReturn);
  1421. return pReturn;
  1422. }
  1423. //-----------------------------------------------------------------------------
  1424. // This method is used to get the current value for a given class and property
  1425. // string. Starting with the IWbemServices interface, it gets an interface
  1426. // for the requested class enums the first instance. Performance is improved
  1427. // by caching the instance interfaces in m_mapClassToInterface.
  1428. //-----------------------------------------------------------------------------
  1429. BOOL CWMILiveHelper::Version5QueryValue(const CString & strClass, const CString & strProperty, CString & strResult)
  1430. {
  1431. strResult.Empty();
  1432. CMSIObject * pObject = Version5GetObject(strClass, NULL);
  1433. ASSERT(pObject);
  1434. if (!pObject)
  1435. return FALSE;
  1436. switch (pObject->IsValid())
  1437. {
  1438. case MOS_INSTANCE:
  1439. {
  1440. BOOL fUseValueMap = FALSE;
  1441. CString strProp(strProperty);
  1442. if (strProp.Left(8) == CString(_T("ValueMap")))
  1443. {
  1444. strProp = strProp.Right(strProp.GetLength() - 8);
  1445. fUseValueMap = TRUE;
  1446. }
  1447. VARIANT variant;
  1448. BSTR propName = strProp.AllocSysString();
  1449. VariantInit(&variant);
  1450. VariantClear(&variant);
  1451. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  1452. {
  1453. // If the property we just got is an array, we should convert it to string
  1454. // containing a list of the items in the array.
  1455. if ((variant.vt & VT_ARRAY) && (variant.vt & VT_BSTR) && variant.parray)
  1456. {
  1457. if (SafeArrayGetDim(variant.parray) == 1)
  1458. {
  1459. long lLower = 0, lUpper = 0;
  1460. SafeArrayGetLBound(variant.parray, 0, &lLower);
  1461. SafeArrayGetUBound(variant.parray, 0, &lUpper);
  1462. CString strWorking;
  1463. BSTR bstr = NULL;
  1464. for (long i = lLower; i <= lUpper; i++)
  1465. if (SUCCEEDED(SafeArrayGetElement(variant.parray, &i, (wchar_t*)&bstr)))
  1466. {
  1467. if (i != lLower)
  1468. strWorking += _T(", ");
  1469. strWorking += bstr;
  1470. }
  1471. strResult = strWorking;
  1472. return TRUE;
  1473. }
  1474. }
  1475. else if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  1476. {
  1477. strResult = V_BSTR(&variant);
  1478. CString strFound;
  1479. if (fUseValueMap && SUCCEEDED(Version5CheckValueMap(strClass, strProp, strResult, strFound)))
  1480. strResult = strFound;
  1481. return TRUE;
  1482. }
  1483. else
  1484. strResult = m_strPropertyUnavail;
  1485. }
  1486. else
  1487. strResult = m_strBadProperty;
  1488. }
  1489. break;
  1490. case MOS_MSG_INSTANCE:
  1491. pObject->GetErrorLabel(strResult);
  1492. break;
  1493. case MOS_NO_INSTANCES:
  1494. default:
  1495. ASSERT(FALSE);
  1496. break;
  1497. }
  1498. return FALSE;
  1499. }
  1500. //-----------------------------------------------------------------------------
  1501. // This method is equivalent to QueryValue, except it returns a DWORD value.
  1502. // If FALSE is returned, then the string in strMessage should be displayed.
  1503. //-----------------------------------------------------------------------------
  1504. BOOL CWMILiveHelper::Version5QueryValueDWORD(const CString & strClass, const CString & strProperty, DWORD & dwResult, CString & strMessage)
  1505. {
  1506. dwResult = 0L;
  1507. strMessage.Empty();
  1508. CMSIObject * pObject = Version5GetObject(strClass, NULL);
  1509. ASSERT(pObject);
  1510. if (!pObject)
  1511. return FALSE;
  1512. switch (pObject->IsValid())
  1513. {
  1514. case MOS_INSTANCE:
  1515. {
  1516. VARIANT variant;
  1517. BSTR propName = strProperty.AllocSysString();
  1518. VariantInit(&variant);
  1519. VariantClear(&variant);
  1520. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  1521. {
  1522. if (VariantChangeType(&variant, &variant, 0, VT_I4) == S_OK)
  1523. {
  1524. dwResult = V_I4(&variant);
  1525. return TRUE;
  1526. }
  1527. else
  1528. strMessage = m_strPropertyUnavail;
  1529. }
  1530. else
  1531. strMessage = m_strBadProperty;
  1532. }
  1533. break;
  1534. case MOS_MSG_INSTANCE:
  1535. pObject->GetErrorLabel(strMessage);
  1536. break;
  1537. case MOS_NO_INSTANCES:
  1538. default:
  1539. ASSERT(FALSE);
  1540. break;
  1541. }
  1542. return FALSE;
  1543. }
  1544. //-----------------------------------------------------------------------------
  1545. // This method is equivalent to QueryValue, except it returns a double float
  1546. // value. If FALSE is returned, then the string in strMessage should
  1547. // be displayed.
  1548. //-----------------------------------------------------------------------------
  1549. BOOL CWMILiveHelper::Version5QueryValueDoubleFloat(const CString & strClass, const CString & strProperty, double & dblResult, CString & strMessage)
  1550. {
  1551. dblResult = 0L;
  1552. strMessage.Empty();
  1553. CMSIObject * pObject = Version5GetObject(strClass, NULL);
  1554. ASSERT(pObject);
  1555. if (!pObject)
  1556. return FALSE;
  1557. switch (pObject->IsValid())
  1558. {
  1559. case MOS_INSTANCE:
  1560. {
  1561. VARIANT variant;
  1562. BSTR propName = strProperty.AllocSysString();
  1563. VariantInit(&variant);
  1564. VariantClear(&variant);
  1565. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  1566. {
  1567. if (VariantChangeType(&variant, &variant, 0, VT_R8) == S_OK)
  1568. {
  1569. dblResult = V_R8(&variant);
  1570. return TRUE;
  1571. }
  1572. else
  1573. strMessage = m_strPropertyUnavail;
  1574. }
  1575. else
  1576. strMessage = m_strBadProperty;
  1577. }
  1578. break;
  1579. case MOS_MSG_INSTANCE:
  1580. pObject->GetErrorLabel(strMessage);
  1581. break;
  1582. case MOS_NO_INSTANCES:
  1583. default:
  1584. ASSERT(FALSE);
  1585. break;
  1586. }
  1587. return FALSE;
  1588. }
  1589. //-----------------------------------------------------------------------------
  1590. // This method is equivalent to QueryValue, except it returns an OLE date
  1591. // & time object. If FALSE is returned, then the string in strMessage should
  1592. // be displayed.
  1593. //-----------------------------------------------------------------------------
  1594. BOOL CWMILiveHelper::Version5QueryValueDateTime(const CString & strClass, const CString & strProperty, COleDateTime & datetime, CString & strMessage)
  1595. {
  1596. datetime.SetDateTime(0, 1, 1, 0, 0, 0);
  1597. strMessage.Empty();
  1598. CMSIObject * pObject = Version5GetObject(strClass, NULL);
  1599. ASSERT(pObject);
  1600. if (!pObject)
  1601. return FALSE;
  1602. switch (pObject->IsValid())
  1603. {
  1604. case MOS_INSTANCE:
  1605. {
  1606. VARIANT variant;
  1607. BSTR propName = strProperty.AllocSysString();
  1608. VariantInit(&variant);
  1609. VariantClear(&variant);
  1610. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  1611. {
  1612. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  1613. {
  1614. // Parse the date and time into an COleDateTime object. Note: we should
  1615. // be able to get an OLE date from WBEM, but for now we need to just
  1616. // deal with the string returned.
  1617. int nYear, nMonth, nDay, nHour, nMin, nSec;
  1618. CString strTemp = V_BSTR(&variant);
  1619. nYear = _ttoi(strTemp.Mid(0, 4));
  1620. nMonth = _ttoi(strTemp.Mid(4, 2));
  1621. nDay = _ttoi(strTemp.Mid(6, 2));
  1622. nHour = _ttoi(strTemp.Mid(8, 2));
  1623. nMin = _ttoi(strTemp.Mid(10, 2));
  1624. nSec = _ttoi(strTemp.Mid(12, 2));
  1625. datetime.SetDateTime(nYear, nMonth, nDay, nHour, nMin, nSec);
  1626. return TRUE;
  1627. }
  1628. else
  1629. strMessage = m_strPropertyUnavail;
  1630. }
  1631. else
  1632. strMessage = m_strBadProperty;
  1633. }
  1634. break;
  1635. case MOS_MSG_INSTANCE:
  1636. pObject->GetErrorLabel(strMessage);
  1637. break;
  1638. case MOS_NO_INSTANCES:
  1639. default:
  1640. ASSERT(FALSE);
  1641. break;
  1642. }
  1643. return FALSE;
  1644. }
  1645. //-----------------------------------------------------------------------------
  1646. // Evaluate whether or not a specific object meets the filtering requirements
  1647. // set by the constraints (filtering are the constraints where one half is
  1648. // a static value).
  1649. //-----------------------------------------------------------------------------
  1650. BOOL CWMILiveHelper::Version5EvaluateFilter(IWbemClassObject * pObject, const GATH_FIELD * pConstraints)
  1651. {
  1652. const GATH_FIELD * pLHS = pConstraints, * pRHS = NULL;
  1653. VARIANT variant;
  1654. CString strValue;
  1655. BSTR propName;
  1656. ASSERT(pObject);
  1657. if (pObject == NULL)
  1658. return FALSE;
  1659. while (pLHS && pLHS->m_pNext)
  1660. {
  1661. pRHS = pLHS->m_pNext;
  1662. VariantInit(&variant);
  1663. // If either the left or right hand side is static, we need to do the check.
  1664. // First check out if the left side is static.
  1665. if (pLHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0 && pRHS->m_pArgs)
  1666. {
  1667. propName = pRHS->m_pArgs->m_strText.AllocSysString();
  1668. strValue.Empty();
  1669. VariantClear(&variant);
  1670. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  1671. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  1672. {
  1673. strValue = V_BSTR(&variant);
  1674. if (strValue.CompareNoCase(pLHS->m_strFormat) != 0)
  1675. return FALSE;
  1676. }
  1677. }
  1678. // Next check out if the right side is static.
  1679. if (pRHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0 && pLHS->m_pArgs)
  1680. {
  1681. propName = pLHS->m_pArgs->m_strText.AllocSysString();
  1682. strValue.Empty();
  1683. VariantClear(&variant);
  1684. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  1685. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  1686. {
  1687. strValue = V_BSTR(&variant);
  1688. if (strValue.CompareNoCase(pRHS->m_strFormat) != 0)
  1689. return FALSE;
  1690. }
  1691. }
  1692. // Advance our pointer to the left hand side by two.
  1693. pLHS = pRHS->m_pNext;
  1694. }
  1695. return TRUE;
  1696. }
  1697. //-----------------------------------------------------------------------------
  1698. // This method uses an object interface and the constraint fields to advance
  1699. // any joined classes to the correct instances.
  1700. //-----------------------------------------------------------------------------
  1701. void CWMILiveHelper::Version5EvaluateJoin(const CString & strClass, IWbemClassObject * pObject, const GATH_FIELD * pConstraints)
  1702. {
  1703. const GATH_FIELD *pLHS = pConstraints, *pRHS = NULL;
  1704. const GATH_FIELD *pEnumerated, *pJoinedTo;
  1705. GATH_FIELD fieldEnumerated, fieldJoinedTo;
  1706. VARIANT variant;
  1707. CString strValue;
  1708. BSTR propName;
  1709. ASSERT(pObject);
  1710. if (pObject == NULL)
  1711. return;
  1712. while (pLHS && pLHS->m_pNext)
  1713. {
  1714. pRHS = pLHS->m_pNext;
  1715. // If either side is static, this is a filter, rather than a join.
  1716. if ((pRHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0) ||
  1717. (pLHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0))
  1718. {
  1719. pLHS = pRHS->m_pNext;
  1720. continue;
  1721. }
  1722. // Find out which side refers to the class we're enumerating.
  1723. if (pRHS->m_strSource.CompareNoCase(strClass) == 0)
  1724. {
  1725. pEnumerated = pRHS;
  1726. pJoinedTo = pLHS;
  1727. }
  1728. else if (pLHS->m_strSource.CompareNoCase(strClass) == 0)
  1729. {
  1730. pEnumerated = pLHS;
  1731. pJoinedTo = pRHS;
  1732. }
  1733. else
  1734. {
  1735. pLHS = pRHS->m_pNext;
  1736. continue;
  1737. }
  1738. // Next, enumerate through the instances of the joined to class until
  1739. // we find one which satisfies the constraint. We can use the EvaluateFilter
  1740. // method to find out when the constraint is met. Set up a field pointer
  1741. // for the constraint (get the value from the enumerated class and use it
  1742. // as a static.
  1743. fieldJoinedTo = *pJoinedTo;
  1744. fieldJoinedTo.m_pNext = NULL;
  1745. VariantInit(&variant);
  1746. strValue.Empty();
  1747. if (pEnumerated->m_pArgs)
  1748. {
  1749. propName = pEnumerated->m_pArgs->m_strText.AllocSysString();
  1750. VariantClear(&variant);
  1751. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  1752. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  1753. {
  1754. strValue = V_BSTR(&variant);
  1755. }
  1756. }
  1757. fieldEnumerated.m_strSource = CString(STATIC_SOURCE);
  1758. fieldEnumerated.m_pNext = &fieldJoinedTo;
  1759. fieldEnumerated.m_strFormat = strValue;
  1760. // Now, enumerate the joined to class until it meets the constraints.
  1761. Version5RemoveObject(pJoinedTo->m_strSource);
  1762. Version5ResetClass(pJoinedTo->m_strSource, &fieldEnumerated);
  1763. Version5GetObject(pJoinedTo->m_strSource, &fieldEnumerated);
  1764. // Advance our pointer to the left hand side by two.
  1765. pLHS = pRHS->m_pNext;
  1766. }
  1767. // Because the GATH_FIELD destructor follows next pointers, we want
  1768. // to unlink our two GATH_FIELD locals. Also, we don't want the
  1769. // destructor for fieldJoinedTo to delete the arguments.
  1770. fieldEnumerated.m_pNext = NULL;
  1771. fieldJoinedTo.m_pArgs = NULL;
  1772. }
  1773. //-----------------------------------------------------------------------------
  1774. // Evaluate whether or not the constraints indicate that a class is being
  1775. // enumerated as a dependency class. This is currently indicated by a single
  1776. // field structure with a static value of "dependency".
  1777. //-----------------------------------------------------------------------------
  1778. BOOL CWMILiveHelper::Version5IsDependencyJoin(const GATH_FIELD * pConstraints)
  1779. {
  1780. if (pConstraints != NULL && pConstraints->m_pNext == NULL)
  1781. if (pConstraints->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0)
  1782. if (pConstraints->m_strFormat.CompareNoCase(CString(DEPENDENCY_JOIN)) == 0)
  1783. return TRUE;
  1784. return FALSE;
  1785. }
  1786. //-----------------------------------------------------------------------------
  1787. // This method is used when a dependency class is being enumerated. In a
  1788. // dependency class, each property of a class instance contains a reference
  1789. // to an instance in another class. This method will cache eache of the
  1790. // instances specified by the dependency class so properties of those instances
  1791. // can be referred to in the line structures.
  1792. //-----------------------------------------------------------------------------
  1793. void CWMILiveHelper::Version5EvaluateDependencyJoin(IWbemClassObject * pObject)
  1794. {
  1795. VARIANT variant, varClassName;
  1796. IWbemClassObject * pNewObject = NULL;
  1797. //if (pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_LOCAL_ONLY) == S_OK)
  1798. //while (pObject->Next(0, NULL, &variant, NULL, NULL) == S_OK)
  1799. VariantInit(&variant);
  1800. VariantClear(&variant);
  1801. if (pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_NONSYSTEM_ONLY) == WBEM_S_NO_ERROR)
  1802. while (pObject->Next(0, NULL, &variant, NULL, NULL) == WBEM_S_NO_ERROR)
  1803. {
  1804. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  1805. {
  1806. // Use the object path to create a pointer to the specified
  1807. // object and store it in the cache.
  1808. CString strObjectPath = V_BSTR(&variant);
  1809. BSTR bstrObjectPath = strObjectPath.AllocSysString();
  1810. HRESULT hRes = m_pIWbemServices->GetObject(bstrObjectPath, 0, NULL, &pNewObject, NULL);
  1811. if (SUCCEEDED(hRes))
  1812. {
  1813. // We need to get the class name of the new object so we know
  1814. // where to cache it. We could parse it out of the object path,
  1815. // but it will be better in the long run to get it by querying
  1816. // the new object.
  1817. if (pNewObject)
  1818. {
  1819. CString strClassName, strClassNameProp(_T("__CLASS"));
  1820. BSTR classNameProp = strClassNameProp.AllocSysString();
  1821. VariantInit(&varClassName);
  1822. VariantClear(&varClassName);
  1823. if (pNewObject->Get(classNameProp, 0L, &varClassName, NULL, NULL) == S_OK)
  1824. if (VariantChangeType(&varClassName, &varClassName, 0, VT_BSTR) == S_OK)
  1825. strClassName = V_BSTR(&varClassName);
  1826. if (!strClassName.IsEmpty())
  1827. {
  1828. CMSIObject * pNewMSIObject = new CMSIObject(pNewObject, CString(_T("")), S_OK, this, MOS_INSTANCE);
  1829. if (pNewMSIObject)
  1830. {
  1831. CMSIObject * pOldObject = NULL;
  1832. if (m_mapClassToInterface.Lookup(strClassName, (void * &) pOldObject) && pOldObject)
  1833. delete pOldObject;
  1834. m_mapClassToInterface.SetAt(strClassName, (void *) pNewMSIObject);
  1835. }
  1836. }
  1837. else
  1838. {
  1839. delete pNewObject;
  1840. pNewObject = NULL;
  1841. }
  1842. }
  1843. }
  1844. }
  1845. VariantClear(&variant);
  1846. }
  1847. }
  1848. //-----------------------------------------------------------------------------
  1849. // Remove the specified IEnumWbemClassObject pointer from the cache.
  1850. //-----------------------------------------------------------------------------
  1851. void CWMILiveHelper::Version5RemoveEnumObject(const CString & strClass)
  1852. {
  1853. CMSIEnumerator * pEnumObject = NULL;
  1854. if (m_mapClassToEnumInterface.Lookup(strClass, (void * &) pEnumObject) && pEnumObject)
  1855. delete pEnumObject;
  1856. m_mapClassToEnumInterface.RemoveKey(strClass);
  1857. }
  1858. //-----------------------------------------------------------------------------
  1859. // Clear out the contents of the caches (forcing the interfaces to be
  1860. // retrieved again).
  1861. //-----------------------------------------------------------------------------
  1862. void CWMILiveHelper::Version5ClearCache()
  1863. {
  1864. CMSIObject * pObject = NULL;
  1865. CMSIEnumerator * pEnumObject = NULL;
  1866. POSITION pos;
  1867. CString strClass;
  1868. for (pos = m_mapClassToInterface.GetStartPosition(); pos != NULL;)
  1869. {
  1870. m_mapClassToInterface.GetNextAssoc(pos, strClass, (void * &) pObject);
  1871. if (pObject)
  1872. delete pObject;
  1873. }
  1874. m_mapClassToInterface.RemoveAll();
  1875. for (pos = m_mapClassToEnumInterface.GetStartPosition(); pos != NULL;)
  1876. {
  1877. m_mapClassToEnumInterface.GetNextAssoc(pos, strClass, (void * &) pEnumObject);
  1878. if (pEnumObject)
  1879. delete pEnumObject;
  1880. }
  1881. m_mapClassToEnumInterface.RemoveAll();
  1882. }
  1883. //-----------------------------------------------------------------------------
  1884. // This function is used to retrieve a pointer to IWbemServices for a
  1885. // particular namespace. The default namespace is cimv2.
  1886. //-----------------------------------------------------------------------------
  1887. IWbemServices * CWMILiveHelper::Version5GetWBEMService(CString * pstrNamespace)
  1888. {
  1889. if (pstrNamespace == NULL || pstrNamespace->IsEmpty())
  1890. return m_pServices;
  1891. // Something like the following is useful for forcing a provider error when
  1892. // testing the error containment:
  1893. //
  1894. // if (*pstrNamespace == _T("MSAPPS")) *pstrNamespace += _T("X");
  1895. IWbemServices * pServices;
  1896. // In 5.0 we kept a map, but we probably won't do it here...
  1897. //
  1898. // if (m_mapNamespaceToService.Lookup(*pstrNamespace, (void * &) pServices) && pServices)
  1899. // return pServices;
  1900. // There is no WBEM services pointer for that namespace, we need to create one.
  1901. CString strNamespace(_T(""));
  1902. if (m_strMachine.IsEmpty())
  1903. strNamespace = CString(_T("\\\\.\\root\\")) + *pstrNamespace;
  1904. else
  1905. {
  1906. if (m_strMachine.Right(1) == CString(_T("\\")))
  1907. strNamespace = m_strMachine + CString(_T("root\\")) + *pstrNamespace;
  1908. else
  1909. strNamespace = m_strMachine + CString(_T("\\root\\")) + *pstrNamespace;
  1910. if (strNamespace.Left(2).Compare(CString(_T("\\\\"))) != 0)
  1911. strNamespace = CString(_T("\\\\")) + strNamespace;
  1912. }
  1913. IWbemLocator * pIWbemLocator = NULL;
  1914. if (CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pIWbemLocator) == S_OK)
  1915. {
  1916. BSTR pNamespace = strNamespace.AllocSysString();
  1917. HRESULT hrServer = pIWbemLocator->ConnectServer(pNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pServices);
  1918. if (pNamespace)
  1919. SysFreeString(pNamespace);
  1920. if (pIWbemLocator)
  1921. {
  1922. pIWbemLocator->Release();
  1923. pIWbemLocator = NULL;
  1924. }
  1925. if (SUCCEEDED(hrServer) && pServices)
  1926. {
  1927. ChangeWBEMSecurity(pServices);
  1928. // In 5.0 we kept a map, but we probably won't do it here...
  1929. //
  1930. // m_mapNamespaceToService.SetAt(*pstrNamespace, (void *) pServices);
  1931. if (m_pIWbemServices)
  1932. m_pIWbemServices->Release();
  1933. m_pIWbemServices = pServices;
  1934. m_pIWbemServices->AddRef();
  1935. return pServices;
  1936. }
  1937. m_hrLastVersion5Error = hrServer;
  1938. }
  1939. return NULL;
  1940. }
  1941. //-----------------------------------------------------------------------------
  1942. // The CMSIEnumerator class encapsulates the WBEM enumerator interface, or
  1943. // implements our own form of enumerator (such as for the LNK command in the
  1944. // template file).
  1945. //
  1946. // Nothing particularly interesting about the constructor or destructor.
  1947. //-----------------------------------------------------------------------------
  1948. CMSIEnumerator::CMSIEnumerator()
  1949. {
  1950. m_enumtype = CMSIEnumerator::CLASS;
  1951. m_fOnlyDups = FALSE;
  1952. m_fGotDuplicate = FALSE;
  1953. m_fMinOfOne = FALSE;
  1954. m_iMinOfOneCount = 0;
  1955. m_pEnum = NULL;
  1956. m_pWMI = NULL;
  1957. m_pConstraints = NULL;
  1958. m_pSavedDup = NULL;
  1959. m_pstrList = NULL;
  1960. m_hresCreation = S_OK;
  1961. }
  1962. CMSIEnumerator::~CMSIEnumerator()
  1963. {
  1964. if (m_pEnum)
  1965. {
  1966. switch (m_enumtype)
  1967. {
  1968. case CMSIEnumerator::WQL:
  1969. break;
  1970. case CMSIEnumerator::LNK:
  1971. m_pWMI->m_enumMap.SetEnumerator(m_strAssocClass, m_pEnum);
  1972. break;
  1973. case CMSIEnumerator::INTERNAL:
  1974. if (m_pstrList)
  1975. {
  1976. delete m_pstrList;
  1977. m_pstrList = NULL;
  1978. }
  1979. break;
  1980. case CMSIEnumerator::CLASS:
  1981. default:
  1982. m_pWMI->m_enumMap.SetEnumerator(m_strClass, m_pEnum);
  1983. break;
  1984. }
  1985. m_pEnum->Release();
  1986. m_pEnum = NULL;
  1987. }
  1988. }
  1989. //-----------------------------------------------------------------------------
  1990. // Creating the CMSIEnumerator object involves determining what sort of
  1991. // enumerator is required. We support the following types:
  1992. //
  1993. // 1. Straight enumeration of a class
  1994. // 2. Enumerate class, with applied constraints
  1995. // 3. Enumerate the results of a WQL statement.
  1996. // 4. Interprete a LNK command to enumerate associated classes.
  1997. // 5. Do internal processing on an INTERNAL type.
  1998. //-----------------------------------------------------------------------------
  1999. HRESULT CMSIEnumerator::Create(const CString & strClass, const GATH_FIELD * pConstraints, CWMIHelper * pWMI)
  2000. {
  2001. if (strClass.IsEmpty() || !pWMI)
  2002. return E_INVALIDARG;
  2003. // Create may be called multiple times (to reset the enumerator). So we may need to
  2004. // release the enumerator pointer.
  2005. if (m_pEnum)
  2006. {
  2007. m_pEnum->Release();
  2008. m_pEnum = NULL;
  2009. }
  2010. // Divide the specified class into class and namespace parts, get the WBEM service.
  2011. CString strNamespacePart(_T("")), strClassPart(strClass);
  2012. int i = strClass.Find(_T(":"));
  2013. if (i != -1)
  2014. {
  2015. strNamespacePart = strClass.Left(i);
  2016. strClassPart = strClass.Right(strClass.GetLength() - i - 1);
  2017. }
  2018. IWbemServices * pServices = pWMI->Version5GetWBEMService(&strNamespacePart);
  2019. if (pServices == NULL)
  2020. return NULL;
  2021. // First, we need to determine what type of enumerator this is. Scan through
  2022. // the constraints - if we see one which has a string starting with "WQL:" or
  2023. // "LNK:", then we know what type this enumerator is.
  2024. CString strStatement;
  2025. const GATH_FIELD * pScanConstraint = pConstraints;
  2026. while (pScanConstraint)
  2027. {
  2028. if (pScanConstraint->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0)
  2029. {
  2030. if (pScanConstraint->m_strFormat.Left(4).CompareNoCase(CString(_T("WQL:"))) == 0)
  2031. m_enumtype = CMSIEnumerator::WQL;
  2032. else if (pScanConstraint->m_strFormat.Left(4).CompareNoCase(CString(_T("LNK:"))) == 0)
  2033. m_enumtype = CMSIEnumerator::LNK;
  2034. else if (pScanConstraint->m_strFormat.Left(4).CompareNoCase(CString(_T("INT:"))) == 0)
  2035. m_enumtype = CMSIEnumerator::INTERNAL;
  2036. if (m_enumtype != CMSIEnumerator::CLASS)
  2037. {
  2038. strStatement = pScanConstraint->m_strFormat;
  2039. strStatement = strStatement.Right(strStatement.GetLength() - 4);
  2040. break;
  2041. }
  2042. }
  2043. pScanConstraint = pScanConstraint->m_pNext;
  2044. }
  2045. // If this is a WQL or a LNK enumerator, processes the statement by replacing
  2046. // [class.property] with the actual value from WBEM. If we find the string
  2047. // "[min-of-one]", make a note of it for later.
  2048. if (m_enumtype == CMSIEnumerator::WQL)
  2049. ProcessEnumString(strStatement, m_fMinOfOne, m_fOnlyDups, pWMI, m_strNoInstanceLabel, TRUE);
  2050. else if (m_enumtype == CMSIEnumerator::LNK)
  2051. if (SUCCEEDED(ParseLNKCommand(strStatement, m_strObjPath, m_strAssocClass, m_strResultClass)))
  2052. {
  2053. // Save the object path for later - so we can change the object without
  2054. // completely reprocessing the statement. Then replace the keywords in
  2055. // the statement and break out the pieces again.
  2056. m_strLNKObject = m_strObjPath;
  2057. ProcessEnumString(strStatement, m_fMinOfOne, m_fOnlyDups, pWMI, m_strNoInstanceLabel);
  2058. ParseLNKCommand(strStatement, m_strObjPath, m_strAssocClass, m_strResultClass);
  2059. }
  2060. // Now, based on the enumerator type, create the WBEM enumerator object.
  2061. switch (m_enumtype)
  2062. {
  2063. case CMSIEnumerator::WQL:
  2064. {
  2065. BSTR language = SysAllocString(L"WQL");
  2066. BSTR query = strStatement.AllocSysString();
  2067. m_hresCreation = pServices->ExecQuery(language, query, WBEM_FLAG_RETURN_IMMEDIATELY, 0, &m_pEnum);
  2068. SysFreeString(query);
  2069. SysFreeString(language);
  2070. }
  2071. break;
  2072. case CMSIEnumerator::LNK:
  2073. {
  2074. m_hresCreation = ParseLNKCommand(strStatement, m_strObjPath, m_strAssocClass, m_strResultClass);
  2075. if (SUCCEEDED(m_hresCreation))
  2076. {
  2077. BSTR className = m_strAssocClass.AllocSysString();
  2078. m_pEnum = pWMI->m_enumMap.GetEnumerator(m_strAssocClass);
  2079. if (m_pEnum)
  2080. m_hresCreation = S_OK;
  2081. else
  2082. m_hresCreation = pServices->CreateInstanceEnum(className, WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &m_pEnum);
  2083. SysFreeString(className);
  2084. }
  2085. }
  2086. break;
  2087. case CMSIEnumerator::INTERNAL:
  2088. // We'll call a function here so we can do whatever processing is required
  2089. // to create this internal enumeration.
  2090. m_hresCreation = CreateInternalEnum(strStatement, pWMI);
  2091. break;
  2092. case CMSIEnumerator::CLASS:
  2093. default:
  2094. {
  2095. BSTR className = strClassPart.AllocSysString();
  2096. m_pEnum = pWMI->m_enumMap.GetEnumerator(strClassPart);
  2097. if (m_pEnum)
  2098. m_hresCreation = S_OK;
  2099. else
  2100. m_hresCreation = pServices->CreateInstanceEnum(className, WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &m_pEnum);
  2101. SysFreeString(className);
  2102. }
  2103. }
  2104. // Set some of the other member variables.
  2105. m_strClass = strClass;
  2106. m_pWMI = pWMI;
  2107. m_iMinOfOneCount = (m_fMinOfOne) ? 1 : 0;
  2108. m_pConstraints = pConstraints;
  2109. if (m_pEnum)
  2110. ChangeWBEMSecurity(m_pEnum);
  2111. // Based on the HRESULT from creating the enumeration, determine what to return.
  2112. // For certain errors, we want to act like the creation succeeded, then supply
  2113. // objects which return the error text.
  2114. if (FAILED(m_hresCreation))
  2115. {
  2116. m_fMinOfOne = TRUE;
  2117. m_iMinOfOneCount = 1;
  2118. }
  2119. pServices->Release();
  2120. return S_OK;
  2121. }
  2122. //-----------------------------------------------------------------------------
  2123. // This function is used to create internal enumeration types (enumerations
  2124. // which are beyond the template file syntax). Basically a bunch of special
  2125. // cases.
  2126. //-----------------------------------------------------------------------------
  2127. HRESULT CMSIEnumerator::CreateInternalEnum(const CString & strInternal, CWMIHelper * pWMI)
  2128. {
  2129. if (strInternal.CompareNoCase(CString(_T("dlls"))) == 0)
  2130. {
  2131. // We want to enumerate all the loaded dlls and exes on the system.
  2132. // This can be done by enumerating the CIM_ProcessExecutable class
  2133. // and removing duplicate file names. We'll keep the filenames (with
  2134. // path information) in a string list.
  2135. if (m_pstrList == NULL)
  2136. {
  2137. m_pstrList = new CStringList;
  2138. if (m_pstrList == NULL)
  2139. return E_FAIL;
  2140. }
  2141. else
  2142. m_pstrList->RemoveAll();
  2143. HRESULT hr = S_OK;
  2144. IWbemServices * pServices = pWMI->Version5GetWBEMService();
  2145. if (pServices)
  2146. {
  2147. BSTR className = SysAllocString(L"CIM_ProcessExecutable");
  2148. IEnumWbemClassObject * pEnum = NULL;
  2149. hr = pServices->CreateInstanceEnum(className, WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnum);
  2150. if (SUCCEEDED(hr))
  2151. {
  2152. IWbemClassObject * pWBEMObject = NULL;
  2153. ULONG uReturned;
  2154. VARIANT variant;
  2155. BSTR propName = SysAllocString(L"Antecedent");
  2156. VariantInit(&variant);
  2157. do
  2158. {
  2159. uReturned = 0;
  2160. hr = pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned);
  2161. if (SUCCEEDED(hr) && pWBEMObject && uReturned)
  2162. {
  2163. // For each instance of CIM_ProcessExecutable, get the
  2164. // Antecedent property (which contains the file path).
  2165. // If it is unique, save it in the list.
  2166. VariantClear(&variant);
  2167. if (pWBEMObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  2168. {
  2169. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  2170. {
  2171. CString strResult = V_BSTR(&variant);
  2172. strResult.MakeLower();
  2173. if (m_pstrList->Find(strResult) == NULL)
  2174. m_pstrList->AddHead(strResult);
  2175. }
  2176. }
  2177. }
  2178. } while (SUCCEEDED(hr) && pWBEMObject && uReturned);
  2179. ::SysFreeString(propName);
  2180. pEnum->Release();
  2181. }
  2182. ::SysFreeString(className);
  2183. pServices->Release();
  2184. }
  2185. return hr;
  2186. }
  2187. return S_OK;
  2188. }
  2189. //-----------------------------------------------------------------------------
  2190. // Help function for ProcessEnumString, used to convert single backslashes
  2191. // into double backslashes (required for WQL statements).
  2192. //-----------------------------------------------------------------------------
  2193. void MakeDoubleBackslashes(CString & strValue)
  2194. {
  2195. CString strTemp(strValue);
  2196. CString strResults;
  2197. while (!strTemp.IsEmpty())
  2198. {
  2199. if (strTemp[0] != _T('\\'))
  2200. {
  2201. int index = strTemp.Find(_T('\\'));
  2202. if (index < 0)
  2203. index = strTemp.GetLength();
  2204. strResults += strTemp.Left(index);
  2205. strTemp = strTemp.Right(strTemp.GetLength() - index);
  2206. }
  2207. else
  2208. {
  2209. strResults += CString("\\\\");
  2210. strTemp = strTemp.Mid(1);
  2211. }
  2212. }
  2213. strValue = strResults;
  2214. }
  2215. //-----------------------------------------------------------------------------
  2216. // This function replaces [class.property] with the actual value of the
  2217. // property, and strings out [min-of-one], indicating if it was present in
  2218. // the fMinOfOne parameter.
  2219. //-----------------------------------------------------------------------------
  2220. void CMSIEnumerator::ProcessEnumString(CString & strStatement, BOOL & fMinOfOne, BOOL & fOnlyDups, CWMIHelper * pWMI, CString & strNoInstanceLabel, BOOL fMakeDoubleBackslashes)
  2221. {
  2222. CString strMinOfOne(_T("min-of-one"));
  2223. CString strOnlyDups(_T("more-than-one"));
  2224. CString strResults;
  2225. fMinOfOne = FALSE;
  2226. fOnlyDups = FALSE;
  2227. while (!strStatement.IsEmpty())
  2228. {
  2229. if (strStatement[0] != _T('['))
  2230. {
  2231. int index = strStatement.Find(_T('['));
  2232. if (index < 0)
  2233. index = strStatement.GetLength();
  2234. strResults += strStatement.Left(index);
  2235. strStatement = strStatement.Right(strStatement.GetLength() - index);
  2236. }
  2237. else
  2238. {
  2239. CString strKeyword;
  2240. strStatement = strStatement.Right(strStatement.GetLength() - 1);
  2241. int index = strStatement.Find(_T(']'));
  2242. if (index < 0)
  2243. break;
  2244. strKeyword = strStatement.Left(index);
  2245. if (strKeyword.Left(strMinOfOne.GetLength()).CompareNoCase(strMinOfOne) == 0)
  2246. {
  2247. fMinOfOne = TRUE;
  2248. int iEqualsIndex = strKeyword.Find(_T('='));
  2249. if (iEqualsIndex > 0)
  2250. strNoInstanceLabel = strKeyword.Right(strKeyword.GetLength() - iEqualsIndex - 1);
  2251. }
  2252. else if (strKeyword.Left(strOnlyDups.GetLength()).CompareNoCase(strOnlyDups) == 0)
  2253. {
  2254. fOnlyDups = TRUE;
  2255. int iEqualsIndex = strKeyword.Find(_T('='));
  2256. if (iEqualsIndex > 0)
  2257. strNoInstanceLabel = strKeyword.Right(strKeyword.GetLength() - iEqualsIndex - 1);
  2258. }
  2259. else if (!strKeyword.IsEmpty())
  2260. {
  2261. int iDotIndex = strKeyword.Find(_T('.'));
  2262. if (iDotIndex >= 0)
  2263. {
  2264. CString strValue;
  2265. if (pWMI->Version5QueryValue(strKeyword.Left(iDotIndex), strKeyword.Right(strKeyword.GetLength() - iDotIndex - 1), strValue))
  2266. {
  2267. if (fMakeDoubleBackslashes)
  2268. MakeDoubleBackslashes(strValue);
  2269. strResults += strValue;
  2270. }
  2271. }
  2272. }
  2273. strStatement = strStatement.Right(strStatement.GetLength() - (index + 1));
  2274. }
  2275. }
  2276. strStatement = strResults;
  2277. }
  2278. //-----------------------------------------------------------------------------
  2279. // Parse the component classes from the LNK command.
  2280. //-----------------------------------------------------------------------------
  2281. HRESULT CMSIEnumerator::ParseLNKCommand(const CString & strStatement, CString & strObjPath, CString & strAssocClass, CString & strResultClass)
  2282. {
  2283. // We need to parse out the LNK statement into two or three components,
  2284. // from the form "objPath->assocClass[->resultClass]", with the
  2285. // brackets indicating that the resultClass is optional.
  2286. CString strWorking(strStatement);
  2287. int iArrowIndex = strWorking.Find(_T("->"));
  2288. if (iArrowIndex == -1)
  2289. return E_INVALIDARG;
  2290. strObjPath = strWorking.Left(iArrowIndex);
  2291. strWorking = strWorking.Right(strWorking.GetLength() - (iArrowIndex + 2));
  2292. iArrowIndex = strWorking.Find(_T("->"));
  2293. if (iArrowIndex == -1)
  2294. strAssocClass = strWorking;
  2295. else
  2296. {
  2297. strAssocClass = strWorking.Left(iArrowIndex);
  2298. strWorking = strWorking.Right(strWorking.GetLength() - (iArrowIndex + 2));
  2299. strResultClass = strWorking;
  2300. strResultClass.MakeLower();
  2301. }
  2302. strAssocClass.TrimRight(); strAssocClass.TrimLeft();
  2303. strObjPath.TrimRight(); strObjPath.TrimLeft();
  2304. strResultClass.TrimRight(); strResultClass.TrimLeft();
  2305. return S_OK;
  2306. }
  2307. //-----------------------------------------------------------------------------
  2308. // The Next method will advance the enumerator based on the type of this
  2309. // enumerator.
  2310. //-----------------------------------------------------------------------------
  2311. HRESULT CMSIEnumerator::Next(CMSIObject ** ppObject)
  2312. {
  2313. if (!ppObject)
  2314. return E_INVALIDARG;
  2315. *ppObject = NULL;
  2316. // If there was an error creating the enumeration, return the error code.
  2317. if (FAILED(m_hresCreation))
  2318. return m_hresCreation;
  2319. if (m_pEnum == NULL && m_enumtype != CMSIEnumerator::INTERNAL)
  2320. return E_UNEXPECTED;
  2321. HRESULT hRes = S_OK;
  2322. IWbemClassObject * pWBEMObject = NULL;
  2323. switch (m_enumtype)
  2324. {
  2325. case CMSIEnumerator::LNK:
  2326. {
  2327. // Scan through the enumerated associate class. Look for one which
  2328. // satisfies our requirements.
  2329. CString strTemp, strAssociatedObject(_T(""));
  2330. ULONG uReturned;
  2331. IWbemClassObject * pAssocObj;
  2332. do
  2333. {
  2334. pAssocObj = NULL;
  2335. uReturned = 0;
  2336. hRes = m_pEnum->Next(TIMEOUT, 1, &pAssocObj, &uReturned);
  2337. if (!pAssocObj || FAILED(hRes) || uReturned != 1)
  2338. {
  2339. // Even if we didn't succeed in getting a new object,
  2340. // we might have a saved one if we're only showing
  2341. // "more-than-one" objects.
  2342. if (m_fOnlyDups && m_pSavedDup && m_fGotDuplicate)
  2343. {
  2344. // We have found one previously, so return it.
  2345. // Make it look like the Next call was successful.
  2346. m_pSavedDup = NULL;
  2347. hRes = S_OK;
  2348. uReturned = 1;
  2349. strAssociatedObject = m_strSavedDup;
  2350. break;
  2351. }
  2352. else
  2353. {
  2354. if (m_pSavedDup)
  2355. {
  2356. // We only got one object instance, so get rid of it.
  2357. m_pSavedDup->Release();
  2358. m_pSavedDup = NULL;
  2359. }
  2360. break;
  2361. }
  2362. }
  2363. if (AssocObjectOK(pAssocObj, strTemp))
  2364. {
  2365. // This object passed the filter - but if we're showing
  2366. // only "more-than-one" objects, save this one and return
  2367. // the saved one.
  2368. if (m_fOnlyDups)
  2369. {
  2370. if (m_pSavedDup)
  2371. {
  2372. // We have found one previously, so return it and
  2373. // save the current.
  2374. IWbemClassObject * pSwap = pAssocObj;
  2375. CString strSwap = strTemp;
  2376. pAssocObj = m_pSavedDup;
  2377. m_pSavedDup = pSwap;
  2378. strTemp = m_strSavedDup;
  2379. m_strSavedDup = strSwap;
  2380. m_fGotDuplicate = TRUE;
  2381. }
  2382. else
  2383. {
  2384. // This is the first one we've found - don't
  2385. // return it until we find another.
  2386. m_pSavedDup = pAssocObj;
  2387. m_strSavedDup = strTemp;
  2388. m_fGotDuplicate = FALSE;
  2389. continue;
  2390. }
  2391. }
  2392. strAssociatedObject = strTemp;
  2393. pAssocObj->Release();
  2394. break;
  2395. }
  2396. pAssocObj->Release();
  2397. } while (pAssocObj);
  2398. // If there is an associated object path, get the object.
  2399. if (!strAssociatedObject.IsEmpty())
  2400. {
  2401. BSTR path = strAssociatedObject.AllocSysString();
  2402. if (m_pWMI->m_pIWbemServices != NULL)
  2403. hRes = m_pWMI->m_pIWbemServices->GetObject(path, 0, NULL, &pWBEMObject, NULL);
  2404. else
  2405. hRes = E_FAIL;
  2406. SysFreeString(path);
  2407. }
  2408. }
  2409. break;
  2410. case CMSIEnumerator::WQL:
  2411. {
  2412. ULONG uReturned;
  2413. hRes = m_pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned);
  2414. }
  2415. break;
  2416. case CMSIEnumerator::INTERNAL:
  2417. hRes = InternalNext(&pWBEMObject);
  2418. break;
  2419. case CMSIEnumerator::CLASS:
  2420. default:
  2421. {
  2422. ULONG uReturned;
  2423. // EvaluateFilter and IsDependencyJoin handle a NULL pConstraints parameter,
  2424. // but for efficiency we're going to have a distinct branch for a non-NULL
  2425. // value (since it will usually be NULL).
  2426. if (m_pConstraints)
  2427. {
  2428. // Keep enumerating the instances of this class until we've
  2429. // found one which satisfies all of the filters.
  2430. do
  2431. {
  2432. pWBEMObject = NULL;
  2433. hRes = m_pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned);
  2434. if (!pWBEMObject || hRes != S_OK || uReturned != 1)
  2435. break;
  2436. else if (m_pWMI->Version5EvaluateFilter(pWBEMObject, m_pConstraints))
  2437. break;
  2438. pWBEMObject->Release();
  2439. } while (pWBEMObject);
  2440. // If this class is being enumerated as a dependency class, then
  2441. // locate all the objects it references. If it isn't, we still
  2442. // need to check for any joins to other classes formed by the constraints.
  2443. if (pWBEMObject)
  2444. if (m_pWMI->Version5IsDependencyJoin(m_pConstraints))
  2445. m_pWMI->Version5EvaluateDependencyJoin(pWBEMObject);
  2446. else
  2447. m_pWMI->Version5EvaluateJoin(m_strClass, pWBEMObject, m_pConstraints);
  2448. }
  2449. else
  2450. hRes = m_pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned);
  2451. }
  2452. break;
  2453. }
  2454. if (pWBEMObject == NULL)
  2455. {
  2456. // There was no object to get. We'll still create a CMSIObject, but
  2457. // we'll set its state to indicate either that there are no instances,
  2458. // or one instance with an error message.
  2459. if (SUCCEEDED(hRes) && (m_iMinOfOneCount == 0))
  2460. *ppObject = new CMSIObject(pWBEMObject, m_strNoInstanceLabel, hRes, m_pWMI, MOS_NO_INSTANCES);
  2461. else
  2462. *ppObject = new CMSIObject(pWBEMObject, m_strNoInstanceLabel, hRes, m_pWMI, MOS_MSG_INSTANCE);
  2463. }
  2464. else
  2465. *ppObject = new CMSIObject(pWBEMObject, m_strNoInstanceLabel, hRes, m_pWMI, MOS_INSTANCE);
  2466. if (m_iMinOfOneCount)
  2467. m_iMinOfOneCount--;
  2468. return S_OK;
  2469. }
  2470. //-----------------------------------------------------------------------------
  2471. // InternalNext is used to return a WBEM object for an internal enumeration
  2472. // (one that requires processing beyond the template file). Basically a
  2473. // set of special cases.
  2474. //-----------------------------------------------------------------------------
  2475. HRESULT CMSIEnumerator::InternalNext(IWbemClassObject ** ppWBEMObject)
  2476. {
  2477. if (m_pstrList && !m_pstrList->IsEmpty())
  2478. {
  2479. CString strNextObject = m_pstrList->RemoveHead();
  2480. if (!strNextObject.IsEmpty())
  2481. {
  2482. IWbemServices * pServices = m_pWMI->Version5GetWBEMService();
  2483. if (pServices)
  2484. {
  2485. BSTR objectpath = strNextObject.AllocSysString();
  2486. HRESULT hr = S_OK;
  2487. if (FAILED(pServices->GetObject(objectpath, 0, NULL, ppWBEMObject, NULL)))
  2488. hr = E_FAIL;
  2489. ::SysFreeString(objectpath);
  2490. pServices->Release();
  2491. return hr;
  2492. }
  2493. }
  2494. }
  2495. return S_OK;
  2496. }
  2497. //-----------------------------------------------------------------------------
  2498. // Reset should just reset the enumerator pointer.
  2499. //-----------------------------------------------------------------------------
  2500. HRESULT CMSIEnumerator::Reset(const GATH_FIELD * pConstraints)
  2501. {
  2502. HRESULT hRes = S_OK;
  2503. if (m_pEnum)
  2504. {
  2505. switch (m_enumtype)
  2506. {
  2507. case CMSIEnumerator::WQL:
  2508. hRes = Create(m_strClass, pConstraints, m_pWMI);
  2509. break;
  2510. case CMSIEnumerator::LNK:
  2511. {
  2512. BOOL fDummy, fDummy2;
  2513. CString strDummy;
  2514. m_strObjPath = m_strLNKObject;
  2515. ProcessEnumString(m_strObjPath, fDummy, fDummy2, m_pWMI, strDummy);
  2516. m_iMinOfOneCount = (m_fMinOfOne) ? 1 : 0;
  2517. hRes = m_pEnum->Reset();
  2518. }
  2519. break;
  2520. case CMSIEnumerator::INTERNAL:
  2521. hRes = Create(m_strClass, pConstraints, m_pWMI);
  2522. break;
  2523. case CMSIEnumerator::CLASS:
  2524. default:
  2525. m_iMinOfOneCount = (m_fMinOfOne) ? 1 : 0;
  2526. hRes = m_pEnum->Reset();
  2527. break;
  2528. }
  2529. }
  2530. else
  2531. hRes = E_UNEXPECTED;
  2532. return hRes;
  2533. }
  2534. //-----------------------------------------------------------------------------
  2535. // Evaluate if the pObject parameter is valid for this LNK enumerator. In
  2536. // particular, we must find the m_strObjPath in one of its properties, and
  2537. // possibly finding another property containing the m_strResultClass string.
  2538. //-----------------------------------------------------------------------------
  2539. BOOL CMSIEnumerator::AssocObjectOK(IWbemClassObject * pObject, CString & strAssociatedObject)
  2540. {
  2541. strAssociatedObject.Empty();
  2542. ASSERT(pObject);
  2543. if (pObject == NULL)
  2544. return FALSE;
  2545. VARIANT variant;
  2546. CString strReturn(_T("")), strValue;
  2547. // Traverse the set of non-system properties. Look for one the is the same
  2548. // as the object path.
  2549. pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_NONSYSTEM_ONLY);
  2550. VariantInit(&variant);
  2551. while (pObject->Next(0, NULL, &variant, NULL, NULL) == WBEM_S_NO_ERROR)
  2552. {
  2553. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  2554. strValue = V_BSTR(&variant);
  2555. VariantClear(&variant);
  2556. if (strValue.CompareNoCase(m_strObjPath) == 0)
  2557. break;
  2558. }
  2559. pObject->EndEnumeration();
  2560. // If we found a property containing the object path, look through for other
  2561. // paths which might be to objects we're insterested in.
  2562. if (strValue.CompareNoCase(m_strObjPath) == 0)
  2563. {
  2564. pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_NONSYSTEM_ONLY);
  2565. while (strReturn.IsEmpty() && (pObject->Next(0, NULL, &variant, NULL, NULL) == WBEM_S_NO_ERROR))
  2566. {
  2567. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  2568. strValue = V_BSTR(&variant);
  2569. if (strValue.CompareNoCase(m_strObjPath) != 0)
  2570. {
  2571. if (m_strResultClass.IsEmpty())
  2572. strReturn = strValue;
  2573. else
  2574. {
  2575. CString strSearch(strValue);
  2576. strSearch.MakeLower();
  2577. if (strSearch.Find(m_strResultClass) != -1)
  2578. strReturn = strValue;
  2579. }
  2580. }
  2581. VariantClear(&variant);
  2582. }
  2583. pObject->EndEnumeration();
  2584. }
  2585. if (!strReturn.IsEmpty())
  2586. {
  2587. strAssociatedObject = strReturn;
  2588. return TRUE;
  2589. }
  2590. return FALSE;
  2591. }
  2592. //-----------------------------------------------------------------------------
  2593. // Implement the CMSIObject class. This is just a thin wrapper for the
  2594. // IWbemClassObject interface.
  2595. //-----------------------------------------------------------------------------
  2596. CMSIObject::CMSIObject(IWbemClassObject * pObject, const CString & strLabel, HRESULT hres, CWMIHelper * pWMI, MSIObjectState objState)
  2597. {
  2598. m_pObject = pObject;
  2599. m_strLabel = strLabel;
  2600. m_hresCreation = hres;
  2601. m_pWMI = pWMI;
  2602. m_objState = objState;
  2603. }
  2604. CMSIObject::~CMSIObject()
  2605. {
  2606. if (m_pObject)
  2607. {
  2608. m_pObject->Release();
  2609. m_pObject = NULL;
  2610. }
  2611. }
  2612. HRESULT CMSIObject::Get(BSTR property, LONG lFlags, VARIANT *pVal, VARTYPE *pvtType, LONG *plFlavor)
  2613. {
  2614. ASSERT(m_objState != MOS_NO_INSTANCES);
  2615. // If there is an object interface, just pass the request on through.
  2616. if (m_pObject)
  2617. return m_pObject->Get(property, lFlags, pVal, NULL /* pvtType */, plFlavor);
  2618. // Otherwise, we need to return the appropriate string.
  2619. CString strReturn;
  2620. GetErrorLabel(strReturn);
  2621. V_BSTR(pVal) = strReturn.AllocSysString();
  2622. pVal->vt = VT_BSTR;
  2623. return S_OK;
  2624. }
  2625. MSIObjectState CMSIObject::IsValid()
  2626. {
  2627. return m_objState;
  2628. }
  2629. HRESULT CMSIObject::GetErrorLabel(CString & strError)
  2630. {
  2631. switch (m_hresCreation)
  2632. {
  2633. case WBEM_E_ACCESS_DENIED:
  2634. strError = m_pWMI->m_strBadProperty; // shouldn't be showing errors this way in 6.0
  2635. break;
  2636. case WBEM_E_TRANSPORT_FAILURE:
  2637. strError = m_pWMI->m_strBadProperty;
  2638. break;
  2639. case S_OK:
  2640. case WBEM_S_FALSE:
  2641. default:
  2642. // This object was created from an enumeration that was marked as "min-of-one",
  2643. // meaning that at least one object, even if it's not valid, needed to be
  2644. // returned from the enumeration. Return the string we saved at object creation.
  2645. if (!m_strLabel.IsEmpty())
  2646. strError = m_strLabel;
  2647. else
  2648. strError = m_pWMI->m_strBadProperty;
  2649. break;
  2650. }
  2651. return S_OK;
  2652. }
  2653. //-----------------------------------------------------------------------------
  2654. // Look up strVal in the ValueMap (if it exists) for strClass.strProperty
  2655. // If the value or the ValueMap is not found, return E_Something.
  2656. //
  2657. // Useful code snippet - this will dump the contents of the cache of
  2658. // saved values. To find all value mapped properties, but this code
  2659. // someplace where it will execute when MSInfo exits, change QueryValue
  2660. // to call CheckValueMap for all properties, then run MSInfo and do a global
  2661. // refresh (like to save an NFO).
  2662. //
  2663. // msiLog.WriteLog(CMSInfoLog::BASIC, _T("BEGIN Dump of ValueMap Cache\r\n"));
  2664. // CString key, val, log;
  2665. // for (POSITION pos = g_mapValueMap.GetStartPosition(); pos != NULL;)
  2666. // {
  2667. // g_mapValueMap.GetNextAssoc(pos, key, val);
  2668. // log.Format(_T(" %s = %s\r\n", key, val);
  2669. // msiLog.WriteLog(CMSInfoLog::BASIC, log);
  2670. // }
  2671. // msiLog.WriteLog(CMSInfoLog::BASIC, _T("END Dump of ValueMap Cache\r\n"));
  2672. //-----------------------------------------------------------------------------
  2673. CMapStringToString g_mapValueMap;
  2674. HRESULT CWMILiveHelper::Version5CheckValueMap(const CString& strClass, const CString& strProperty, const CString& strVal, CString &strResult)
  2675. {
  2676. IWbemClassObject * pWBEMClassObject = NULL;
  2677. HRESULT hrMap = S_OK, hr = S_OK;
  2678. VARIANT vArray, vMapArray;
  2679. IWbemQualifierSet * qual = NULL;
  2680. // Check the cache of saved values.
  2681. CString strLookup = strClass + CString(_T(".")) + strProperty + CString(_T(":")) + strVal;
  2682. if (g_mapValueMap.Lookup(strLookup, strResult))
  2683. return S_OK;
  2684. // Get the class object (not instance) for this class.
  2685. IWbemServices * pServices = Version5GetWBEMService();
  2686. if (!pServices)
  2687. return E_FAIL;
  2688. CString strFullClass(_T("\\\\.\\root\\cimv2:"));
  2689. strFullClass += strClass;
  2690. BSTR bstrObjectPath = strFullClass.AllocSysString();
  2691. hr = pServices->GetObject(bstrObjectPath, WBEM_FLAG_USE_AMENDED_QUALIFIERS, NULL, &pWBEMClassObject, NULL);
  2692. ::SysFreeString(bstrObjectPath);
  2693. pServices->Release();
  2694. if (FAILED(hr))
  2695. return hr;
  2696. // Get the qualifiers from the class object.
  2697. BSTR bstrProperty = strProperty.AllocSysString();
  2698. hr = pWBEMClassObject->GetPropertyQualifierSet(bstrProperty, &qual);
  2699. ::SysFreeString(bstrProperty);
  2700. if (SUCCEEDED(hr) && qual)
  2701. {
  2702. // Get the ValueMap and Value arrays.
  2703. hrMap = qual->Get(L"ValueMap", 0, &vMapArray, NULL);
  2704. hr = qual->Get(L"Values", 0, &vArray, NULL);
  2705. if (SUCCEEDED(hr) && vArray.vt == (VT_BSTR | VT_ARRAY))
  2706. {
  2707. // Get the property value we're mapping.
  2708. long index;
  2709. if (SUCCEEDED(hrMap))
  2710. {
  2711. SAFEARRAY * pma = V_ARRAY(&vMapArray);
  2712. long lLowerBound = 0, lUpperBound = 0 ;
  2713. SafeArrayGetLBound(pma, 1, &lLowerBound);
  2714. SafeArrayGetUBound(pma, 1, &lUpperBound);
  2715. BSTR vMap;
  2716. for (long x = lLowerBound; x <= lUpperBound; x++)
  2717. {
  2718. SafeArrayGetElement(pma, &x, &vMap);
  2719. if (0 == strVal.CompareNoCase((LPCTSTR)vMap))
  2720. {
  2721. index = x;
  2722. break; // found it
  2723. }
  2724. }
  2725. }
  2726. else
  2727. {
  2728. // Shouldn't hit this case - if mof is well formed
  2729. // means there is no value map where we are expecting one.
  2730. // If the strVal we are looking for is a number, treat it
  2731. // as an index for the Values array. If it's a string,
  2732. // then this is an error.
  2733. TCHAR * szTest = NULL;
  2734. index = _tcstol((LPCTSTR)strVal, &szTest, 10);
  2735. if (szTest == NULL || (index == 0 && *szTest != 0) || strVal.IsEmpty())
  2736. hr = E_FAIL;
  2737. }
  2738. // Lookup the string.
  2739. if (SUCCEEDED(hr))
  2740. {
  2741. SAFEARRAY * psa = V_ARRAY(&vArray);
  2742. long ix[1] = {index};
  2743. BSTR str2;
  2744. hr = SafeArrayGetElement(psa, ix, &str2);
  2745. if (SUCCEEDED(hr))
  2746. {
  2747. strResult = str2;
  2748. SysFreeString(str2);
  2749. hr = S_OK;
  2750. }
  2751. else
  2752. {
  2753. hr = WBEM_E_VALUE_OUT_OF_RANGE;
  2754. }
  2755. }
  2756. }
  2757. qual->Release();
  2758. }
  2759. if (SUCCEEDED(hr))
  2760. g_mapValueMap.SetAt(strLookup, strResult);
  2761. return hr;
  2762. }
  2763. //-----------------------------------------------------------------------------
  2764. // The CEnumMap is a utility class to cache IEnumWbemClassObject pointers.
  2765. // There will be one instance of this class used to improve performance
  2766. // by avoiding the high overhead associated with creating enumerators for
  2767. // certain classes.
  2768. //-----------------------------------------------------------------------------
  2769. IEnumWbemClassObject * CEnumMap::GetEnumerator(const CString & strClass)
  2770. {
  2771. IEnumWbemClassObject * pEnum = NULL;
  2772. IEnumWbemClassObject * pNewEnum = NULL;
  2773. if (m_mapEnum.Lookup(strClass, (void * &) pEnum))
  2774. {
  2775. if (pEnum && SUCCEEDED(pEnum->Clone(&pNewEnum)) && pNewEnum)
  2776. pNewEnum->Reset();
  2777. else
  2778. pNewEnum = NULL;
  2779. }
  2780. return pNewEnum;
  2781. }
  2782. void CEnumMap::SetEnumerator(const CString & strClass, IEnumWbemClassObject * pEnum)
  2783. {
  2784. if (pEnum)
  2785. {
  2786. IEnumWbemClassObject * pEnumExisting = NULL;
  2787. if (m_mapEnum.Lookup(strClass, (void * &) pEnumExisting))
  2788. {
  2789. ; //WRITE(_T("SetEnumerator for %s, already exists, ignoring.\r\n"), strClass);
  2790. }
  2791. else
  2792. {
  2793. pEnum->AddRef();
  2794. m_mapEnum.SetAt(strClass, pEnum);
  2795. }
  2796. }
  2797. }
  2798. void CEnumMap::Reset()
  2799. {
  2800. IEnumWbemClassObject * pEnum = NULL;
  2801. CString key;
  2802. for (POSITION pos = m_mapEnum.GetStartPosition(); pos != NULL;)
  2803. {
  2804. m_mapEnum.GetNextAssoc(pos, key, (void * &) pEnum);
  2805. if (pEnum)
  2806. pEnum->Release();
  2807. }
  2808. m_mapEnum.RemoveAll();
  2809. }