//****************************************************************************** // // Copyright (c) 1999-2000, Microsoft Corporation, All rights reserved // //***************************************************************************** #include "precomp.h" #include #include #include #include #include CMonitor::CMonitor() : CGuardable(false), m_CreationSink(this), m_DeletionSink(this), m_DataSink(this), m_ModificationInSink(this), m_ModificationOutSink(this), m_lRef(0), m_pNamespace(NULL), m_wszDataQuery(NULL), m_pCallback(NULL), m_wszCreationEvent(NULL), m_wszDeletionEvent(NULL), m_wszModificationInEvent(NULL), m_wszModificationOutEvent(NULL), m_bFirstPollDone(FALSE) { } CMonitor::~CMonitor() { if(m_lRef != 0) { ERRORTRACE((LOG_ESS, "Destroying monitor '%S' with non-0 ref-count: %d", m_wszDataQuery, m_lRef)); } if(m_pNamespace) m_pNamespace->Release(); if(m_pCallback) m_pCallback->Release(); delete [] m_wszDataQuery; delete [] m_wszCreationEvent; delete [] m_wszDeletionEvent; delete [] m_wszModificationInEvent; delete [] m_wszModificationOutEvent; } ULONG CMonitor::AddRef() { return InterlockedIncrement(&m_lRef); } ULONG CMonitor::Release() { // CMonitor is not controlled by its ref-count --- it is explicitely // deleted by its parent. return InterlockedDecrement(&m_lRef); } HRESULT CMonitor::Construct(CEssNamespace* pNamespace, CMonitorCallback* pCallback, LPCWSTR wszQuery) { HRESULT hres; // // Parse the query // CTextLexSource src(wszQuery); QL1_Parser parser(&src); QL_LEVEL_1_RPN_EXPRESSION* pExp = NULL; int nRes = parser.Parse(&pExp); if (nRes) { ERRORTRACE((LOG_ESS, "Unable to parse data monitor query '%S'\n", wszQuery)); return WBEM_E_UNPARSABLE_QUERY; } CDeleteMe dm(pExp); m_bWithin = !pExp->Tolerance.m_bExact; if(m_bWithin) { m_dwMsInterval = (DWORD)(pExp->Tolerance.m_fTolerance * 1000); } // // Construct the tree for the membership test // CContextMetaData Meta(new CEssMetaData(pNamespace), GetCurrentEssContext()); hres = m_MembershipTest.CreateFromQuery(&Meta, pExp, 0, 0x7FFFFFFF); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Unable to construct a tree from the data " "monitor query '%S': 0x%X\n", wszQuery, hres)); return WBEM_E_UNPARSABLE_QUERY; } // // Construct event queries we could issue to implement the monitor without // polling // hres = ConstructWatchQueries(pExp, &m_wszCreationEvent, &m_wszDeletionEvent, &m_wszModificationInEvent, &m_wszModificationOutEvent); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Unable to construct a watch query from " "'%S': 0x%x\n", wszQuery, hres)); return hres; } m_pNamespace = pNamespace; m_pNamespace->AddRef(); m_pCallback = pCallback; if(m_pCallback) m_pCallback->AddRef(); // // Remove the tolerance clause from the data query, in case we need to poll // m_wszDataQuery = pExp->GetText(); return WBEM_S_NO_ERROR; } HRESULT CMonitor::ConstructWatchQueries(QL_LEVEL_1_RPN_EXPRESSION* pDataQ, LPWSTR* pwszCreationEvent, LPWSTR* pwszDeletionEvent, LPWSTR* pwszModificationInEvent, LPWSTR* pwszModificationOutEvent) { // // BUGBUG: could do real analysis to make things faster // LPCWSTR wszTargetClassName = pDataQ->bsClassName; *pwszCreationEvent = NULL; *pwszDeletionEvent = NULL; *pwszModificationInEvent = NULL; *pwszModificationOutEvent = NULL; *pwszCreationEvent = new WCHAR[wcslen(wszTargetClassName) + 200]; *pwszDeletionEvent = new WCHAR[wcslen(wszTargetClassName) + 200]; *pwszModificationInEvent = new WCHAR[wcslen(wszTargetClassName) + 200]; *pwszModificationOutEvent = new WCHAR[wcslen(wszTargetClassName) + 200]; if(*pwszCreationEvent == NULL || *pwszDeletionEvent == NULL || *pwszModificationInEvent == NULL || *pwszModificationOutEvent == NULL) { delete *pwszCreationEvent; delete *pwszDeletionEvent; delete *pwszModificationInEvent; delete *pwszModificationOutEvent; return WBEM_E_OUT_OF_MEMORY; } swprintf(*pwszCreationEvent, L"select * from __InstanceCreationEvent where " L"TargetInstance isa \"%s\"", wszTargetClassName); swprintf(*pwszDeletionEvent, L"select * from __InstanceDeletionEvent where " L"TargetInstance isa \"%s\"", wszTargetClassName); swprintf(*pwszModificationInEvent, L"select * from __InstanceModificationEvent where " L"TargetInstance isa \"%s\"", wszTargetClassName); swprintf(*pwszModificationOutEvent, L"select * from __InstanceModificationEvent where " L"TargetInstance isa \"%s\"", wszTargetClassName); return WBEM_S_NO_ERROR; } HRESULT CMonitor::ActivateByGuard() { return WBEM_E_CRITICAL_ERROR; } HRESULT CMonitor::DeactivateByGuard() { return WBEM_E_CRITICAL_ERROR; } HRESULT CMonitor::Start(IWbemContext* pContext) { HRESULT hres; // // First, we'll attempt to implement it without polling --- using events // hres = ImplementUsingEvents(pContext); if(FAILED(hres)) { // Serious error that will prevent polling from working as well! return hres; } else if(hres == WBEM_S_NO_ERROR) { // Success of event implementation m_bUsingEvents = true; return hres; } // WBEM_S_FALSE means: cannot activate using events, use polling // // Whether an error or normal failure occurred, attempt to activate using // polling now. // hres = ImplementUsingPolling(pContext); m_bUsingEvents = false; return hres; } HRESULT CMonitor::Stop(IWbemContext* pContext) { // // Stop it the way it was started // HRESULT hres; if(m_bUsingEvents) hres = StopUsingEvents(pContext); else hres = StopUsingPolling(pContext); // // Notify our clients that we are stopping // DWORD dwCount; { CInCritSec ics(&m_cs); dwCount = m_map.size(); } FireStop(dwCount); return hres; } HRESULT CMonitor::ImplementUsingEvents(IWbemContext* pContext) { HRESULT hres; // BUGBUG: propagate security context // // Remember that the data query is still outstanding and therefore // extra race-condition book-keeping is still necessary // m_bDataDone = false; // // Subscribe to all the event queries we have stashed. It is assumed // that the namespace is locked at this point. // hres = m_pNamespace->InternalRegisterNotificationSink(L"WQL", m_wszCreationEvent, 0, WMIMSG_FLAG_QOS_EXPRESS, pContext, &m_CreationSink, true, NULL ); if(SUCCEEDED(hres)) { hres = m_pNamespace->InternalRegisterNotificationSink(L"WQL", m_wszDeletionEvent, 0, WMIMSG_FLAG_QOS_EXPRESS, pContext, &m_DeletionSink, true, NULL ); if(SUCCEEDED(hres)) { hres = m_pNamespace->InternalRegisterNotificationSink(L"WQL", m_wszModificationInEvent, 0, WMIMSG_FLAG_QOS_EXPRESS, pContext, &m_ModificationInSink, true, NULL ); if(SUCCEEDED(hres)) { hres = m_pNamespace->InternalRegisterNotificationSink(L"WQL", m_wszModificationOutEvent, 0, WMIMSG_FLAG_QOS_EXPRESS, pContext, &m_ModificationOutSink, true, NULL ); } } } if(FAILED(hres)) { UnregisterAll(); if(hres == WBEMESS_E_REGISTRATION_TOO_PRECISE) { // // There are no event providers to survice all of these requests. // That's OK --- it just means that we need to use polling to do // this monitor. // return WBEM_S_FALSE; } else { ERRORTRACE((LOG_ESS, "Monitor '%S' encountered error 0x%X trying " "to activate its event queries\n", m_wszDataQuery, hres)); return WBEM_S_FALSE; } } // // Now that we are subscribed for events, issue the data query // hres = m_pNamespace->ExecQuery(m_wszDataQuery, 0, &m_DataSink); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Monitor '%S' encountered error 0x%X issuing its " "initializing data query\n", m_wszDataQuery, hres)); return hres; } return WBEM_S_NO_ERROR; } HRESULT CMonitor::StopUsingEvents(IWbemContext* pContext) { return UnregisterAll(); } HRESULT CMonitor::UnregisterAll() { // // Cancel all queries // m_pNamespace->RemoveNotificationSink(&m_CreationSink); m_pNamespace->RemoveNotificationSink(&m_DeletionSink); m_pNamespace->RemoveNotificationSink(&m_ModificationInSink); m_pNamespace->RemoveNotificationSink(&m_ModificationOutSink); return WBEM_S_NO_ERROR; } HRESULT CMonitor::ImplementUsingPolling(IWbemContext* pContext) { HRESULT hres; // // As usual, polling is only possible if a WITHIN clause is specified // if(!m_bWithin) return WBEMESS_E_REGISTRATION_TOO_PRECISE; // // Basically, we will keep issuing our data query every m_dwMsInterval // seconds and issue events if needed. // // Construct the timer instruction that will do that // m_pInst = new CMonitorInstruction(m_pNamespace, this); if(m_pInst == NULL) return WBEM_E_OUT_OF_MEMORY; m_pInst->AddRef(); hres = m_pInst->Initialize(L"WQL", m_wszDataQuery, m_dwMsInterval); if (SUCCEEDED(hres)) { hres = m_pNamespace->GetTimerGenerator().Set( m_pInst, CWbemTime::GetZero()); } if (FAILED(hres)) { m_pInst->Release(); return hres; } return hres; } HRESULT CMonitor::StopUsingPolling(IWbemContext* pContext) { // // Cancel the timer instruction // CIdentityTest Test(m_pInst); ((CTimerGenerator&)m_pNamespace->GetTimerGenerator()).Remove(&Test); m_pInst->Release(); m_pInst = NULL; return S_OK; } // This function is called on both creation and mod-in events. There appear // to be no differences between the two. HRESULT CMonitor::ProcessPossibleAdd(_IWmiObject* pObj, bool bEvent) { HRESULT hres; // // First, we need to run the object through the membership test, if it // came from an event --- no need to do that for a poll // if(bEvent) { CSortedArray aTrues; hres = m_MembershipTest.Evaluate(pObj, aTrues); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Unable to verify set membership for incoming " "object in monitor '%S': 0x%X\n", m_wszDataQuery)); return hres; } if(aTrues.Size() == 0) { // // Not in the set --- this can happen because our event // registrations // might be a bit broader that we'd want them to be. Later on, we // might want to count these false hits and if we get too many of // them, perhaps switch to polling... // return WBEM_S_FALSE; } } // // Get its path // VARIANT v; VariantInit(&v); hres = pObj->Get(L"__RELPATH", 0, &v, NULL, NULL); if(FAILED(hres) || V_VT(&v) != VT_BSTR) return WBEM_E_CRITICAL_ERROR; CClearMe cm(&v); // // Determine if firing is necessary // bool bFire; DWORD dwObjectCount; { CInCritSec ics(&m_cs); // // See if we already have it // TIterator it = m_map.find(V_BSTR(&v)); if(it == m_map.end() || !it->second) { // it is either not there, or thre with FALSE as the presence // indicator --- in either case, it's not thought to be in the set // // There is one test remaining --- if this is a data-add, we must // make sure that we have not heard of this object before, for if // we have, we must ignore this info --- events always override. // if(!bEvent && m_mapHeard.find(V_BSTR(&v)) != m_mapHeard.end()) { bFire = false; } else { bFire = true; m_map[V_BSTR(&v)] = true; dwObjectCount = m_map.size(); if(!m_bDataDone) m_mapHeard[V_BSTR(&v)] = true; } } else { // It's already there bFire = false; } } if(bFire) FireAssert(pObj, V_BSTR(&v), bEvent, dwObjectCount); return WBEM_S_NO_ERROR; } HRESULT CMonitor::ProcessModOut(_IWmiObject* pObj, bool bEvent) { HRESULT hres; // // First, we need to make sure that the object has really been modified // out of the set --- need to test that it's no longer in the set // CSortedArray aTrues; hres = m_MembershipTest.Evaluate(pObj, aTrues); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Unable to verify set membership for outgoing " "object in monitor '%S': 0x%X\n", m_wszDataQuery)); return hres; } if(aTrues.Size() != 0) { // // The object is still in the set --- false positive! // This can happen because our event registrations // might be a bit broader that we'd want them to be. Later on, we // might want to count these false hits and if we get too many of them, // perhaps switch to polling... // // return WBEM_S_FALSE; } // // The object really has moved out of the set. As long as it is currently // listed as 'in', an event should be raised // return ProcessPossibleRemove(pObj, bEvent); } HRESULT CMonitor::ProcessDelete(_IWmiObject* pObj, bool bEvent) { HRESULT hres; // // As long as it is currently listed as 'in', an event should be raised // return ProcessPossibleRemove(pObj, bEvent); } HRESULT CMonitor::ProcessPossibleRemove(_IWmiObject* pObj, bool bEvent) { HRESULT hres; // // Get its path // VARIANT v; VariantInit(&v); hres = pObj->Get(L"__RELPATH", 0, &v, NULL, NULL); if(FAILED(hres) || V_VT(&v) != VT_BSTR) return WBEM_E_CRITICAL_ERROR; CClearMe cm(&v); // // Determine if firing is necessary // BOOL bFire; DWORD dwLeft; { CInCritSec ics(&m_cs); // // If we are not out of the data query yet, record the fact that // we have seen this instance, since we must ignore any data object // that is overriden by an event // if(!m_bDataDone) m_mapHeard[V_BSTR(&v)] = true; // // See if we have it // TIterator it = m_map.find(V_BSTR(&v)); if(it == m_map.end()) { // It's not there --- just ignore bFire = false; } else { // // There: remove and fire // m_map.erase(it); bFire = true; dwLeft = m_map.size(); } } if(bFire) FireRetract(pObj, V_BSTR(&v), bEvent, dwLeft); return WBEM_S_NO_ERROR; } HRESULT CMonitor::ProcessDataDone(HRESULT hresData, IWbemClassObject* pError) { DWORD dwCount; { CInCritSec ics(&m_cs); dwCount = m_map.size(); m_bDataDone = true; m_mapHeard.clear(); } if(FAILED(hresData)) { // // Monitor data execution failed. We need to alert our clients to this // fact, and schedule reinitialization. // FireError(hresData, pError); // BUGBUG: Need to reinitialize! } else { FireReady(dwCount); } return WBEM_S_NO_ERROR; } HRESULT CMonitor::FireAssert(_IWmiObject* pObj, LPCWSTR wszPath, bool bEvent, DWORD dwTotalItems) { CMonitorCallback* pCallback = GetCallback(); CTemplateReleaseMe rm1(pCallback); if(pCallback) return pCallback->Assert(pObj, wszPath, bEvent, dwTotalItems); else return WBEM_S_FALSE; } HRESULT CMonitor::FireRetract(_IWmiObject* pObj, LPCWSTR wszPath, bool bEvent, DWORD dwTotalItems) { CMonitorCallback* pCallback = GetCallback(); CTemplateReleaseMe rm1(pCallback); if(pCallback) return pCallback->Retract(pObj, wszPath, bEvent, dwTotalItems); else return WBEM_S_FALSE; } HRESULT CMonitor::FireReady(DWORD dwTotalItems) { CMonitorCallback* pCallback = GetCallback(); CTemplateReleaseMe rm1(pCallback); if(pCallback) return pCallback->GoingUp(dwTotalItems); else return WBEM_S_FALSE; } HRESULT CMonitor::FireStop(DWORD dwTotalItems) { CMonitorCallback* pCallback = GetCallback(); CTemplateReleaseMe rm1(pCallback); if(pCallback) return pCallback->GoingDown(dwTotalItems); else return WBEM_S_FALSE; } HRESULT CMonitor::FireError(HRESULT hresError, IWbemClassObject* pErrorObj) { CMonitorCallback* pCallback = GetCallback(); CTemplateReleaseMe rm1(pCallback); if(pCallback) return pCallback->Error(hresError, pErrorObj); else return WBEM_S_FALSE; } RELEASE_ME CMonitorCallback* CMonitor::GetCallback() { CInCritSec ics(&m_cs); if(m_pCallback) m_pCallback->AddRef(); return m_pCallback; } HRESULT CMonitor::CCreationSink::Indicate(long lNumEvents, IWbemEvent** apEvents, CEventContext* pContext) { HRESULT hres; for(long i = 0; i < lNumEvents; i++) { // // Extract its TargetInstance and add it // VARIANT v; hres = apEvents[i]->Get(L"TargetInstance", 0, &v, NULL, NULL); if(FAILED(hres) || V_VT(&v) != VT_UNKNOWN) { ERRORTRACE((LOG_ESS, "Invalid instance creation event without " "TargetInstance recieved by monitor '%S'\n", m_pOuter->m_wszDataQuery)); continue; } CClearMe cm(&v); _IWmiObject* pObj; hres = V_UNKNOWN(&v)->QueryInterface(IID__IWmiObject, (void**)&pObj); CReleaseMe rm(pObj); hres = m_pOuter->ProcessPossibleAdd(pObj, true); } return WBEM_S_NO_ERROR; } HRESULT CMonitor::CModificationInSink::Indicate(long lNumEvents, IWbemEvent** apEvents, CEventContext* pContext) { HRESULT hres; for(long i = 0; i < lNumEvents; i++) { // // Extract its TargetInstance and add it // VARIANT v; hres = apEvents[i]->Get(L"TargetInstance", 0, &v, NULL, NULL); if(FAILED(hres) || V_VT(&v) != VT_UNKNOWN) { ERRORTRACE((LOG_ESS, "Invalid instance creation event without " "TargetInstance recieved by monitor '%S'\n", m_pOuter->m_wszDataQuery)); continue; } CClearMe cm(&v); _IWmiObject* pObj; hres = V_UNKNOWN(&v)->QueryInterface(IID__IWmiObject, (void**)&pObj); CReleaseMe rm(pObj); hres = m_pOuter->ProcessPossibleAdd(pObj, true); } return WBEM_S_NO_ERROR; } HRESULT CMonitor::CDeletionSink::Indicate(long lNumEvents, IWbemEvent** apEvents, CEventContext* pContext) { HRESULT hres; for(long i = 0; i < lNumEvents; i++) { // // Extract its TargetInstance and remove it // VARIANT v; hres = apEvents[i]->Get(L"TargetInstance", 0, &v, NULL, NULL); if(FAILED(hres) || V_VT(&v) != VT_UNKNOWN) { ERRORTRACE((LOG_ESS, "Invalid instance deletion event without " "TargetInstance recieved by monitor '%S'\n", m_pOuter->m_wszDataQuery)); continue; } CClearMe cm(&v); _IWmiObject* pObj; hres = V_UNKNOWN(&v)->QueryInterface(IID__IWmiObject, (void**)&pObj); CReleaseMe rm(pObj); hres = m_pOuter->ProcessDelete(pObj, true); } return WBEM_S_NO_ERROR; } HRESULT CMonitor::CModificationOutSink::Indicate(long lNumEvents, IWbemEvent** apEvents, CEventContext* pContext) { HRESULT hres; for(long i = 0; i < lNumEvents; i++) { // // Extract its TargetInstance and remove it // VARIANT v; hres = apEvents[i]->Get(L"TargetInstance", 0, &v, NULL, NULL); if(FAILED(hres) || V_VT(&v) != VT_UNKNOWN) { ERRORTRACE((LOG_ESS, "Invalid instance deletion event without " "TargetInstance recieved by monitor '%S'\n", m_pOuter->m_wszDataQuery)); continue; } CClearMe cm(&v); _IWmiObject* pObj; hres = V_UNKNOWN(&v)->QueryInterface(IID__IWmiObject, (void**)&pObj); CReleaseMe rm(pObj); hres = m_pOuter->ProcessModOut(pObj, true); } return WBEM_S_NO_ERROR; } STDMETHODIMP CMonitor::CDataSink::Indicate(long lNumObjects, IWbemClassObject** apObjects) { HRESULT hres; for(long i = 0; i < lNumObjects; i++) { // // Just add it // _IWmiObject* pObj; hres = apObjects[i]->QueryInterface(IID__IWmiObject, (void**)&pObj); CReleaseMe rm(pObj); hres = m_pOuter->ProcessPossibleAdd(pObj, false); } return WBEM_S_NO_ERROR; } STDMETHODIMP CMonitor::CDataSink::SetStatus(long lFlags, HRESULT hresResult, BSTR, IWbemClassObject* pError) { if(lFlags != WBEM_STATUS_COMPLETE) return WBEM_S_NO_ERROR; m_pOuter->ProcessDataDone(hresResult, pError); return WBEM_S_NO_ERROR; } HRESULT CMonitor::ProcessPollObject(_IWmiObject* pObj) { HRESULT hres; // // An object came back from the query --- mark it in the table as 'present' // // // Get its path // VARIANT v; VariantInit(&v); hres = pObj->Get(L"__RELPATH", 0, &v, NULL, NULL); if(FAILED(hres) || V_VT(&v) != VT_BSTR) return WBEM_E_CRITICAL_ERROR; CClearMe cm(&v); LPCWSTR wszPath = V_BSTR(&v); // // Determine if firing is necessary // bool bFire; DWORD dwObjectCount; { CInCritSec ics(&m_cs); // // See if we already have it // TIterator it = m_map.find(wszPath); if(it == m_map.end()) { // This instance was not there before --- fire an assert! bFire = true; dwObjectCount = m_map.size(); } else { // It's already there bFire = false; } // // Set the presense indicator to true --- otherwise we will delete it // when the query is over // m_map[wszPath] = true; } if(bFire) FireAssert(pObj, wszPath, m_bFirstPollDone, dwObjectCount); return WBEM_S_NO_ERROR; } HRESULT CMonitor::ProcessPollQueryDone(HRESULT hresQuery, IWbemClassObject* pError) { // // The polling query has finished, so anything in the map marked with // 'false' for the presence indicator has not come in this time and we must // fire a retract. At the same time, we will reset all the presense // indicators for the next time // CWStringArray awsRetracts; DWORD dwCountBefore; { CInCritSec ics(&m_cs); dwCountBefore = m_map.size(); // // note --- for loop not incrementing iterator. This is because // sometimes we have to remove things from the map // for(TIterator it = m_map.begin(); it != m_map.end(); ) { if(!it->second) { // Not there --- write down for retract if(awsRetracts.Add(it->first) < 0) return WBEM_E_OUT_OF_MEMORY; // // Remove this guy from the map. This will advance the // iterator // it = m_map.erase(it); } else { // It is there --- reset the indicator it->second = true; it++; } } } // // Fire all the retracts we have accomulated // for(int i = 0; i < awsRetracts.Size(); i++) { FireRetract(NULL, awsRetracts[i], true, dwCountBefore - i); } // // Set the "done with the first query" indicator // m_bFirstPollDone = true; return S_OK; } /* QL_LEVEL_1_RPN_EXPRESSION* pEventQ = new QL_LEVEL_1_RPN_EXPRESSION; // // Add all the properties that the data query was looking for, but with // TargetInstance // for(int i = 0; i < pDataQ->nNumberOfProperties; i++) { CPropertuName& OldName = pDataQ->pRequestedPropertyNames[i]; CPropertyName NewName; NewName.AddElement(L"TargetInstance") for(int j = 0; j < OldName.GetNumElements(); i++) { NewName.AddElement(OldName.GetStringAt(i)) } pEventQ->AddProperty(NewName); } // // Add "TargetInstance isa "MyClass"" there // QL_LEVEL_1_TOKEN Token; Token.PropertyName.AddElement(L"TargetInstance"); Token.nTokenType = QL1_OP_EXPRESSION; Token.nOperator = QL1_OPERATOR_ISA; V_VT(&Token.vConstValue) = VT_BSTR; V_BSTR(&Token.vConstValue) = SysAllocString(pDataQ->bsClassName); Token.m_bPropComp = FALSE; Token.dwPropertyFunction = Token.dwConstFunction = 0; pEventQ->AddToken(Token); // // Convert the original query to DNF // CDNFExpression DNF; QL_LEVEL_1_TOKEN* aTokens = pDataQ->pArrayOfTokens; DNF.ConstructFromTokens(aTokens); // // Reduce it to just the part about the keys // CDNFExpression* pKeyDNF = NULL; hres = DNF.GetNecessaryProjection(&Filter, &pKeyDNF); if(FAILED(hres)) return hres; // // Stick those back into the event query // if(pKeyDNF->GetNumTerms() > 0) { hres = pKeyDNF->AddToRPN(pEventQ); if(FAILED(hres)) return hres; Token.nTokenType = QL1_AND; pEventQ->AddToken(Token); } // // At this point, our creation and deletion queries are complete. // But for our modification query, we need to add all the properties we // are interested in with TargetInstance.X <> PreviousInstance.X // QL_LEVEL_1_RPN_EXPRESSION* pModEventQ = new QL_LEVEL_1_RPN_EXPRESSION(*pEventQ); // // Add all the properties that the data query was looking for, but with // TargetInstance.X <> PreviousInstance.X // for(int i = 0; i < pDataQ->nNumberOfProperties; i++) { CPropertuName& OldName = pDataQ->pRequestedPropertyNames[i]; Token.PropertyName.Empty(); Token.PropertyName2.Empty(); Token.PropertyName.AddElement(L"TargetInstance") Token.PropertyName2.AddElement(L"PreviousInstance") for(int j = 0; j < OldName.GetNumElements(); i++) { Token.PropertyName.AddElement(OldName.GetStringAt(i)) Token.PropertyName2.AddElement(OldName.GetStringAt(i)) } Token.m_bPropComp = TRUE; Token.nTokenType = QL1_OP_EXPRESSION; Token.nOperator = QL1_OPERATOR_NOTEQUALS; pModEventQ->AddToken(Token); Token.nTokenType = QL1_AND; pModEventQ->AddToken(Token); } // // Construct results // *ppCreationEventQ = new QL_LEVEL_1_RPN_EXPRESSION(*pEventQ); pEventQ->SetClassName(L"__InstanceCreationEvent"); *ppDeletionEventQ = new QL_LEVEL_1_RPN_EXPRESSION(*pEventQ); pEventQ->SetClassName(L"__InstanceDeletionEvent"); *pp */