/*++

Copyright (C) 1997-1999 Microsoft Corporation

Module Name:

    ntperf.cpp

Abstract:

    Mapped NT5 Perf counter provider

History:

    raymcc      02-Dec-97   Created.        
    raymcc      20-Feb-98   Updated to use new initializer.
    bobw         8-Jun-98   tuned up

--*/

#include "wpheader.h"
#include <stdio.h>
#include "oahelp.inl"

#define __WBEMSECURITY  1

//***************************************************************************
//
//  CNt5PerfProvider constructor
//
//***************************************************************************
// ok

CNt5PerfProvider::CNt5PerfProvider(enumCLSID OriginClsid)
{
    m_lRef = 0;
    m_OriginClsid = OriginClsid;
    m_hClassMapMutex = CreateMutex(NULL, FALSE, NULL);
}

//***************************************************************************
//
//  CNt5PerfProvider destructor
//
//***************************************************************************
// ok

CNt5PerfProvider::~CNt5PerfProvider()
{
    int i;
    CClassMapInfo *pClassElem;

    assert (m_lRef == 0);

    for (i = 0; i < m_aCache.Size(); i++) {
        pClassElem = (CClassMapInfo *) m_aCache[i];
        m_PerfObject.RemoveClass (pClassElem->m_pClassDef);
        delete pClassElem;
    }
    m_aCache.Empty(); // reset the buffer pointers

    if (m_hClassMapMutex != 0)
        CloseHandle(m_hClassMapMutex);

    // RegCloseKey(HKEY_PERFORMANCE_DATA); // causes more problems than it solves

}

//***************************************************************************
//
//  CNt5PerfProvider::AddRef
//
//  Standard COM AddRef().
//
//***************************************************************************
// ok

ULONG CNt5PerfProvider::AddRef()
{
    return InterlockedIncrement(&m_lRef);
}

//***************************************************************************
//
//  CNt5PerfProvider::Release
//
//  Standard COM Release().
//
//***************************************************************************
// ok

ULONG CNt5PerfProvider::Release()
{
    long lRef = InterlockedDecrement(&m_lRef);
    if(lRef == 0) {
        delete this;
    }
    return lRef;
}

//***************************************************************************
//
//  CNt5PerfProvider::QueryInterface
//
//  Standard COM QueryInterface().  We have to support two interfaces,
//  the IWbemHiPerfProvider interface itself to provide the objects and
//  the IWbemProviderInit interface to initialize the provider.
//
//***************************************************************************
// ok

HRESULT CNt5PerfProvider::QueryInterface(REFIID riid, void** ppv)
{
    HRESULT hReturn;

    if (riid == IID_IUnknown || riid == IID_IWbemHiPerfProvider) {
        *ppv = (IWbemHiPerfProvider*) this;
        AddRef();
        hReturn = S_OK;
    } else if (riid == IID_IWbemProviderInit) {
        *ppv = (IWbemProviderInit *) this;
        AddRef();
        hReturn = S_OK;
    } else {
        hReturn = E_NOINTERFACE;
    }
    return hReturn;
}

//***************************************************************************
//
//  CNt5PerfProvider::Initialize
//
//  Called once during startup.  Indicates to the provider which
//  namespace it is being invoked for and which User.  It also supplies
//  a back pointer to CIMOM so that class definitions can be retrieved.
//
//  We perform any one-time initialization in this routine. The
//  final call to Release() is for any cleanup.
//
//  <wszUser>           The current user.
//  <lFlags>            Reserved.
//  <wszNamespace>      The namespace for which we are being activated.
//  <wszLocale>         The locale under which we are to be running.
//  <pNamespace>        An active pointer back into the current namespace
//                      from which we can retrieve schema objects.
//  <pCtx>              The user's context object.  We simply reuse this
//                      during any reentrant operations into CIMOM.
//  <pInitSink>         The sink to which we indicate our readiness.
//
//***************************************************************************
// ok

HRESULT CNt5PerfProvider::Initialize( 
    /* [unique][in] */  LPWSTR wszUser,
    /* [in] */          LONG lFlags,
    /* [in] */          LPWSTR wszNamespace,
    /* [unique][in] */  LPWSTR wszLocale,
    /* [in] */          IWbemServices __RPC_FAR *pNamespace,
    /* [in] */          IWbemContext __RPC_FAR *pCtx,
    /* [in] */          IWbemProviderInitSink __RPC_FAR *pInitSink
    )
{
    UNREFERENCED_PARAMETER(wszUser);
    UNREFERENCED_PARAMETER(lFlags);
    UNREFERENCED_PARAMETER(wszNamespace);
    UNREFERENCED_PARAMETER(wszLocale);
    UNREFERENCED_PARAMETER(pNamespace);
    UNREFERENCED_PARAMETER(pCtx);

    pInitSink->SetStatus(0, WBEM_S_INITIALIZED);

    return NO_ERROR;
}
    
//***************************************************************************
//
//  CNt5PerfProvider::QueryInstances
//
//  Called whenever a complete, fresh list of instances for a given
//  class is required.   The objects are constructed and sent back to the
//  caller through the sink.  The sink can be used in-line as here, or
//  the call can return and a separate thread could be used to deliver
//  the instances to the sink.
//
//  Parameters:
//  <pNamespace>        A pointer to the relevant namespace.  This
//                      should not be AddRef'ed or retained past the
//                      execution of this method.
//  <wszClass>          The class name for which instances are required.
//  <lFlags>            Reserved.
//  <pCtx>              The user-supplied context (used during callbacks
//                      into CIMOM).
//  <pSink>             The sink to which to deliver the objects.  The objects
//                      can be delivered synchronously through the duration
//                      of this call or asynchronously (assuming we
//                      had a separate thread).  A IWbemObjectSink::SetStatus
//                      call is required at the end of the sequence.
//
//***************************************************************************
//  ok
HRESULT CNt5PerfProvider::QueryInstances( 
    /* [in] */          IWbemServices __RPC_FAR *pNamespace,
    /* [string][in] */  WCHAR __RPC_FAR *wszClass,
    /* [in] */          long lFlags,
    /* [in] */          IWbemContext __RPC_FAR *pCtx,
    /* [in] */          IWbemObjectSink __RPC_FAR *pSink
    )
{
    HRESULT hReturn;
    BOOL bRes ;
    CClassMapInfo *pClsMap = NULL;

    UNREFERENCED_PARAMETER(lFlags);

#ifdef __WBEMSECURITY
    hReturn = CoImpersonateClient(); // make sure we're legit.

    BOOL    fRevert = SUCCEEDED( hReturn );

    // The following error appears to occur when we are in-proc and there is no
    // proxy/stub, so we are effectively impersonating already

    if ( RPC_E_CALL_COMPLETE == hReturn ) {
        hReturn = S_OK;
    } 

    if (S_OK == hReturn) {
        hReturn = CheckImpersonationLevel();
    }
    // Check Registry security here.
    if ((hReturn != S_OK) || (!HasPermission())) {
        // if Impersonation level is incorrect or
        // the caller doesn't have permission to read
        // from the registry, then they cannot continue
        hReturn = WBEM_E_ACCESS_DENIED;
    }

#else
    hReturn = S_OK;
#endif

    if (hReturn == S_OK) {

        if (pNamespace == 0 || wszClass == 0 || pSink == 0) {
            hReturn = WBEM_E_INVALID_PARAMETER;
        } else {

            // Ensure the class is in our cache and mapped.
            // ============================================
            bRes = MapClass(pNamespace, wszClass, pCtx);

            if (bRes == FALSE)  {
                // Class is not one of ours.
                hReturn = WBEM_E_INVALID_CLASS;
            } else {
                pClsMap = FindClassMap(wszClass);
                if (pClsMap == NULL) {
                    hReturn = WBEM_E_INVALID_CLASS;
                }
            }

            if (hReturn == NO_ERROR) {
                // Refresh the instances.
                // ======================

                PerfHelper::QueryInstances(&m_PerfObject, pClsMap, pSink);

                // Tell CIMOM we are finished.
                // ===========================

                pSink->SetStatus(0, WBEM_NO_ERROR, 0, 0);
                hReturn = NO_ERROR;
            }
        }
    } else {
        // return error
    }

#ifdef __WBEMSECURITY
    // Revert if we successfuly impersonated the user
    if ( fRevert )
    {
        CoRevertToSelf();
    }
#endif

    return hReturn;
}    

//***************************************************************************
//
//  CNt5PerfProvider::CreateRefresher
//
//  Called whenever a new refresher is needed by the client.
//
//  Parameters:
//  <pNamespace>        A pointer to the relevant namespace.  Not used.
//  <lFlags>            Not used.
//  <ppRefresher>       Receives the requested refresher.
//
//***************************************************************************        
// ok
HRESULT CNt5PerfProvider::CreateRefresher( 
     /* [in] */ IWbemServices __RPC_FAR *pNamespace,
     /* [in] */ long lFlags,
     /* [out] */ IWbemRefresher __RPC_FAR *__RPC_FAR *ppRefresher
     )
{
    HRESULT hReturn;
    CNt5Refresher *pNewRefresher;

    UNREFERENCED_PARAMETER(lFlags);

#ifdef __WBEMSECURITY
    hReturn = CoImpersonateClient(); // make sure we're legit.

    BOOL    fRevert = SUCCEEDED( hReturn );

    // The following error appears to occur when we are in-proc and there is no
    // proxy/stub, so we are effectively impersonating already

    if ( RPC_E_CALL_COMPLETE == hReturn ) {
        hReturn = S_OK;
    } 

    if (S_OK == hReturn) {
        hReturn = CheckImpersonationLevel();
    }
    // Check Registry security here.
    if ((hReturn != S_OK) || (!HasPermission())) {
        // if Impersonation level is incorrect or
        // the caller doesn't have permission to read
        // from the registry, then they cannot continue
        hReturn = WBEM_E_ACCESS_DENIED;
    }

#else
    hReturn = S_OK;
#endif

    if (hReturn == S_OK) {

        if (pNamespace == 0 || ppRefresher == 0) {
            hReturn = WBEM_E_INVALID_PARAMETER;
        } else {
            // Construct a new empty refresher.
            // ================================        
            pNewRefresher = new CNt5Refresher (this);

            if (pNewRefresher != NULL) {
                // Follow COM rules and AddRef() the thing before sending it back.
                // ===============================================================
                pNewRefresher->AddRef();
                *ppRefresher = pNewRefresher;
    
                hReturn = NO_ERROR;
            } else {
                hReturn = WBEM_E_OUT_OF_MEMORY;
            }
        }
    }

#ifdef __WBEMSECURITY
    // Revert if we successfuly impersonated the user
    if ( fRevert )
    {
        CoRevertToSelf();
    }
#endif

    return hReturn;
}

//***************************************************************************
//
//  CNt5PerfProvider::CreateRefresherObject
//
//  Called whenever a user wants to include an object in a refresher.
//     
//  Parameters:
//  <pNamespace>        A pointer to the relevant namespace in CIMOM.
//  <pTemplate>         A pointer to a copy of the object which is to be
//                      added.  This object itself cannot be used, as
//                      it not owned locally.        
//  <pRefresher>        The refresher to which to add the object.
//  <lFlags>            Not used.
//  <pContext>          Not used here.
//  <ppRefreshable>     A pointer to the internal object which was added
//                      to the refresher.
//  <plId>              The Object Id (for identification during removal).        
//
//***************************************************************************        
// ok

HRESULT CNt5PerfProvider::CreateRefresherObject( 
    /* [in] */ IWbemServices __RPC_FAR *pNamespace,
    /* [in] */ IWbemObjectAccess __RPC_FAR *pTemplate,
    /* [in] */ IWbemRefresher __RPC_FAR *pRefresher,
    /* [in] */ long lFlags,
    /* [in] */ IWbemContext __RPC_FAR *pContext,
    /* [string][in] */ LPCWSTR wszClass,
    /* [in] */ IWbemHiPerfEnum __RPC_FAR *pHiPerfEnum,
    /* [out] */ IWbemObjectAccess __RPC_FAR *__RPC_FAR *ppRefreshable,
    /* [out] */ long __RPC_FAR *plId
    )
{
    IWbemClassObject    *pOriginal = 0;
    IWbemClassObject    *pNewCopy = 0;    
    IWbemObjectAccess   *pNewAccess = 0;
    CNt5Refresher       *pRef = 0;
    CClassMapInfo       *pClsMap;
    VARIANT             v; 
    BOOL                bRes;
    HRESULT             hReturn = NO_ERROR;

    UNREFERENCED_PARAMETER(lFlags);

    if (ppRefreshable != NULL) {
        // Initialize the argument
        *ppRefreshable = 0;
    }

    // init the variant 
    VariantInit(&v);

    if (pTemplate != NULL) {
        // Make a copy of the template object.
        // ===================================
        hReturn = pTemplate->QueryInterface(IID_IWbemClassObject, (LPVOID *) &pOriginal);
        if (hReturn == NO_ERROR) {
            hReturn = pOriginal->Clone(&pNewCopy);

            // Get the class name of the object.
            // =================================
            if (hReturn == NO_ERROR) {
                hReturn = pOriginal->Get(CBSTR(cszClassName), 0, &v, 0, 0);
                if ((hReturn == NO_ERROR) && (v.vt != VT_BSTR)) {
                    hReturn = WBEM_E_INVALID_CLASS;
                }
            }

            // We are now done with the original object
            // ========================================
            pOriginal->Release();   
        }

        if (hReturn == NO_ERROR) {
            // We now get the IWbemObjectAccess form of the cloned object
            // and release the unused interface.
            // ==========================================================
            hReturn = pNewCopy->QueryInterface(IID_IWbemObjectAccess, (LPVOID *) &pNewAccess);
            if (hReturn == NO_ERROR) {
                pNewCopy->Release();    // We don't need the IWbemClassObject interface any more
                // We now have an IWbemObjectAccess pointer for the refreshable
                // object in <pNewAccess>.
                // ============================================================
            }
        }
    } else {
        // copy the class name passed in
        v.vt = VT_BSTR;
        v.bstrVal = SysAllocString(wszClass);
    }

    if (hReturn == NO_ERROR) {
        // cast refresher pointer to our refresher object
        pRef = (CNt5Refresher *) pRefresher;

        // Map the class info for this instance.
        // =====================================
        bRes = MapClass(pNamespace, V_BSTR(&v), pContext);
        if (bRes == FALSE) {
           // Class is not one of ours.
           if (pNewAccess != NULL) pNewAccess->Release();
           hReturn = WBEM_E_INVALID_CLASS;
        } else {
            pClsMap = FindClassMap(V_BSTR(&v));
            if (pClsMap == 0) {
                if (pNewAccess != NULL) pNewAccess->Release();
                hReturn = WBEM_E_INVALID_CLASS;
            } else {
                // Add the object to the refresher.
                if (pHiPerfEnum != NULL) {
                    // then this is an Enum object so add it
                    bRes = pRef->AddEnum (
                                pHiPerfEnum,
                                pClsMap,
                                plId);
                    if (bRes) {    
                        // Return new ID to caller
                        // ==========================
                        hReturn = NO_ERROR;
                    } else {
                        // unable to add enumerator
                        hReturn = GetLastError();
                    }
                } else {
                    // This method will AddRef() the object before returning.
                    // ======================================================
                    bRes = pRef->AddObject(
                                &pNewAccess, 
                                pClsMap,        
                                plId);
                    if (bRes) {    
                        // Return object to the user.
                        // ==========================
                        *ppRefreshable = pNewAccess;
                        hReturn = NO_ERROR;
                    } else {
                        // unable to add object
                        hReturn = GetLastError();
                    }
                }
            }
        }
    }

    VariantClear(&v);    

    return hReturn;
}

//***************************************************************************
//
//  CNt5PerfProvider::CreateRefreshableObject
//
//  Called whenever a user wants to include an object in a refresher.
//     
//  Parameters:
//  <pNamespace>        A pointer to the relevant namespace in CIMOM.
//  <pTemplate>         A pointer to a copy of the object which is to be
//                      added.  This object itself cannot be used, as
//                      it not owned locally.        
//  <pRefresher>        The refresher to which to add the object.
//  <lFlags>            Not used.
//  <pContext>          Not used here.
//  <ppRefreshable>     A pointer to the internal object which was added
//                      to the refresher.
//  <plId>              The Object Id (for identification during removal).        
//
//***************************************************************************        
// ok

HRESULT CNt5PerfProvider::CreateRefreshableObject( 
    /* [in] */ IWbemServices __RPC_FAR *pNamespace,
    /* [in] */ IWbemObjectAccess __RPC_FAR *pTemplate,
    /* [in] */ IWbemRefresher __RPC_FAR *pRefresher,
    /* [in] */ long lFlags,
    /* [in] */ IWbemContext __RPC_FAR *pContext,
    /* [out] */ IWbemObjectAccess __RPC_FAR *__RPC_FAR *ppRefreshable,
    /* [out] */ long __RPC_FAR *plId
    )
{


    HRESULT             hReturn = NO_ERROR;

#ifdef __WBEMSECURITY
    hReturn = CoImpersonateClient(); // make sure we're legit.

    BOOL    fRevert = SUCCEEDED( hReturn );

    // The following error appears to occur when we are in-proc and there is no
    // proxy/stub, so we are effectively impersonating already

    if ( RPC_E_CALL_COMPLETE == hReturn ) {
        hReturn = S_OK;
    } 

    if (S_OK == hReturn) {
        hReturn = CheckImpersonationLevel();
    }
    // Check Registry security here.
    if ((hReturn != S_OK) || (!HasPermission())) {
        // if Impersonation level is incorrect or
        // the caller doesn't have permission to read
        // from the registry, then they cannot continue
        hReturn = WBEM_E_ACCESS_DENIED;
    }

#else
    hReturn = S_OK;
#endif

    if (hReturn == S_OK) {
    
        hReturn = CreateRefresherObject( 
            pNamespace,
            pTemplate,
            pRefresher,
            lFlags,
            pContext,
            NULL,
            NULL,
            ppRefreshable,
            plId);
    }

#ifdef __WBEMSECURITY
    // Revert if we successfuly impersonated the user
    if ( fRevert )
    {
        CoRevertToSelf();
    }
#endif

    return hReturn;
}
    
//***************************************************************************
//
//  CNt5PerfProvider::StopRefreshing
//
//  Called whenever a user wants to remove an object from a refresher.
//     
//  Parameters:
//  <pRefresher>            The refresher object from which we are to 
//                          remove the perf object.
//  <lId>                   The ID of the object.
//  <lFlags>                Not used.
//  
//***************************************************************************        
// ok
        
HRESULT CNt5PerfProvider::StopRefreshing( 
    /* [in] */ IWbemRefresher __RPC_FAR *pRefresher,
    /* [in] */ long lId,
    /* [in] */ long lFlags
    )
{
    CNt5Refresher *pRef;
    BOOL bRes ;
    HRESULT hReturn;

    UNREFERENCED_PARAMETER(lFlags);

#ifdef __WBEMSECURITY
    hReturn = CoImpersonateClient(); // make sure we're legit.

    BOOL    fRevert = SUCCEEDED( hReturn );

    // The following error appears to occur when we are in-proc and there is no
    // proxy/stub, so we are effectively impersonating already

    if ( RPC_E_CALL_COMPLETE == hReturn ) {
        hReturn = S_OK;
    } 

    if (S_OK == hReturn) {
        hReturn = CheckImpersonationLevel();
    }
    // Check Registry security here.
    if ((hReturn != S_OK) || (!HasPermission())) {
        // if Impersonation level is incorrect or
        // the caller doesn't have permission to read
        // from the registry, then they cannot continue
        hReturn = WBEM_E_ACCESS_DENIED;
    }

#else
    hReturn = S_OK;
#endif

    if (hReturn == S_OK) {

        pRef = (CNt5Refresher *) pRefresher;

        bRes = pRef->RemoveObject(lId);
        if (bRes == FALSE) {
            hReturn = WBEM_E_FAILED;
        } else {
            hReturn = WBEM_NO_ERROR;
        }
    }

#ifdef __WBEMSECURITY
    // Revert if we successfuly impersonated the user
    if ( fRevert )
    {
        CoRevertToSelf();
    }
#endif
    
    return hReturn;
}
 
HRESULT CNt5PerfProvider::CreateRefreshableEnum( 
        /* [in] */ IWbemServices __RPC_FAR *pNamespace,
        /* [string][in] */ LPCWSTR wszClass,
        /* [in] */ IWbemRefresher __RPC_FAR *pRefresher,
        /* [in] */ long lFlags,
        /* [in] */ IWbemContext __RPC_FAR *pContext,
        /* [in] */ IWbemHiPerfEnum __RPC_FAR *pHiPerfEnum,
        /* [out] */ long __RPC_FAR *plId)
{
    HRESULT     hReturn;

#ifdef __WBEMSECURITY
    hReturn = CoImpersonateClient(); // make sure we're legit.

    BOOL    fRevert = SUCCEEDED( hReturn );

    // The following error appears to occur when we are in-proc and there is no
    // proxy/stub, so we are effectively impersonating already

    if ( RPC_E_CALL_COMPLETE == hReturn ) {
        hReturn = S_OK;
    } 

    if (S_OK == hReturn) {
        hReturn = CheckImpersonationLevel();
    }
    // Check Registry security here.
    if ((hReturn != S_OK) || (!HasPermission())) {
        // if Impersonation level is incorrect or
        // the caller doesn't have permission to read
        // from the registry, then they cannot continue
        hReturn = WBEM_E_ACCESS_DENIED;
    }

#else
    hReturn = S_OK;
#endif

    if (hReturn == S_OK) {

        hReturn = CreateRefresherObject( 
            pNamespace,
            NULL,
            pRefresher,
            lFlags,
            pContext,
            wszClass,
            pHiPerfEnum,
            NULL,
            plId);
    }

#ifdef __WBEMSECURITY
    // Revert if we successfuly impersonated the user
    if ( fRevert )
    {
        CoRevertToSelf();
    }
#endif
    
    return hReturn;
}
 
HRESULT CNt5PerfProvider::GetObjects( 
        /* [in] */ IWbemServices __RPC_FAR *pNamespace,
        /* [in] */ long lNumObjects,
        /* [size_is][in] */ IWbemObjectAccess __RPC_FAR *__RPC_FAR *apObj,
        /* [in] */ long lFlags,
        /* [in] */ IWbemContext __RPC_FAR *pContext)
{
    DBG_UNREFERENCED_PARAMETER(pNamespace);
    DBG_UNREFERENCED_PARAMETER(lNumObjects);
    DBG_UNREFERENCED_PARAMETER(apObj);
    DBG_UNREFERENCED_PARAMETER(lFlags);
    DBG_UNREFERENCED_PARAMETER(pContext);

    return WBEM_E_METHOD_NOT_IMPLEMENTED;
}  
 
//***************************************************************************
//
//  CNt5PerfProvider::MapClass
//
//  Adds the class map to an internal cache.
//
//  <pClsMap>           The pointer to the map info to add.  This pointer
//                      is acquired by this function and should not be
//                      deleted by the caller.
//
//***************************************************************************
// ok
BOOL CNt5PerfProvider::AddClassMap(
    IN CClassMapInfo *pClsMap
    )
{
    DWORD           dwResult = ERROR_SUCCESS;
    int             i;
    CClassMapInfo   *pTracer;
    int             nNumElements;

    if (m_hClassMapMutex != 0) {
        if (WAIT_OBJECT_0 == WaitForSingleObject(m_hClassMapMutex, cdwClassMapTimeout)) {
            nNumElements = m_aCache.Size();

			// Because of a problem in which perflibs seem to ignore supported methods of updating
			// the perflib names database (lodctr/unlodctr), do a quick initial traversal to ensure
			// that we don't have any duplicate object indices, since this can cause real problems
			// during adding and refreshing, since incorrect indexes can be returned.

            for (i = 0; i < nNumElements; i++) {
                pTracer = (CClassMapInfo *) m_aCache[i];

				// We've got a problem -- we cannot add this class
                if (pClsMap->m_dwObjectId == pTracer->m_dwObjectId )
				{
					ReleaseMutex( m_hClassMapMutex );
                    return FALSE;
                }
            }

            for (i = 0; i < nNumElements; i++) {
                pTracer = (CClassMapInfo *) m_aCache[i];
                if (_wcsicmp(pClsMap->m_pszClassName, pTracer->m_pszClassName) < 0) {
                    m_aCache.InsertAt(i, pClsMap);
                    break;
                }
            }
    
            if (i == nNumElements) {
                // If here, add it to the end.
                // ===========================
                m_aCache.Add(pClsMap);
            }

            // make sure the library is in the list
            dwResult = m_PerfObject.AddClass (pClsMap->m_pClassDef, TRUE);
            ReleaseMutex(m_hClassMapMutex);
        } else {
            dwResult = ERROR_LOCK_FAILED;
        }
    }
    return (dwResult == ERROR_SUCCESS);
}    

//***************************************************************************
//
//  CNt5PerfProvider::FindClassMap
//
//***************************************************************************
// ok
CClassMapInfo *CNt5PerfProvider::FindClassMap(
    LPWSTR pszClassName
    )
{
    int             l = 0;
    int             u;
    int             m;
    CClassMapInfo   *pClsMap;
    CClassMapInfo   *pClsMapReturn = NULL;

    // Binary search the cache.
    // ========================

    if (m_hClassMapMutex != 0) {
        if (WAIT_OBJECT_0 == WaitForSingleObject(m_hClassMapMutex, cdwClassMapTimeout)) {

            u = m_aCache.Size() - 1;

            while (l <= u) {
                m = (l + u) / 2;

                pClsMap = (CClassMapInfo *) m_aCache[m];

                if (pClsMap != NULL) {
                    if (_wcsicmp(pszClassName, pClsMap->m_pszClassName) < 0) {
                        u = m - 1;
                    } else if (_wcsicmp(pszClassName, pClsMap->m_pszClassName) > 0) {
                        l = m + 1;
                    } else {   // Hit!
                        pClsMapReturn = pClsMap;
                        break;
                    }
                } else {
                    break;
                }
            }
            ReleaseMutex(m_hClassMapMutex);
        }
    }
    return pClsMapReturn;
}

//***************************************************************************
//
//  CNt5PerfProvider::MapClass
//
//  Retrieves the requested class and places it in the cache.
//  
//  Parameters:
//      pNs         The namespace which contains the class definition.
//      wsClass     The class name.
//      pCtx        The inbound context object.  Only used for reentrant
//                  calls.
//
//***************************************************************************
// ok
BOOL CNt5PerfProvider::MapClass(
    IN IWbemServices *pNs,
    IN WCHAR *wszClass,
    IN IWbemContext *pCtx    
    )
{
    HRESULT             hRes = 0;
    BOOL                bReturn = FALSE; 
    IWbemClassObject    *pClsDef = 0;
    IWbemQualifierSet   *pQSet = 0;
    VARIANT             v;
    CClassMapInfo       *pMapInfo = 0;

    if (m_hClassMapMutex != 0) {
        if (WAIT_OBJECT_0 == WaitForSingleObject(m_hClassMapMutex, cdwClassMapTimeout)) {
            // See if the class is already in the cache.
            // =========================================
            if (FindClassMap(wszClass) != 0) {
                // already loaded so quit now
                bReturn = TRUE;
            } else {
                // Get the class definition from CIMOM.
                // ====================================
                hRes = pNs->GetObject(CBSTR(wszClass), 0, pCtx, &pClsDef, 0);
                if (hRes == NO_ERROR) {
                    // Verify the class is one of ours by checking
                    // the "provider" qualifier to ensure it matches
                    // the name that we we have for this component.
                    // =============================================
                    hRes = pClsDef->GetQualifierSet(&pQSet);
                    if (hRes == NO_ERROR) {
                        VariantInit(&v);
                        hRes = pQSet->Get(CBSTR(cszProvider), 0, &v, 0);
                        pQSet->Release();
                        
                        if ((hRes == NO_ERROR) && (v.vt == VT_BSTR)) {
                            if (_wcsicmp(V_BSTR(&v), cszProviderName) == 0) {
                                // Get the property handles and mappings to the perf counter ids
                                // by calling the Map() method of CClassMapInfo.
                                // ==============================================================
                                pMapInfo = new CClassMapInfo;
                                if (pMapInfo != NULL) {
                                    if (pMapInfo->Map(pClsDef)) {
                                        // Add it to the cache.
                                        // ====================
                                        bReturn = AddClassMap(pMapInfo);
                                    } else {
                                        // unable to add this to the cache
                                        delete pMapInfo;
                                        //pClsDef->Release(); // this was not AddRef'd so no need to Release
                                    }
                                } else {
                                    // inable to create new class
                                    bReturn = FALSE;
                                }
                            } else {
                                // unable to read provider qualifier so bail
                                // as this isn't a dynamic provider
                                pClsDef->Release(); 
                            }
                        } else {
                            SetLastError ((DWORD)WBEM_E_INVALID_PROVIDER_REGISTRATION);
                        }
                        VariantClear(&v);
                    } else {
                        // unable to get qualifiers
                        pClsDef->Release();
                    }
                } else {
                    // Unable to retrieve the class definition
                }
            } 
            ReleaseMutex(m_hClassMapMutex);
        }
    }
    return bReturn;
}    

//***************************************************************************
//
//  CNt5PerfProvider::HasPermission
//
//  tests to see if the caller has permission to access the functions
//  
//  Parameters:
//      void        N/A
//
//***************************************************************************
// ok
BOOL CNt5PerfProvider::HasPermission (void)
{
    DWORD   dwStatus;
    HKEY    hKeyTest;
    BOOL    bReturn;

    dwStatus = RegOpenKeyExW (
        HKEY_LOCAL_MACHINE,
        (LPCWSTR)L"Software\\Microsoft\\Windows NT\\CurrentVersion\\WbemPerf",
        0, KEY_READ, &hKeyTest);

    if ((dwStatus == ERROR_SUCCESS) || (dwStatus == ERROR_FILE_NOT_FOUND)) {
        bReturn = TRUE;
        if (dwStatus == ERROR_SUCCESS) RegCloseKey (hKeyTest);
    } else  {
        bReturn = FALSE;
    }

    return bReturn;
}

//***************************************************************************
//
//  CNt5PerfProvider::CheckImpersonationLevel
//
//  tests caller's security impersonation level for correct access
//  
//  Only call here if CoImpersonate worked.
//
//  Parameters:
//      void        N/A
//
//***************************************************************************
// ok
HRESULT CNt5PerfProvider::CheckImpersonationLevel (void)
{
    HRESULT hr = WBEM_E_ACCESS_DENIED;
    BOOL    bReturn;

    // Now, let's check the impersonation level.  First, get the thread token
    HANDLE hThreadTok;
    DWORD dwImp, dwBytesReturned;

    bReturn = OpenThreadToken(
        GetCurrentThread(),
        TOKEN_QUERY,
        TRUE,
        &hThreadTok);

    if (!bReturn) {

        // If the CoImpersonate works, but the OpenThreadToken fails, we are running under the
        // process token (either local system, or if we are running with /exe, the rights of
        // the logged in user).  In either case, impersonation rights don't apply.  We have the
        // full rights of that user.

        hr = WBEM_S_NO_ERROR;

    } else {
        // We really do have a thread token, so let's retrieve its level

        bReturn = GetTokenInformation(
            hThreadTok,
            TokenImpersonationLevel,
            &dwImp,
            sizeof(DWORD),
            &dwBytesReturned);

        if (bReturn) {
            // Is the impersonation level Impersonate?
            if ((dwImp == SecurityImpersonation) || (dwImp == SecurityDelegation)) {
                hr = WBEM_S_NO_ERROR;
            } else {
                hr = WBEM_E_ACCESS_DENIED;
            }
        } else {
            hr = WBEM_E_FAILED;
        }

        // Done with this handle
        CloseHandle(hThreadTok);
    }

    return hr;

}