/*++ Copyright (C) 1996-2001 Microsoft Corporation Module Name: WMIMERGER.H Abstract: Implements _IWmiMerger History: 16-Nov-00 sanjes Created. --*/ #ifndef _WMIMERGER_H_ #define _WMIMERGER_H_ #include "internalmerger.h" #include "mergerreq.h" // forward class definitions class CWmiMerger; // // Support class for CWmiMerger. // // Basically, CWmiMerger holds onto many instances of CWmiMergerRecord // class CWmiMergerRecord { private: BOOL m_fHasInstances; BOOL m_fHasChildren; DWORD m_dwLevel; WString m_wsClass; CWmiMerger* m_pMerger; CInternalMerger* m_pInternalMerger; CMergerSink* m_pDestSink; CPointerArray m_ChildArray; bool m_bScheduledChildRequest; IWbemContext* m_pExecutionContext; bool m_bStatic; public: CWmiMergerRecord( CWmiMerger* pMerger, BOOL fHasInstances, BOOL fHasChildren, LPCWSTR pwszClass, CMergerSink* pDestSink, DWORD dwLevel, bool bStatic ); ~CWmiMergerRecord(); CMergerSink* GetChildSink( void ); CMergerSink* GetOwnSink( void ); CMergerSink* GetDestSink( void ); LPCWSTR GetClass( void ) { return m_wsClass; } LPCWSTR GetName( void ) { return GetClass(); } BOOL IsClass( LPCWSTR pwszClass ) { return m_wsClass.EqualNoCase( pwszClass ); } DWORD GetLevel( void ) { return m_dwLevel; } bool IsStatic( void ) { return m_bStatic; } HRESULT AttachInternalMerger( CWbemClass* pClass, CWbemNamespace* pNamespace, IWbemContext* pCtx, BOOL fDerivedFromTarget, bool bStatic ); HRESULT AddChild( CWmiMergerRecord* pRecord ); CWmiMerger* GetWmiMerger( void ) { return m_pMerger; } // If we have an internal merger, we tell it to cancel void Cancel( HRESULT hRes ); bool ScheduledChildRequest( void ) { return m_bScheduledChildRequest; } void SetScheduledChildRequest( void ) { m_bScheduledChildRequest = true; } CWmiMergerRecord* GetChildRecord( int nIndex ); HRESULT SetExecutionContext( IWbemContext* pContext ); IWbemContext* GetExecutionContext( void ) { return m_pExecutionContext; } // We can only cancel child sinks if we have an internal merger. //void CancelChildSink( void ) { if ( NULL != m_pInternalMerger ) m_pInternalMerger->CancelChildSink(); } void SetIsStatic( bool b ) { m_bStatic = b; } }; // Allows us to locate values quickly by name template class CSortedUniquePointerArray : public CUniquePointerArray { public: int Insert( TMember* pNewElement ); TMember* Find( LPCWSTR pwszName, int* pnIndex = NULL ); int RemoveAtNoDelete( int nIndex ); BOOL Verify( void ); }; template int CSortedUniquePointerArray::Insert( TMember* pNewElement ) { int nLowIndex = 0, nHighIndex = m_Array.Size(); // Binary search of the ids to find an index at which to insert // If we find our element, this is a failure. while ( nLowIndex < nHighIndex ) { int nMid = (nLowIndex + nHighIndex) / 2; int nTest = _wcsicmp( ((TMember*) m_Array[nMid])->GetName(), pNewElement->GetName() ); if ( nTest < 0 ) { nLowIndex = nMid + 1; } else if ( nTest > 0 ) { nHighIndex = nMid; } else { _DBG_ASSERT( 0 ); // Index already exists return -1; } } // WHILE looking for index // Found the location, if it's at the end, check if we need to do an insert or add // We insert if the element at the end is > the element we want to insert BOOL bInsert = true; if ( nLowIndex == GetSize() - 1 ) { bInsert = ( _wcsicmp( ((TMember*) m_Array[nLowIndex])->GetName(), pNewElement->GetName() ) > 0 ); } // Stick it in (careful to add to the end if the selected index is the end // and the current element is not greater than the new one). if ( bInsert ) { return InsertAt( nLowIndex, pNewElement ); } return Add( pNewElement ); } template TMember* CSortedUniquePointerArray::Find( LPCWSTR pwszName, int* pnIndex ) { int nLowIndex = 0, nHighIndex = m_Array.Size(); // Binary search of the values to find a the requested name. while ( nLowIndex < nHighIndex ) { int nMid = (nLowIndex + nHighIndex) / 2; int nTest = _wcsicmp( ((TMember*) m_Array[nMid])->GetName(), pwszName ); if ( nTest < 0 ) { nLowIndex = nMid + 1; } else if ( nTest > 0 ) { nHighIndex = nMid; } else { // Found it if ( NULL != pnIndex ) { *pnIndex = nMid; } return (TMember*) m_Array[nMid]; } } // WHILE looking for index // Didn't find it return NULL; } // Removes the element, but does not auto-delete it template int CSortedUniquePointerArray::RemoveAtNoDelete( int nIndex ) { if ( nIndex >= m_Array.Size() ) { return -1; } m_Array.RemoveAt( nIndex ); return nIndex; } template BOOL CSortedUniquePointerArray::Verify( void ) { BOOL fReturn = TRUE; for ( int x = 0; fReturn && x < GetSize() - 1; x++ ) { // Should be in ascending order LPCWSTR pwszFirst = GetAt( x )->GetName(); LPCWSTR pwszSecond = GetAt( x+1 )->GetName(); fReturn = ( _wcsicmp( GetAt( x )->GetName(), GetAt( x+1 )->GetName() ) < 0 ); _DBG_ASSERT( fReturn ); if ( !fReturn ) { CSortedUniquePointerArray tempArray; for ( int y = 0; y < GetSize(); y++ ) { tempArray.Insert( GetAt( y ) ); } } } return fReturn; } //****************************************************************************** //****************************************************************************** // // class CWmiMerger // // This class implements the WMI Merger. // //****************************************************************************** class CWmiMerger : public _IWmiArbitratee, public _IWmiArbitratedQuery { private: long m_lRefCount; WString m_wsTargetClassName; CMergerTargetSink* m_pTargetSink; _IWmiCoreHandle* m_pTask; _IWmiArbitrator* m_pArbitrator; CWbemNamespace* m_pNamespace; DWORD m_dwProviderDeliveryPing; DWORD m_dwMaxLevel; DWORD m_dwMinReqLevel; CSortedUniquePointerArray m_MergerRecord; // // the sinks of the merger are wired objects // thy have a refcount a-la-COM, but they are destructed by the // Manager of the m_MergerSinks Array // CUniquePointerArray m_MergerSinks; long m_lNumArbThrottled; HRESULT m_hOperationRes; bool m_bMergerThrottlingEnabled; CCritSec m_cs; long m_lDebugMemUsed; CWmiMergerRequestMgr* m_pRequestMgr; void CleanChildRequests(CWmiMergerRecord* pParentRecord, int startingWith); public: // No access CWmiMerger( CWbemNamespace* pNamespace ); ~CWmiMerger(); protected: HRESULT GetLevelAndSuperClass( _IWmiObject* pObj, DWORD* pdwLevel, _variant_t & vSuperClass ); public: /* IUnknown methods */ STDMETHOD(QueryInterface)(REFIID riid, LPVOID FAR* ppvObj); STDMETHOD_(ULONG, AddRef)(THIS); STDMETHOD_(ULONG, Release)(THIS); /* _IWmiArbitratee methods */ STDMETHOD(SetOperationResult)( ULONG uFlags, HRESULT hRes ); STDMETHOD(SetTaskHandle)( _IWmiCoreHandle* pTask ); STDMETHOD(DumpDebugInfo)( ULONG uFlags, const BSTR strFile ); /* _IWmiArbitratedQuery methods */ STDMETHOD(IsMerger)( void ); // Sets initial parameters for merger. Establishes the target class and sink for the // query associated with the merger STDMETHOD(Initialize)( _IWmiArbitrator* pArbitrator, _IWmiCoreHandle* pTask, LPCWSTR pwszTargetClass, IWbemObjectSink* pTargetSink, CMergerSink** ppFinalSink ); // Called to request a delivery sink for a class in the query chain. The returned // sink is determined by the specified flags as well as settings on the parent class STDMETHOD(RegisterSinkForClass)( LPCWSTR pwszClass, _IWmiObject* pClass, IWbemContext* pContext, BOOL fHasChildren, BOOL fHasInstances, BOOL fDerivedFromTarget, bool bStatic, CMergerSink* pDestSink, CMergerSink** ppOwnSink, CMergerSink** ppChildSink ); // Called to request a delivery sink for child classes in the query chain. This is especially // important when instances are merged under the covers. STDMETHOD(GetChildSink)( LPCWSTR pwszClass, CBasicObjectSink** ppSink ); // Can be used to holdoff indicates - if we're merging instances from multiple providers, we need // to ensure that we don't get lopsided in the number of objects we've got queued up for merging. STDMETHOD(Throttle)( void ); // Merger will hold information regarding the total number of objects it has queued up waiting // for merging and the amount of memory consumed by those objects. STDMETHOD(GetQueuedObjectInfo)( DWORD* pdwNumQueuedObjects, DWORD* pdwQueuedObjectMemSize ); // If this is called, all underlying sinks will be cancelled in order to prevent accepting additional // objects. This will also automatically free up resources consumed by queued objects. STDMETHOD(Cancel)( void ); // Helper function for creating our sinks - this will add the sink to our array of // sinks which will get destroyed when we are released HRESULT CreateMergingSink( MergerSinkType eType, IWbemObjectSink* pDestSink, CInternalMerger* pMerger, CMergerSink** ppSink ); // If this is called, all underlying sinks will be cancelled in order to prevent accepting additional // objects. This will also automatically free up resources consumed by queued objects. HRESULT Cancel( HRESULT hRes ); // Final Shutdown. Called when the target sink is released. At this point, we should // unregister ourselves from the world HRESULT Shutdown( void ); // Registers arbitrated requests HRESULT RegisterArbitratedInstRequest( CWbemObject* pClassDef, long lFlags, IWbemContext* pCtx, CBasicObjectSink* pSink, BOOL bComplexQuery, CWbemNamespace* pNs ); HRESULT RegisterArbitratedQueryRequest( CWbemObject* pClassDef, long lFlags, LPCWSTR Query, LPCWSTR QueryFormat, IWbemContext* pCtx, CBasicObjectSink* pSink, CWbemNamespace* pNs ); HRESULT RegisterArbitratedStaticRequest( CWbemObject* pClassDef, long lFlags, IWbemContext* pCtx, CBasicObjectSink* pSink, CWbemNamespace* pNs, QL_LEVEL_1_RPN_EXPRESSION* pParsedQuery ); // Executes a Merger Parent Request - Cycles through parent object requests and executes // them as appropriate HRESULT Exec_MergerParentRequest( CWmiMergerRecord* pParentRecord, CBasicObjectSink* pSink ); // Executes a Merger Child Request - Cycles through child classes of the given parent // class, and executes the appropriate requests HRESULT Exec_MergerChildRequest( CWmiMergerRecord* pParentRecord, CBasicObjectSink* pSink ); // Schedules a Merger Parent Request if one is necessary HRESULT ScheduleMergerParentRequest( IWbemContext* pCtx ); // Schedules a Merger Child Request HRESULT ScheduleMergerChildRequest( CWmiMergerRecord* pParentRecord ); // Enables/Disables merger throttling in all records (on by default) void EnableMergerThrottling( bool b ) { m_bMergerThrottlingEnabled = b; } // Returns whether or not we have a single static request in the merger BOOL IsSingleStaticRequest( void ); bool MergerThrottlingEnabled( void ) { return m_bMergerThrottlingEnabled; } _IWmiCoreHandle* GetTask( void ) { return m_pTask; } // Help us track that *something* is going on // We are intentionally not wrapping thread safety around these guys, since assigning and // retrieving the value is an atomic operation and realistically, if any contention occurs // setting the values, they should all, more or less reflect the same tick (remember, they're // all jumping in at the same time, so this shouldn't really be a problem. void PingDelivery( DWORD dwLastPing ) { m_dwProviderDeliveryPing = dwLastPing; } DWORD GetLastDeliveryTime( void ) { return m_dwProviderDeliveryPing; } HRESULT ReportMemoryUsage( long lAdjustment ); // Helper functions for tracking the number of threads potentially being throttled by // the arbitrator long IncrementArbitratorThrottling( void ) { return InterlockedIncrement( &m_lNumArbThrottled ); } long DecrementArbitratorThrottling( void ) { return InterlockedDecrement( &m_lNumArbThrottled ); } long NumArbitratorThrottling( void ) { return m_lNumArbThrottled ; } }; #endif