//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1993 - 2002.
//
//  File:       perfCI.cxx
//
//  Contents:   Functions for collecting data to Performance Monitor
//
//  History:    23-March-94     t-joshh    Created
//              10-May-99       dlee       Cleanup
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <perfci.hxx>

#include "prfutil.hxx"
#include "perfobj2.hxx"

extern FILTER_DATA_DEFINITION FILTERDataDefinition;
extern CI_DATA_DEFINITION CIDataDefinition;

extern BOOL g_fPerfmonCounterHackIsProcessDetached;

CReadUserPerfData   * g_pReadUserPerfData = 0;
CReadKernelPerfData * g_pReadKernelPerfData = 0;

WCHAR FILTERPerformanceKeyName[] =
        TEXT("SYSTEM\\CurrentControlSet\\Services\\ContentFilter\\Performance");
WCHAR CIPerformanceKeyName[] =
        TEXT("SYSTEM\\CurrentControlSet\\Services\\ContentIndex\\Performance");

WCHAR FirstCounterKeyName [] = TEXT("First Counter");
WCHAR FirstHelpKeyName [] = TEXT("First Help");

const CI_DATA_DEFINITION CIDataDefinitionFixed = {
    {   sizeof(CI_DATA_DEFINITION)+
        CI_SIZE_OF_COUNTER_BLOCK,     // Total Bytes ( Size of this header, the counter definitions
                                      // and the size of the actual counter data )
        sizeof(CI_DATA_DEFINITION),   // Definition length ( This header and the counter definitions )
        sizeof(PERF_OBJECT_TYPE),     // Header Length ( This header )
        CIOBJECT,                     // Object Name Title Index
        0,                            // Object Name Title
        CIOBJECT,                     // Object Help Title Index
        0,                            // Object Help Title
        PERF_DETAIL_NOVICE,           // Detail Level
        CI_TOTAL_NUM_COUNTERS,        // Number of Counters
        0,                            // Default Counters
        0,                            // Num Instances
        0,                            // Code Page
        {0,0},                        // Perf Time
        {0,0}                         // Perf Freq
    },
    {   sizeof(PERF_COUNTER_DEFINITION),    // Wordlist
        NUM_WORDLIST,
        0,
        NUM_WORDLIST,
        0,
        0,
        PERF_DETAIL_NOVICE,
        PERF_COUNTER_RAWCOUNT,
        sizeof(DWORD),
        NUM_WORDLIST_OFF
    },
    {   sizeof(PERF_COUNTER_DEFINITION),    // PersistentIndex
        NUM_PERSISTENT_INDEX,
        0,
        NUM_PERSISTENT_INDEX,
        0,
        0,
        PERF_DETAIL_NOVICE,
        PERF_COUNTER_RAWCOUNT,
        sizeof(DWORD),
        NUM_PERSISTENT_INDEX_OFF
    },
    {   sizeof(PERF_COUNTER_DEFINITION),    // Index Size
        INDEX_SIZE,
        0,
        INDEX_SIZE,
        0,
        0,
        PERF_DETAIL_NOVICE,
        PERF_COUNTER_RAWCOUNT,
        sizeof(DWORD),
        INDEX_SIZE_OFF
    },
    {   sizeof(PERF_COUNTER_DEFINITION),    // Files to-be-filtered
        FILES_TO_BE_FILTERED,
        0,
        FILES_TO_BE_FILTERED,
        0,
        0,
        PERF_DETAIL_NOVICE,
        PERF_COUNTER_RAWCOUNT,
        sizeof(DWORD),
        FILES_TO_BE_FILTERED_OFF
    },
    {   sizeof(PERF_COUNTER_DEFINITION),    // Number of unique keys
        NUM_UNIQUE_KEY,
        0,
        NUM_UNIQUE_KEY,
        0,
        0,
        PERF_DETAIL_NOVICE,
        PERF_COUNTER_RAWCOUNT,
        sizeof(DWORD),
        NUM_UNIQUE_KEY_OFF
    },
    {   sizeof(PERF_COUNTER_DEFINITION),    // Running Queries
        RUNNING_QUERIES,
        0,
        RUNNING_QUERIES,
        0,
        0,
        PERF_DETAIL_NOVICE,
        PERF_COUNTER_RAWCOUNT,
        sizeof(DWORD),
        RUNNING_QUERIES_OFF
    },
    {   sizeof(PERF_COUNTER_DEFINITION),    // Merge Progress
        MERGE_PROGRESS,
        0,
        MERGE_PROGRESS,
        0,
        0,
        PERF_DETAIL_NOVICE,
        PERF_COUNTER_RAWCOUNT,
        sizeof(DWORD),
        MERGE_PROGRESS_OFF
    },
    {   sizeof(PERF_COUNTER_DEFINITION),    // Number of documents filtered
        DOCUMENTS_FILTERED,
        0,
        DOCUMENTS_FILTERED,
        0,
        0,
        PERF_DETAIL_NOVICE,
        PERF_COUNTER_RAWCOUNT,
        sizeof(DWORD),
        DOCUMENTS_FILTERED_OFF
    },
    {   sizeof(PERF_COUNTER_DEFINITION),    // Number of unique documents
        NUM_DOCUMENTS,
        0,
        NUM_DOCUMENTS,
        0,
        0,
        PERF_DETAIL_NOVICE,
        PERF_COUNTER_RAWCOUNT,
        sizeof(DWORD),
        NUM_DOCUMENTS_OFF
    },
    {   sizeof(PERF_COUNTER_DEFINITION),    // Total queries
        TOTAL_QUERIES,
        0,
        TOTAL_QUERIES,
        0,
        0,
        PERF_DETAIL_NOVICE,
        PERF_COUNTER_RAWCOUNT,
        sizeof(DWORD),
        TOTAL_QUERIES_OFF
    },
    {   sizeof(PERF_COUNTER_DEFINITION),    // Files deferred for filtering (Secondary Q)
        DEFERRED_FILTER_FILES,
        0,
        DEFERRED_FILTER_FILES,
        0,
        0,
        PERF_DETAIL_NOVICE,
        PERF_COUNTER_RAWCOUNT,
        sizeof(DWORD),
        DEFERRED_FILTER_FILES_OFF
    }
};

const FILTER_DATA_DEFINITION FILTERDataDefinitionFixed = {
    {   sizeof(FILTER_DATA_DEFINITION)+
        FILTER_SIZE_OF_COUNTER_BLOCK, // Total Bytes ( Size of this header, the counter definitions
                                      // and the size of the actual counter data )
        sizeof(FILTER_DATA_DEFINITION),   // Definition length ( This header and the counter definitions )
        sizeof(PERF_OBJECT_TYPE),     // Header Length ( This header )
        FILTEROBJECT,                 // Object Name Title Index
        0,                            // Object Name Title
        FILTEROBJECT,                 // Object Help Title Index
        0,                            // Object Help Title
        PERF_DETAIL_NOVICE,           // Detail Level
        FILTER_TOTAL_NUM_COUNTERS,    // Number of Counters
        0,                            // Default Counters
        0,                            // Num Instances
        0,                            // Code Page
        {0,0},                        // Perf Time
        {0,0}                         // Perf Freq
    },
    {   sizeof(PERF_COUNTER_DEFINITION),    // Total Filter Time
        FILTER_TIME_TOTAL,
        0,
        FILTER_TIME_TOTAL,
        0,
        0,
        PERF_DETAIL_NOVICE,
        PERF_COUNTER_RAWCOUNT,
        sizeof(DWORD),
        FILTER_TIME_TOTAL_OFF
    },
    {   sizeof(PERF_COUNTER_DEFINITION),    // Binding Time for one file
        BIND_TIME,
        0,
        BIND_TIME,
        0,
        -1,
        PERF_DETAIL_NOVICE,
        PERF_COUNTER_RAWCOUNT,
        sizeof(DWORD),
        BIND_TIME_OFF
    },
    {   sizeof(PERF_COUNTER_DEFINITION),    // Filter Time
        FILTER_TIME,
        0,
        FILTER_TIME,
        0,
        0,
        PERF_DETAIL_NOVICE,
        PERF_COUNTER_RAWCOUNT,
        sizeof(DWORD),
        FILTER_TIME_OFF
    }
};

//+---------------------------------------------------------------------------
//
//  Function:   CloseKey
//
//  Synopsis:   Close the registry key handle
//
//  Arguments:  [hOpenKey] -- Key.  NULL if closed.
//
//----------------------------------------------------------------------------

inline void CloseKey ( HKEY hOpenKey )
{
   if ( 0 != hOpenKey )
       RegCloseKey (hOpenKey); // close key to registry
}

CStaticMutexSem g_mtxQPerf;   // Serialization during "ReadUser/KernelPerfData"
LONG            g_cKernelRefs = 0;
LONG            g_cUserRefs = 0;
UINT            g_KernSeqNo;  // "CI" sequence number
UINT            g_UserSeqNo;  // "Filter" sequence number

//+---------------------------------------------------------------------------
//
//  Function :  InitializeFILTERPerformanceData
//
//  Purpose :   Build and initialize the performance data structure and create
//              perfCI.ini file
//
//  Arguments :
//              [pInstance] --  dummy variable
//
//  History :   23-March-94     t-joshh     Created
//
//  Note    :   Must start cidaemon before executing this function
//
//----------------------------------------------------------------------------

DWORD InitializeFILTERPerformanceData( LPWSTR pInstance )
{
    CLock lock( g_mtxQPerf );

    g_cUserRefs++;

    if ( g_cUserRefs > 1 )
        return NO_ERROR;

    //
    // Start with a clean slate. Note that in some cases the final Done() may
    // have been called but the dll wasn't unloaded.
    //

    RtlCopyMemory( &FILTERDataDefinition,
                   &FILTERDataDefinitionFixed,
                   sizeof FILTERDataDefinition );

    //
    //  Open the registry which contain the last key's index
    //

    HKEY hKeyPerf = 0;
    LONG status = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                                FILTERPerformanceKeyName,
                                0L, KEY_READ,
                                &hKeyPerf );

    if (status != ERROR_SUCCESS)
    {
        CloseKey( hKeyPerf );
        PerfDebugOut(( DEB_ERROR, "Error in RegOpenKeyEx\n"));
        return status;
    }

    //
    //  Get the index of the first counter
    //

    DWORD dwFirstCounter;
    DWORD size = sizeof dwFirstCounter;
    DWORD type;
    status = RegQueryValueEx( hKeyPerf, FirstCounterKeyName, 0L, &type,
                              (LPBYTE)&dwFirstCounter, &size);

    if (status != ERROR_SUCCESS)
    {
        PerfDebugOut(( DEB_ERROR, "Error in Query First Counter\n"));
        CloseKey( hKeyPerf );
        return status;
    }

    //
    //  Get the index of the first help
    //

    DWORD dwFirstHelp;
    size = sizeof dwFirstHelp;
    status = RegQueryValueEx( hKeyPerf, FirstHelpKeyName,
                              0L, &type, (LPBYTE)&dwFirstHelp, &size );

    if (status != ERROR_SUCCESS)
    {
        PerfDebugOut(( DEB_ERROR, "Error in Query First Help Key\n"));
        CloseKey( hKeyPerf );
        return status;
    }

    //
    //  Update the index of both title and help of each counter
    //

    FILTERDataDefinition.FILTERObjectType.ObjectNameTitleIndex += dwFirstCounter;
    FILTERDataDefinition.FILTERObjectType.ObjectHelpTitleIndex += dwFirstHelp;

    PERF_COUNTER_DEFINITION * pTmp = (PERF_COUNTER_DEFINITION *) ((BYTE *)&FILTERDataDefinition
                                     + sizeof(PERF_OBJECT_TYPE) );

    for ( unsigned i = 0;
          i < FILTERDataDefinition.FILTERObjectType.NumCounters;
          i++)
    {
        pTmp->CounterNameTitleIndex += dwFirstCounter;
        pTmp->CounterHelpTitleIndex += dwFirstHelp;
        pTmp++;
    }

    //
    //  Close the registry key
    //

    CloseKey( hKeyPerf );

    DWORD dwErr = ERROR_SUCCESS;
    BOOL fNoServer = FALSE;

    CTranslateSystemExceptions translate;
    TRY
    {
        if ( 0 == g_pReadUserPerfData )
            g_pReadUserPerfData = new CReadUserPerfData;

        if ( g_pReadUserPerfData->InitForRead() )
            g_UserSeqNo = g_pReadUserPerfData->GetSeqNo();
        else
        {
            fNoServer = TRUE;
            dwErr = ERROR_CAN_NOT_COMPLETE;
        }

        PerfDebugOut((DEB_ITRACE, "InitializeFilterPerformanceData : Done\n" ));
    }
    CATCH( CException, e )
    {
        dwErr = ERROR_CAN_NOT_COMPLETE; // lie here
    }
    END_CATCH;

    if ( NO_ERROR != dwErr )
    {
        delete g_pReadUserPerfData;
        g_pReadUserPerfData = 0;

        // Lie if cisvc isn't running, and Collect() will return no data

        if ( fNoServer )
            dwErr = NO_ERROR;
    }

    return dwErr;
} //InitializeFILTERPerformanceData

//+---------------------------------------------------------------------------
//
//  Function : CollectFILTERPerformanceData
//
//  Purpose :  Collect Performance Data of Content Index to PerfMon
//
//  Arguments:
//    [lpValueName] -- pointer to a wide character string passed by registry
//
//    [lppData] -- IN: pointer to the address of the buffer to receive the
//                 completed PerfDataBlock and subordinate structures. This
//                 routine will append its data to the buffer starting at
//                 the point referenced by *lppData.
//
//                 OUT: points to the first byte after the data structure
//                 added by this routine. This routine updated the value at
//                 lppdata after appending its data.
//
//    [lpcbTotalBytes] -- IN: the address of the DWORD that tells the size in bytes
//                        of the buffer referenced by the lppData argument
//
//                        OUT: the number of bytes added by this routine is written
//                        to the DWORD pointed to by this argument
//
//    [lpNumObjectTypes] -- IN: the address of the DWORD to receive the number of
//                          objects added by this routine
//
//                          OUT: the number of objects added by this routine is written
//                          to the DWORD pointed to by this argument
//
//  History :   23-March-94     t-joshh     Created
//
//  Return : ERROR_MORE_DATA if the size of the input buffer is too small
//           ERROR_SUCCESS   if success
//----------------------------------------------------------------------------

DWORD CollectFILTERPerformanceData( LPWSTR  lpValueName,
                                    LPVOID  *lppData,
                                    LPDWORD lpcbTotalBytes,
                                    LPDWORD lpNumObjectTypes)
{
    //
    // if initial procedure failed, exit
    //
    if ( 0 == g_pReadUserPerfData || !g_pReadUserPerfData->InitOK())
    {
        *lpcbTotalBytes = 0;
        *lpNumObjectTypes = 0;
        return ERROR_SUCCESS; // yes, this is a successful exit
    }

    if ( g_pReadUserPerfData->GetSeqNo() != g_UserSeqNo )
    {
        CLock lock( g_mtxQPerf );

        g_UserSeqNo = g_pReadUserPerfData->GetSeqNo();
        if (!g_pReadUserPerfData->InitForRead())
        {
            *lpcbTotalBytes = 0;
            *lpNumObjectTypes = 0;
            return ERROR_SUCCESS; // yes, this is a successful exit
        }
    }

    //
    // see if this is a foreign (i.e. non-NT) computer data request
    //

    DWORD dwQueryType = GetQueryType (lpValueName);

    if (dwQueryType == QUERY_FOREIGN)
    {
        //
        // this routine does not service requests for data from
        // Non-NT computers
        //
        *lpcbTotalBytes = 0;
        *lpNumObjectTypes = 0;
        return ERROR_SUCCESS;
    }

    //
    // If the caller only wanted some counter, check if we have them
    //

    if (dwQueryType == QUERY_ITEMS)
    {
        if ( !(IsNumberInUnicodeList (
               FILTERDataDefinition.FILTERObjectType.ObjectNameTitleIndex,
                lpValueName)))
        {
            //
            // request received for data object not provided by this routine
            //
            *lpcbTotalBytes = 0;
            *lpNumObjectTypes = 0;
            return ERROR_SUCCESS;
        }
    }

    //
    //  Check whether there is enough space allocated in the lppData
    //

    ULONG ulSpaceNeeded = sizeof(FILTER_DATA_DEFINITION);

    if ( *lpcbTotalBytes < (DWORD) ulSpaceNeeded)
    {
        *lpcbTotalBytes = 0;
        *lpNumObjectTypes = 0;
        return ERROR_MORE_DATA;
    }


    //
    //  Copy the Data Definition to the buffer first
    //

    FILTER_DATA_DEFINITION * pFILTERDataDefinition = (FILTER_DATA_DEFINITION *) *lppData;

    RtlCopyMemory(pFILTERDataDefinition,
           &FILTERDataDefinition,
           sizeof(FILTER_DATA_DEFINITION));

    PERF_INSTANCE_DEFINITION * pFILTERInstanceDefinition = (PERF_INSTANCE_DEFINITION *)( (BYTE *)*lppData
                                                       + sizeof(FILTER_DATA_DEFINITION));

    PerfDebugOut(( DEB_ITRACE, "No. of Instance %d\n", g_pReadUserPerfData->NumberOfInstance() ));

    //
    //  Check how many instance exist (how many OFS drive have cidaemon running on)
    //
    for ( int i = 0;
          i < g_pReadUserPerfData->NumberOfInstance();
          i++ )
    {
        //
        // Check whether there is enough space
        //

        UINT uiLen = wcslen(g_pReadUserPerfData->GetInstanceName(i));
        ulSpaceNeeded += ( sizeof(PERF_INSTANCE_DEFINITION) +
                           FILTER_SIZE_OF_COUNTER_BLOCK +
                           (4+1+uiLen) * sizeof(WCHAR) );

        if ( *lpcbTotalBytes < ulSpaceNeeded )
        {
            *lpcbTotalBytes = 0;
            *lpNumObjectTypes = 0;
            return ERROR_MORE_DATA;
        }

        //
        // Make a copy of the instance name with UNICODE_STRING type
        //

        UNICODE_STRING usName;

        usName.Length = (USHORT) uiLen * sizeof(WCHAR);
        usName.MaximumLength = (USHORT) (uiLen+1)*sizeof(WCHAR);
        usName.Buffer = g_pReadUserPerfData->GetInstanceName(i);

        PERF_COUNTER_BLOCK * pCounterBlock;

        MonBuildInstanceDefinition ( pFILTERInstanceDefinition,
                                     (PVOID *) &pCounterBlock,
                                     0,
                                     0,
                                     PERF_NO_UNIQUE_ID, // use name, not index
                                     &usName );

        pCounterBlock->ByteLength = FILTER_SIZE_OF_COUNTER_BLOCK;

        //
        // Put each counter value into the buffer
        //

        DWORD * pdwCounter = (DWORD *) ((BYTE *)pCounterBlock + sizeof(PERF_COUNTER_BLOCK));

        for ( UINT j = 0 ;
              j < FILTERDataDefinition.FILTERObjectType.NumCounters;
              j++)
        {
            *pdwCounter = g_pReadUserPerfData->GetCounterValue( (int)i, (int)j );
            pdwCounter++;
        }

        //
        // Point to the next location of instance definition
        //

        pFILTERInstanceDefinition = (PERF_INSTANCE_DEFINITION *)pdwCounter;
    }

    *lppData = (LPVOID) pFILTERInstanceDefinition;

    //
    //  Fill in the number of instances
    //

    pFILTERDataDefinition->FILTERObjectType.NumInstances = g_pReadUserPerfData->NumberOfInstance();

    //
    // Number of Object are always 1
    //

    *lpNumObjectTypes = 1;

    //
    //  Fill in the number of bytes copied including object and counter
    //  definition and counter data
    //

    *lpcbTotalBytes = (DWORD) ((BYTE *) *lppData - (BYTE *) pFILTERDataDefinition);

    pFILTERDataDefinition->FILTERObjectType.TotalByteLength = *lpcbTotalBytes;

    PerfDebugOut((DEB_ITRACE, "CollectFilterPerformanceData : Done\n"));

    return ERROR_SUCCESS;
} //CollectFILTERPerformanceData

//+---------------------------------------------------------------------------
//
//  Function :  DoneFILTERPerformanceData
//
//  Purpose :   dummy function
//
//  Argument :  none
//
//  History :   23-March-94     t-joshh     Created
//
//----------------------------------------------------------------------------

DWORD DoneFILTERPerformanceData( void )
{
    CLock lock( g_mtxQPerf );

    //
    // A bug in a perfmon dll makes them call this function after we've
    // been process detached!  They call us in their process detach, which
    // is well after we've been detached and destroyed our heap.
    //

    if ( g_fPerfmonCounterHackIsProcessDetached )
        return ERROR_SUCCESS;

    g_cUserRefs--;

    if ( 0 == g_cUserRefs )
    {
        delete g_pReadUserPerfData;
        g_pReadUserPerfData = 0;
    }

    return ERROR_SUCCESS;
} //DoneFILTERPerformanceData

//+---------------------------------------------------------------------------
//
//  Function :  InitializeCIPerformanceData
//
//  Purpose :   Build and initialize the performance data structure and create
//              perfCI.ini file
//
//  Arguments :
//              [pInstance] --  dummy variable
//
//  History :   23-March-94     t-joshh     Created
//
//----------------------------------------------------------------------------

DWORD InitializeCIPerformanceData( LPWSTR pInstance )
{
    LONG status;
    HKEY hKeyPerf = 0;
    DWORD size;
    DWORD type;
    DWORD dwFirstCounter;
    DWORD dwFirstHelp;

    CLock lock( g_mtxQPerf );

    g_cKernelRefs++;

    if ( g_cKernelRefs > 1 )
        return NO_ERROR;

    //
    // Start with a clean slate. Note that in some cases the final Done() may
    // have been called but the dll wasn't unloaded.
    //

    RtlCopyMemory( &CIDataDefinition,
                   &CIDataDefinitionFixed,
                   sizeof CIDataDefinition );

    //
    //  Open the registry which contain the last key's index
    //
    status = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                           CIPerformanceKeyName,
                           0L, KEY_READ,
                           &hKeyPerf );

    if (status != ERROR_SUCCESS)
    {
        CloseKey( hKeyPerf );
        PerfDebugOut(( DEB_ERROR,  "Error in RegOpenKeyEx\n"));
        return status;
    }

    //
    //  Get the index of the first counter
    //
    size = sizeof (dwFirstCounter);
    status = RegQueryValueEx( hKeyPerf, FirstCounterKeyName, 0L, &type,
                              (LPBYTE)&dwFirstCounter, &size);

    if (status != ERROR_SUCCESS)
    {
        PerfDebugOut(( DEB_ERROR, "Error in Query First Counter\n"));
        CloseKey( hKeyPerf );
        return status;
    }

    //
    //  Get the index of the first help
    //
    size = sizeof (dwFirstHelp);
    status = RegQueryValueEx( hKeyPerf, FirstHelpKeyName,
                              0L, &type, (LPBYTE)&dwFirstHelp, &size );

    if (status != ERROR_SUCCESS)
    {
        PerfDebugOut(( DEB_ERROR, "Error in Query First Help Key\n" ));
        CloseKey( hKeyPerf );
        return status;
    }

    //
    //  Update the index of both title and help of each counter
    //

    CIDataDefinition.CIObjectType.ObjectNameTitleIndex += dwFirstCounter;
    CIDataDefinition.CIObjectType.ObjectHelpTitleIndex += dwFirstHelp;

    PERF_COUNTER_DEFINITION * pTmp = (PERF_COUNTER_DEFINITION *) ((BYTE *)&CIDataDefinition
                                     + sizeof(PERF_OBJECT_TYPE) );

    for ( unsigned i = 0;
          i < CIDataDefinition.CIObjectType.NumCounters;
          i++)
    {
        pTmp->CounterNameTitleIndex += dwFirstCounter;
        pTmp->CounterHelpTitleIndex += dwFirstHelp;
        pTmp += 1;
    }

    //
    //  Close the registry key
    //
    CloseKey( hKeyPerf );

    DWORD dwErr = ERROR_SUCCESS;
    BOOL fNoServer = FALSE;

    CTranslateSystemExceptions translate;
    TRY
    {
        if ( 0 == g_pReadKernelPerfData )
            g_pReadKernelPerfData = new CReadKernelPerfData;

        BOOL fOK = g_pReadKernelPerfData->InitForRead();

        if ( fOK )
            g_KernSeqNo = g_pReadKernelPerfData->GetSeqNo();
        else
        {
            fNoServer = TRUE;
            dwErr = ERROR_CAN_NOT_COMPLETE;
        }

        PerfDebugOut(( DEB_ITRACE, "InitialCIPerformanceData : Finish\n" ));
    }
    CATCH( CException, e )
    {
        dwErr = ERROR_CAN_NOT_COMPLETE; // lie here
    }
    END_CATCH;

    if ( NO_ERROR != dwErr )
    {
        delete g_pReadKernelPerfData;
        g_pReadKernelPerfData = 0;

        // Lie if cisvc isn't running, and Collect() will return no data

        if ( fNoServer )
            dwErr = NO_ERROR;
    }

    return dwErr;
} //InitializeCIPerformanceData

//+---------------------------------------------------------------------------
//
//  Function : CollectCIPerformanceData
//
//  Purpose :  Collect Performance Data of Content Index to PerfMon
//
//  Arguments:
//    [lpValueName] -- pointer to a wide character string passed by registry
//
//    [lppData] -- IN: pointer to the address of the buffer to receive the
//                 completed PerfDataBlock and subordinate structures. This
//                 routine will append its data to the buffer starting at
//                 the point referenced by *lppData.
//
//                 OUT: points to the first byte after the data structure
//                 added by this routine. This routine updated the value at
//                 lppdata after appending its data.
//
//    [lpcbTotalBytes] -- IN: the address of the DWORD that tells the size in bytes
//                        of the buffer referenced by the lppData argument
//
//                        OUT: the number of bytes added by this routine is written
//                        to the DWORD pointed to by this argument
//
//    [lpNumObjectTypes] -- IN: the address of the DWORD to receive the number of
//                          objects added by this routine
//
//                          OUT: the number of objects added by this routine is written
//                          to the DWORD pointed to by this argument
//
//  History :   23-March-94     t-joshh     Created
//
//  Return : ERROR_MORE_DATA if the size of the input buffer is too small
//           ERROR_SUCCESS   if success
//----------------------------------------------------------------------------

DWORD CollectCIPerformanceData( LPWSTR  lpValueName,
                                LPVOID  *lppData,
                                LPDWORD lpcbTotalBytes,
                                LPDWORD lpNumObjectTypes)
{
    ULONG ulSpaceNeeded = 0;
    DWORD dwQueryType;

    //
    // if initial procedure failed, exit
    //
    if ( 0 == g_pReadKernelPerfData || !g_pReadKernelPerfData->InitOK())
    {
        *lpcbTotalBytes = 0;
        *lpNumObjectTypes = 0;
        return ERROR_SUCCESS; // yes, this is a successful exit
    }

    if ( g_pReadKernelPerfData->GetSeqNo() != g_KernSeqNo )
    {
        CLock lock( g_mtxQPerf );

        g_pReadKernelPerfData->InitForRead();

        if (!g_pReadKernelPerfData->InitOK())
        {
            *lpcbTotalBytes = 0;
            *lpNumObjectTypes = 0;
            return ERROR_SUCCESS; // yes, this is a successful exit
        }

        g_KernSeqNo = g_pReadKernelPerfData->GetSeqNo();
    }

    //
    // see if this is a foreign (i.e. non-NT) computer data request
    //
    dwQueryType = GetQueryType (lpValueName);

    if (dwQueryType == QUERY_FOREIGN)
    {
        //
        // this routine does not service requests for data from
        // Non-NT computers
        //
        *lpcbTotalBytes = 0;
        *lpNumObjectTypes = 0;
        return ERROR_SUCCESS;
    }

    //
    // If the caller only wanted some counter, check if we have them
    //
    if (dwQueryType == QUERY_ITEMS)
    {
        if ( !(IsNumberInUnicodeList(
               CIDataDefinition.CIObjectType.ObjectNameTitleIndex,
               lpValueName)) )
        {
            //
            // request received for data object not provided by this routine
            //
            *lpcbTotalBytes = 0;
            *lpNumObjectTypes = 0;
            return ERROR_SUCCESS;
        }
    }

    //
    //  Check whether there is enough space allocated in the lppData
    //

    ulSpaceNeeded = sizeof(CI_DATA_DEFINITION);

    if ( *lpcbTotalBytes < (DWORD) ulSpaceNeeded)
    {
        *lpcbTotalBytes = 0;
        *lpNumObjectTypes = 0;
        return ERROR_MORE_DATA;
    }


    //
    //  Copy the Data Definition to the buffer first
    //
    CI_DATA_DEFINITION * pCIDataDefinition = (CI_DATA_DEFINITION *) *lppData;

    RtlCopyMemory(pCIDataDefinition,
           &CIDataDefinition,
           sizeof(CI_DATA_DEFINITION));

    PERF_INSTANCE_DEFINITION * pCIInstanceDefinition = (PERF_INSTANCE_DEFINITION *)( (BYTE *)*lppData
                                                       + sizeof(CI_DATA_DEFINITION));

    PerfDebugOut(( DEB_ITRACE, "*lppData: %#x\n", *lppData ));
    PerfDebugOut(( DEB_ITRACE, "pCIInstanceDefinition: %#x\n", pCIInstanceDefinition ));

    //
    //  Check how many instance exist (how many OFS drive have cidaemon running on)
    //
    for ( int i = 0;
          i < g_pReadKernelPerfData->NumberOfInstance();
          i++ )
    {
        //
        // Check whether there is enough space
        //

        UINT uiLen = wcslen(g_pReadKernelPerfData->GetInstanceName(i));
        ulSpaceNeeded += ( sizeof(PERF_INSTANCE_DEFINITION) +
                           CI_SIZE_OF_COUNTER_BLOCK +
                           (4+1+uiLen) * sizeof(WCHAR) );

        if ( *lpcbTotalBytes < ulSpaceNeeded )
        {
            *lpcbTotalBytes = (DWORD) 0;
            *lpNumObjectTypes = (DWORD) 0;
            return ERROR_MORE_DATA;
        }

        //
        // Make a copy of the instance name with UNICODE_STRING type
        //

        UNICODE_STRING usName;

        usName.Length = (USHORT) uiLen * sizeof(WCHAR);
        usName.MaximumLength = (USHORT) (uiLen+1)*sizeof(WCHAR);
        usName.Buffer = g_pReadKernelPerfData->GetInstanceName(i);

        PERF_COUNTER_BLOCK * pCounterBlock;

        MonBuildInstanceDefinition ( pCIInstanceDefinition,
                                     (PVOID *) &pCounterBlock,
                                     0,
                                     0,
                                     PERF_NO_UNIQUE_ID, // use name, not index
                                     &usName );

        pCounterBlock->ByteLength = CI_SIZE_OF_COUNTER_BLOCK;

        //
        // Refresh the buffer
        //
        g_pReadKernelPerfData->Refresh( i );

        //
        // Put each counter value into the buffer
        //
        DWORD * pdwCounter = (DWORD *) ((BYTE *)pCounterBlock + sizeof(PERF_COUNTER_BLOCK));

        for ( UINT j = 0 ;
              j < CIDataDefinition.CIObjectType.NumCounters;
              j++)
        {
            *pdwCounter = g_pReadKernelPerfData->GetCounterValue( (int)j );
            pdwCounter++;
        }

        //
        // Point to the next location of instance definition
        //
        pCIInstanceDefinition = (PERF_INSTANCE_DEFINITION *)pdwCounter;
    }

    *lppData = (LPVOID) pCIInstanceDefinition;

    //
    //  Fill in the number of instances
    //
    pCIDataDefinition->CIObjectType.NumInstances = g_pReadKernelPerfData->NumberOfInstance();

    //
    // Number of Object are always 1
    //
    *lpNumObjectTypes = 1;

    //
    //  Fill in the number of bytes copied including object and counter
    //  definition and counter data
    //
    *lpcbTotalBytes = (DWORD) ((BYTE *) *lppData - (BYTE *) pCIDataDefinition);

    pCIDataDefinition->CIObjectType.TotalByteLength = *lpcbTotalBytes;

    PerfDebugOut(( DEB_ITRACE, "CollectCIPerformanceData : Done\n" ));

    Win4Assert( *lpcbTotalBytes == EIGHT_BYTE_MULTIPLE(*lpcbTotalBytes) );
    return ERROR_SUCCESS;
} //CollectCIPerformanceData

//+---------------------------------------------------------------------------
//
//  Function :  DoneCIPerformanceData
//
//  Purpose :   dummy function
//
//  Argument :  none
//
//  History :   23-March-94     t-joshh     Created
//
//----------------------------------------------------------------------------

DWORD DoneCIPerformanceData( void )
{
    CLock lock( g_mtxQPerf );

    //
    // A bug in a perfmon dll makes them call this function after we've
    // been process detached!  They call us in their process detach, which
    // is well after we've been detached and destroyed our heap.
    //

    if ( g_fPerfmonCounterHackIsProcessDetached )
        return ERROR_SUCCESS;

    g_cKernelRefs--;

    if ( 0 == g_cKernelRefs )
    {
        delete g_pReadKernelPerfData;
        g_pReadKernelPerfData = 0;
    }

    return ERROR_SUCCESS;
} //DoneCIPerformanceData