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

2284 lines
65 KiB

  1. //=============================================================================
  2. // File: provider.cpp
  3. // Author: a-jammar
  4. // Covers: CDataProvider
  5. //
  6. // Copyright (c) 1998-1999 Microsoft Corporation
  7. //
  8. // This file contains functions implementing the class which uses WBEM to
  9. // retrieve information, including handling enumeration of class instances.
  10. // For usage, see gathint.h.
  11. //
  12. // NOTE: define GATH_STANDALONE if this class is being used in an application
  13. // which doesn't call CoInitialize().
  14. //=============================================================================
  15. #include "stdafx.h"
  16. #include "gather.h"
  17. #include "gathint.h"
  18. #pragma warning(disable : 4099)
  19. #include "wbemcli.h"
  20. #pragma warning(default : 4099)
  21. #include "resource.h"
  22. #include "resrc1.h"
  23. #ifndef WBEM_FLAG_USE_AMENDED_QUALIFIERS
  24. #define WBEM_FLAG_USE_AMENDED_QUALIFIERS 0x20000
  25. #endif
  26. #ifdef PROFILE_MSINFO
  27. CFile fileProfile(_T("c:\\msinfo32.txt"), CFile::modeCreate | CFile::modeWrite);
  28. static DWORD dwProfileTime;
  29. inline void START_TIMER()
  30. {
  31. dwProfileTime = ::GetTickCount();
  32. }
  33. inline void END_TIMER(const CString & strOperation)
  34. {
  35. USES_CONVERSION;
  36. DWORD dwDelta = ::GetTickCount() - dwProfileTime;
  37. CString strMessage;
  38. strMessage.Format(_T("%ld.%03d\t%s\r\n"), dwDelta / 1000, dwDelta % 100, strOperation);
  39. fileProfile.Write((const void *) (LPCTSTR) strMessage, strMessage.GetLength()*sizeof(TCHAR));
  40. }
  41. inline void END_TIMER(const CString & strOperation, const CString & strArg)
  42. {
  43. USES_CONVERSION;
  44. DWORD dwDelta = ::GetTickCount() - dwProfileTime;
  45. CString strTemp;
  46. CString strMessage;
  47. strTemp.Format(strOperation, strArg);
  48. strMessage.Format(_T("%ld.%03d\t%s\r\n"), dwDelta / 1000, dwDelta % 100, strTemp);
  49. fileProfile.Write((const void *) (LPCTSTR) strMessage, strMessage.GetLength()*sizeof(TCHAR));
  50. }
  51. inline void WRITE(const CString & strMessage)
  52. {
  53. fileProfile.Write((const void *) (LPCTSTR) strMessage, strMessage.GetLength()*sizeof(TCHAR));
  54. }
  55. inline void WRITE(const CString & strMessage, const CString & strArg)
  56. {
  57. CString strTemp;
  58. strTemp.Format(strMessage, strArg);
  59. fileProfile.Write((const void *) (LPCTSTR) strTemp, strTemp.GetLength()*sizeof(TCHAR));
  60. }
  61. #else
  62. #define START_TIMER()
  63. #define END_TIMER(X, Y)
  64. inline void WRITE(const CString & strMessage) {};
  65. inline void WRITE(const CString & strMessage, const CString & strArg) {};
  66. #endif
  67. // TIMEOUT is used when enumerating classes.
  68. #define TIMEOUT -1
  69. //-----------------------------------------------------------------------------
  70. // This function is used to change the impersonation of certain WBEM interface
  71. // pointers.
  72. //-----------------------------------------------------------------------------
  73. inline HRESULT ChangeWBEMSecurity(IUnknown * pUnknown)
  74. {
  75. IClientSecurity * pCliSec = NULL;
  76. HRESULT hr = pUnknown->QueryInterface(IID_IClientSecurity, (void **) &pCliSec);
  77. if (FAILED(hr))
  78. return hr;
  79. hr = pCliSec->SetBlanket(pUnknown, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
  80. pCliSec->Release();
  81. return hr;
  82. }
  83. //-----------------------------------------------------------------------------
  84. // The constructor just initializes some member variables.
  85. //-----------------------------------------------------------------------------
  86. CDataProvider::CDataProvider()
  87. {
  88. m_fInitialized = FALSE;
  89. m_pIWbemServices = NULL;
  90. m_pGatherer = NULL;
  91. m_dwRefreshingCategoryID = 0;
  92. }
  93. //-----------------------------------------------------------------------------
  94. // The destructor releases the IWbemServices pointer, if we have one, and
  95. // empties the interface pointer caches.
  96. //-----------------------------------------------------------------------------
  97. CDataProvider::~CDataProvider()
  98. {
  99. if (m_fInitialized)
  100. ClearCache();
  101. if (m_pIWbemServices)
  102. {
  103. m_pIWbemServices->Release();
  104. m_pIWbemServices = NULL;
  105. }
  106. // Remove all of the WBEM service pointers for different namespaces
  107. IWbemServices * pServices;
  108. CString strNamespace;
  109. for (POSITION pos = m_mapNamespaceToService.GetStartPosition(); pos != NULL;)
  110. {
  111. m_mapNamespaceToService.GetNextAssoc(pos, strNamespace, (void * &) pServices);
  112. if (pServices)
  113. pServices->Release();
  114. }
  115. m_mapNamespaceToService.RemoveAll();
  116. m_fInitialized = FALSE;
  117. #ifdef GATH_STANDALONE
  118. CoUninitialize();
  119. #endif
  120. }
  121. //-----------------------------------------------------------------------------
  122. // The create method actually attempts to get a WBEM interface pointer for the
  123. // specified machine. If no machine is specified, the local computer is used.
  124. //-----------------------------------------------------------------------------
  125. BOOL CDataProvider::Create(const CString & strComputer, CDataGatherer * pGatherer)
  126. {
  127. IWbemLocator * pIWbemLocator = NULL;
  128. CString strNamespace;
  129. AFX_MANAGE_STATE(::AfxGetStaticModuleState());
  130. // The create method should never be called twice.
  131. ASSERT(!m_fInitialized);
  132. if (m_fInitialized)
  133. return FALSE;
  134. m_strComputer = strComputer;
  135. m_pGatherer = pGatherer;
  136. // The namespace string either uses the computer name, or a '.' for the local computer.
  137. if (strComputer.IsEmpty())
  138. strNamespace = CString(_T("\\\\.\\root\\cimv2"));
  139. else
  140. {
  141. strNamespace = strComputer + CString(_T("\\root\\cimv2"));
  142. if (strNamespace.Left(2).Compare(CString(_T("\\\\"))) != 0)
  143. strNamespace = CString(_T("\\\\")) + strNamespace;
  144. }
  145. #ifdef GATH_STANDALONE
  146. CoInitialize(NULL);
  147. #endif
  148. // Initialize security.
  149. CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0);
  150. // TBD: make sure WBEM is started.
  151. // We get a WBEM interface pointer by first creating a WBEM locator interface, then
  152. // using it to connect to a server to get an IWbemServices pointer.
  153. ASSERT(m_pIWbemServices == NULL);
  154. if (CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pIWbemLocator) == S_OK)
  155. {
  156. BSTR pNamespace = strNamespace.AllocSysString();
  157. if (msiLog.IsLogging(CMSInfoLog::WMI))
  158. msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Connect \"%s\" - "), strNamespace);
  159. HRESULT hrServer = pIWbemLocator->ConnectServer(pNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &m_pIWbemServices);
  160. if (msiLog.IsLogging(CMSInfoLog::WMI))
  161. msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
  162. if (hrServer != S_OK)
  163. {
  164. m_pIWbemServices = NULL;
  165. switch (hrServer)
  166. {
  167. case WBEM_E_OUT_OF_MEMORY:
  168. pGatherer->SetLastError(GATH_ERR_NOWBEMOUTOFMEM);
  169. break;
  170. case WBEM_E_ACCESS_DENIED:
  171. pGatherer->SetLastError(GATH_ERR_NOWBEMACCESSDENIED);
  172. break;
  173. case WBEM_E_INVALID_NAMESPACE:
  174. pGatherer->SetLastError(GATH_ERR_NOWBEMBADSERVER);
  175. break;
  176. case WBEM_E_TRANSPORT_FAILURE:
  177. pGatherer->SetLastError(GATH_ERR_NOWBEMNETWORKFAILURE);
  178. break;
  179. case WBEM_E_FAILED:
  180. case WBEM_E_INVALID_PARAMETER:
  181. default:
  182. pGatherer->SetLastError(GATH_ERR_NOWBEMCONNECT);
  183. }
  184. }
  185. else if (m_pIWbemServices)
  186. ChangeWBEMSecurity(m_pIWbemServices);
  187. if (pNamespace)
  188. SysFreeString(pNamespace);
  189. if (pIWbemLocator)
  190. {
  191. pIWbemLocator->Release();
  192. pIWbemLocator = NULL;
  193. }
  194. }
  195. else
  196. pGatherer->SetLastError(GATH_ERR_NOWBEMLOCATOR);
  197. // Load in the strings for TRUE and FALSE. (When we query for a boolean value,
  198. // we want to return a string representing the results).
  199. m_strTrue.LoadString(IDS_TRUE);
  200. ASSERT(!m_strTrue.IsEmpty());
  201. if (m_strTrue.IsEmpty())
  202. m_strTrue = CString("1");
  203. m_strFalse.LoadString(IDS_FALSE);
  204. ASSERT(!m_strFalse.IsEmpty());
  205. if (m_strFalse.IsEmpty())
  206. m_strFalse = CString("0");
  207. m_strBadProperty.LoadString(IDS_BAD_PROPERTY);
  208. ASSERT(!m_strBadProperty.IsEmpty());
  209. m_strPropertyUnavail.LoadString(IDS_PROPERTY_UNAVAILABLE);
  210. ASSERT(!m_strPropertyUnavail.IsEmpty());
  211. m_strAccessDeniedLabel.LoadString(IDS_WBEM_ACCESS_DENIED);
  212. ASSERT(!m_strAccessDeniedLabel.IsEmpty());
  213. m_strTransportFailureLabel.LoadString(IDS_WBEM_TRANSPORT_FAILURE);
  214. ASSERT(!m_strTransportFailureLabel.IsEmpty());
  215. m_dwRefreshingCategoryID = 0;
  216. m_fInitialized = (m_pIWbemServices != NULL);
  217. return (m_fInitialized);
  218. }
  219. //-----------------------------------------------------------------------------
  220. // This function is used to retrieve a pointer to IWbemServices for a
  221. // particular namespace. The default namespace is cimv2.
  222. //-----------------------------------------------------------------------------
  223. IWbemServices * CDataProvider::GetWBEMService(CString * pstrNamespace)
  224. {
  225. if (pstrNamespace == NULL || pstrNamespace->IsEmpty())
  226. return m_pIWbemServices;
  227. // Something like the following is useful for forcing a provider error when
  228. // testing the error containment:
  229. //
  230. // if (*pstrNamespace == _T("MSAPPS")) *pstrNamespace += _T("X");
  231. IWbemServices * pServices;
  232. if (m_mapNamespaceToService.Lookup(*pstrNamespace, (void * &) pServices) && pServices)
  233. return pServices;
  234. // There is no WBEM services pointer for that namespace, we need to create one.
  235. CString strNamespace(_T(""));
  236. if (m_strComputer.IsEmpty())
  237. strNamespace = CString(_T("\\\\.\\root\\")) + *pstrNamespace;
  238. else
  239. {
  240. if (m_strComputer.Right(1) == CString(_T("\\")))
  241. strNamespace = m_strComputer + CString(_T("root\\")) + *pstrNamespace;
  242. else
  243. strNamespace = m_strComputer + CString(_T("\\root\\")) + *pstrNamespace;
  244. if (strNamespace.Left(2).Compare(CString(_T("\\\\"))) != 0)
  245. strNamespace = CString(_T("\\\\")) + strNamespace;
  246. }
  247. IWbemLocator * pIWbemLocator = NULL;
  248. if (CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pIWbemLocator) == S_OK)
  249. {
  250. BSTR pNamespace = strNamespace.AllocSysString();
  251. if (msiLog.IsLogging(CMSInfoLog::WMI))
  252. msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Connect \"%s\" - "), strNamespace);
  253. HRESULT hrServer = pIWbemLocator->ConnectServer(pNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pServices);
  254. if (msiLog.IsLogging(CMSInfoLog::WMI))
  255. msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
  256. if (pNamespace)
  257. SysFreeString(pNamespace);
  258. if (pIWbemLocator)
  259. {
  260. pIWbemLocator->Release();
  261. pIWbemLocator = NULL;
  262. }
  263. if (SUCCEEDED(hrServer) && pServices)
  264. {
  265. ChangeWBEMSecurity(pServices);
  266. m_mapNamespaceToService.SetAt(*pstrNamespace, (void *) pServices);
  267. return pServices;
  268. }
  269. }
  270. return NULL;
  271. }
  272. //-----------------------------------------------------------------------------
  273. // This method is used to get the current value for a given class and property
  274. // string. Starting with the IWbemServices interface, it gets an interface
  275. // for the requested class enums the first instance. Performance is improved
  276. // by caching the instance interfaces in m_mapClassToInterface.
  277. //-----------------------------------------------------------------------------
  278. BOOL CDataProvider::QueryValue(const CString & strClass, const CString & strProperty, CString & strResult)
  279. {
  280. strResult.Empty();
  281. ASSERT(m_fInitialized && m_pIWbemServices);
  282. if (!m_fInitialized || m_pIWbemServices == NULL)
  283. return FALSE;
  284. CMSIObject * pObject = GetObject(strClass, NULL);
  285. ASSERT(pObject);
  286. if (!pObject)
  287. return FALSE;
  288. switch (pObject->IsValid())
  289. {
  290. case MOS_INSTANCE:
  291. {
  292. BOOL fUseValueMap = FALSE;
  293. CString strProp(strProperty);
  294. if (strProp.Left(8) == CString(_T("ValueMap")))
  295. {
  296. strProp = strProp.Right(strProp.GetLength() - 8);
  297. fUseValueMap = TRUE;
  298. }
  299. VARIANT variant;
  300. BSTR propName = strProp.AllocSysString();
  301. VariantInit(&variant);
  302. VariantClear(&variant);
  303. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  304. {
  305. // If the property we just got is an array, we should convert it to string
  306. // containing a list of the items in the array.
  307. if ((variant.vt & VT_ARRAY) && (variant.vt & VT_BSTR) && variant.parray)
  308. {
  309. if (SafeArrayGetDim(variant.parray) == 1)
  310. {
  311. long lLower = 0, lUpper = 0;
  312. SafeArrayGetLBound(variant.parray, 0, &lLower);
  313. SafeArrayGetUBound(variant.parray, 0, &lUpper);
  314. CString strWorking;
  315. BSTR bstr = NULL;
  316. for (long i = lLower; i <= lUpper; i++)
  317. if (SUCCEEDED(SafeArrayGetElement(variant.parray, &i, (wchar_t*)&bstr)))
  318. {
  319. if (i != lLower)
  320. strWorking += _T(", ");
  321. strWorking += bstr;
  322. }
  323. strResult = strWorking;
  324. return TRUE;
  325. }
  326. }
  327. else if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  328. {
  329. strResult = V_BSTR(&variant);
  330. CString strFound;
  331. if (fUseValueMap && SUCCEEDED(CheckValueMap(strClass, strProp, strResult, strFound)))
  332. strResult = strFound;
  333. return TRUE;
  334. }
  335. else
  336. strResult = m_strPropertyUnavail;
  337. }
  338. else
  339. strResult = m_strBadProperty;
  340. }
  341. break;
  342. case MOS_MSG_INSTANCE:
  343. pObject->GetErrorLabel(strResult);
  344. break;
  345. case MOS_NO_INSTANCES:
  346. default:
  347. ASSERT(FALSE);
  348. break;
  349. }
  350. return FALSE;
  351. }
  352. //-----------------------------------------------------------------------------
  353. // This method is equivalent to QueryValue, except it returns a DWORD value.
  354. // If FALSE is returned, then the string in strMessage should be displayed.
  355. //-----------------------------------------------------------------------------
  356. BOOL CDataProvider::QueryValueDWORD(const CString & strClass, const CString & strProperty, DWORD & dwResult, CString & strMessage)
  357. {
  358. dwResult = 0L;
  359. strMessage.Empty();
  360. ASSERT(m_fInitialized && m_pIWbemServices);
  361. if (!m_fInitialized || m_pIWbemServices == NULL)
  362. return FALSE;
  363. CMSIObject * pObject = GetObject(strClass, NULL);
  364. ASSERT(pObject);
  365. if (!pObject)
  366. return FALSE;
  367. switch (pObject->IsValid())
  368. {
  369. case MOS_INSTANCE:
  370. {
  371. VARIANT variant;
  372. BSTR propName = strProperty.AllocSysString();
  373. VariantInit(&variant);
  374. VariantClear(&variant);
  375. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  376. {
  377. if (VariantChangeType(&variant, &variant, 0, VT_I4) == S_OK)
  378. {
  379. dwResult = V_I4(&variant);
  380. return TRUE;
  381. }
  382. else
  383. strMessage = m_strPropertyUnavail;
  384. }
  385. else
  386. strMessage = m_strBadProperty;
  387. }
  388. break;
  389. case MOS_MSG_INSTANCE:
  390. pObject->GetErrorLabel(strMessage);
  391. break;
  392. case MOS_NO_INSTANCES:
  393. default:
  394. ASSERT(FALSE);
  395. break;
  396. }
  397. return FALSE;
  398. }
  399. //-----------------------------------------------------------------------------
  400. // This method is equivalent to QueryValue, except it returns a double float
  401. // value. If FALSE is returned, then the string in strMessage should
  402. // be displayed.
  403. //-----------------------------------------------------------------------------
  404. BOOL CDataProvider::QueryValueDoubleFloat(const CString & strClass, const CString & strProperty, double & dblResult, CString & strMessage)
  405. {
  406. dblResult = 0L;
  407. strMessage.Empty();
  408. ASSERT(m_fInitialized && m_pIWbemServices);
  409. if (!m_fInitialized || m_pIWbemServices == NULL)
  410. return FALSE;
  411. CMSIObject * pObject = GetObject(strClass, NULL);
  412. ASSERT(pObject);
  413. if (!pObject)
  414. return FALSE;
  415. switch (pObject->IsValid())
  416. {
  417. case MOS_INSTANCE:
  418. {
  419. VARIANT variant;
  420. BSTR propName = strProperty.AllocSysString();
  421. VariantInit(&variant);
  422. VariantClear(&variant);
  423. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  424. {
  425. if (VariantChangeType(&variant, &variant, 0, VT_R8) == S_OK)
  426. {
  427. dblResult = V_R8(&variant);
  428. return TRUE;
  429. }
  430. else
  431. strMessage = m_strPropertyUnavail;
  432. }
  433. else
  434. strMessage = m_strBadProperty;
  435. }
  436. break;
  437. case MOS_MSG_INSTANCE:
  438. pObject->GetErrorLabel(strMessage);
  439. break;
  440. case MOS_NO_INSTANCES:
  441. default:
  442. ASSERT(FALSE);
  443. break;
  444. }
  445. return FALSE;
  446. }
  447. //-----------------------------------------------------------------------------
  448. // This method is equivalent to QueryValue, except it returns an OLE date
  449. // & time object. If FALSE is returned, then the string in strMessage should
  450. // be displayed.
  451. //-----------------------------------------------------------------------------
  452. BOOL CDataProvider::QueryValueDateTime(const CString & strClass, const CString & strProperty, COleDateTime & datetime, CString & strMessage)
  453. {
  454. datetime.SetDateTime(0, 1, 1, 0, 0, 0);
  455. strMessage.Empty();
  456. ASSERT(m_fInitialized && m_pIWbemServices);
  457. if (!m_fInitialized || m_pIWbemServices == NULL)
  458. return FALSE;
  459. CMSIObject * pObject = GetObject(strClass, NULL);
  460. ASSERT(pObject);
  461. if (!pObject)
  462. return FALSE;
  463. switch (pObject->IsValid())
  464. {
  465. case MOS_INSTANCE:
  466. {
  467. VARIANT variant;
  468. BSTR propName = strProperty.AllocSysString();
  469. VariantInit(&variant);
  470. VariantClear(&variant);
  471. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  472. {
  473. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  474. {
  475. // Parse the date and time into an COleDateTime object. Note: we should
  476. // be able to get an OLE date from WBEM, but for now we need to just
  477. // deal with the string returned.
  478. int nYear, nMonth, nDay, nHour, nMin, nSec;
  479. CString strTemp = V_BSTR(&variant);
  480. nYear = atoi(strTemp.Mid(0, 4));
  481. nMonth = atoi(strTemp.Mid(4, 2));
  482. nDay = atoi(strTemp.Mid(6, 2));
  483. nHour = atoi(strTemp.Mid(8, 2));
  484. nMin = atoi(strTemp.Mid(10, 2));
  485. nSec = atoi(strTemp.Mid(12, 2));
  486. datetime.SetDateTime(nYear, nMonth, nDay, nHour, nMin, nSec);
  487. return TRUE;
  488. }
  489. else
  490. strMessage = m_strPropertyUnavail;
  491. }
  492. else
  493. strMessage = m_strBadProperty;
  494. }
  495. break;
  496. case MOS_MSG_INSTANCE:
  497. pObject->GetErrorLabel(strMessage);
  498. break;
  499. case MOS_NO_INSTANCES:
  500. default:
  501. ASSERT(FALSE);
  502. break;
  503. }
  504. return FALSE;
  505. }
  506. //-----------------------------------------------------------------------------
  507. // Reset the CMSIEnumerator pointer to the start of the enumeration (and
  508. // make sure there is one). Remove the object pointer, so the first call
  509. // to GetObject will return the first item in the enumerator.
  510. //-----------------------------------------------------------------------------
  511. BOOL CDataProvider::ResetClass(const CString & strClass, const GATH_FIELD * pConstraints)
  512. {
  513. CMSIEnumerator * pMSIEnumerator = GetEnumObject(strClass, pConstraints);
  514. if (pMSIEnumerator == NULL)
  515. return FALSE;
  516. // Reset the enumerator, and remove the cached object pointer if there is one.
  517. pMSIEnumerator->Reset(pConstraints);
  518. RemoveObject(strClass);
  519. CMSIObject * pObject = GetObject(strClass, pConstraints);
  520. if (pObject == NULL || pObject->IsValid() == CDataProvider::MOS_NO_INSTANCES)
  521. return FALSE;
  522. return TRUE;
  523. }
  524. //-----------------------------------------------------------------------------
  525. // Move the cached IWbemClassObject pointer to the next instance.
  526. //-----------------------------------------------------------------------------
  527. BOOL CDataProvider::EnumClass(const CString & strClass, const GATH_FIELD * pConstraints)
  528. {
  529. ASSERT(m_pIWbemServices);
  530. ASSERT(m_fInitialized);
  531. if (!m_fInitialized)
  532. return FALSE;
  533. // Verify that there is an object enumerator in place.
  534. if (GetEnumObject(strClass, pConstraints) == NULL)
  535. return FALSE;
  536. // If there is an object interface, remove it, then make a new one.
  537. // Then retrieve the object pointer (this will do the Next on the
  538. // enumerator to get the next instance).
  539. RemoveObject(strClass);
  540. CMSIObject * pObject = GetObject(strClass, pConstraints);
  541. if (pObject && (pObject->IsValid() == CDataProvider::MOS_INSTANCE))
  542. return TRUE;
  543. return FALSE;
  544. // if ((pObject == NULL) || (pObject->IsValid() == CDataProvider::MOS_NO_INSTANCES))
  545. // return FALSE;
  546. }
  547. //-----------------------------------------------------------------------------
  548. // Retrieve the interface pointer for the specified IEnumWbemClassObject.
  549. // If there isn't one cached, create one and cache it. It's possible for the
  550. // pConstraints parameter to contain a field specify a WBEM SQL condition for
  551. // this enumerator.
  552. //-----------------------------------------------------------------------------
  553. CMSIEnumerator * CDataProvider::GetEnumObject(const CString & strClass, const GATH_FIELD * pConstraints)
  554. {
  555. ASSERT(m_pIWbemServices);
  556. if (m_pIWbemServices == NULL)
  557. return NULL;
  558. // See if we've cached this enumerator object.
  559. CMSIEnumerator * pReturn = NULL;
  560. if (m_mapClassToEnumInterface.Lookup(strClass, (void * &) pReturn))
  561. return pReturn;
  562. // We'll need to create this enumerator here, and save it in the cache.
  563. pReturn = new CMSIEnumerator;
  564. if (pReturn == NULL)
  565. return NULL;
  566. if (FAILED(pReturn->Create(strClass, pConstraints, this)))
  567. {
  568. delete pReturn;
  569. return NULL;
  570. }
  571. m_mapClassToEnumInterface.SetAt(strClass, (void *) pReturn);
  572. return pReturn;
  573. }
  574. //-----------------------------------------------------------------------------
  575. // This function scans through the list of constraints to see if any of them
  576. // are SQL filters for an enumerator. If one is, the string which serves
  577. // as a SQL statement is returned, after being processed to replace any
  578. // keywords (such as class.property values).
  579. //-----------------------------------------------------------------------------
  580. #if FALSE
  581. CString CDataProvider::GetSQLStatement(const GATH_FIELD * pConstraints, BOOL & fMinOfOne)
  582. {
  583. CString strMinOfOne(_T("min-of-one"));
  584. CString strSQLKeyword(SQL_FILTER);
  585. const GATH_FIELD * pSQLConstraint = pConstraints;
  586. fMinOfOne = FALSE;
  587. // Look through the constaints to see if one starts with the SQL keyword.
  588. while (pSQLConstraint)
  589. {
  590. if (pSQLConstraint->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0)
  591. if (pSQLConstraint->m_strFormat.Left(strSQLKeyword.GetLength()).CompareNoCase(strSQLKeyword) == 0)
  592. break;
  593. pSQLConstraint = pSQLConstraint->m_pNext;
  594. }
  595. if (pSQLConstraint == NULL)
  596. return CString(_T(""));
  597. // Strip off the SQL keyword.
  598. CString strWorking(pSQLConstraint->m_strFormat);
  599. strWorking = strWorking.Right(strWorking.GetLength() - strSQLKeyword.GetLength());
  600. // Now, replace any keywords of the form "[class.property]" with the actual
  601. // value of the property.
  602. CString strResults;
  603. while (!strWorking.IsEmpty())
  604. {
  605. if (strWorking[0] != _T('['))
  606. {
  607. int index = strWorking.Find(_T('['));
  608. if (index < 0)
  609. index = strWorking.GetLength();
  610. strResults += strWorking.Left(index);
  611. strWorking = strWorking.Right(strWorking.GetLength() - index);
  612. }
  613. else
  614. {
  615. CString strKeyword;
  616. strWorking = strWorking.Right(strWorking.GetLength() - 1);
  617. int index = strWorking.Find(_T(']'));
  618. if (index < 0)
  619. {
  620. ASSERT(FALSE);
  621. return CString(_T(""));
  622. }
  623. strKeyword = strWorking.Left(index);
  624. if (strKeyword.CompareNoCase(strMinOfOne) == 0)
  625. fMinOfOne = TRUE;
  626. else if (!strKeyword.IsEmpty())
  627. {
  628. int iDotIndex = strKeyword.Find(_T('.'));
  629. if (iDotIndex >= 0)
  630. {
  631. CString strValue;
  632. if (QueryValue(strKeyword.Left(iDotIndex), strKeyword.Right(strKeyword.GetLength() - iDotIndex - 1), strValue))
  633. strResults += strValue;
  634. }
  635. }
  636. strWorking = strWorking.Right(strWorking.GetLength() - (index + 1));
  637. }
  638. }
  639. return strResults;
  640. }
  641. //-----------------------------------------------------------------------------
  642. // This function indicates if the only constraint specified is a SQL statement.
  643. //-----------------------------------------------------------------------------
  644. BOOL CDataProvider::IsSQLConstraint(const GATH_FIELD * pConstraints)
  645. {
  646. CString strSQLKeyword(SQL_FILTER);
  647. if (pConstraints)
  648. if (pConstraints->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0)
  649. if (pConstraints->m_strFormat.Left(strSQLKeyword.GetLength()).CompareNoCase(strSQLKeyword) == 0)
  650. return TRUE;
  651. return FALSE;
  652. }
  653. #endif
  654. //-----------------------------------------------------------------------------
  655. // Retrieve the interface pointer for the specified IWbemClassObject.
  656. // If there isn't one cached, create one and cache it.
  657. //-----------------------------------------------------------------------------
  658. CMSIObject * CDataProvider::GetObject(const CString & strClass, const GATH_FIELD * pConstraints, CString * pstrLabel)
  659. {
  660. ASSERT(m_pIWbemServices);
  661. if (m_pIWbemServices == NULL)
  662. return NULL;
  663. CMSIObject * pReturn = NULL;
  664. if (m_mapClassToInterface.Lookup(strClass, (void * &) pReturn))
  665. return pReturn;
  666. // We don't have one of these objects cached. Get one from the enumerator.
  667. CMSIEnumerator * pEnumerator = GetEnumObject(strClass);
  668. if (pEnumerator)
  669. {
  670. HRESULT hr = pEnumerator->Next(&pReturn);
  671. if (S_OK != hr)
  672. {
  673. if (pReturn)
  674. delete pReturn;
  675. pReturn = NULL;
  676. if (m_pGatherer)
  677. {
  678. if (m_dwRefreshingCategoryID)
  679. m_pGatherer->SetLastErrorHR(hr, m_dwRefreshingCategoryID);
  680. else
  681. m_pGatherer->SetLastErrorHR(hr);
  682. }
  683. }
  684. }
  685. if (pReturn)
  686. m_mapClassToInterface.SetAt(strClass, (void *) pReturn);
  687. return pReturn;
  688. }
  689. //-----------------------------------------------------------------------------
  690. // Evaluate whether or not a specific object meets the filtering requirements
  691. // set by the constraints (filtering are the constraints where one half is
  692. // a static value).
  693. //-----------------------------------------------------------------------------
  694. BOOL CDataProvider::EvaluateFilter(IWbemClassObject * pObject, const GATH_FIELD * pConstraints)
  695. {
  696. const GATH_FIELD * pLHS = pConstraints, * pRHS = NULL;
  697. VARIANT variant;
  698. CString strValue;
  699. BSTR propName;
  700. ASSERT(m_pIWbemServices && pObject);
  701. if (m_pIWbemServices == NULL || pObject == NULL)
  702. return FALSE;
  703. while (pLHS && pLHS->m_pNext)
  704. {
  705. pRHS = pLHS->m_pNext;
  706. VariantInit(&variant);
  707. // If either the left or right hand side is static, we need to do the check.
  708. // First check out if the left side is static.
  709. if (pLHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0 && pRHS->m_pArgs)
  710. {
  711. propName = pRHS->m_pArgs->m_strText.AllocSysString();
  712. strValue.Empty();
  713. VariantClear(&variant);
  714. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  715. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  716. {
  717. strValue = V_BSTR(&variant);
  718. if (strValue.CompareNoCase(pLHS->m_strFormat) != 0)
  719. return FALSE;
  720. }
  721. }
  722. // Next check out if the right side is static.
  723. if (pRHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0 && pLHS->m_pArgs)
  724. {
  725. propName = pLHS->m_pArgs->m_strText.AllocSysString();
  726. strValue.Empty();
  727. VariantClear(&variant);
  728. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  729. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  730. {
  731. strValue = V_BSTR(&variant);
  732. if (strValue.CompareNoCase(pRHS->m_strFormat) != 0)
  733. return FALSE;
  734. }
  735. }
  736. // Advance our pointer to the left hand side by two.
  737. pLHS = pRHS->m_pNext;
  738. }
  739. return TRUE;
  740. }
  741. //-----------------------------------------------------------------------------
  742. // This method uses an object interface and the constraint fields to advance
  743. // any joined classes to the correct instances.
  744. //-----------------------------------------------------------------------------
  745. void CDataProvider::EvaluateJoin(const CString & strClass, IWbemClassObject * pObject, const GATH_FIELD * pConstraints)
  746. {
  747. const GATH_FIELD *pLHS = pConstraints, *pRHS = NULL;
  748. const GATH_FIELD *pEnumerated, *pJoinedTo;
  749. GATH_FIELD fieldEnumerated, fieldJoinedTo;
  750. VARIANT variant;
  751. CString strValue;
  752. BSTR propName;
  753. ASSERT(m_pIWbemServices && pObject);
  754. if (m_pIWbemServices == NULL || pObject == NULL)
  755. return;
  756. while (pLHS && pLHS->m_pNext)
  757. {
  758. pRHS = pLHS->m_pNext;
  759. // If either side is static, this is a filter, rather than a join.
  760. if ((pRHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0) ||
  761. (pLHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0))
  762. {
  763. pLHS = pRHS->m_pNext;
  764. continue;
  765. }
  766. // Find out which side refers to the class we're enumerating.
  767. if (pRHS->m_strSource.CompareNoCase(strClass) == 0)
  768. {
  769. pEnumerated = pRHS;
  770. pJoinedTo = pLHS;
  771. }
  772. else if (pLHS->m_strSource.CompareNoCase(strClass) == 0)
  773. {
  774. pEnumerated = pLHS;
  775. pJoinedTo = pRHS;
  776. }
  777. else
  778. {
  779. pLHS = pRHS->m_pNext;
  780. continue;
  781. }
  782. // Next, enumerate through the instances of the joined to class until
  783. // we find one which satisfies the constraint. We can use the EvaluateFilter
  784. // method to find out when the constraint is met. Set up a field pointer
  785. // for the constraint (get the value from the enumerated class and use it
  786. // as a static.
  787. fieldJoinedTo = *pJoinedTo;
  788. fieldJoinedTo.m_pNext = NULL;
  789. VariantInit(&variant);
  790. strValue.Empty();
  791. if (pEnumerated->m_pArgs)
  792. {
  793. propName = pEnumerated->m_pArgs->m_strText.AllocSysString();
  794. VariantClear(&variant);
  795. if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  796. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  797. {
  798. strValue = V_BSTR(&variant);
  799. }
  800. }
  801. fieldEnumerated.m_strSource = CString(STATIC_SOURCE);
  802. fieldEnumerated.m_pNext = &fieldJoinedTo;
  803. fieldEnumerated.m_strFormat = strValue;
  804. // Now, enumerate the joined to class until it meets the constraints.
  805. RemoveObject(pJoinedTo->m_strSource);
  806. ResetClass(pJoinedTo->m_strSource, &fieldEnumerated);
  807. GetObject(pJoinedTo->m_strSource, &fieldEnumerated);
  808. // Advance our pointer to the left hand side by two.
  809. pLHS = pRHS->m_pNext;
  810. }
  811. // Because the GATH_FIELD destructor follows next pointers, we want
  812. // to unlink our two GATH_FIELD locals. Also, we don't want the
  813. // destructor for fieldJoinedTo to delete the arguments.
  814. fieldEnumerated.m_pNext = NULL;
  815. fieldJoinedTo.m_pArgs = NULL;
  816. }
  817. //-----------------------------------------------------------------------------
  818. // Evaluate whether or not the constraints indicate that a class is being
  819. // enumerated as a dependency class. This is currently indicated by a single
  820. // field structure with a static value of "dependency".
  821. //-----------------------------------------------------------------------------
  822. BOOL CDataProvider::IsDependencyJoin(const GATH_FIELD * pConstraints)
  823. {
  824. if (pConstraints != NULL && pConstraints->m_pNext == NULL)
  825. if (pConstraints->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0)
  826. if (pConstraints->m_strFormat.CompareNoCase(CString(DEPENDENCY_JOIN)) == 0)
  827. return TRUE;
  828. return FALSE;
  829. }
  830. //-----------------------------------------------------------------------------
  831. // This method is used when a dependency class is being enumerated. In a
  832. // dependency class, each property of a class instance contains a reference
  833. // to an instance in another class. This method will cache eache of the
  834. // instances specified by the dependency class so properties of those instances
  835. // can be referred to in the line structures.
  836. //-----------------------------------------------------------------------------
  837. void CDataProvider::EvaluateDependencyJoin(IWbemClassObject * pObject)
  838. {
  839. VARIANT variant, varClassName;
  840. IWbemClassObject * pNewObject = NULL;
  841. ASSERT(m_pIWbemServices);
  842. if (m_pIWbemServices == NULL)
  843. return;
  844. //if (pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_LOCAL_ONLY) == S_OK)
  845. //while (pObject->Next(0, NULL, &variant, NULL, NULL) == S_OK)
  846. VariantInit(&variant);
  847. VariantClear(&variant);
  848. if (pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_NONSYSTEM_ONLY) == WBEM_S_NO_ERROR)
  849. while (pObject->Next(0, NULL, &variant, NULL, NULL) == WBEM_S_NO_ERROR)
  850. {
  851. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  852. {
  853. // Use the object path to create a pointer to the specified
  854. // object and store it in the cache.
  855. CString strObjectPath = V_BSTR(&variant);
  856. BSTR bstrObjectPath = strObjectPath.AllocSysString();
  857. HRESULT hRes = m_pIWbemServices->GetObject(bstrObjectPath, 0, NULL, &pNewObject, NULL);
  858. if (SUCCEEDED(hRes))
  859. {
  860. // We need to get the class name of the new object so we know
  861. // where to cache it. We could parse it out of the object path,
  862. // but it will be better in the long run to get it by querying
  863. // the new object.
  864. if (pNewObject)
  865. {
  866. CString strClassName, strClassNameProp(_T("__CLASS"));
  867. BSTR classNameProp = strClassNameProp.AllocSysString();
  868. VariantInit(&varClassName);
  869. VariantClear(&varClassName);
  870. if (pNewObject->Get(classNameProp, 0L, &varClassName, NULL, NULL) == S_OK)
  871. if (VariantChangeType(&varClassName, &varClassName, 0, VT_BSTR) == S_OK)
  872. strClassName = V_BSTR(&varClassName);
  873. if (!strClassName.IsEmpty())
  874. {
  875. CMSIObject * pNewMSIObject = new CMSIObject(pNewObject, CString(_T("")), S_OK, this, CDataProvider::MOS_INSTANCE);
  876. if (pNewMSIObject)
  877. {
  878. CMSIObject * pOldObject = NULL;
  879. if (m_mapClassToInterface.Lookup(strClassName, (void * &) pOldObject) && pOldObject)
  880. delete pOldObject;
  881. m_mapClassToInterface.SetAt(strClassName, (void *) pNewMSIObject);
  882. }
  883. }
  884. else
  885. {
  886. delete pNewObject;
  887. pNewObject = NULL;
  888. }
  889. }
  890. }
  891. }
  892. VariantClear(&variant);
  893. }
  894. }
  895. //-----------------------------------------------------------------------------
  896. // Remove the specified IEnumWbemClassObject pointer from the cache.
  897. //-----------------------------------------------------------------------------
  898. void CDataProvider::RemoveEnumObject(const CString & strClass)
  899. {
  900. CMSIEnumerator * pEnumObject = NULL;
  901. if (m_mapClassToEnumInterface.Lookup(strClass, (void * &) pEnumObject) && pEnumObject)
  902. delete pEnumObject;
  903. m_mapClassToEnumInterface.RemoveKey(strClass);
  904. }
  905. //-----------------------------------------------------------------------------
  906. // Remove the specified IWbemClassObject pointer from the cache.
  907. //-----------------------------------------------------------------------------
  908. void CDataProvider::RemoveObject(const CString & strClass)
  909. {
  910. CMSIObject * pObject = NULL;
  911. if (m_mapClassToInterface.Lookup(strClass, (void * &) pObject) && pObject)
  912. delete pObject;
  913. m_mapClassToInterface.RemoveKey(strClass);
  914. }
  915. //-----------------------------------------------------------------------------
  916. // Clear out the contents of the caches (forcing the interfaces to be
  917. // retrieved again).
  918. //-----------------------------------------------------------------------------
  919. void CDataProvider::ClearCache()
  920. {
  921. CMSIObject * pObject = NULL;
  922. CMSIEnumerator * pEnumObject = NULL;
  923. POSITION pos;
  924. CString strClass;
  925. ASSERT(m_fInitialized);
  926. if (!m_fInitialized)
  927. return;
  928. for (pos = m_mapClassToInterface.GetStartPosition(); pos != NULL;)
  929. {
  930. m_mapClassToInterface.GetNextAssoc(pos, strClass, (void * &) pObject);
  931. if (pObject)
  932. delete pObject;
  933. }
  934. m_mapClassToInterface.RemoveAll();
  935. for (pos = m_mapClassToEnumInterface.GetStartPosition(); pos != NULL;)
  936. {
  937. m_mapClassToEnumInterface.GetNextAssoc(pos, strClass, (void * &) pEnumObject);
  938. if (pEnumObject)
  939. delete pEnumObject;
  940. }
  941. m_mapClassToEnumInterface.RemoveAll();
  942. }
  943. //-----------------------------------------------------------------------------
  944. // Implement the CMSIObject class. This is just a thin wrapper for the
  945. // IWbemClassObject interface.
  946. //-----------------------------------------------------------------------------
  947. CMSIObject::CMSIObject(IWbemClassObject * pObject, const CString & strLabel, HRESULT hres, CDataProvider * pProvider, CDataProvider::MSIObjectState objState)
  948. {
  949. m_pObject = pObject;
  950. m_strLabel = strLabel;
  951. m_hresCreation = hres;
  952. m_pProvider = pProvider;
  953. m_objState = objState;
  954. }
  955. CMSIObject::~CMSIObject()
  956. {
  957. if (m_pObject)
  958. {
  959. m_pObject->Release();
  960. m_pObject = NULL;
  961. }
  962. }
  963. HRESULT CMSIObject::Get(BSTR property, LONG lFlags, VARIANT *pVal, VARTYPE *pvtType, LONG *plFlavor)
  964. {
  965. ASSERT(m_objState != CDataProvider::MOS_NO_INSTANCES);
  966. // If there is an object interface, just pass the request on through.
  967. if (m_pObject)
  968. return m_pObject->Get(property, lFlags, pVal, pvtType, plFlavor);
  969. // Otherwise, we need to return the appropriate string.
  970. CString strReturn;
  971. GetErrorLabel(strReturn);
  972. V_BSTR(pVal) = strReturn.AllocSysString();
  973. pVal->vt = VT_BSTR;
  974. return S_OK;
  975. }
  976. CDataProvider::MSIObjectState CMSIObject::IsValid()
  977. {
  978. return m_objState;
  979. }
  980. HRESULT CMSIObject::GetErrorLabel(CString & strError)
  981. {
  982. switch (m_hresCreation)
  983. {
  984. case WBEM_E_ACCESS_DENIED:
  985. strError = m_pProvider->m_strAccessDeniedLabel;
  986. break;
  987. case WBEM_E_TRANSPORT_FAILURE:
  988. strError = m_pProvider->m_strTransportFailureLabel;
  989. break;
  990. case S_OK:
  991. case WBEM_S_FALSE:
  992. default:
  993. // This object was created from an enumeration that was marked as "min-of-one",
  994. // meaning that at least one object, even if it's not valid, needed to be
  995. // returned from the enumeration. Return the string we saved at object creation.
  996. if (!m_strLabel.IsEmpty())
  997. strError = m_strLabel;
  998. else
  999. strError = m_pProvider->m_strBadProperty;
  1000. break;
  1001. }
  1002. return S_OK;
  1003. }
  1004. //-----------------------------------------------------------------------------
  1005. // The CEnumMap is a utility class to cache IEnumWbemClassObject pointers.
  1006. // There will be one instance of this class used to improve performance
  1007. // by avoiding the high overhead associated with creating enumerators for
  1008. // certain classes.
  1009. //-----------------------------------------------------------------------------
  1010. IEnumWbemClassObject * CEnumMap::GetEnumerator(const CString & strClass)
  1011. {
  1012. IEnumWbemClassObject * pEnum = NULL;
  1013. IEnumWbemClassObject * pNewEnum = NULL;
  1014. if (m_mapEnum.Lookup(strClass, (void * &) pEnum))
  1015. {
  1016. if (pEnum && SUCCEEDED(pEnum->Clone(&pNewEnum)) && pNewEnum)
  1017. {
  1018. START_TIMER();
  1019. pNewEnum->Reset();
  1020. END_TIMER(_T("clone HIT [%s]\r\n"), strClass);
  1021. }
  1022. else
  1023. pNewEnum = NULL;
  1024. }
  1025. else
  1026. {
  1027. WRITE(_T("GetEnumerator MISS for %s.\r\n"), strClass);
  1028. }
  1029. return pNewEnum;
  1030. }
  1031. void CEnumMap::SetEnumerator(const CString & strClass, IEnumWbemClassObject * pEnum)
  1032. {
  1033. if (pEnum)
  1034. {
  1035. IEnumWbemClassObject * pEnumExisting = NULL;
  1036. if (m_mapEnum.Lookup(strClass, (void * &) pEnumExisting))
  1037. {
  1038. WRITE(_T("SetEnumerator for %s, already exists, ignoring.\r\n"), strClass);
  1039. }
  1040. else
  1041. {
  1042. pEnum->AddRef();
  1043. m_mapEnum.SetAt(strClass, pEnum);
  1044. WRITE(_T("SetEnumerator for %s.\r\n"), strClass);
  1045. }
  1046. }
  1047. else
  1048. {
  1049. WRITE(_T("SetEnumerator with NULL pointer for %s, ignoring.\r\n"), strClass);
  1050. }
  1051. }
  1052. void CEnumMap::Reset()
  1053. {
  1054. IEnumWbemClassObject * pEnum = NULL;
  1055. CString key;
  1056. WRITE(_T("CEnumMap::Reset()\r\n"));
  1057. for (POSITION pos = m_mapEnum.GetStartPosition(); pos != NULL;)
  1058. {
  1059. m_mapEnum.GetNextAssoc(pos, key, (void * &) pEnum);
  1060. if (pEnum)
  1061. pEnum->Release();
  1062. }
  1063. m_mapEnum.RemoveAll();
  1064. }
  1065. //-----------------------------------------------------------------------------
  1066. // The CMSIEnumerator class encapsulates the WBEM enumerator interface, or
  1067. // implements our own form of enumerator (such as for the LNK command in the
  1068. // template file).
  1069. //
  1070. // Nothing particularly interesting about the constructor or destructor.
  1071. //-----------------------------------------------------------------------------
  1072. CMSIEnumerator::CMSIEnumerator()
  1073. {
  1074. m_enumtype = CMSIEnumerator::CLASS;
  1075. m_fOnlyDups = FALSE;
  1076. m_fGotDuplicate = FALSE;
  1077. m_fMinOfOne = FALSE;
  1078. m_iMinOfOneCount = 0;
  1079. m_pEnum = NULL;
  1080. m_pProvider = NULL;
  1081. m_pConstraints = NULL;
  1082. m_pSavedDup = NULL;
  1083. m_pstrList = NULL;
  1084. m_hresCreation = S_OK;
  1085. }
  1086. CMSIEnumerator::~CMSIEnumerator()
  1087. {
  1088. if (m_pEnum)
  1089. {
  1090. switch (m_enumtype)
  1091. {
  1092. case CMSIEnumerator::WQL:
  1093. break;
  1094. case CMSIEnumerator::LNK:
  1095. m_pProvider->m_enumMap.SetEnumerator(m_strAssocClass, m_pEnum);
  1096. break;
  1097. case CMSIEnumerator::INTERNAL:
  1098. if (m_pstrList)
  1099. {
  1100. delete m_pstrList;
  1101. m_pstrList = NULL;
  1102. }
  1103. break;
  1104. case CMSIEnumerator::CLASS:
  1105. default:
  1106. m_pProvider->m_enumMap.SetEnumerator(m_strClass, m_pEnum);
  1107. break;
  1108. }
  1109. m_pEnum->Release();
  1110. m_pEnum = NULL;
  1111. }
  1112. }
  1113. //-----------------------------------------------------------------------------
  1114. // Creating the CMSIEnumerator object involves determining what sort of
  1115. // enumerator is required. We support the following types:
  1116. //
  1117. // 1. Straight enumeration of a class
  1118. // 2. Enumerate class, with applied constraints
  1119. // 3. Enumerate the results of a WQL statement.
  1120. // 4. Interprete a LNK command to enumerate associated classes.
  1121. // 5. Do internal processing on an INTERNAL type.
  1122. //-----------------------------------------------------------------------------
  1123. HRESULT CMSIEnumerator::Create(const CString & strClass, const GATH_FIELD * pConstraints, CDataProvider * pProvider)
  1124. {
  1125. if (strClass.IsEmpty() || !pProvider || !pProvider->m_pIWbemServices)
  1126. return E_INVALIDARG;
  1127. // Create may be called multiple times (to reset the enumerator). So we may need to
  1128. // release the enumerator pointer.
  1129. if (m_pEnum)
  1130. {
  1131. m_pEnum->Release();
  1132. m_pEnum = NULL;
  1133. }
  1134. // Divide the specified class into class and namespace parts, get the WBEM service.
  1135. CString strNamespacePart(_T("")), strClassPart(strClass);
  1136. int i = strClass.Find(_T(":"));
  1137. if (i != -1)
  1138. {
  1139. strNamespacePart = strClass.Left(i);
  1140. strClassPart = strClass.Right(strClass.GetLength() - i - 1);
  1141. }
  1142. IWbemServices * pServices = pProvider->GetWBEMService(&strNamespacePart);
  1143. if (pServices == NULL)
  1144. return NULL;
  1145. // First, we need to determine what type of enumerator this is. Scan through
  1146. // the constraints - if we see one which has a string starting with "WQL:" or
  1147. // "LNK:", then we know what type this enumerator is.
  1148. CString strStatement;
  1149. const GATH_FIELD * pScanConstraint = pConstraints;
  1150. while (pScanConstraint)
  1151. {
  1152. if (pScanConstraint->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0)
  1153. {
  1154. if (pScanConstraint->m_strFormat.Left(4).CompareNoCase(CString(_T("WQL:"))) == 0)
  1155. m_enumtype = CMSIEnumerator::WQL;
  1156. else if (pScanConstraint->m_strFormat.Left(4).CompareNoCase(CString(_T("LNK:"))) == 0)
  1157. m_enumtype = CMSIEnumerator::LNK;
  1158. else if (pScanConstraint->m_strFormat.Left(4).CompareNoCase(CString(_T("INT:"))) == 0)
  1159. m_enumtype = CMSIEnumerator::INTERNAL;
  1160. if (m_enumtype != CMSIEnumerator::CLASS)
  1161. {
  1162. strStatement = pScanConstraint->m_strFormat;
  1163. strStatement = strStatement.Right(strStatement.GetLength() - 4);
  1164. break;
  1165. }
  1166. }
  1167. pScanConstraint = pScanConstraint->m_pNext;
  1168. }
  1169. // If this is a WQL or a LNK enumerator, processes the statement by replacing
  1170. // [class.property] with the actual value from WBEM. If we find the string
  1171. // "[min-of-one]", make a note of it for later.
  1172. if (m_enumtype == CMSIEnumerator::WQL)
  1173. ProcessEnumString(strStatement, m_fMinOfOne, m_fOnlyDups, pProvider, m_strNoInstanceLabel, TRUE);
  1174. else if (m_enumtype == CMSIEnumerator::LNK)
  1175. if (SUCCEEDED(ParseLNKCommand(strStatement, m_strObjPath, m_strAssocClass, m_strResultClass)))
  1176. {
  1177. // Save the object path for later - so we can change the object without
  1178. // completely reprocessing the statement. Then replace the keywords in
  1179. // the statement and break out the pieces again.
  1180. m_strLNKObject = m_strObjPath;
  1181. ProcessEnumString(strStatement, m_fMinOfOne, m_fOnlyDups, pProvider, m_strNoInstanceLabel);
  1182. ParseLNKCommand(strStatement, m_strObjPath, m_strAssocClass, m_strResultClass);
  1183. }
  1184. // Now, based on the enumerator type, create the WBEM enumerator object.
  1185. switch (m_enumtype)
  1186. {
  1187. case CMSIEnumerator::WQL:
  1188. {
  1189. BSTR language = SysAllocString(_T("WQL"));
  1190. BSTR query = SysAllocString(strStatement);
  1191. if (msiLog.IsLogging(CMSInfoLog::WMI))
  1192. msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Create WQL enum \"%s\" - "), strStatement);
  1193. START_TIMER();
  1194. m_hresCreation = pServices->ExecQuery(language, query, WBEM_FLAG_RETURN_IMMEDIATELY, 0, &m_pEnum);
  1195. END_TIMER(_T("create WQL[%s]"), strStatement);
  1196. if (msiLog.IsLogging(CMSInfoLog::WMI))
  1197. msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
  1198. SysFreeString(query);
  1199. SysFreeString(language);
  1200. }
  1201. break;
  1202. case CMSIEnumerator::LNK:
  1203. {
  1204. m_hresCreation = ParseLNKCommand(strStatement, m_strObjPath, m_strAssocClass, m_strResultClass);
  1205. if (SUCCEEDED(m_hresCreation))
  1206. {
  1207. BSTR className = m_strAssocClass.AllocSysString();
  1208. START_TIMER();
  1209. m_pEnum = pProvider->m_enumMap.GetEnumerator(m_strAssocClass);
  1210. if (m_pEnum)
  1211. m_hresCreation = S_OK;
  1212. else
  1213. {
  1214. if (msiLog.IsLogging(CMSInfoLog::WMI))
  1215. msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Create enum \"%s\" - "), m_strAssocClass);
  1216. m_hresCreation = pServices->CreateInstanceEnum(className, WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &m_pEnum);
  1217. if (msiLog.IsLogging(CMSInfoLog::WMI))
  1218. msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
  1219. }
  1220. END_TIMER(_T("create LNK[%s]"), m_strAssocClass);
  1221. SysFreeString(className);
  1222. }
  1223. }
  1224. break;
  1225. case CMSIEnumerator::INTERNAL:
  1226. // We'll call a function here so we can do whatever processing is required
  1227. // to create this internal enumeration.
  1228. m_hresCreation = CreateInternalEnum(strStatement, pProvider);
  1229. break;
  1230. case CMSIEnumerator::CLASS:
  1231. default:
  1232. {
  1233. BSTR className = SysAllocString(strClassPart);
  1234. START_TIMER();
  1235. m_pEnum = pProvider->m_enumMap.GetEnumerator(strClassPart);
  1236. if (m_pEnum)
  1237. m_hresCreation = S_OK;
  1238. else
  1239. {
  1240. if (msiLog.IsLogging(CMSInfoLog::WMI))
  1241. msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Create enum \"%s\" - "), strClassPart);
  1242. m_hresCreation = pServices->CreateInstanceEnum(className, WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &m_pEnum);
  1243. if (msiLog.IsLogging(CMSInfoLog::WMI))
  1244. msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
  1245. }
  1246. END_TIMER(_T("create CLS[%s]"), strClassPart);
  1247. SysFreeString(className);
  1248. }
  1249. }
  1250. // Set some of the other member variables.
  1251. m_strClass = strClass;
  1252. m_pProvider = pProvider;
  1253. m_iMinOfOneCount = (m_fMinOfOne) ? 1 : 0;
  1254. m_pConstraints = pConstraints;
  1255. if (m_pEnum)
  1256. ChangeWBEMSecurity(m_pEnum);
  1257. // Based on the HRESULT from creating the enumeration, determine what to return.
  1258. // For certain errors, we want to act like the creation succeeded, then supply
  1259. // objects which return the error text.
  1260. if (FAILED(m_hresCreation))
  1261. {
  1262. m_fMinOfOne = TRUE;
  1263. m_iMinOfOneCount = 1;
  1264. }
  1265. return S_OK;
  1266. }
  1267. //-----------------------------------------------------------------------------
  1268. // This function is used to create internal enumeration types (enumerations
  1269. // which are beyond the template file syntax). Basically a bunch of special
  1270. // cases.
  1271. //-----------------------------------------------------------------------------
  1272. HRESULT CMSIEnumerator::CreateInternalEnum(const CString & strInternal, CDataProvider * pProvider)
  1273. {
  1274. if (strInternal.CompareNoCase(CString(_T("dlls"))) == 0)
  1275. {
  1276. // We want to enumerate all the loaded dlls and exes on the system.
  1277. // This can be done by enumerating the CIM_ProcessExecutable class
  1278. // and removing duplicate file names. We'll keep the filenames (with
  1279. // path information) in a string list.
  1280. if (m_pstrList == NULL)
  1281. {
  1282. m_pstrList = new CStringList;
  1283. if (m_pstrList == NULL)
  1284. return E_FAIL;
  1285. }
  1286. else
  1287. m_pstrList->RemoveAll();
  1288. HRESULT hr = S_OK;
  1289. IWbemServices * pServices = pProvider->GetWBEMService();
  1290. if (pServices)
  1291. {
  1292. BSTR className = SysAllocString(_T("CIM_ProcessExecutable"));
  1293. IEnumWbemClassObject * pEnum = NULL;
  1294. hr = pServices->CreateInstanceEnum(className, WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnum);
  1295. if (SUCCEEDED(hr))
  1296. {
  1297. IWbemClassObject * pWBEMObject = NULL;
  1298. ULONG uReturned;
  1299. VARIANT variant;
  1300. BSTR propName = SysAllocString(_T("Antecedent"));
  1301. VariantInit(&variant);
  1302. do
  1303. {
  1304. uReturned = 0;
  1305. hr = pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned);
  1306. if (SUCCEEDED(hr) && pWBEMObject && uReturned)
  1307. {
  1308. // For each instance of CIM_ProcessExecutable, get the
  1309. // Antecedent property (which contains the file path).
  1310. // If it is unique, save it in the list.
  1311. VariantClear(&variant);
  1312. if (pWBEMObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK)
  1313. {
  1314. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  1315. {
  1316. CString strResult = V_BSTR(&variant);
  1317. strResult.MakeLower();
  1318. if (m_pstrList->Find(strResult) == NULL)
  1319. m_pstrList->AddHead(strResult);
  1320. }
  1321. }
  1322. }
  1323. } while (SUCCEEDED(hr) && pWBEMObject && uReturned);
  1324. ::SysFreeString(propName);
  1325. pEnum->Release();
  1326. }
  1327. ::SysFreeString(className);
  1328. }
  1329. return hr;
  1330. }
  1331. return S_OK;
  1332. }
  1333. //-----------------------------------------------------------------------------
  1334. // Help function for ProcessEnumString, used to convert single backslashes
  1335. // into double backslashes (required for WQL statements).
  1336. //-----------------------------------------------------------------------------
  1337. void MakeDoubleBackslashes(CString & strValue)
  1338. {
  1339. CString strTemp(strValue);
  1340. CString strResults;
  1341. while (!strTemp.IsEmpty())
  1342. {
  1343. if (strTemp[0] != _T('\\'))
  1344. {
  1345. int index = strTemp.Find(_T('\\'));
  1346. if (index < 0)
  1347. index = strTemp.GetLength();
  1348. strResults += strTemp.Left(index);
  1349. strTemp = strTemp.Right(strTemp.GetLength() - index);
  1350. }
  1351. else
  1352. {
  1353. strResults += CString("\\\\");
  1354. strTemp = strTemp.Mid(1);
  1355. }
  1356. }
  1357. strValue = strResults;
  1358. }
  1359. //-----------------------------------------------------------------------------
  1360. // This function replaces [class.property] with the actual value of the
  1361. // property, and strings out [min-of-one], indicating if it was present in
  1362. // the fMinOfOne parameter.
  1363. //-----------------------------------------------------------------------------
  1364. void CMSIEnumerator::ProcessEnumString(CString & strStatement, BOOL & fMinOfOne, BOOL & fOnlyDups, CDataProvider * pProvider, CString & strNoInstanceLabel, BOOL fMakeDoubleBackslashes)
  1365. {
  1366. CString strMinOfOne(_T("min-of-one"));
  1367. CString strOnlyDups(_T("more-than-one"));
  1368. CString strResults;
  1369. fMinOfOne = FALSE;
  1370. fOnlyDups = FALSE;
  1371. while (!strStatement.IsEmpty())
  1372. {
  1373. if (strStatement[0] != _T('['))
  1374. {
  1375. int index = strStatement.Find(_T('['));
  1376. if (index < 0)
  1377. index = strStatement.GetLength();
  1378. strResults += strStatement.Left(index);
  1379. strStatement = strStatement.Right(strStatement.GetLength() - index);
  1380. }
  1381. else
  1382. {
  1383. CString strKeyword;
  1384. strStatement = strStatement.Right(strStatement.GetLength() - 1);
  1385. int index = strStatement.Find(_T(']'));
  1386. if (index < 0)
  1387. break;
  1388. strKeyword = strStatement.Left(index);
  1389. if (strKeyword.Left(strMinOfOne.GetLength()).CompareNoCase(strMinOfOne) == 0)
  1390. {
  1391. fMinOfOne = TRUE;
  1392. int iEqualsIndex = strKeyword.Find(_T('='));
  1393. if (iEqualsIndex > 0)
  1394. strNoInstanceLabel = strKeyword.Right(strKeyword.GetLength() - iEqualsIndex - 1);
  1395. }
  1396. else if (strKeyword.Left(strOnlyDups.GetLength()).CompareNoCase(strOnlyDups) == 0)
  1397. {
  1398. fOnlyDups = TRUE;
  1399. int iEqualsIndex = strKeyword.Find(_T('='));
  1400. if (iEqualsIndex > 0)
  1401. strNoInstanceLabel = strKeyword.Right(strKeyword.GetLength() - iEqualsIndex - 1);
  1402. }
  1403. else if (!strKeyword.IsEmpty())
  1404. {
  1405. int iDotIndex = strKeyword.Find(_T('.'));
  1406. if (iDotIndex >= 0)
  1407. {
  1408. CString strValue;
  1409. if (pProvider->QueryValue(strKeyword.Left(iDotIndex), strKeyword.Right(strKeyword.GetLength() - iDotIndex - 1), strValue))
  1410. {
  1411. if (fMakeDoubleBackslashes)
  1412. MakeDoubleBackslashes(strValue);
  1413. strResults += strValue;
  1414. }
  1415. }
  1416. }
  1417. strStatement = strStatement.Right(strStatement.GetLength() - (index + 1));
  1418. }
  1419. }
  1420. strStatement = strResults;
  1421. }
  1422. //-----------------------------------------------------------------------------
  1423. // Parse the component classes from the LNK command.
  1424. //-----------------------------------------------------------------------------
  1425. HRESULT CMSIEnumerator::ParseLNKCommand(const CString & strStatement, CString & strObjPath, CString & strAssocClass, CString & strResultClass)
  1426. {
  1427. // We need to parse out the LNK statement into two or three components,
  1428. // from the form "objPath->assocClass[->resultClass]", with the
  1429. // brackets indicating that the resultClass is optional.
  1430. CString strWorking(strStatement);
  1431. int iArrowIndex = strWorking.Find(_T("->"));
  1432. if (iArrowIndex == -1)
  1433. return E_INVALIDARG;
  1434. strObjPath = strWorking.Left(iArrowIndex);
  1435. strWorking = strWorking.Right(strWorking.GetLength() - (iArrowIndex + 2));
  1436. iArrowIndex = strWorking.Find(_T("->"));
  1437. if (iArrowIndex == -1)
  1438. strAssocClass = strWorking;
  1439. else
  1440. {
  1441. strAssocClass = strWorking.Left(iArrowIndex);
  1442. strWorking = strWorking.Right(strWorking.GetLength() - (iArrowIndex + 2));
  1443. strResultClass = strWorking;
  1444. strResultClass.MakeLower();
  1445. }
  1446. strAssocClass.TrimRight(); strAssocClass.TrimLeft();
  1447. strObjPath.TrimRight(); strObjPath.TrimLeft();
  1448. strResultClass.TrimRight(); strResultClass.TrimLeft();
  1449. return S_OK;
  1450. }
  1451. //-----------------------------------------------------------------------------
  1452. // The Next method will advance the enumerator based on the type of this
  1453. // enumerator.
  1454. //-----------------------------------------------------------------------------
  1455. HRESULT CMSIEnumerator::Next(CMSIObject ** ppObject)
  1456. {
  1457. if (!ppObject)
  1458. return E_INVALIDARG;
  1459. *ppObject = NULL;
  1460. // If there was an error creating the enumeration, return the error code.
  1461. if (FAILED(m_hresCreation))
  1462. return m_hresCreation;
  1463. if (m_pEnum == NULL && m_enumtype != CMSIEnumerator::INTERNAL)
  1464. return E_UNEXPECTED;
  1465. HRESULT hRes = S_OK;
  1466. IWbemClassObject * pWBEMObject = NULL;
  1467. switch (m_enumtype)
  1468. {
  1469. case CMSIEnumerator::LNK:
  1470. {
  1471. // Scan through the enumerated associate class. Look for one which
  1472. // satisfies our requirements.
  1473. CString strTemp, strAssociatedObject(_T(""));
  1474. ULONG uReturned;
  1475. IWbemClassObject * pAssocObj;
  1476. if (msiLog.IsLogging(CMSInfoLog::WMI))
  1477. msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Enumerate LNK \"%s\" - "), m_strAssocClass);
  1478. do
  1479. {
  1480. pAssocObj = NULL;
  1481. uReturned = 0;
  1482. START_TIMER();
  1483. hRes = m_pEnum->Next(TIMEOUT, 1, &pAssocObj, &uReturned);
  1484. END_TIMER(_T("next LNK[%s]"), m_strAssocClass);
  1485. if (!pAssocObj || FAILED(hRes) || uReturned != 1)
  1486. {
  1487. // Even if we didn't succeed in getting a new object,
  1488. // we might have a saved one if we're only showing
  1489. // "more-than-one" objects.
  1490. if (m_fOnlyDups && m_pSavedDup && m_fGotDuplicate)
  1491. {
  1492. // We have found one previously, so return it.
  1493. // Make it look like the Next call was successful.
  1494. m_pSavedDup = NULL;
  1495. hRes = S_OK;
  1496. uReturned = 1;
  1497. strAssociatedObject = m_strSavedDup;
  1498. break;
  1499. }
  1500. else
  1501. {
  1502. if (m_pSavedDup)
  1503. {
  1504. // We only got one object instance, so get rid of it.
  1505. m_pSavedDup->Release();
  1506. m_pSavedDup = NULL;
  1507. }
  1508. break;
  1509. }
  1510. }
  1511. if (AssocObjectOK(pAssocObj, strTemp))
  1512. {
  1513. // This object passed the filter - but if we're showing
  1514. // only "more-than-one" objects, save this one and return
  1515. // the saved one.
  1516. if (m_fOnlyDups)
  1517. {
  1518. if (m_pSavedDup)
  1519. {
  1520. // We have found one previously, so return it and
  1521. // save the current.
  1522. IWbemClassObject * pSwap = pAssocObj;
  1523. CString strSwap = strTemp;
  1524. pAssocObj = m_pSavedDup;
  1525. m_pSavedDup = pSwap;
  1526. strTemp = m_strSavedDup;
  1527. m_strSavedDup = strSwap;
  1528. m_fGotDuplicate = TRUE;
  1529. }
  1530. else
  1531. {
  1532. // This is the first one we've found - don't
  1533. // return it until we find another.
  1534. m_pSavedDup = pAssocObj;
  1535. m_strSavedDup = strTemp;
  1536. m_fGotDuplicate = FALSE;
  1537. continue;
  1538. }
  1539. }
  1540. strAssociatedObject = strTemp;
  1541. pAssocObj->Release();
  1542. break;
  1543. }
  1544. pAssocObj->Release();
  1545. } while (pAssocObj);
  1546. if (msiLog.IsLogging(CMSInfoLog::WMI))
  1547. msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
  1548. // If there is an associated object path, get the object.
  1549. if (!strAssociatedObject.IsEmpty())
  1550. {
  1551. BSTR path = strAssociatedObject.AllocSysString();
  1552. hRes = m_pProvider->m_pIWbemServices->GetObject(path, 0, NULL, &pWBEMObject, NULL);
  1553. SysFreeString(path);
  1554. }
  1555. }
  1556. break;
  1557. case CMSIEnumerator::WQL:
  1558. {
  1559. ULONG uReturned;
  1560. if (msiLog.IsLogging(CMSInfoLog::WMI))
  1561. msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Enumerate WQL \"%s\" - "), m_strClass);
  1562. START_TIMER();
  1563. hRes = m_pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned);
  1564. END_TIMER(_T("next WQL[%s]"), m_strClass);
  1565. if (msiLog.IsLogging(CMSInfoLog::WMI))
  1566. msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
  1567. }
  1568. break;
  1569. case CMSIEnumerator::INTERNAL:
  1570. hRes = InternalNext(&pWBEMObject);
  1571. break;
  1572. case CMSIEnumerator::CLASS:
  1573. default:
  1574. {
  1575. ULONG uReturned;
  1576. if (msiLog.IsLogging(CMSInfoLog::WMI))
  1577. msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Enumerate CLASS \"%s\" - "), m_strClass);
  1578. // EvaluateFilter and IsDependencyJoin handle a NULL pConstraints parameter,
  1579. // but for efficiency we're going to have a distinct branch for a non-NULL
  1580. // value (since it will usually be NULL).
  1581. if (m_pConstraints)
  1582. {
  1583. // Keep enumerating the instances of this class until we've
  1584. // found one which satisfies all of the filters.
  1585. do
  1586. {
  1587. pWBEMObject = NULL;
  1588. START_TIMER();
  1589. hRes = m_pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned);
  1590. END_TIMER(_T("next CLS[%s]"), m_strClass);
  1591. if (!pWBEMObject || hRes != S_OK || uReturned != 1)
  1592. break;
  1593. else if (m_pProvider->EvaluateFilter(pWBEMObject, m_pConstraints))
  1594. break;
  1595. pWBEMObject->Release();
  1596. } while (pWBEMObject);
  1597. // If this class is being enumerated as a dependency class, then
  1598. // locate all the objects it references. If it isn't, we still
  1599. // need to check for any joins to other classes formed by the constraints.
  1600. if (pWBEMObject)
  1601. if (m_pProvider->IsDependencyJoin(m_pConstraints))
  1602. m_pProvider->EvaluateDependencyJoin(pWBEMObject);
  1603. else
  1604. m_pProvider->EvaluateJoin(m_strClass, pWBEMObject, m_pConstraints);
  1605. }
  1606. else
  1607. hRes = m_pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned);
  1608. if (msiLog.IsLogging(CMSInfoLog::WMI))
  1609. msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
  1610. }
  1611. break;
  1612. }
  1613. if (pWBEMObject == NULL)
  1614. {
  1615. // There was no object to get. We'll still create a CMSIObject, but
  1616. // we'll set its state to indicate either that there are no instances,
  1617. // or one instance with an error message.
  1618. if (SUCCEEDED(hRes) && (m_iMinOfOneCount == 0))
  1619. *ppObject = new CMSIObject(pWBEMObject, m_strNoInstanceLabel, hRes, m_pProvider, CDataProvider::MOS_NO_INSTANCES);
  1620. else
  1621. *ppObject = new CMSIObject(pWBEMObject, m_strNoInstanceLabel, hRes, m_pProvider, CDataProvider::MOS_MSG_INSTANCE);
  1622. }
  1623. else
  1624. *ppObject = new CMSIObject(pWBEMObject, m_strNoInstanceLabel, hRes, m_pProvider, CDataProvider::MOS_INSTANCE);
  1625. if (m_iMinOfOneCount)
  1626. m_iMinOfOneCount--;
  1627. return S_OK;
  1628. }
  1629. //-----------------------------------------------------------------------------
  1630. // InternalNext is used to return a WBEM object for an internal enumeration
  1631. // (one that requires processing beyond the template file). Basically a
  1632. // set of special cases.
  1633. //-----------------------------------------------------------------------------
  1634. HRESULT CMSIEnumerator::InternalNext(IWbemClassObject ** ppWBEMObject)
  1635. {
  1636. if (m_pstrList && !m_pstrList->IsEmpty())
  1637. {
  1638. CString strNextObject = m_pstrList->RemoveHead();
  1639. if (!strNextObject.IsEmpty())
  1640. {
  1641. IWbemServices * pServices = m_pProvider->GetWBEMService();
  1642. if (pServices)
  1643. {
  1644. BSTR objectpath = ::SysAllocString(strNextObject);
  1645. HRESULT hr = S_OK;
  1646. if (FAILED(pServices->GetObject(objectpath, 0, NULL, ppWBEMObject, NULL)))
  1647. hr = E_FAIL;
  1648. ::SysFreeString(objectpath);
  1649. return hr;
  1650. }
  1651. }
  1652. }
  1653. return S_OK;
  1654. }
  1655. //-----------------------------------------------------------------------------
  1656. // Reset should just reset the enumerator pointer.
  1657. //-----------------------------------------------------------------------------
  1658. HRESULT CMSIEnumerator::Reset(const GATH_FIELD * pConstraints)
  1659. {
  1660. HRESULT hRes = S_OK;
  1661. if (m_pEnum)
  1662. {
  1663. switch (m_enumtype)
  1664. {
  1665. case CMSIEnumerator::WQL:
  1666. START_TIMER();
  1667. hRes = Create(m_strClass, pConstraints, m_pProvider);
  1668. END_TIMER(_T("reset WQL[%s]"), m_strClass);
  1669. break;
  1670. case CMSIEnumerator::LNK:
  1671. {
  1672. BOOL fDummy, fDummy2;
  1673. CString strDummy;
  1674. m_strObjPath = m_strLNKObject;
  1675. ProcessEnumString(m_strObjPath, fDummy, fDummy2, m_pProvider, strDummy);
  1676. m_iMinOfOneCount = (m_fMinOfOne) ? 1 : 0;
  1677. START_TIMER();
  1678. hRes = m_pEnum->Reset();
  1679. END_TIMER(_T("reset LNK[%s]"), m_strAssocClass);
  1680. }
  1681. break;
  1682. case CMSIEnumerator::INTERNAL:
  1683. hRes = Create(m_strClass, pConstraints, m_pProvider);
  1684. break;
  1685. case CMSIEnumerator::CLASS:
  1686. default:
  1687. m_iMinOfOneCount = (m_fMinOfOne) ? 1 : 0;
  1688. START_TIMER();
  1689. hRes = m_pEnum->Reset();
  1690. END_TIMER(_T("reset CLS[%s]"), m_strClass);
  1691. break;
  1692. }
  1693. }
  1694. else
  1695. hRes = E_UNEXPECTED;
  1696. return hRes;
  1697. }
  1698. //-----------------------------------------------------------------------------
  1699. // Evaluate if the pObject parameter is valid for this LNK enumerator. In
  1700. // particular, we must find the m_strObjPath in one of its properties, and
  1701. // possibly finding another property containing the m_strResultClass string.
  1702. //-----------------------------------------------------------------------------
  1703. BOOL CMSIEnumerator::AssocObjectOK(IWbemClassObject * pObject, CString & strAssociatedObject)
  1704. {
  1705. strAssociatedObject.Empty();
  1706. ASSERT(pObject);
  1707. if (pObject == NULL)
  1708. return FALSE;
  1709. VARIANT variant;
  1710. CString strReturn(_T("")), strValue;
  1711. // Traverse the set of non-system properties. Look for one the is the same
  1712. // as the object path.
  1713. pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_NONSYSTEM_ONLY);
  1714. VariantInit(&variant);
  1715. while (pObject->Next(0, NULL, &variant, NULL, NULL) == WBEM_S_NO_ERROR)
  1716. {
  1717. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  1718. strValue = V_BSTR(&variant);
  1719. VariantClear(&variant);
  1720. if (strValue.CompareNoCase(m_strObjPath) == 0)
  1721. break;
  1722. }
  1723. pObject->EndEnumeration();
  1724. // If we found a property containing the object path, look through for other
  1725. // paths which might be to objects we're insterested in.
  1726. if (strValue.CompareNoCase(m_strObjPath) == 0)
  1727. {
  1728. pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_NONSYSTEM_ONLY);
  1729. while (strReturn.IsEmpty() && (pObject->Next(0, NULL, &variant, NULL, NULL) == WBEM_S_NO_ERROR))
  1730. {
  1731. if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK)
  1732. strValue = V_BSTR(&variant);
  1733. if (strValue.CompareNoCase(m_strObjPath) != 0)
  1734. {
  1735. if (m_strResultClass.IsEmpty())
  1736. strReturn = strValue;
  1737. else
  1738. {
  1739. CString strSearch(strValue);
  1740. strSearch.MakeLower();
  1741. if (strSearch.Find(m_strResultClass) != -1)
  1742. strReturn = strValue;
  1743. }
  1744. }
  1745. VariantClear(&variant);
  1746. }
  1747. pObject->EndEnumeration();
  1748. }
  1749. if (!strReturn.IsEmpty())
  1750. {
  1751. strAssociatedObject = strReturn;
  1752. return TRUE;
  1753. }
  1754. return FALSE;
  1755. }
  1756. //-----------------------------------------------------------------------------
  1757. // Look up strVal in the ValueMap (if it exists) for strClass.strProperty
  1758. // If the value or the ValueMap is not found, return E_Something.
  1759. //
  1760. // Useful code snippet - this will dump the contents of the cache of
  1761. // saved values. To find all value mapped properties, but this code
  1762. // someplace where it will execute when MSInfo exits, change QueryValue
  1763. // to call CheckValueMap for all properties, then run MSInfo and do a global
  1764. // refresh (like to save an NFO).
  1765. //
  1766. // msiLog.WriteLog(CMSInfoLog::BASIC, _T("BEGIN Dump of ValueMap Cache\r\n"));
  1767. // CString key, val, log;
  1768. // for (POSITION pos = g_mapValueMap.GetStartPosition(); pos != NULL;)
  1769. // {
  1770. // g_mapValueMap.GetNextAssoc(pos, key, val);
  1771. // log.Format(_T(" %s = %s\r\n", key, val);
  1772. // msiLog.WriteLog(CMSInfoLog::BASIC, log);
  1773. // }
  1774. // msiLog.WriteLog(CMSInfoLog::BASIC, _T("END Dump of ValueMap Cache\r\n"));
  1775. //-----------------------------------------------------------------------------
  1776. CMapStringToString g_mapValueMap;
  1777. HRESULT CDataProvider::CheckValueMap(const CString& strClass, const CString& strProperty, const CString& strVal, CString &strResult)
  1778. {
  1779. IWbemClassObject * pWBEMClassObject = NULL;
  1780. HRESULT hrMap = S_OK, hr = S_OK;
  1781. VARIANT vArray, vMapArray;
  1782. IWbemQualifierSet * qual = NULL;
  1783. // Check the cache of saved values.
  1784. CString strLookup = strClass + CString(_T(".")) + strProperty + CString(_T(":")) + strVal;
  1785. if (g_mapValueMap.Lookup(strLookup, strResult))
  1786. return S_OK;
  1787. // Get the class object (not instance) for this class.
  1788. IWbemServices * pServices = GetWBEMService();
  1789. if (!pServices)
  1790. return E_FAIL;
  1791. CString strFullClass(_T("\\\\.\\root\\cimv2:"));
  1792. strFullClass += strClass;
  1793. BSTR bstrObjectPath = ::SysAllocString(strFullClass);
  1794. hr = pServices->GetObject(bstrObjectPath, WBEM_FLAG_USE_AMENDED_QUALIFIERS, NULL, &pWBEMClassObject, NULL);
  1795. ::SysFreeString(bstrObjectPath);
  1796. if (FAILED(hr))
  1797. return hr;
  1798. // Get the qualifiers from the class object.
  1799. BSTR bstrProperty = ::SysAllocString(strProperty);
  1800. hr = pWBEMClassObject->GetPropertyQualifierSet(bstrProperty, &qual);
  1801. ::SysFreeString(bstrProperty);
  1802. if (SUCCEEDED(hr) && qual)
  1803. {
  1804. // Get the ValueMap and Value arrays.
  1805. hrMap = qual->Get(_T("ValueMap"), 0, &vMapArray, NULL);
  1806. hr = qual->Get(_T("Values"), 0, &vArray, NULL);
  1807. if (SUCCEEDED(hr) && vArray.vt == (VT_BSTR | VT_ARRAY))
  1808. {
  1809. // Get the property value we're mapping.
  1810. long index;
  1811. if (SUCCEEDED(hrMap))
  1812. {
  1813. SAFEARRAY * pma = V_ARRAY(&vMapArray);
  1814. long lLowerBound = 0, lUpperBound = 0 ;
  1815. SafeArrayGetLBound(pma, 1, &lLowerBound);
  1816. SafeArrayGetUBound(pma, 1, &lUpperBound);
  1817. BSTR vMap;
  1818. for (long x = lLowerBound; x <= lUpperBound; x++)
  1819. {
  1820. SafeArrayGetElement(pma, &x, &vMap);
  1821. if (0 == strVal.CompareNoCase((LPCTSTR)vMap))
  1822. {
  1823. index = x;
  1824. break; // found it
  1825. }
  1826. }
  1827. }
  1828. else
  1829. {
  1830. // Shouldn't hit this case - if mof is well formed
  1831. // means there is no value map where we are expecting one.
  1832. // If the strVal we are looking for is a number, treat it
  1833. // as an index for the Values array. If it's a string,
  1834. // then this is an error.
  1835. TCHAR * szTest = NULL;
  1836. index = _tcstol((LPCTSTR)strVal, &szTest, 10);
  1837. if (szTest == NULL || (index == 0 && *szTest != 0) || strVal.IsEmpty())
  1838. hr = E_FAIL;
  1839. }
  1840. // Lookup the string.
  1841. if (SUCCEEDED(hr))
  1842. {
  1843. SAFEARRAY * psa = V_ARRAY(&vArray);
  1844. long ix[1] = {index};
  1845. BSTR str2;
  1846. hr = SafeArrayGetElement(psa, ix, &str2);
  1847. if (SUCCEEDED(hr))
  1848. {
  1849. strResult = str2;
  1850. SysFreeString(str2);
  1851. hr = S_OK;
  1852. }
  1853. else
  1854. {
  1855. hr = WBEM_E_VALUE_OUT_OF_RANGE;
  1856. }
  1857. }
  1858. }
  1859. qual->Release();
  1860. }
  1861. if (SUCCEEDED(hr))
  1862. g_mapValueMap.SetAt(strLookup, strResult);
  1863. return hr;
  1864. }