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.

727 lines
19 KiB

  1. //*****************************************************************************
  2. //
  3. // WBEMTSS.CPP
  4. //
  5. // Copyright (c) 1996-1999, Microsoft Corporation, All rights reserved
  6. //
  7. // This file implements the classes used by the Timer Subsystem.
  8. //
  9. // Classes implemented:
  10. //
  11. // 26-Nov-96 raymcc Draft
  12. // 28-Dec-96 a-richm Alpha PDK Release
  13. // 12-Apr-97 a-levn Extensive changes
  14. //
  15. //*****************************************************************************
  16. #include "precomp.h"
  17. #include <stdio.h>
  18. #include "ess.h"
  19. #include "wbemtss.h"
  20. CStaticCritSec CWBEMTimerInstruction::mstatic_cs;
  21. CWBEMTimerInstruction::CWBEMTimerInstruction()
  22. : m_lRefCount(1), m_bSkipIfPassed(FALSE), m_pNamespace(NULL),
  23. m_pGenerator(NULL), m_bRemoved(FALSE)
  24. {
  25. }
  26. CWBEMTimerInstruction::~CWBEMTimerInstruction()
  27. {
  28. if(m_pNamespace) m_pNamespace->Release();
  29. }
  30. CWbemTime CWBEMTimerInstruction::GetFirstFiringTime() const
  31. {
  32. CWbemTime FirstTime = ComputeFirstFiringTime();
  33. if(FirstTime.IsZero())
  34. {
  35. // Instruction says: fire now
  36. // ==========================
  37. FirstTime = CWbemTime::GetCurrentTime();
  38. }
  39. else if(SkipIfPassed())
  40. {
  41. FirstTime = SkipMissed(FirstTime);
  42. }
  43. return FirstTime;
  44. }
  45. CWbemTime CWBEMTimerInstruction::GetStartingFiringTime(CWbemTime OldTime) const
  46. {
  47. //
  48. // If SkipIfPassed is set, we need to set the starting firing time to the
  49. // next one after current
  50. //
  51. if(SkipIfPassed())
  52. return SkipMissed(OldTime);
  53. //
  54. // Otherwise, just leave it be --- the firing logic will figure out how many
  55. // we must have missed
  56. //
  57. return OldTime;
  58. }
  59. CWbemTime CWBEMTimerInstruction::SkipMissed(IN CWbemTime OldTime,
  60. OUT long* plMissedFiringCount) const
  61. {
  62. long lMissedCount = 0;
  63. CWbemTime Firing = OldTime;
  64. CWbemTime CurrentTime = CWbemTime::GetCurrentTime();
  65. while(Firing < CurrentTime)
  66. {
  67. Firing = ComputeNextFiringTime(Firing);
  68. lMissedCount++;
  69. }
  70. if(SkipIfPassed())
  71. lMissedCount = 0;
  72. if(plMissedFiringCount)
  73. *plMissedFiringCount = lMissedCount;
  74. return Firing;
  75. }
  76. CWbemTime CWBEMTimerInstruction::GetNextFiringTime(IN CWbemTime LastFiringTime,
  77. OUT long* plMissedFiringCount) const
  78. {
  79. CWbemTime NextFiring = ComputeNextFiringTime(LastFiringTime);
  80. NextFiring = SkipMissed(NextFiring, plMissedFiringCount);
  81. return NextFiring;
  82. }
  83. HRESULT CWBEMTimerInstruction::CheckObject(IWbemClassObject* pInst)
  84. {
  85. HRESULT hres;
  86. VARIANT v;
  87. VariantInit(&v);
  88. CClearMe cm(&v);
  89. hres = pInst->Get(L"SkipIfPassed", 0, &v, NULL, NULL);
  90. if(FAILED(hres))
  91. return hres;
  92. if(V_VT(&v) != VT_BOOL)
  93. return WBEM_E_INVALID_OBJECT;
  94. hres = pInst->Get(L"__CLASS", 0, &v, NULL, NULL);
  95. if(FAILED(hres))
  96. return hres;
  97. if(V_VT(&v) != VT_BSTR)
  98. return WBEM_E_INVALID_OBJECT;
  99. if(!wbem_wcsicmp(V_BSTR(&v),
  100. CAbsoluteTimerInstruction::GetWbemClassName()))
  101. {
  102. return CAbsoluteTimerInstruction::CheckObject(pInst);
  103. }
  104. else if(!wbem_wcsicmp(V_BSTR(&v),
  105. CIntervalTimerInstruction::GetWbemClassName()))
  106. {
  107. return CIntervalTimerInstruction::CheckObject(pInst);
  108. }
  109. else if(!wbem_wcsicmp(V_BSTR(&v),
  110. CRecurringTimerInstruction::GetWbemClassName()))
  111. {
  112. return CRecurringTimerInstruction::CheckObject(pInst);
  113. }
  114. else
  115. {
  116. return WBEM_E_INVALID_CLASS;
  117. }
  118. }
  119. HRESULT CWBEMTimerInstruction::LoadFromWbemObject(
  120. LPCWSTR wszNamespace,
  121. ADDREF IWbemServices* pNamespace,
  122. CWinMgmtTimerGenerator* pGenerator,
  123. IN IWbemClassObject* pObject,
  124. OUT RELEASE_ME CWBEMTimerInstruction*& pInstruction)
  125. {
  126. HRESULT hres;
  127. VARIANT v;
  128. VariantInit(&v);
  129. CClearMe cm(&v);
  130. hres = pObject->Get(L"__CLASS", 0, &v, NULL, NULL);
  131. if(FAILED(hres)) return hres;
  132. if(V_VT(&v) != VT_BSTR) return WBEM_E_INVALID_OBJECT;
  133. if(!wbem_wcsicmp(V_BSTR(&v), CAbsoluteTimerInstruction::GetWbemClassName()))
  134. {
  135. pInstruction = _new CAbsoluteTimerInstruction;
  136. }
  137. else if(!wbem_wcsicmp(V_BSTR(&v), CIntervalTimerInstruction::GetWbemClassName()))
  138. {
  139. pInstruction = _new CIntervalTimerInstruction;
  140. }
  141. else if(!wbem_wcsicmp(V_BSTR(&v),CRecurringTimerInstruction::GetWbemClassName()))
  142. {
  143. pInstruction = _new CRecurringTimerInstruction;
  144. }
  145. else
  146. {
  147. return WBEM_E_INVALID_CLASS;
  148. }
  149. if(pInstruction == NULL)
  150. return WBEM_E_OUT_OF_MEMORY;
  151. try
  152. {
  153. pInstruction->m_wsNamespace = wszNamespace;
  154. pInstruction->m_pGenerator = pGenerator;
  155. pInstruction->m_pNamespace = pNamespace;
  156. if(pNamespace) pNamespace->AddRef();
  157. VariantClear(&v);
  158. hres = pObject->Get(L"TimerId", 0, &v, NULL, NULL);
  159. if(FAILED(hres)) return hres;
  160. if(V_VT(&v) != VT_BSTR) return WBEM_E_INVALID_OBJECT;
  161. pInstruction->m_wsTimerId = V_BSTR(&v);
  162. VariantClear(&v);
  163. hres = pObject->Get(L"SkipIfPassed", 0, &v, NULL, NULL);
  164. if(FAILED(hres)) return hres;
  165. if(V_VT(&v) != VT_BOOL) return WBEM_E_INVALID_OBJECT;
  166. pInstruction->m_bSkipIfPassed = (V_BOOL(&v) != VARIANT_FALSE);
  167. hres = pInstruction->LoadFromWbemObject(pObject);
  168. }
  169. catch( CX_MemoryException& )
  170. {
  171. delete pInstruction;
  172. pInstruction = NULL;
  173. hres = WBEM_E_OUT_OF_MEMORY;
  174. }
  175. return hres;
  176. }
  177. HRESULT CWBEMTimerInstruction::Fire(long lNumTimes, CWbemTime NextFiringTime)
  178. {
  179. // Notify the sink
  180. // ===============
  181. HRESULT hres = m_pGenerator->FireInstruction(this, lNumTimes);
  182. return hres;
  183. }
  184. HRESULT CWBEMTimerInstruction::StoreNextFiring(CWbemTime When)
  185. {
  186. SCODE sc;
  187. // Create an instance of the NextFiring class
  188. // ==========================================
  189. IWbemClassObject* pClass = NULL;
  190. sc = m_pNamespace->GetObject(CWbemBSTR(L"__TimerNextFiring"), 0, NULL, &pClass, NULL);
  191. if(FAILED(sc)) return sc;
  192. CReleaseMe rm0(pClass);
  193. IWbemClassObject* pInstance = NULL;
  194. sc = pClass->SpawnInstance(0, &pInstance);
  195. if(FAILED(sc)) return sc;
  196. CReleaseMe rm1(pInstance);
  197. // Set the timer id
  198. // ================
  199. VARIANT varID;
  200. V_VT(&varID) = VT_BSTR;
  201. V_BSTR(&varID) = SysAllocString(m_wsTimerId);
  202. if(V_BSTR(&varID) == NULL)
  203. return WBEM_E_OUT_OF_MEMORY;
  204. sc = pInstance->Put(L"TimerID", 0, &varID, 0);
  205. VariantClear(&varID);
  206. if(FAILED(sc))
  207. return sc;
  208. // Set the next firing time
  209. // ========================
  210. VARIANT varNext;
  211. V_VT(&varNext) = VT_BSTR;
  212. V_BSTR(&varNext) = SysAllocStringLen(NULL, 100);
  213. if(V_BSTR(&varNext) == NULL)
  214. return WBEM_E_OUT_OF_MEMORY;
  215. StringCchPrintfW( V_BSTR(&varNext), 100, L"%I64d", When.Get100nss());
  216. sc = pInstance->Put(L"NextEvent64BitTime", 0, &varNext, 0);
  217. VariantClear(&varNext);
  218. if(FAILED(sc))
  219. return sc;
  220. //
  221. // Save the instance in the repository using an internal API
  222. //
  223. IWbemInternalServices* pIntServ = NULL;
  224. sc = m_pNamespace->QueryInterface(IID_IWbemInternalServices,
  225. (void**)&pIntServ);
  226. if(FAILED(sc))
  227. {
  228. ERRORTRACE((LOG_ESS, "Unable to aquire internal services from core: "
  229. "0x%X\n", sc));
  230. return sc;
  231. }
  232. CReleaseMe rm2(pIntServ);
  233. sc = pIntServ->InternalPutInstance(pInstance);
  234. return sc;
  235. }
  236. HRESULT CWBEMTimerInstruction::MarkForRemoval()
  237. {
  238. CInCritSec incs(&mstatic_cs);
  239. m_bRemoved = TRUE;
  240. DWORD cLen = wcslen(m_wsTimerId)+100;
  241. LPWSTR wszPath = _new WCHAR[cLen];
  242. if(wszPath == NULL)
  243. return WBEM_E_OUT_OF_MEMORY;
  244. StringCchPrintfW(wszPath,
  245. cLen,
  246. L"__TimerNextFiring=\"%S\"",
  247. (LPCWSTR)m_wsTimerId );
  248. HRESULT hres = m_pNamespace->DeleteInstance(CWbemBSTR(wszPath), 0, NULL, NULL);
  249. delete [] wszPath;
  250. return hres;
  251. }
  252. CWbemTime CAbsoluteTimerInstruction::ComputeFirstFiringTime() const
  253. {
  254. return m_When;
  255. }
  256. CWbemTime CAbsoluteTimerInstruction::ComputeNextFiringTime(
  257. CWbemTime LastFiringTime) const
  258. {
  259. return CWbemTime::GetInfinity();
  260. }
  261. // static
  262. HRESULT CAbsoluteTimerInstruction::CheckObject(IWbemClassObject* pInst)
  263. {
  264. //
  265. // Check if EventDateTime is actually a date, and not an interval
  266. //
  267. VARIANT v;
  268. VariantInit(&v);
  269. CClearMe cm(&v);
  270. HRESULT hres = pInst->Get(L"EventDateTime", 0, &v, NULL, NULL);
  271. if(FAILED(hres)) return hres;
  272. if(V_VT(&v) != VT_BSTR)
  273. return WBEM_E_ILLEGAL_NULL;
  274. //
  275. // Check for * --- invalid
  276. //
  277. if(wcschr(V_BSTR(&v), L'*'))
  278. return WBEM_E_INVALID_PROPERTY;
  279. //
  280. // Check for ':' --- interval --- invalid
  281. //
  282. if(V_BSTR(&v)[21] == L':')
  283. return WBEM_E_INVALID_PROPERTY_TYPE;
  284. return WBEM_S_NO_ERROR;
  285. }
  286. HRESULT CAbsoluteTimerInstruction::LoadFromWbemObject(IWbemClassObject* pObject)
  287. {
  288. VARIANT v;
  289. VariantInit(&v);
  290. HRESULT hres = pObject->Get(L"EventDateTime", 0, &v, NULL, NULL);
  291. if(FAILED(hres)) return hres;
  292. if(V_VT(&v) != VT_BSTR) return WBEM_E_INVALID_OBJECT;
  293. BOOL bRes = m_When.SetDMTF(V_BSTR(&v));
  294. VariantClear(&v);
  295. return (bRes ? WBEM_S_NO_ERROR : WBEM_E_INVALID_OBJECT);
  296. }
  297. HRESULT CAbsoluteTimerInstruction::Fire(long lNumTimes,
  298. CWbemTime NextFiringTime)
  299. {
  300. // Fire it
  301. // =======
  302. HRESULT hres = CWBEMTimerInstruction::Fire(lNumTimes, NextFiringTime);
  303. {
  304. CInCritSec incs(&mstatic_cs);
  305. if(!m_bRemoved)
  306. {
  307. // Save the next firing time in WinMgmt
  308. // ====================================
  309. StoreNextFiring(NextFiringTime);
  310. }
  311. }
  312. return hres;
  313. }
  314. CWbemTime CIntervalTimerInstruction::ComputeFirstFiringTime() const
  315. {
  316. if(!m_Start.IsZero())
  317. return m_Start;
  318. else
  319. {
  320. // Indicate that current time should be used
  321. return CWbemTime::GetCurrentTime() + m_Interval;
  322. }
  323. }
  324. CWbemTime CIntervalTimerInstruction::ComputeNextFiringTime(
  325. CWbemTime LastFiringTime) const
  326. {
  327. if(m_Interval.IsZero())
  328. {
  329. return CWbemTime::GetInfinity();
  330. }
  331. return LastFiringTime + m_Interval;
  332. }
  333. HRESULT CIntervalTimerInstruction::LoadFromWbemObject(IWbemClassObject* pObject)
  334. {
  335. VARIANT v;
  336. VariantInit(&v);
  337. HRESULT hres = pObject->Get(L"IntervalBetweenEvents", 0, &v, NULL, NULL);
  338. if(FAILED(hres)) return hres;
  339. if(V_VT(&v) != VT_I4 || V_I4(&v) == 0)
  340. return WBEM_E_INVALID_OBJECT;
  341. m_Interval.SetMilliseconds(V_I4(&v));
  342. return S_OK;
  343. }
  344. CWinMgmtTimerGenerator::CWinMgmtTimerGenerator(CEss* pEss) : CTimerGenerator(),
  345. m_pEss(pEss)
  346. {
  347. }
  348. HRESULT CWinMgmtTimerGenerator::LoadTimerEventObject(
  349. LPCWSTR wszNamespace,
  350. IWbemServices* pNamespace,
  351. IWbemClassObject * pInstObject,
  352. IWbemClassObject * pNextFiring)
  353. {
  354. CWBEMTimerInstruction* pInst;
  355. CWbemTime When;
  356. HRESULT hres;
  357. hres = CWBEMTimerInstruction::LoadFromWbemObject(wszNamespace, pNamespace,
  358. this, pInstObject, pInst);
  359. if(FAILED(hres)) return hres;
  360. if(pNextFiring)
  361. {
  362. VARIANT v;
  363. VariantInit(&v);
  364. pNextFiring->Get(L"NextEvent64BitTime", 0 ,&v, NULL, NULL);
  365. if(V_VT(&v) != VT_BSTR)
  366. {
  367. delete pInst;
  368. return WBEM_E_FAILED;
  369. }
  370. __int64 i64;
  371. swscanf(V_BSTR(&v), L"%I64d", &i64);
  372. VariantClear(&v);
  373. When.Set100nss(i64);
  374. //
  375. // Ask the instruction to determine what the real first firing time
  376. // should be, given the fact what it was planned to be before we shut
  377. // down
  378. //
  379. When = pInst->GetStartingFiringTime(When);
  380. }
  381. else
  382. {
  383. When = CWbemTime::GetZero();
  384. }
  385. // Remove old
  386. // ==========
  387. VARIANT vID;
  388. VariantInit(&vID);
  389. hres = pInstObject->Get(TIMER_ID_PROPNAME, 0, &vID, NULL, NULL);
  390. if(FAILED(hres)) return hres;
  391. Remove(wszNamespace, V_BSTR(&vID));
  392. VariantClear(&vID);
  393. hres = Set(pInst, When);
  394. pInst->Release();
  395. return hres;
  396. }
  397. HRESULT CWinMgmtTimerGenerator::CheckTimerInstruction(IWbemClassObject* pInst)
  398. {
  399. return CWBEMTimerInstruction::CheckObject(pInst);
  400. }
  401. HRESULT CWinMgmtTimerGenerator::LoadTimerEventObject(
  402. LPCWSTR wszNamespace,
  403. IWbemClassObject * pInstObject)
  404. {
  405. IWbemServices* pNamespace;
  406. HRESULT hres = m_pEss->GetNamespacePointer(wszNamespace,TRUE,&pNamespace);
  407. if(FAILED(hres))
  408. return hres;
  409. hres = LoadTimerEventObject(wszNamespace, pNamespace, pInstObject);
  410. pNamespace->Release();
  411. return hres;
  412. }
  413. SCODE CWinMgmtTimerGenerator::LoadTimerEventQueue(LPCWSTR wszNamespace,
  414. IWbemServices* pNamespace)
  415. {
  416. SCODE sc;
  417. ULONG uRet;
  418. WCHAR pwcsCount[4] = L"";
  419. int iInstanceCount = 1;
  420. IEnumWbemClassObject* pEnum;
  421. sc = pNamespace->CreateInstanceEnum(CWbemBSTR(L"__TimerInstruction"),
  422. WBEM_FLAG_DEEP, NULL,
  423. &pEnum);
  424. if(FAILED(sc)) return sc;
  425. while (1)
  426. {
  427. IWbemClassObject* pInstruction;
  428. sc = pEnum->Next( WBEM_INFINITE, 1, &pInstruction, &uRet);
  429. if(FAILED(sc)) return sc;
  430. if(sc != WBEM_S_NO_ERROR)
  431. break;
  432. // Get the next firing object
  433. // ==========================
  434. VARIANT vID;
  435. VariantInit(&vID);
  436. sc = pInstruction->Get(L"TimerID", 0, &vID, NULL, NULL);
  437. if(FAILED(sc)) return sc;
  438. DWORD cLen = wcslen(V_BSTR(&vID)) + 100;
  439. LPWSTR wszPath = _new WCHAR[cLen];
  440. if(wszPath == NULL)
  441. return WBEM_E_OUT_OF_MEMORY;
  442. StringCchPrintfW( wszPath,
  443. cLen,
  444. L"__TimerNextFiring.TimerID=\"%s\"",
  445. V_BSTR(&vID) );
  446. VariantClear(&vID);
  447. IWbemClassObject* pNextFiring = 0;
  448. if(FAILED(pNamespace->GetObject(CWbemBSTR(wszPath), 0, NULL, &pNextFiring, NULL)))
  449. {
  450. pNextFiring = NULL;
  451. }
  452. delete [] wszPath;
  453. LoadTimerEventObject(wszNamespace, pNamespace, pInstruction,
  454. pNextFiring);
  455. if(pNextFiring) pNextFiring->Release();
  456. pInstruction->Release();
  457. }
  458. pEnum->Release();
  459. return WBEM_S_NO_ERROR;
  460. }
  461. HRESULT CWinMgmtTimerGenerator::Remove(LPCWSTR wszNamespace, LPCWSTR wszId)
  462. {
  463. CIdTest test(wszNamespace, wszId);
  464. return CTimerGenerator::Remove(&test);
  465. }
  466. BOOL CWinMgmtTimerGenerator::CIdTest::operator()(CTimerInstruction* pInst)
  467. {
  468. if(pInst->GetInstructionType() != INSTTYPE_WBEM)
  469. return FALSE;
  470. CWBEMTimerInstruction* pWbemInst = (CWBEMTimerInstruction*)pInst;
  471. if(wcscmp(m_wszId, pWbemInst->GetTimerId()))
  472. return FALSE;
  473. if(wbem_wcsicmp(m_wszNamespace, pWbemInst->GetNamespace()))
  474. return FALSE;
  475. return TRUE;
  476. }
  477. HRESULT CWinMgmtTimerGenerator::Remove(LPCWSTR wszNamespace)
  478. {
  479. CNamespaceTest test(wszNamespace);
  480. return CTimerGenerator::Remove(&test);
  481. }
  482. BOOL CWinMgmtTimerGenerator::CNamespaceTest::operator()(
  483. CTimerInstruction* pInst)
  484. {
  485. if(pInst->GetInstructionType() != INSTTYPE_WBEM)
  486. return FALSE;
  487. CWBEMTimerInstruction* pWbemInst = (CWBEMTimerInstruction*)pInst;
  488. if(wbem_wcsicmp(m_wszNamespace, pWbemInst->GetNamespace()))
  489. return FALSE;
  490. return TRUE;
  491. }
  492. HRESULT CWinMgmtTimerGenerator::FireInstruction(
  493. CWBEMTimerInstruction* pInst, long lNumFirings)
  494. {
  495. HRESULT hres;
  496. CEventRepresentation Event;
  497. Event.type = e_EventTypeTimer;
  498. Event.wsz1 = (LPWSTR)pInst->GetNamespace();
  499. Event.wsz2 = (LPWSTR)pInst->GetTimerId();
  500. Event.wsz3 = NULL;
  501. Event.dw1 = (DWORD)lNumFirings;
  502. // Create the actual IWbemClassObject representing the event
  503. // ========================================================
  504. Event.nObjects = 1;
  505. Event.apObjects = _new IWbemClassObject*[1];
  506. if(Event.apObjects == NULL)
  507. return WBEM_E_OUT_OF_MEMORY;
  508. CVectorDeleteMe<IWbemClassObject*> vdm1(Event.apObjects);
  509. IWbemClassObject* pClass = // internal
  510. CEventRepresentation::GetEventClass(m_pEss, e_EventTypeTimer);
  511. if(pClass == NULL)
  512. return WBEM_E_OUT_OF_MEMORY;
  513. hres = pClass->SpawnInstance(0, &(Event.apObjects[0]));
  514. if(FAILED(hres))
  515. return hres;
  516. CReleaseMe rm1(Event.apObjects[0]);
  517. VARIANT v;
  518. VariantInit(&v);
  519. V_VT(&v) = VT_BSTR;
  520. V_BSTR(&v) = SysAllocString(pInst->GetTimerId());
  521. if(V_BSTR(&v) == NULL)
  522. return WBEM_E_OUT_OF_MEMORY;
  523. hres = Event.apObjects[0]->Put(L"TimerId", 0, &v, 0);
  524. VariantClear(&v);
  525. if(FAILED(hres))
  526. return hres;
  527. V_VT(&v) = VT_I4;
  528. V_I4(&v) = lNumFirings;
  529. hres = Event.apObjects[0]->Put(L"NumFirings", 0, &v, 0);
  530. VariantClear(&v);
  531. if(FAILED(hres))
  532. return hres;
  533. // Decorate it
  534. // ===========
  535. hres = m_pEss->DecorateObject(Event.apObjects[0], pInst->GetNamespace());
  536. if(FAILED(hres))
  537. return hres;
  538. // Give it to the ESS
  539. // ==================
  540. hres = m_pEss->ProcessEvent(Event, 0);
  541. // ignore error
  542. return WBEM_S_NO_ERROR;
  543. }
  544. HRESULT CWinMgmtTimerGenerator::Shutdown()
  545. {
  546. // Get the base class to shut everything down
  547. // ==========================================
  548. HRESULT hres = CTimerGenerator::Shutdown();
  549. hres = SaveAndRemove((LONG)FALSE);
  550. return hres;
  551. }
  552. HRESULT CWinMgmtTimerGenerator::SaveAndRemove(LONG lIsSystemShutDown)
  553. {
  554. // Store next firing times for all the instructions in the list
  555. // ============================================================
  556. CTimerInstruction* pInst;
  557. CWbemTime NextTime;
  558. while(m_Queue.Dequeue(pInst, NextTime) == S_OK)
  559. {
  560. // Convert to the right class
  561. // ==========================
  562. if(pInst->GetInstructionType() == INSTTYPE_WBEM)
  563. {
  564. CWBEMTimerInstruction* pWbemInst = (CWBEMTimerInstruction*)pInst;
  565. pWbemInst->StoreNextFiring(NextTime);
  566. }
  567. if (!lIsSystemShutDown)
  568. {
  569. pInst->Release();
  570. }
  571. }
  572. return S_OK;
  573. }
  574. void CWinMgmtTimerGenerator::DumpStatistics(FILE* f, long lFlags)
  575. {
  576. fprintf(f, "%d timer instructions in queue\n",
  577. m_Queue.GetNumInstructions());
  578. }