//***************************************************************************** // // WBEMTSS.CPP // // Copyright (c) 1996-1999, Microsoft Corporation, All rights reserved // // This file implements the classes used by the Timer Subsystem. // // Classes implemented: // // 26-Nov-96 raymcc Draft // 28-Dec-96 a-richm Alpha PDK Release // 12-Apr-97 a-levn Extensive changes // //***************************************************************************** #include "precomp.h" #include #include "ess.h" #include "wbemtss.h" CStaticCritSec CWBEMTimerInstruction::mstatic_cs; CWBEMTimerInstruction::CWBEMTimerInstruction() : m_lRefCount(1), m_bSkipIfPassed(FALSE), m_pNamespace(NULL), m_pGenerator(NULL), m_bRemoved(FALSE) { } CWBEMTimerInstruction::~CWBEMTimerInstruction() { if(m_pNamespace) m_pNamespace->Release(); } CWbemTime CWBEMTimerInstruction::GetFirstFiringTime() const { CWbemTime FirstTime = ComputeFirstFiringTime(); if(FirstTime.IsZero()) { // Instruction says: fire now // ========================== FirstTime = CWbemTime::GetCurrentTime(); } else if(SkipIfPassed()) { FirstTime = SkipMissed(FirstTime); } return FirstTime; } CWbemTime CWBEMTimerInstruction::GetStartingFiringTime(CWbemTime OldTime) const { // // If SkipIfPassed is set, we need to set the starting firing time to the // next one after current // if(SkipIfPassed()) return SkipMissed(OldTime); // // Otherwise, just leave it be --- the firing logic will figure out how many // we must have missed // return OldTime; } CWbemTime CWBEMTimerInstruction::SkipMissed(IN CWbemTime OldTime, OUT long* plMissedFiringCount) const { long lMissedCount = 0; CWbemTime Firing = OldTime; CWbemTime CurrentTime = CWbemTime::GetCurrentTime(); while(Firing < CurrentTime) { Firing = ComputeNextFiringTime(Firing); lMissedCount++; } if(SkipIfPassed()) lMissedCount = 0; if(plMissedFiringCount) *plMissedFiringCount = lMissedCount; return Firing; } CWbemTime CWBEMTimerInstruction::GetNextFiringTime(IN CWbemTime LastFiringTime, OUT long* plMissedFiringCount) const { CWbemTime NextFiring = ComputeNextFiringTime(LastFiringTime); NextFiring = SkipMissed(NextFiring, plMissedFiringCount); return NextFiring; } HRESULT CWBEMTimerInstruction::CheckObject(IWbemClassObject* pInst) { HRESULT hres; VARIANT v; VariantInit(&v); CClearMe cm(&v); hres = pInst->Get(L"SkipIfPassed", 0, &v, NULL, NULL); if(FAILED(hres)) return hres; if(V_VT(&v) != VT_BOOL) return WBEM_E_INVALID_OBJECT; hres = pInst->Get(L"__CLASS", 0, &v, NULL, NULL); if(FAILED(hres)) return hres; if(V_VT(&v) != VT_BSTR) return WBEM_E_INVALID_OBJECT; if(!wbem_wcsicmp(V_BSTR(&v), CAbsoluteTimerInstruction::GetWbemClassName())) { return CAbsoluteTimerInstruction::CheckObject(pInst); } else if(!wbem_wcsicmp(V_BSTR(&v), CIntervalTimerInstruction::GetWbemClassName())) { return CIntervalTimerInstruction::CheckObject(pInst); } else if(!wbem_wcsicmp(V_BSTR(&v), CRecurringTimerInstruction::GetWbemClassName())) { return CRecurringTimerInstruction::CheckObject(pInst); } else { return WBEM_E_INVALID_CLASS; } } HRESULT CWBEMTimerInstruction::LoadFromWbemObject( LPCWSTR wszNamespace, ADDREF IWbemServices* pNamespace, CWinMgmtTimerGenerator* pGenerator, IN IWbemClassObject* pObject, OUT RELEASE_ME CWBEMTimerInstruction*& pInstruction) { HRESULT hres; VARIANT v; VariantInit(&v); CClearMe cm(&v); hres = pObject->Get(L"__CLASS", 0, &v, NULL, NULL); if(FAILED(hres)) return hres; if(V_VT(&v) != VT_BSTR) return WBEM_E_INVALID_OBJECT; if(!wbem_wcsicmp(V_BSTR(&v), CAbsoluteTimerInstruction::GetWbemClassName())) { pInstruction = _new CAbsoluteTimerInstruction; } else if(!wbem_wcsicmp(V_BSTR(&v), CIntervalTimerInstruction::GetWbemClassName())) { pInstruction = _new CIntervalTimerInstruction; } else if(!wbem_wcsicmp(V_BSTR(&v),CRecurringTimerInstruction::GetWbemClassName())) { pInstruction = _new CRecurringTimerInstruction; } else { return WBEM_E_INVALID_CLASS; } if(pInstruction == NULL) return WBEM_E_OUT_OF_MEMORY; try { pInstruction->m_wsNamespace = wszNamespace; pInstruction->m_pGenerator = pGenerator; pInstruction->m_pNamespace = pNamespace; if(pNamespace) pNamespace->AddRef(); VariantClear(&v); hres = pObject->Get(L"TimerId", 0, &v, NULL, NULL); if(FAILED(hres)) return hres; if(V_VT(&v) != VT_BSTR) return WBEM_E_INVALID_OBJECT; pInstruction->m_wsTimerId = V_BSTR(&v); VariantClear(&v); hres = pObject->Get(L"SkipIfPassed", 0, &v, NULL, NULL); if(FAILED(hres)) return hres; if(V_VT(&v) != VT_BOOL) return WBEM_E_INVALID_OBJECT; pInstruction->m_bSkipIfPassed = (V_BOOL(&v) != VARIANT_FALSE); hres = pInstruction->LoadFromWbemObject(pObject); } catch( CX_MemoryException& ) { delete pInstruction; pInstruction = NULL; hres = WBEM_E_OUT_OF_MEMORY; } return hres; } HRESULT CWBEMTimerInstruction::Fire(long lNumTimes, CWbemTime NextFiringTime) { // Notify the sink // =============== HRESULT hres = m_pGenerator->FireInstruction(this, lNumTimes); return hres; } HRESULT CWBEMTimerInstruction::StoreNextFiring(CWbemTime When) { SCODE sc; // Create an instance of the NextFiring class // ========================================== IWbemClassObject* pClass = NULL; sc = m_pNamespace->GetObject(CWbemBSTR(L"__TimerNextFiring"), 0, NULL, &pClass, NULL); if(FAILED(sc)) return sc; CReleaseMe rm0(pClass); IWbemClassObject* pInstance = NULL; sc = pClass->SpawnInstance(0, &pInstance); if(FAILED(sc)) return sc; CReleaseMe rm1(pInstance); // Set the timer id // ================ VARIANT varID; V_VT(&varID) = VT_BSTR; V_BSTR(&varID) = SysAllocString(m_wsTimerId); if(V_BSTR(&varID) == NULL) return WBEM_E_OUT_OF_MEMORY; sc = pInstance->Put(L"TimerID", 0, &varID, 0); VariantClear(&varID); if(FAILED(sc)) return sc; // Set the next firing time // ======================== VARIANT varNext; V_VT(&varNext) = VT_BSTR; V_BSTR(&varNext) = SysAllocStringLen(NULL, 100); if(V_BSTR(&varNext) == NULL) return WBEM_E_OUT_OF_MEMORY; StringCchPrintfW( V_BSTR(&varNext), 100, L"%I64d", When.Get100nss()); sc = pInstance->Put(L"NextEvent64BitTime", 0, &varNext, 0); VariantClear(&varNext); if(FAILED(sc)) return sc; // // Save the instance in the repository using an internal API // IWbemInternalServices* pIntServ = NULL; sc = m_pNamespace->QueryInterface(IID_IWbemInternalServices, (void**)&pIntServ); if(FAILED(sc)) { ERRORTRACE((LOG_ESS, "Unable to aquire internal services from core: " "0x%X\n", sc)); return sc; } CReleaseMe rm2(pIntServ); sc = pIntServ->InternalPutInstance(pInstance); return sc; } HRESULT CWBEMTimerInstruction::MarkForRemoval() { CInCritSec incs(&mstatic_cs); m_bRemoved = TRUE; DWORD cLen = wcslen(m_wsTimerId)+100; LPWSTR wszPath = _new WCHAR[cLen]; if(wszPath == NULL) return WBEM_E_OUT_OF_MEMORY; StringCchPrintfW(wszPath, cLen, L"__TimerNextFiring=\"%S\"", (LPCWSTR)m_wsTimerId ); HRESULT hres = m_pNamespace->DeleteInstance(CWbemBSTR(wszPath), 0, NULL, NULL); delete [] wszPath; return hres; } CWbemTime CAbsoluteTimerInstruction::ComputeFirstFiringTime() const { return m_When; } CWbemTime CAbsoluteTimerInstruction::ComputeNextFiringTime( CWbemTime LastFiringTime) const { return CWbemTime::GetInfinity(); } // static HRESULT CAbsoluteTimerInstruction::CheckObject(IWbemClassObject* pInst) { // // Check if EventDateTime is actually a date, and not an interval // VARIANT v; VariantInit(&v); CClearMe cm(&v); HRESULT hres = pInst->Get(L"EventDateTime", 0, &v, NULL, NULL); if(FAILED(hres)) return hres; if(V_VT(&v) != VT_BSTR) return WBEM_E_ILLEGAL_NULL; // // Check for * --- invalid // if(wcschr(V_BSTR(&v), L'*')) return WBEM_E_INVALID_PROPERTY; // // Check for ':' --- interval --- invalid // if(V_BSTR(&v)[21] == L':') return WBEM_E_INVALID_PROPERTY_TYPE; return WBEM_S_NO_ERROR; } HRESULT CAbsoluteTimerInstruction::LoadFromWbemObject(IWbemClassObject* pObject) { VARIANT v; VariantInit(&v); HRESULT hres = pObject->Get(L"EventDateTime", 0, &v, NULL, NULL); if(FAILED(hres)) return hres; if(V_VT(&v) != VT_BSTR) return WBEM_E_INVALID_OBJECT; BOOL bRes = m_When.SetDMTF(V_BSTR(&v)); VariantClear(&v); return (bRes ? WBEM_S_NO_ERROR : WBEM_E_INVALID_OBJECT); } HRESULT CAbsoluteTimerInstruction::Fire(long lNumTimes, CWbemTime NextFiringTime) { // Fire it // ======= HRESULT hres = CWBEMTimerInstruction::Fire(lNumTimes, NextFiringTime); { CInCritSec incs(&mstatic_cs); if(!m_bRemoved) { // Save the next firing time in WinMgmt // ==================================== StoreNextFiring(NextFiringTime); } } return hres; } CWbemTime CIntervalTimerInstruction::ComputeFirstFiringTime() const { if(!m_Start.IsZero()) return m_Start; else { // Indicate that current time should be used return CWbemTime::GetCurrentTime() + m_Interval; } } CWbemTime CIntervalTimerInstruction::ComputeNextFiringTime( CWbemTime LastFiringTime) const { if(m_Interval.IsZero()) { return CWbemTime::GetInfinity(); } return LastFiringTime + m_Interval; } HRESULT CIntervalTimerInstruction::LoadFromWbemObject(IWbemClassObject* pObject) { VARIANT v; VariantInit(&v); HRESULT hres = pObject->Get(L"IntervalBetweenEvents", 0, &v, NULL, NULL); if(FAILED(hres)) return hres; if(V_VT(&v) != VT_I4 || V_I4(&v) == 0) return WBEM_E_INVALID_OBJECT; m_Interval.SetMilliseconds(V_I4(&v)); return S_OK; } CWinMgmtTimerGenerator::CWinMgmtTimerGenerator(CEss* pEss) : CTimerGenerator(), m_pEss(pEss) { } HRESULT CWinMgmtTimerGenerator::LoadTimerEventObject( LPCWSTR wszNamespace, IWbemServices* pNamespace, IWbemClassObject * pInstObject, IWbemClassObject * pNextFiring) { CWBEMTimerInstruction* pInst; CWbemTime When; HRESULT hres; hres = CWBEMTimerInstruction::LoadFromWbemObject(wszNamespace, pNamespace, this, pInstObject, pInst); if(FAILED(hres)) return hres; if(pNextFiring) { VARIANT v; VariantInit(&v); pNextFiring->Get(L"NextEvent64BitTime", 0 ,&v, NULL, NULL); if(V_VT(&v) != VT_BSTR) { delete pInst; return WBEM_E_FAILED; } __int64 i64; swscanf(V_BSTR(&v), L"%I64d", &i64); VariantClear(&v); When.Set100nss(i64); // // Ask the instruction to determine what the real first firing time // should be, given the fact what it was planned to be before we shut // down // When = pInst->GetStartingFiringTime(When); } else { When = CWbemTime::GetZero(); } // Remove old // ========== VARIANT vID; VariantInit(&vID); hres = pInstObject->Get(TIMER_ID_PROPNAME, 0, &vID, NULL, NULL); if(FAILED(hres)) return hres; Remove(wszNamespace, V_BSTR(&vID)); VariantClear(&vID); hres = Set(pInst, When); pInst->Release(); return hres; } HRESULT CWinMgmtTimerGenerator::CheckTimerInstruction(IWbemClassObject* pInst) { return CWBEMTimerInstruction::CheckObject(pInst); } HRESULT CWinMgmtTimerGenerator::LoadTimerEventObject( LPCWSTR wszNamespace, IWbemClassObject * pInstObject) { IWbemServices* pNamespace; HRESULT hres = m_pEss->GetNamespacePointer(wszNamespace,TRUE,&pNamespace); if(FAILED(hres)) return hres; hres = LoadTimerEventObject(wszNamespace, pNamespace, pInstObject); pNamespace->Release(); return hres; } SCODE CWinMgmtTimerGenerator::LoadTimerEventQueue(LPCWSTR wszNamespace, IWbemServices* pNamespace) { SCODE sc; ULONG uRet; WCHAR pwcsCount[4] = L""; int iInstanceCount = 1; IEnumWbemClassObject* pEnum; sc = pNamespace->CreateInstanceEnum(CWbemBSTR(L"__TimerInstruction"), WBEM_FLAG_DEEP, NULL, &pEnum); if(FAILED(sc)) return sc; while (1) { IWbemClassObject* pInstruction; sc = pEnum->Next( WBEM_INFINITE, 1, &pInstruction, &uRet); if(FAILED(sc)) return sc; if(sc != WBEM_S_NO_ERROR) break; // Get the next firing object // ========================== VARIANT vID; VariantInit(&vID); sc = pInstruction->Get(L"TimerID", 0, &vID, NULL, NULL); if(FAILED(sc)) return sc; DWORD cLen = wcslen(V_BSTR(&vID)) + 100; LPWSTR wszPath = _new WCHAR[cLen]; if(wszPath == NULL) return WBEM_E_OUT_OF_MEMORY; StringCchPrintfW( wszPath, cLen, L"__TimerNextFiring.TimerID=\"%s\"", V_BSTR(&vID) ); VariantClear(&vID); IWbemClassObject* pNextFiring = 0; if(FAILED(pNamespace->GetObject(CWbemBSTR(wszPath), 0, NULL, &pNextFiring, NULL))) { pNextFiring = NULL; } delete [] wszPath; LoadTimerEventObject(wszNamespace, pNamespace, pInstruction, pNextFiring); if(pNextFiring) pNextFiring->Release(); pInstruction->Release(); } pEnum->Release(); return WBEM_S_NO_ERROR; } HRESULT CWinMgmtTimerGenerator::Remove(LPCWSTR wszNamespace, LPCWSTR wszId) { CIdTest test(wszNamespace, wszId); return CTimerGenerator::Remove(&test); } BOOL CWinMgmtTimerGenerator::CIdTest::operator()(CTimerInstruction* pInst) { if(pInst->GetInstructionType() != INSTTYPE_WBEM) return FALSE; CWBEMTimerInstruction* pWbemInst = (CWBEMTimerInstruction*)pInst; if(wcscmp(m_wszId, pWbemInst->GetTimerId())) return FALSE; if(wbem_wcsicmp(m_wszNamespace, pWbemInst->GetNamespace())) return FALSE; return TRUE; } HRESULT CWinMgmtTimerGenerator::Remove(LPCWSTR wszNamespace) { CNamespaceTest test(wszNamespace); return CTimerGenerator::Remove(&test); } BOOL CWinMgmtTimerGenerator::CNamespaceTest::operator()( CTimerInstruction* pInst) { if(pInst->GetInstructionType() != INSTTYPE_WBEM) return FALSE; CWBEMTimerInstruction* pWbemInst = (CWBEMTimerInstruction*)pInst; if(wbem_wcsicmp(m_wszNamespace, pWbemInst->GetNamespace())) return FALSE; return TRUE; } HRESULT CWinMgmtTimerGenerator::FireInstruction( CWBEMTimerInstruction* pInst, long lNumFirings) { HRESULT hres; CEventRepresentation Event; Event.type = e_EventTypeTimer; Event.wsz1 = (LPWSTR)pInst->GetNamespace(); Event.wsz2 = (LPWSTR)pInst->GetTimerId(); Event.wsz3 = NULL; Event.dw1 = (DWORD)lNumFirings; // Create the actual IWbemClassObject representing the event // ======================================================== Event.nObjects = 1; Event.apObjects = _new IWbemClassObject*[1]; if(Event.apObjects == NULL) return WBEM_E_OUT_OF_MEMORY; CVectorDeleteMe vdm1(Event.apObjects); IWbemClassObject* pClass = // internal CEventRepresentation::GetEventClass(m_pEss, e_EventTypeTimer); if(pClass == NULL) return WBEM_E_OUT_OF_MEMORY; hres = pClass->SpawnInstance(0, &(Event.apObjects[0])); if(FAILED(hres)) return hres; CReleaseMe rm1(Event.apObjects[0]); VARIANT v; VariantInit(&v); V_VT(&v) = VT_BSTR; V_BSTR(&v) = SysAllocString(pInst->GetTimerId()); if(V_BSTR(&v) == NULL) return WBEM_E_OUT_OF_MEMORY; hres = Event.apObjects[0]->Put(L"TimerId", 0, &v, 0); VariantClear(&v); if(FAILED(hres)) return hres; V_VT(&v) = VT_I4; V_I4(&v) = lNumFirings; hres = Event.apObjects[0]->Put(L"NumFirings", 0, &v, 0); VariantClear(&v); if(FAILED(hres)) return hres; // Decorate it // =========== hres = m_pEss->DecorateObject(Event.apObjects[0], pInst->GetNamespace()); if(FAILED(hres)) return hres; // Give it to the ESS // ================== hres = m_pEss->ProcessEvent(Event, 0); // ignore error return WBEM_S_NO_ERROR; } HRESULT CWinMgmtTimerGenerator::Shutdown() { // Get the base class to shut everything down // ========================================== HRESULT hres = CTimerGenerator::Shutdown(); hres = SaveAndRemove((LONG)FALSE); return hres; } HRESULT CWinMgmtTimerGenerator::SaveAndRemove(LONG lIsSystemShutDown) { // Store next firing times for all the instructions in the list // ============================================================ CTimerInstruction* pInst; CWbemTime NextTime; while(m_Queue.Dequeue(pInst, NextTime) == S_OK) { // Convert to the right class // ========================== if(pInst->GetInstructionType() == INSTTYPE_WBEM) { CWBEMTimerInstruction* pWbemInst = (CWBEMTimerInstruction*)pInst; pWbemInst->StoreNextFiring(NextTime); } if (!lIsSystemShutDown) { pInst->Release(); } } return S_OK; } void CWinMgmtTimerGenerator::DumpStatistics(FILE* f, long lFlags) { fprintf(f, "%d timer instructions in queue\n", m_Queue.GetNumInstructions()); }