/*++ Copyright (C) 1996-2001 Microsoft Corporation Module Name: Abstract: History: --*/ // RefreshCooker.cpp #include "precomp.h" #include #include #include #include "Refresher.h" #include "CookerUtils.h" //////////////////////////////////////////////////////////////////////////// // // CRefresher // ========== // // The refresher class implements both the IWbemRefresher and the // IWMIRefreshableCooker interfaces. It contains an instance cache and // an enumerator cache as well as maintaining an internal refresher to // track the raw data. // //////////////////////////////////////////////////////////////////////////// CRefresher::CRefresher() : m_pRefresher( NULL ), m_pConfig( NULL ), m_lRef( 0 ), m_bOK( FALSE ), m_dwRefreshId(0) { #ifdef _VERBOSE DbgPrintfA(0,"------------ CRefresher %08x \n",this); #endif WMISTATUS dwStatus = WBEM_NO_ERROR; // Initialize the internal refresher // ================================= dwStatus = CoCreateInstance( CLSID_WbemRefresher, NULL, CLSCTX_INPROC_SERVER, IID_IWbemRefresher, (void**) &m_pRefresher ); // Get the refresher configuration interface // ========================================= if ( SUCCEEDED( dwStatus ) ) { dwStatus = m_pRefresher->QueryInterface( IID_IWbemConfigureRefresher, (void**)&m_pConfig ); } m_bOK = SUCCEEDED( dwStatus ); } CRefresher::~CRefresher() { if (m_pRefresher ) m_pRefresher->Release(); if ( m_pConfig ) m_pConfig->Release(); } /////////////////////////////////////////////////////////////////////////////// // // Private methods // /////////////////////////////////////////////////////////////////////////////// WMISTATUS CRefresher::SearchCookingClassCache( WCHAR* wszCookingClass, /*out*/ CWMISimpleObjectCooker* & pObjectCooker_out ) /////////////////////////////////////////////////////////////////////////////// // // SearchCookingClassCache enumerates the cache looking for a class name // that matches the wszCookingClass parameter // // Parameters: // wszCookingClass - The name of the WMI cooking class // ppObjectCooker - The instance of the object cooker // /////////////////////////////////////////////////////////////////////////////// { WMISTATUS dwStatus = WBEM_E_NOT_FOUND; CWMISimpleObjectCooker* pObjectCooker = NULL; // Enumerate through the cache looking for the record m_CookingClassCache.BeginEnum(); while ( S_OK == m_CookingClassCache.Next( &pObjectCooker ) ) { // Compare the names // ================= if ( 0 == wbem_wcsicmp( pObjectCooker->GetCookingClassName(), wszCookingClass ) ) { pObjectCooker_out = pObjectCooker; dwStatus = WBEM_NO_ERROR; break; } } m_CookingClassCache.EndEnum(); return dwStatus; } WMISTATUS CRefresher::CreateObjectCooker( WCHAR* wszCookingClassName, IWbemObjectAccess* pCookingAccess, IWbemObjectAccess* pRawAccess, CWMISimpleObjectCooker** ppObjectCooker, IWbemServices * pNamespace) /////////////////////////////////////////////////////////////////////////////// // // CreateObjectCooker will create and initialize a new object cooker and add // it to the cache // // Parameters: // pNamespace - The namespace pointer where the objects are located // pCookingAccess - The WMI cooking object in need of a cooker // wszCookingClassName // - The name of the cooking class // ppObjectCooker - The parameter to pass back the new object cooker // /////////////////////////////////////////////////////////////////////////////// { if (NULL == ppObjectCooker) return WBEM_E_INVALID_PARAMETER; CWMISimpleObjectCooker* pObjectCooker = NULL; WCHAR* wszRawClassName; long lID; pObjectCooker = new CWMISimpleObjectCooker( wszCookingClassName, pCookingAccess, pRawAccess, pNamespace ); if ( NULL == pObjectCooker ) return WBEM_E_OUT_OF_MEMORY; WMISTATUS dwStatus = pObjectCooker->GetLastHR(); // Add the object cooker to the cache if ( SUCCEEDED( dwStatus ) ) { dwStatus = m_CookingClassCache.Add( pObjectCooker, wszCookingClassName, &lID ); } if (FAILED(dwStatus)) { delete pObjectCooker; pObjectCooker = NULL; } *ppObjectCooker = pObjectCooker; return dwStatus; } WMISTATUS CRefresher::AddRawInstance( IWbemServices* pService, IWbemContext * pCtx, IWbemObjectAccess* pCookingInst, IWbemObjectAccess** ppRawInst ) /////////////////////////////////////////////////////////////////////////////// // // AddRawInstance is called to add the corresponding raw instance of a // cooked object to the internal refresher. We first extract the key value // from the cooked object and create the raw instance path using the raw // class name // // Parameters: // pService - The namespace pointer where the objects are located // pCookingInst - The WMI cooking instance // ppRawInst - The WMI raw instance that was added to the internal // refresher // /////////////////////////////////////////////////////////////////////////////// { WMISTATUS dwStatus = WBEM_NO_ERROR; IWbemClassObject* pObj = NULL; // The alternate representation of pCookingInst _variant_t varRelPath; // The RELPATH value WCHAR* wszRawClassName = NULL; // The name of the raw class // Get the fully specified instance path for the cooking object // ============================================================ pCookingInst->QueryInterface( IID_IWbemClassObject, (void**)&pObj ); CReleaseMe arObj( pObj ); dwStatus = pObj->Get( L"__RELPATH", 0, &varRelPath, NULL, NULL ); if ( SUCCEEDED( dwStatus ) ) { // Verify the property type // ======================== if ( varRelPath.vt != VT_BSTR ) { dwStatus = WBEM_E_TYPE_MISMATCH; } if ( SUCCEEDED( dwStatus ) ) { IWbemClassObject* pRawInst = NULL; WCHAR* wszKeys = NULL; WCHAR* wszRawInst = NULL; // Extract the key name // ==================== wszKeys = wcsstr( varRelPath.bstrVal, L"=" ) + 1; // Get the raw class name // ====================== dwStatus = GetRawClassName( pCookingInst, &wszRawClassName ); if (SUCCEEDED(dwStatus)) { wmilib::auto_buffer adRawClassName( wszRawClassName ); // Append the key to the raw class name // ==================================== size_t length = wcslen( wszRawClassName ) + wcslen( wszKeys ) + 10; wszRawInst = new WCHAR[ length ]; if (!wszRawInst) return WBEM_E_OUT_OF_MEMORY; wmilib::auto_buffer adRawInst( wszRawInst ); StringCchPrintfW( wszRawInst, length , L"%s=%s", wszRawClassName, wszKeys ); // Add a raw instance to the internal refresher // ============================================ dwStatus = m_pConfig->AddObjectByPath( pService, wszRawInst, 0, pCtx, &pRawInst, NULL ); CReleaseMe arRawInst( pRawInst ); if (SUCCEEDED(dwStatus)) { // Return the IWbemObjectAccess interface of the raw instance // ========================================================== dwStatus = pRawInst->QueryInterface( IID_IWbemObjectAccess, (void**)ppRawInst ); } } } } return dwStatus; } WMISTATUS CRefresher::AddRawEnum( IWbemServices* pNamespace, IWbemContext * pCtx, WCHAR * wszRawClassName, IWbemHiPerfEnum** ppRawEnum, long* plID ) /////////////////////////////////////////////////////////////////////////////// // // AddRawEnum is called to add the corresponding raw enumerator to the // internal refrehser. In order to add the raw enumerator to the refresher, // we must determine the raw class name, therefore, we must create a // cooking class in order to get the AutoCook_RawClass qualifier. // // Parameters: // pNamespace - The namespace pointer where the objects are located // wszRawClassName - The name of the cooking class // ppRawEnum - The raw WMI enumerator that was added to the // internal refresher // plID - The refresher ID of the raw enumerator // /////////////////////////////////////////////////////////////////////////////// { // Add the Raw enumerator to the internal refresher WMISTATUS dwStatus = m_pConfig->AddEnum( pNamespace, wszRawClassName, 0, pCtx, ppRawEnum, plID ); #ifdef _VERBOSE DbgPrintfA(0,"wszRawClassName %S pEnum %08x hr %08x\n",wszRawClassName,*ppRawEnum,dwStatus); #endif return dwStatus; } /////////////////////////////////////////////////////////////////////////////// // // COM methods // /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CRefresher::QueryInterface(REFIID riid, void** ppv) /////////////////////////////////////////////////////////////////////////////// // // Standard QueryInterface // // Parameters: // riid - the ID of the requested interface // ppv - a pointer to the interface pointer // /////////////////////////////////////////////////////////////////////////////// //ok { if (NULL == ppv) return E_POINTER; if(riid == IID_IUnknown) *ppv = (LPVOID)(IUnknown*)(IWMIRefreshableCooker*)this; else if(riid == IID_IWMIRefreshableCooker) *ppv = (LPVOID)(IWMIRefreshableCooker*)this; else if(riid == IID_IWbemRefresher) *ppv = (LPVOID)(IWbemRefresher*)this; else { *ppv = NULL; return E_NOINTERFACE; } ((IUnknown*)*ppv)->AddRef(); return S_OK; } STDMETHODIMP_(ULONG) CRefresher::AddRef() { return InterlockedIncrement(&m_lRef); } STDMETHODIMP_(ULONG) CRefresher::Release() { long lRef = InterlockedDecrement(&m_lRef); if(lRef == 0) delete this; return lRef; } STDMETHODIMP CRefresher::AddInstance( /*[in] */ IWbemServices* pNamespace, // The object's namespace /*[in] */ IWbemContext * pCtx, // The Context /*[in] */ IWbemObjectAccess* pCookingInstance, // Cooking class definition /*[in] */ IWbemObjectAccess* pRefreshableRawInstance, // Raw instance /*[out] */ IWbemObjectAccess** ppRefreshableInstance, // Cooking instance /*[out] */ long* plId ) /////////////////////////////////////////////////////////////////////////////// // // AddInstance is called to add a WMI cooking instance to the refresher. The // refreshable instance is a clone of the WMI instance that is passed in // by pCookingInstance. Once the instance is cloned, the corresponding raw // instance is added to the internal refresher, and then the cloned // cooked instance and the refreshable raw instance are added to the object // cooker. If a cooker does not already exist in the cooker cache, one // is created. // // Parameters: // pNamespace - The namespace where the objects are located // pCtx - IWbemContext implementation // pCookingInstance - The instance to be cooked // pRefreshableRawInstance // - U N U S E D P A R A M // ppRefreshableInstance // - The refreshable cooking instance passed back to // the client // plId - The ID of the instance // /////////////////////////////////////////////////////////////////////////////// { HRESULT hResult = S_OK; CWMISimpleObjectCooker* pObjectCooker = NULL; IWbemObjectAccess* pInternalRawInst = NULL; // The raw instance for the short term local refresher solution // For now, we expect that the pRefreshableRawInstance parameter will be NULL // since we are using an internal refresher to manage the raw instances // ========================================================================== if ( NULL == pNamespace || NULL == pCookingInstance || NULL != pRefreshableRawInstance ) { return WBEM_E_INVALID_PARAMETER; } IWbemClassObject* pNewClassObj = NULL; IWbemClassObject* pClassObj = pCookingInstance; pClassObj->AddRef(); CReleaseMe arClassObj( pClassObj ); hResult = pClassObj->Clone( &pNewClassObj ); if (SUCCEEDED(hResult)) { CReleaseMe arNewClassObj( pNewClassObj ); hResult = pNewClassObj->QueryInterface( IID_IWbemObjectAccess, (void**)ppRefreshableInstance ); // Add the instance to the object cooker if ( SUCCEEDED( hResult ) ) { // Get the raw instance (add it to the internal refresher) hResult = AddRawInstance( pNamespace, pCtx, *ppRefreshableInstance, &pInternalRawInst ); CReleaseMe arInternalRawInst( pInternalRawInst ); // Retrieve the class cooker if ( SUCCEEDED( hResult ) ) { WCHAR* wszClassName = NULL; // Get the cooked class' name hResult = GetClassName( pCookingInstance, &wszClassName ); wmilib::auto_buffer adaClassName( wszClassName ); if ( SUCCEEDED( hResult ) ) { // Search for an existing cooking cache object hResult = SearchCookingClassCache( wszClassName, pObjectCooker ); // If it does not exist, create a new one if ( FAILED ( hResult ) ) { hResult = CreateObjectCooker( wszClassName, pCookingInstance, pInternalRawInst, &pObjectCooker, pNamespace ); } } } } // Add the cooking instance if ( SUCCEEDED( hResult ) ) { hResult = pObjectCooker->SetCookedInstance( *ppRefreshableInstance, plId ); if ( SUCCEEDED( hResult ) ) { // Add the raw instance to the cooker hResult = pObjectCooker->BeginCooking( *plId, pInternalRawInst, m_dwRefreshId ); } } } return hResult; } STDMETHODIMP CRefresher::AddEnum( /*[in] */ IWbemServices* pNamespace, /*[in] */ IWbemContext * pContext, /*[in, string] */ LPCWSTR wszCookingClass, /*[in] */ IWbemHiPerfEnum* pRefreshableEnum, /*[out] */ long* plId ) /////////////////////////////////////////////////////////////////////////////// // // AddEnum is called whenever a new cooked enumerator is added to the // refresher. WMI passes an IWbemHiPerfEnum object to the provider which // will be used for the cooked enumerator. The corresponding raw enumerator // is obtained when the added to the internal refresher. Both of these // enumerators as well as a cooking class template is added to the // enumerator cache. // // Parameters: // pNamespace - The namespace where the objects are located // wszCookingClass - The name of the enumerators' cooking class // pRefreshableEnum // - The enumerator to be used for the cooked classes // plId - The ID of the enumerator // /////////////////////////////////////////////////////////////////////////////// { HRESULT hResult = WBEM_NO_ERROR; IWbemHiPerfEnum* pRawEnum = NULL; long lRawID = 0; // Verify our 'in' parameters // ========================== if ( NULL == pNamespace || NULL == wszCookingClass || NULL == pRefreshableEnum ) { hResult = WBEM_E_INVALID_PARAMETER; } if ( SUCCEEDED( hResult ) ) { // Get the cooking object // ====================== IWbemClassObject* pCookedObject = NULL; IWbemClassObject* pRawObject = NULL; BSTR strCookedClassName = SysAllocString( wszCookingClass ); if (NULL == strCookedClassName) return WBEM_E_OUT_OF_MEMORY; CSysFreeMe afCookedClassName( strCookedClassName ); hResult = pNamespace->GetObject( strCookedClassName, 0, NULL, &pCookedObject, NULL ); CReleaseMe arCookedObject( pCookedObject ); if ( SUCCEEDED( hResult ) ) { WCHAR* wszRawClassName = NULL; hResult = GetRawClassName( pCookedObject, &wszRawClassName ); wmilib::auto_buffer adRawClassName( wszRawClassName ); if ( SUCCEEDED( hResult )) { BSTR strRawClassName = SysAllocString(wszRawClassName); if (NULL == strRawClassName) return WBEM_E_OUT_OF_MEMORY; CSysFreeMe sfm(strRawClassName); hResult = pNamespace->GetObject( strRawClassName, 0, NULL, &pRawObject, NULL ); if ( SUCCEEDED( hResult ) ) { CReleaseMe arRawObject( pRawObject ); // Add the raw enumerator to our internal refresher // ================================================ hResult = AddRawEnum( pNamespace, pContext, wszRawClassName, &pRawEnum, &lRawID ); CReleaseMe arRawEnum( pRawEnum ); if ( SUCCEEDED( hResult ) ) { // Add the cooked enumerator to the enumerator cache // ================================================= hResult = m_EnumCache.AddEnum( wszCookingClass, pCookedObject, // this is acquired by CWMISimpleObjectCooker and CEnumeratorManager pRawObject, pRefreshableEnum, pRawEnum, lRawID, (DWORD*)plId ); // set the three bits *plId |= WMI_COOKED_ENUM_MASK; } } } } } return hResult; } STDMETHODIMP CRefresher:: Remove( /*[in] */ long lId ) /////////////////////////////////////////////////////////////////////////////// // // Remove is used to remove an object from the refresher. Depending on the // object, the corresponding removal is performed. // // Parameters: // lID - The ID of the object to be removed // /////////////////////////////////////////////////////////////////////////////// { HRESULT hResult = S_OK; // Is it an instance ID? // ===================== if ( lId == ( lId & ~WMI_COOKED_ENUM_MASK ) ) { CWMISimpleObjectCooker* pCooker = NULL; hResult = m_CookingClassCache.BeginEnum(); while ( S_OK == m_CookingClassCache.Next( &pCooker ) ) { pCooker->Remove( lId ); } hResult = m_CookingClassCache.EndEnum(); } else { long RawId; hResult = m_EnumCache.RemoveEnum( (lId & ~WMI_COOKED_ENUM_MASK) , &RawId ); if (SUCCEEDED(hResult)){ m_pConfig->Remove(RawId,0); } } return hResult; } STDMETHODIMP CRefresher::Refresh() /////////////////////////////////////////////////////////////////////////////// // // Refresh is called when the refreshers' objects are to be updated. The // instances are updated by explicitly enumerating through the instance // cache. The enumerators' refresh is performed with the enumerator // cache. // // Parameters: (none) // /////////////////////////////////////////////////////////////////////////////// { HRESULT hResult = S_OK; CWMISimpleObjectCooker* pCooker = NULL; // Refresh the internal refresher // ============================== m_dwRefreshId++; hResult = m_pRefresher->Refresh( 0L ); if ( SUCCEEDED( hResult ) ) { // INSTANCES: Update the instance values for every class // ===================================================== hResult = m_CookingClassCache.BeginEnum(); while ( S_OK == m_CookingClassCache.Next( &pCooker ) ) { // And update all of the instances // =============================== pCooker->Recalc(m_dwRefreshId); } hResult = m_CookingClassCache.EndEnum(); // ENUMERATORS: Merge and update the values for items in the enumerator // ==================================================================== if ( SUCCEEDED( hResult ) ) { hResult = m_EnumCache.Refresh(m_dwRefreshId); } } return hResult; } STDMETHODIMP CRefresher::Refresh( long lFlags ) /////////////////////////////////////////////////////////////////////////////// // // This is the IWbemRefresher::Refresh implementation and is simply a call // through. // /////////////////////////////////////////////////////////////////////////////// { HRESULT hResult = WBEM_NO_ERROR; hResult = Refresh(); return hResult; }