/*++ BUILD Version: 0001    // Increment this if a change has global effects

Copyright (c) 1992-1993   Microsoft Corporation

Module Name:

    counters.c

Abstract:

    This module contains the routines to calculate "DataPoint" values from
    the registry data.

    The algoritms were lifted from RussBls's "Data.C" in winmeter.

    All the math is done in floating point to get the correct results, at
    the sacrifice of efficiency on a 386 with not 387. We can always
    revisit these routines later.

Revision History:

    Bob Watson  11/04/92
        -- modified calculations to use more integer math and "early
            exits" to improve efficiency on slower & non-coprocessor
            machines
--*/

//==========================================================================//
//                                  Includes                                //
//==========================================================================//
#include "perfmon.h"       // perfmon include files
#include "counters.h"      // Exported declarations for this file
#include "perfmsg.h"       // message file definitions

//==========================================================================//
//                                  Constants                               //
//==========================================================================//

#ifdef DBG_COUNTER_DATA
#undef DBG_COUNTER_DATA
#endif
//#define DBG_COUNTER_DATA

#define INVERT             PERF_COUNTER_TIMER_INV
#define NS100_INVERT       PERF_100NSEC_TIMER_INV
#define NS100              PERF_100NSEC_TIMER
#define TIMER_MULTI        PERF_COUNTER_MULTI_TIMER
#define TIMER_MULTI_INVERT PERF_COUNTER_MULTI_TIMER_INV
#define NS100_MULTI        PERF_100NSEC_MULTI_TIMER
#define NS100_MULTI_INVERT PERF_100NSEC_MULTI_TIMER_INV


#define FRACTION 1
#define BULK     1

#define TOO_BIG   (FLOAT)1500000000

//==========================================================================//
//                              Local Functions                             //
//==========================================================================//
#define eLIntToFloat(LI)    (FLOAT)( ((LARGE_INTEGER *)(LI))->QuadPart )

static LPTSTR  cszSpace = TEXT(" ");


FLOAT
eGetTimeInterval(
    IN PLARGE_INTEGER pliCurrentTime,
    IN PLARGE_INTEGER pliPreviousTime,
    IN PLARGE_INTEGER pliFreq
)
/*++

Routine Description:

    Get the difference between the current and previous time counts,
        then divide by the frequency.

Arguments:

    IN pCurrentTime
    IN pPreviousTime
        used to compute the duration of this sample (the time between
        samples

    IN pliFreq
        # of  counts (clock ticks) per second

Return Value:

    Floating point representation of Time Interval (seconds)
--*/
{
    FLOAT   eTimeDifference;
    FLOAT   eFreq;
    FLOAT   eTimeInterval ;

    LARGE_INTEGER liDifference;

    // Get the number of counts that have occured since the last sample

    liDifference.QuadPart = pliCurrentTime->QuadPart -
            pliPreviousTime->QuadPart;

    if (liDifference.QuadPart <= 0) {
        return (FLOAT) 0.0f;
    } else {
        eTimeDifference = eLIntToFloat(&liDifference);

        // Get the counts per second

        eFreq = eLIntToFloat(pliFreq) ;
        if (eFreq <= 0.0f)
           return (FLOAT) 0.0f;

        // Get the time since the last sample.

        eTimeInterval = eTimeDifference / eFreq ;

        return (eTimeInterval) ;
    }
} // eGetTimeInterval

FLOAT
Counter_Counter_Common(
    IN PLINESTRUCT pLineStruct,
    IN INT iType
)
/*++

Routine Description:

    Take the difference between the current and previous counts
        then divide by the time interval

Arguments:

    IN pLineStruct
        Line structure containing data to perform computations on

    IN iType
        Counter Type


Return Value:

    Floating point representation of outcome
--*/
{
    FLOAT   eTimeInterval;
    FLOAT   eDifference;
    FLOAT   eCount ;
    BOOL    bValueDrop = FALSE ;

    LARGE_INTEGER   liDifference;

    if (iType != BULK) {
        pLineStruct->lnaCounterValue[0].HighPart = 0;
    }

    liDifference.QuadPart = pLineStruct->lnaCounterValue[0].QuadPart -
                        pLineStruct->lnaOldCounterValue[0].QuadPart;

    if (liDifference.QuadPart <= 0) {
        if (bReportEvents && (liDifference.QuadPart < 0)) {
            wMessageIndex = 0;
            dwMessageDataBytes = 0;
            szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
            if (pLineStruct->lnInstanceName != NULL){
                if (pLineStruct->lnPINName != NULL) {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                } else {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
            } else {
                szMessageArray[wMessageIndex++] = cszSpace;
                szMessageArray[wMessageIndex++] = cszSpace;
            }
            if (iType != BULK) {
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnaCounterValue[0].LowPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnaOldCounterValue[0].LowPart;
            } else {  // 8 byte counter values
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnaCounterValue[0].LowPart;
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnaCounterValue[0].HighPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnaOldCounterValue[0].LowPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnaOldCounterValue[0].HighPart;
            }
            dwMessageDataBytes *= sizeof(DWORD); // convert index to size
            ReportEvent (hEventLog,
                EVENTLOG_WARNING_TYPE,      // error type
                0,                          // category (not used)
                (DWORD)PERFMON_ERROR_NEGATIVE_VALUE, // event,
                NULL,                       // SID (not used),
                wMessageIndex,              // number of strings
                dwMessageDataBytes,         // sizeof raw data
                szMessageArray,             // message text array
                (LPVOID)&dwMessageData[0]); // raw data
        }
        return (FLOAT) 0.0f;
    } else {
        eTimeInterval = eGetTimeInterval(&pLineStruct->lnNewTime,
                                        &pLineStruct->lnOldTime,
                                        &pLineStruct->lnPerfFreq) ;
        if (eTimeInterval <= 0.0f) {
            if ((eTimeInterval < 0.0f) && bReportEvents) {
                wMessageIndex = 0;
                dwMessageDataBytes = 0;
                szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
                szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
                szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
                if (pLineStruct->lnInstanceName != NULL){
                    if (pLineStruct->lnPINName != NULL) {
                        szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                        szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    } else {
                        szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                        szMessageArray[wMessageIndex++] = cszSpace;
                    }
                } else {
                    szMessageArray[wMessageIndex++] = cszSpace;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnNewTime.LowPart;
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnNewTime.HighPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnOldTime.LowPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnOldTime.HighPart;
                dwMessageDataBytes *= sizeof(DWORD); // convert index to size
                ReportEvent (hEventLog,
                    EVENTLOG_ERROR_TYPE,        // error type
                    0,                          // category (not used)
                    (DWORD)PERFMON_ERROR_NEGATIVE_TIME, // event,
                    NULL,                       // SID (not used),
                    wMessageIndex,             // number of strings
                    dwMessageDataBytes,         // sizeof raw data
                    szMessageArray,             // message text array
                    (LPVOID)&dwMessageData[0]); // raw data
                return (FLOAT) 0.0f;
            }
        } else {
            eDifference = eLIntToFloat (&liDifference);

            eCount         = eDifference / eTimeInterval ;

            if (bValueDrop && (eCount > ((FLOAT)TOO_BIG))) {
                // ignore this bogus data since it is too big for
                // the wrap-around case
                wMessageIndex = 0;
                dwMessageDataBytes = 0;
                szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
                szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
                szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
                if (pLineStruct->lnInstanceName != NULL){
                    if (pLineStruct->lnPINName != NULL) {
                        szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                        szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    } else {
                        szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                        szMessageArray[wMessageIndex++] = cszSpace;
                    }
                } else {
                    szMessageArray[wMessageIndex++] = cszSpace;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    liDifference.LowPart;
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    liDifference.HighPart;
                dwMessageDataBytes *= sizeof(DWORD); // convert index to size
                ReportEvent (hEventLog,
                    EVENTLOG_WARNING_TYPE,        // error type
                    0,                          // category (not used)
                    (DWORD)PERFMON_ERROR_VALUE_OUT_OF_BOUNDS, // event
                    NULL,                       // SID (not used),
                    wMessageIndex,             // number of strings
                    dwMessageDataBytes,         // sizeof raw data
                    szMessageArray,             // message text array
                    (LPVOID)&dwMessageData[0]); // raw data
                eCount = (FLOAT) 0.0f ;
            }
            return(eCount) ;
        }
    }
    return (FLOAT) 0.0f;
} // Counter_Counter_Common


FLOAT
Counter_Average_Timer(
    IN PLINESTRUCT pLineStruct
)
/*++

Routine Description:

    Take the differences between the current and previous times and counts
    divide the time interval by the counts multiply by 10,000,000 (convert
    from 100 nsec to sec)

Arguments:

    IN pLineStruct
        Line structure containing data to perform computations on

Return Value:

    Floating point representation of outcome
--*/
{
    FLOAT   eTimeInterval;
    FLOAT   eCount;

    LARGE_INTEGER    liDifference;

    // Get the current and previous counts.

    pLineStruct->lnaCounterValue[1].HighPart = 0;
    liDifference.QuadPart = pLineStruct->lnaCounterValue[1].QuadPart -
            pLineStruct->lnaOldCounterValue[1].QuadPart;

    if ( liDifference.QuadPart <= 0) {
        if ((liDifference.QuadPart < 0) && bReportEvents) {
            wMessageIndex = 0;
            dwMessageDataBytes = 0;
            szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
            if (pLineStruct->lnInstanceName != NULL){
                if (pLineStruct->lnPINName != NULL) {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                } else {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
            } else {
                szMessageArray[wMessageIndex++] = cszSpace;
                szMessageArray[wMessageIndex++] = cszSpace;
            }
            dwMessageData[dwMessageDataBytes++] =       // recent data
                pLineStruct->lnaCounterValue[1].LowPart;
            dwMessageData[dwMessageDataBytes++] =       // previous data
                pLineStruct->lnaOldCounterValue[1].LowPart;
            dwMessageDataBytes *= sizeof(DWORD); // convert index to size
            ReportEvent (hEventLog,
                EVENTLOG_WARNING_TYPE,        // error type
                0,                          // category (not used)
                (DWORD)PERFMON_ERROR_NEGATIVE_VALUE, // event,
                NULL,                       // SID (not used),
                wMessageIndex,             // number of strings
                dwMessageDataBytes,         // sizeof raw data
                szMessageArray,             // message text array
                (LPVOID)&dwMessageData[0]); // raw data
        }
        return (FLOAT) 0.0f;
    } else {
        // Get the amount of time that has passed since the last sample
        eTimeInterval = eGetTimeInterval(&pLineStruct->lnaCounterValue[0],
                                            &pLineStruct->lnaOldCounterValue[0],
                                            &pLineStruct->lnPerfFreq) ;

        if (eTimeInterval <= 0.0f) { // return 0 if negative time has passed
            if ((eTimeInterval < 0.0f) & bReportEvents) {
                wMessageIndex = 0;
                dwMessageDataBytes = 0;
                szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
                szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
                szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
                if (pLineStruct->lnInstanceName != NULL){
                    if (pLineStruct->lnPINName != NULL) {
                        szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                        szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    } else {
                        szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                        szMessageArray[wMessageIndex++] = cszSpace;
                    }
                } else {
                    szMessageArray[wMessageIndex++] = cszSpace;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnaCounterValue[0].LowPart;
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnaCounterValue[0].HighPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnaOldCounterValue[0].LowPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnaOldCounterValue[0].HighPart;
                dwMessageDataBytes *= sizeof(DWORD); // convert index to size
                ReportEvent (hEventLog,
                    EVENTLOG_ERROR_TYPE,        // error type
                    0,                          // category (not used)
                    (DWORD)PERFMON_ERROR_NEGATIVE_TIME, // event,
                    NULL,                       // SID (not used),
                    wMessageIndex,              // number of strings
                    dwMessageDataBytes,         // sizeof raw data
                    szMessageArray,             // message text array
                    (LPVOID)&dwMessageData[0]); // raw data
            }
            return (0.0f);
        } else {
            // Get the number of counts in this time interval.
            eCount = eTimeInterval / eLIntToFloat (&liDifference);
            return(eCount) ;
        }
    }
} //Counter_Average_Timer



FLOAT
Counter_Average_Bulk(
    IN PLINESTRUCT pLineStruct
)
/*++

Routine Description:

    Take the differences between the current and previous byte counts and
    operation counts divide the bulk count by the operation counts

Arguments:

    IN pLineStruct
        Line structure containing data to perform computations on

Return Value:

    Floating point representation of outcome
--*/
{
    FLOAT   eBulkDelta;
    FLOAT   eDifference;
    FLOAT   eCount;

    LARGE_INTEGER liDifference;
    LARGE_INTEGER liBulkDelta;

    // Get the bulk count increment since the last sample

    liBulkDelta.QuadPart = pLineStruct->lnaCounterValue[0].QuadPart -
            pLineStruct->lnaOldCounterValue[0].QuadPart;

    if (liBulkDelta.QuadPart <= 0) {
        if ((liBulkDelta.QuadPart < 0) && bReportEvents) {
            wMessageIndex = 0;
            dwMessageDataBytes = 0;
            szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
            if (pLineStruct->lnInstanceName != NULL){
                if (pLineStruct->lnPINName != NULL) {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                } else {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
            } else {
                szMessageArray[wMessageIndex++] = cszSpace;
                szMessageArray[wMessageIndex++] = cszSpace;
            }
            dwMessageData[dwMessageDataBytes++] =       // recent data
                pLineStruct->lnaCounterValue[0].LowPart;
            dwMessageData[dwMessageDataBytes++] =       // recent data
                pLineStruct->lnaCounterValue[0].HighPart;
            dwMessageData[dwMessageDataBytes++] =       // previous data
                pLineStruct->lnaOldCounterValue[0].LowPart;
            dwMessageData[dwMessageDataBytes++] =       // previous data
                pLineStruct->lnaOldCounterValue[0].HighPart;
            dwMessageDataBytes *= sizeof(DWORD); // convert index to size
            ReportEvent (hEventLog,
                EVENTLOG_WARNING_TYPE,        // error type
                0,                          // category (not used)
                (DWORD)PERFMON_ERROR_NEGATIVE_VALUE, // event,
                NULL,                       // SID (not used),
                wMessageIndex,             // number of strings
                dwMessageDataBytes,         // sizeof raw data
                szMessageArray,             // message text array
                (LPVOID)&dwMessageData[0]); // raw data
        }
        return (FLOAT) 0.0f;
    } else {
        // Get the current and previous counts.
        pLineStruct->lnaCounterValue[1].HighPart = 0;
        liDifference.QuadPart = pLineStruct->lnaCounterValue[1].QuadPart -
                pLineStruct->lnaOldCounterValue[1].QuadPart;

        // Get the number of counts in this time interval.

        if ( liDifference.QuadPart <= 0) {
            if ((liDifference.QuadPart < 0) && bReportEvents) {
                // Counter value invalid
                wMessageIndex = 0;
                dwMessageDataBytes = 0;
                szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
                szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
                szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
                if (pLineStruct->lnInstanceName != NULL){
                    if (pLineStruct->lnPINName != NULL) {
                        szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                        szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    } else {
                        szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                        szMessageArray[wMessageIndex++] = cszSpace;
                    }
                } else {
                    szMessageArray[wMessageIndex++] = cszSpace;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnaCounterValue[1].LowPart;
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnaCounterValue[1].HighPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnaOldCounterValue[1].LowPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnaOldCounterValue[1].HighPart;
                dwMessageDataBytes *= sizeof(DWORD); // convert index to size
                ReportEvent (hEventLog,
                    EVENTLOG_WARNING_TYPE,        // error type
                    0,                          // category (not used)
                    (DWORD)PERFMON_ERROR_NEGATIVE_VALUE, // event,
                    NULL,                       // SID (not used),
                    wMessageIndex,             // number of strings
                    dwMessageDataBytes,         // sizeof raw data
                    szMessageArray,             // message text array
                    (LPVOID)&dwMessageData[0]); // raw data
            }
            return (FLOAT) 0.0f;
        } else {
            eBulkDelta = eLIntToFloat (&liBulkDelta);
            eDifference = eLIntToFloat (&liDifference);
            eCount = eBulkDelta / eDifference ;

            // Scale the value to up to 1 second

            return(eCount) ;
        }
    }
} // Counter_Average_Bulk



FLOAT
Counter_Timer_Common(
    IN  PLINESTRUCT pLineStruct,
    IN  INT iType
)
/*++

Routine Description:

    Take the difference between the current and previous counts,
        Normalize the count (counts per interval)
        divide by the time interval (count = % of interval)
        if (invert)
            subtract from 1 (the normalized size of an interval)
        multiply by 100 (convert to a percentage)
        this value from 100.

Arguments:

    IN pLineStruct
        Line structure containing data to perform computations on

    IN iType
        Counter Type

Return Value:

    Floating point representation of outcome
--*/
{
    FLOAT   eTimeInterval;
    FLOAT   eDifference;
    FLOAT   eFreq;
    FLOAT   eFraction;
    FLOAT   eMultiBase;
    FLOAT   eCount ;

    LARGE_INTEGER   liTimeInterval;
    LARGE_INTEGER   liDifference;
    LARGE_INTEGER   liFreq;

    // test to see if the previous sample was 0, if so, return 0 since
    // the difference between a "valid" value and 0 will likely exceed
    // 100%. It's better to keep the value at 0 until a valid one can
    // be displayed, rather than display a 100% spike, then a valid value.

    if (pLineStruct->lnaOldCounterValue[0].QuadPart == 0) {
        return (FLOAT)0.0f;
    }

    // Get the amount of time that has passed since the last sample

    if (iType == NS100 ||
        iType == NS100_INVERT ||
        iType == NS100_MULTI ||
        iType == NS100_MULTI_INVERT) {
            liTimeInterval.QuadPart = pLineStruct->lnNewTime100Ns.QuadPart -
                pLineStruct->lnOldTime100Ns.QuadPart;
            eTimeInterval = eLIntToFloat (&liTimeInterval);
    } else {
            liTimeInterval.QuadPart = pLineStruct->lnNewTime.QuadPart -
                             pLineStruct->lnOldTime.QuadPart;
            eTimeInterval = eGetTimeInterval(&pLineStruct->lnNewTime,
                                            &pLineStruct->lnOldTime,
                                            &pLineStruct->lnPerfFreq) ;
    }

    if (liTimeInterval.QuadPart <= 0) {
        if ((liTimeInterval.QuadPart < 0) && bReportEvents) {
            wMessageIndex = 0;
            dwMessageDataBytes = 0;
            szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
            if (pLineStruct->lnInstanceName != NULL){
                if (pLineStruct->lnPINName != NULL) {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                } else {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
            } else {
                szMessageArray[wMessageIndex++] = cszSpace;
                szMessageArray[wMessageIndex++] = cszSpace;
            }
            dwMessageData[dwMessageDataBytes++] =       // recent data
                pLineStruct->lnNewTime.LowPart;
            dwMessageData[dwMessageDataBytes++] =       // recent data
                pLineStruct->lnNewTime.HighPart;
            dwMessageData[dwMessageDataBytes++] =       // previous data
                pLineStruct->lnOldTime.LowPart;
            dwMessageData[dwMessageDataBytes++] =       // previous data
                pLineStruct->lnOldTime.HighPart;
            dwMessageDataBytes *= sizeof(DWORD); // convert index to size
            ReportEvent (hEventLog,
                EVENTLOG_ERROR_TYPE,        // error type
                0,                          // category (not used)
                (DWORD)PERFMON_ERROR_NEGATIVE_TIME, // event,
                NULL,                       // SID (not used),
                wMessageIndex,             // number of strings
                dwMessageDataBytes,         // sizeof raw data
                szMessageArray,             // message text array
                (LPVOID)&dwMessageData[0]); // raw data
        }
       return (FLOAT) 0.0f;
    }
    // Get the current and previous counts.

    liDifference.QuadPart = pLineStruct->lnaCounterValue[0].QuadPart -
            pLineStruct->lnaOldCounterValue[0].QuadPart;

    // Get the number of counts in this time interval.
    // (1, 2, 3 or any number of seconds could have gone by since
    // the last sample)

    eDifference = eLIntToFloat (&liDifference) ;

    if (iType == 0 || iType == INVERT)
    {
        // Get the counts per interval (second)
        liFreq.QuadPart = pLineStruct->lnPerfFreq.QuadPart;

        if ((liFreq.QuadPart <= 0) && bReportEvents) {
            wMessageIndex = 0;
            dwMessageDataBytes = 0;
            szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
            if (pLineStruct->lnInstanceName != NULL){
                if (pLineStruct->lnPINName != NULL) {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                } else {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
            } else {
                szMessageArray[wMessageIndex++] = cszSpace;
                szMessageArray[wMessageIndex++] = cszSpace;
            }
            dwMessageData[dwMessageDataBytes++] = liFreq.LowPart;
            dwMessageData[dwMessageDataBytes++] = liFreq.HighPart;
            dwMessageDataBytes *= sizeof(DWORD); // convert index to size
            ReportEvent (hEventLog,
                EVENTLOG_ERROR_TYPE,        // error type
                0,                          // category (not used)
                (DWORD)PERFMON_ERROR_BAD_FREQUENCY, // event,
                NULL,                       // SID (not used),
                wMessageIndex,             // number of strings
                dwMessageDataBytes,         // sizeof raw data
                szMessageArray,             // message text array
                (LPVOID)&dwMessageData[0]); // raw data
           return (FLOAT) 0.0f;
        } else {
            eFreq = eLIntToFloat(&pLineStruct->lnPerfFreq) ;
        }
        // Calculate the fraction of the counts that are used by whatever
        // we are measuring to convert to units per second

        eFraction = eDifference / eFreq ;
    }
    else
    {
        // for 100 NS counters, the frequency is not included since it
        // would cancel out since both numerator & denominator are returned
        // in 100 NS units.  Non "100 NS" counter types are normalized to
        // seconds.
        eFraction = eDifference ;
        liFreq.QuadPart = 10000000;
    }

    // Calculate the fraction of time used by what were measuring.

    if (eTimeInterval > 0.0)
        eCount = eFraction / eTimeInterval ;
    else
        eCount = 0.0;

    // If this is  an inverted count take care of the inversion.

    if (iType == INVERT || iType == NS100_INVERT)
        eCount = (FLOAT) 1.0 - eCount ;

    if (eCount <= (FLOAT)0.0f) {
        // the threshold for reporting an error is -.1 since some timers
        // have a small margin of error that should never exceed this value
        // but can fall below 0 at times. Typically this error is no more
        // than -0.01
        if ((eCount < (FLOAT)-0.1f) && bReportEvents) {
            wMessageIndex = 0;
            dwMessageDataBytes = 0;
            szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
            if (pLineStruct->lnInstanceName != NULL){
                if (pLineStruct->lnPINName != NULL) {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                } else {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
            } else {
                szMessageArray[wMessageIndex++] = cszSpace;
                szMessageArray[wMessageIndex++] = cszSpace;
            }
            dwMessageData[dwMessageDataBytes++] =       // recent data
                pLineStruct->lnaCounterValue[0].LowPart;
            dwMessageData[dwMessageDataBytes++] =       // recent data
                pLineStruct->lnaCounterValue[0].HighPart;
            dwMessageData[dwMessageDataBytes++] =       // previous data
                pLineStruct->lnaOldCounterValue[0].LowPart;
            dwMessageData[dwMessageDataBytes++] =       // previous data
                pLineStruct->lnaOldCounterValue[0].HighPart;
            dwMessageDataBytes *= sizeof(DWORD); // convert index to size
            ReportEvent (hEventLog,
                EVENTLOG_WARNING_TYPE,        // error type
                0,                          // category (not used)
                (DWORD)PERFMON_ERROR_NEGATIVE_VALUE, // event,
                NULL,                       // SID (not used),
                wMessageIndex,             // number of strings
                dwMessageDataBytes,         // sizeof raw data
                szMessageArray,             // message text array
                (LPVOID)&dwMessageData[0]); // raw data
        }
        // don't just return here, since 0 is possibly a valid value
        eCount = (FLOAT)0.0f;
    }

    // If this is a multi count take care of the base
    if (iType == TIMER_MULTI || iType == NS100_MULTI ||
        iType == TIMER_MULTI_INVERT || iType == NS100_MULTI_INVERT) {

        if (pLineStruct->lnaCounterValue[1].LowPart <= 0) {
#if 0
            if ((pLineStruct->lnaCounterValue[1].LowPart < 0) &&
                 bReportEvents) {
                wMessageIndex = 0;
                dwMessageDataBytes = 0;
                szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
                szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
                szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
                if (pLineStruct->lnInstanceName != NULL){
                    if (pLineStruct->lnPINName != NULL) {
                        szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                        szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    } else {
                        szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                        szMessageArray[wMessageIndex++] = cszSpace;
                    }
                } else {
                    szMessageArray[wMessageIndex++] = cszSpace;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnaCounterValue[1].LowPart;
                dwMessageDataBytes *= sizeof (DWORD);
                ReportEvent (hEventLog,
                    EVENTLOG_ERROR_TYPE,        // error type
                    0,                          // category (not used)
                    (DWORD)PERFMON_ERROR_INVALID_BASE, // event,
                    NULL,                       // SID (not used),
                    wMessageIndex,             // number of strings
                    dwMessageDataBytes,         // sizeof raw data
                    szMessageArray,             // message text array
                    (LPVOID)&dwMessageData[0]); // raw data
            }
#endif
            return (FLOAT) 0.0f;
        } else {
            eMultiBase  = (FLOAT)pLineStruct->lnaCounterValue[1].LowPart ;
        }

        // If this is an inverted multi count take care of the inversion.
        if (iType == TIMER_MULTI_INVERT || iType == NS100_MULTI_INVERT) {
            eCount = (FLOAT) eMultiBase - eCount ;
        }
        eCount /= eMultiBase;
    }

    // Scale the value to up to 100.

    eCount *= 100.0f ;

    if (((eCount > 100.0f) && (bCapPercentsAt100)) &&
        iType != NS100_MULTI &&
        iType != NS100_MULTI_INVERT &&
        iType != TIMER_MULTI &&
        iType != TIMER_MULTI_INVERT) {
        if (bReportEvents) {
            wMessageIndex = 0;
			dwMessageDataBytes = 0;
            szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
            if (pLineStruct->lnInstanceName != NULL){
                if (pLineStruct->lnPINName != NULL) {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                } else {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
            } else {
                szMessageArray[wMessageIndex++] = cszSpace;
                szMessageArray[wMessageIndex++] = cszSpace;
            }
            dwMessageData[dwMessageDataBytes++] =
                pLineStruct->lnaCounterValue[0].LowPart;
            dwMessageData[dwMessageDataBytes++] =
                pLineStruct->lnaCounterValue[0].HighPart;
            dwMessageData[dwMessageDataBytes++] =
                pLineStruct->lnaOldCounterValue[0].LowPart;
            dwMessageData[dwMessageDataBytes++] =
                pLineStruct->lnaOldCounterValue[0].HighPart;
            dwMessageData[dwMessageDataBytes++] = liTimeInterval.LowPart;
            dwMessageData[dwMessageDataBytes++] = liTimeInterval.HighPart;
            dwMessageData[dwMessageDataBytes++] = liFreq.LowPart;
            dwMessageData[dwMessageDataBytes++] = liFreq.HighPart;
            dwMessageDataBytes *= sizeof(DWORD);
            ReportEvent (hEventLog,
                EVENTLOG_WARNING_TYPE,      // error type
                0,                          // category (not used)
                (DWORD)PERFMON_ERROR_VALUE_OUT_OF_RANGE, // event,
                NULL,                       // SID (not used),
                wMessageIndex,              // number of strings
                dwMessageDataBytes,         // sizeof raw data
                szMessageArray,             // message text array
                (LPVOID)&dwMessageData[0]); // raw data
        }
        eCount = 100.0f; // limit value to 100.0%
    }
    return(eCount) ;
} // Counter_Timer_Common


FLOAT
Counter_Raw_Fraction(
    IN PLINESTRUCT pLineStruct,
    IN BOOL        bLargeValue
)
/*++

Routine Description:

    Evaluate a raw fraction (no time, just two values: Numerator and
        Denominator) and multiply by 100 (to make a percentage;

Arguments:

    IN pLineStruct
        Line structure containing data to perform computations on

Return Value:

    Floating point representation of outcome
--*/
{
    FLOAT   eCount ;

    LARGE_INTEGER   liNumerator;

    if (pLineStruct->lnaCounterValue[0].LowPart == 0) {
        // numerator is 0 so just bail here
        return (FLOAT)0.0f;
    } else {
        if (!bLargeValue) {
            liNumerator.QuadPart =
                pLineStruct->lnaCounterValue[0].LowPart * 100L;
        } else {
            liNumerator.QuadPart =
                pLineStruct->lnaCounterValue[0].QuadPart * 100L;
        }
    }

    // now test and compute base (denominator)
    if (pLineStruct->lnaCounterValue[1].QuadPart == 0 ) {
        // invalid value for denominator
        if (bReportEvents) {
            wMessageIndex = 0;
            dwMessageDataBytes = 0;
            szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
            if (pLineStruct->lnInstanceName != NULL){
                if (pLineStruct->lnPINName != NULL) {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                } else {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
            } else {
                szMessageArray[wMessageIndex++] = cszSpace;
                szMessageArray[wMessageIndex++] = cszSpace;
            }
            dwMessageData[dwMessageDataBytes++] =       // recent data
                pLineStruct->lnaCounterValue[0].LowPart;
            dwMessageData[dwMessageDataBytes++] =       // recent data
                pLineStruct->lnaCounterValue[1].LowPart;
                dwMessageDataBytes *= sizeof (DWORD);
            ReportEvent (hEventLog,
                EVENTLOG_ERROR_TYPE,        // error type
                0,                          // category (not used)
                (DWORD)PERFMON_ERROR_INVALID_BASE, // event,
                NULL,                       // SID (not used),
                wMessageIndex,              // number of strings
                dwMessageDataBytes,         // sizeof raw data
                szMessageArray,             // message text array
                (LPVOID)&dwMessageData[0]); // raw data
        }
        return (0.0f);
    } else {
        // if base is OK, then get fraction
        eCount = eLIntToFloat(&liNumerator)  /
                 (FLOAT) pLineStruct->lnaCounterValue[1].QuadPart;
        return(eCount) ;
    }
} // Counter_Raw_Fraction


FLOAT
eElapsedTime(
    PLINESTRUCT pLineStruct,
    INT iType
)
/*++

Routine Description:

    Converts 100NS elapsed time to fractional seconds

Arguments:

    IN pLineStruct
        Line structure containing data to perform computations on

    IN iType
        Unused.

Return Value:

    Floating point representation of elapsed time in seconds
--*/
{
    FLOAT   eSeconds ;

    LARGE_INTEGER   liDifference;

    if (pLineStruct->lnaCounterValue[0].QuadPart <= 0) {
        // no data [start time = 0] so return 0
        // this really doesn't warrant an error message
        return (FLOAT) 0.0f;
    } else {
        // otherwise compute difference between current time and start time
        liDifference.QuadPart =
            pLineStruct->lnNewTime.QuadPart -   // sample time in obj. units
            pLineStruct->lnaCounterValue[0].QuadPart;   // start time in obj. units

        if ((liDifference.QuadPart <= 0)  ||
            (pLineStruct->lnObject.PerfFreq.QuadPart <= 0)) {

            if ((bReportEvents) && ((liDifference.QuadPart < 0)  ||
                (pLineStruct->lnObject.PerfFreq.QuadPart < 0))) {
                wMessageIndex = 0;
                dwMessageDataBytes = 0;
                szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
                szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
                szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
                if (pLineStruct->lnInstanceName != NULL){
                    if (pLineStruct->lnPINName != NULL) {
                        szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                        szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    } else {
                        szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                        szMessageArray[wMessageIndex++] = cszSpace;
                    }
                } else {
                    szMessageArray[wMessageIndex++] = cszSpace;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
                if (liDifference.QuadPart < 0) {
                    dwMessageData[dwMessageDataBytes++] =
                        pLineStruct->lnNewTime.LowPart;
                    dwMessageData[dwMessageDataBytes++] =
                        pLineStruct->lnNewTime.HighPart;
                    dwMessageData[dwMessageDataBytes++] =
                        pLineStruct->lnaCounterValue[0].LowPart;
                    dwMessageData[dwMessageDataBytes++] =
                        pLineStruct->lnaCounterValue[0].HighPart;
                    dwMessageDataBytes *= sizeof(DWORD); // convert index to size
                    ReportEvent (hEventLog,
                        EVENTLOG_ERROR_TYPE,        // error type
                        0,                          // category (not used)
                        (DWORD)PERFMON_ERROR_NEGATIVE_TIME, // event,
                        NULL,                       // SID (not used),
                        wMessageIndex,             // number of strings
                        dwMessageDataBytes,         // sizeof raw data
                        szMessageArray,             // message text array
                        (LPVOID)&dwMessageData[0]); // raw data
                } else {
                    dwMessageData[dwMessageDataBytes++] =
                        pLineStruct->lnObject.PerfFreq.LowPart;
                    dwMessageData[dwMessageDataBytes++] =
                        pLineStruct->lnObject.PerfFreq.HighPart;
                    dwMessageDataBytes *= sizeof(DWORD); // convert index to size
                    ReportEvent (hEventLog,
                        EVENTLOG_ERROR_TYPE,        // error type
                        0,                          // category (not used)
                        (DWORD)PERFMON_ERROR_BAD_FREQUENCY, // event,
                        NULL,                       // SID (not used),
                        wMessageIndex,             // number of strings
                        dwMessageDataBytes,         // sizeof raw data
                        szMessageArray,             // message text array
                        (LPVOID)&dwMessageData[0]); // raw data
                }
            }
            return (FLOAT) 0.0f;
        } else {
            // convert to fractional seconds using object counter
            eSeconds = eLIntToFloat (&liDifference);
            eSeconds /= eLIntToFloat (&pLineStruct->lnObject.PerfFreq);

            return (eSeconds);
        }
    }

} // eElapsedTime


FLOAT
Sample_Common(
    PLINESTRUCT pLineStruct,
    INT iType
)
/*++

Routine Description:

    Divites "Top" differenced by Base Difference

Arguments:

    IN pLineStruct
        Line structure containing data to perform computations on

    IN iType
        Counter Type

Return Value:

    Floating point representation of outcome
--*/
{
    double  eCount ;

    LONG    lDifference;
    LONG    lBaseDifference;

    double  dReturn;

    lDifference = pLineStruct->lnaCounterValue[0].LowPart -
        pLineStruct->lnaOldCounterValue[0].LowPart ;

    if (lDifference <= 0) {
        if ((lDifference < 0) && bReportEvents) {
            wMessageIndex = 0;
            dwMessageDataBytes = 0;
            szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
            if (pLineStruct->lnInstanceName != NULL){
                if (pLineStruct->lnPINName != NULL) {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                } else {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
            } else {
                szMessageArray[wMessageIndex++] = cszSpace;
                szMessageArray[wMessageIndex++] = cszSpace;
            }
            dwMessageData[dwMessageDataBytes++] =       // recent data
                pLineStruct->lnaCounterValue[0].LowPart;
            dwMessageData[dwMessageDataBytes++] =       // previous data
                pLineStruct->lnaOldCounterValue[0].LowPart;
            dwMessageDataBytes *= sizeof(DWORD); // convert index to size
            ReportEvent (hEventLog,
                EVENTLOG_WARNING_TYPE,      // error type
                0,                          // category (not used)
                (DWORD)PERFMON_ERROR_NEGATIVE_VALUE, // event,
                NULL,                       // SID (not used),
                wMessageIndex,              // number of strings
                dwMessageDataBytes,         // sizeof raw data
                szMessageArray,             // message text array
                (LPVOID)&dwMessageData[0]); // raw data
        }
        dReturn = (double) 0.0f;
    } else {
        lBaseDifference = pLineStruct->lnaCounterValue[1].LowPart -
            pLineStruct->lnaOldCounterValue[1].LowPart ;

        if ( lBaseDifference <= 0 ) {
            // invalid value
            if ((lBaseDifference < 0 ) && bReportEvents) {
                wMessageIndex = 0;
                dwMessageDataBytes = 0;
                szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
                szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
                szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
                if (pLineStruct->lnInstanceName != NULL){
                    if (pLineStruct->lnPINName != NULL) {
                        szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                        szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    } else {
                        szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                        szMessageArray[wMessageIndex++] = cszSpace;
                    }
                } else {
                    szMessageArray[wMessageIndex++] = cszSpace;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnaCounterValue[1].LowPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnaOldCounterValue[1].LowPart;
                dwMessageDataBytes *= sizeof(DWORD); // convert index to size
                ReportEvent (hEventLog,
                    EVENTLOG_ERROR_TYPE,        // error type
                    0,                          // category (not used)
                    (DWORD)PERFMON_ERROR_INVALID_BASE, // event,
                    NULL,                       // SID (not used),
                    wMessageIndex,              // number of strings
                    dwMessageDataBytes,         // sizeof raw data
                    szMessageArray,             // message text array
                    (LPVOID)&dwMessageData[0]); // raw data
            }
            dReturn = (0.0f);
        } else {
            eCount = lDifference / lBaseDifference ;

            if (iType == FRACTION) {
                eCount *= 100.0f ;
            }
            dReturn = eCount;
        }
    }

    return (FLOAT)dReturn;
} // Sample_Common


//==========================================================================//
//                             Exported Functions                           //
//==========================================================================//


/*****************************************************************************
 * Counter_Counter - Take the difference between the current and previous
 *                   counts then divide by the time interval
 ****************************************************************************/
#define Counter_Counter(pLineStruct)      \
        Counter_Counter_Common(pLineStruct, 0)

/*****************************************************************************
 * Counter_Bulk    - Take the difference between the current and previous
 *                   counts then divide by the time interval
 *                   Same as a Counter_counter except it uses large_ints
 ****************************************************************************/
#define Counter_Bulk(pLineStruct)         \
        Counter_Counter_Common(pLineStruct, BULK)


/*****************************************************************************
 * Counter_Timer100Ns -
 *
 *      Need to review with RussBl exactly what he is doing here.
 ****************************************************************************/
#define Counter_Timer100Ns(pLineStruct)     \
        Counter_Timer_Common(pLineStruct, NS100)

/*****************************************************************************
 * Counter_Timer100Ns_Inv -
 *
 *      Need to review with RussBl exactly what he is doing here.
 ****************************************************************************/
#define Counter_Timer100Ns_Inv(pLineStruct)     \
        Counter_Timer_Common(pLineStruct, NS100_INVERT)

/*****************************************************************************
 * Counter_Timer_Multi -
 *
 *      Need to review with RussBl exactly what he is doing here.
 ****************************************************************************/
#define Counter_Timer_Multi(pLineStruct)     \
        Counter_Timer_Common(pLineStruct, TIMER_MULTI)

/*****************************************************************************
 * Counter_Timer_Multi_Inv -
 *
 *      Need to review with RussBl exactly what he is doing here.
 ****************************************************************************/
#define Counter_Timer_Multi_Inv(pLineStruct)       \
        Counter_Timer_Common(pLineStruct, TIMER_MULTI_INVERT)

/*****************************************************************************
 * Counter_Timer100Ns_Multi -
 *
 *      Need to review with RussBl exactly what he is doing here.
 ****************************************************************************/
#define Counter_Timer100Ns_Multi(pLineStruct)     \
        Counter_Timer_Common(pLineStruct, NS100_MULTI)

/*****************************************************************************
 * Counter_Timer100Ns_Multi_Inv -
 *
 *      Need to review with RussBl exactly what he is doing here.
 ****************************************************************************/
#define Counter_Timer100Ns_Multi_Inv(pLineStruct)    \
        Counter_Timer_Common(pLineStruct, NS100_MULTI_INVERT)

/*****************************************************************************
 * Counter_Timer - Take the difference between the current and previous
 *                 counts,
 *                 Normalize the count (counts per interval)
 *                 divide by the time interval (count = % of interval)
 *                 multiply by 100 (convert to a percentage)
 *                 this value from 100.
 ****************************************************************************/
#define Counter_Timer(pLineStruct)       \
        Counter_Timer_Common(pLineStruct, 0)

/*****************************************************************************
 * Counter_Timer_Inv - Take the difference between the current and previous
 *                     counts,
 *                     Normalize the count (counts per interval)
 *                     divide by the time interval (count = % of interval)
 *                     subtract from 1 (the normalized size of an interval)
 *                     multiply by 100 (convert to a percentage)
 *                     this value from 100.
 ****************************************************************************/
#define Counter_Timer_Inv(pLineStruct)         \
      Counter_Timer_Common(pLineStruct, INVERT)

/*****************************************************************************
 * Sample_Counter -
 ****************************************************************************/
#define Sample_Counter(pLineStruct)      \
      Sample_Common(pLineStruct, 0)

/*****************************************************************************
 * Sample_Fraction -
 ****************************************************************************/
#define Sample_Fraction(pLineStruct)     \
     Sample_Common(pLineStruct, FRACTION)

/*****************************************************************************
 * Counter_Rawcount - This is just a raw count.
 ****************************************************************************/
#define Counter_Rawcount(pLineStruct)     \
   ((FLOAT) (pLineStruct->lnaCounterValue[0].LowPart))

/*****************************************************************************
 * Counter_Large_Rawcount - This is just a raw count.
 ****************************************************************************/
#define Counter_Large_Rawcount(pLineStruct)     \
   ((FLOAT) eLIntToFloat(&(pLineStruct->lnaCounterValue[0])))

/*****************************************************************************
 * Counter_Elapsed_Time -
 ****************************************************************************/
#define Counter_Elapsed_Time(pLineStruct)         \
    eElapsedTime (pLineStruct, 0)

#define CQLFLAGS_LARGE   ((DWORD)0x00000001)
#define CQLFLAGS_100NS   ((DWORD)0x00000002)

FLOAT Counter_Queuelen(PLINESTRUCT pLineStruct, DWORD dwFlags)
/*++

Routine Description:

    Take the difference between the current and previous counts,
        divide by the time interval (count = decimal fraction of interval)
        Value can exceed 1.00.

Arguments:

    IN pLineStruct
        Line structure containing data to perform computations on

    IN iType
        Counter Type

Return Value:

    Floating point representation of outcome
--*/
{
    FLOAT   eTimeDiff;
    FLOAT   eDifference;
    FLOAT   eCount;

    LONGLONG    llDifference;
    LONGLONG    llTimeDiff;

    // Get the amount of time that has passed since the last sample

    if (dwFlags & CQLFLAGS_100NS) {
        llTimeDiff = pLineStruct->lnNewTime100Ns.QuadPart -
                 pLineStruct->lnOldTime100Ns.QuadPart;
    } else {
        llTimeDiff = pLineStruct->lnNewTime.QuadPart -
                 pLineStruct->lnOldTime.QuadPart;
    }

    if (llTimeDiff <= 0) {
        if ((llTimeDiff < 0 )  && bReportEvents) {
            wMessageIndex = 0;
            dwMessageDataBytes = 0;
            szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
            if (pLineStruct->lnInstanceName != NULL){
                if (pLineStruct->lnPINName != NULL) {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                } else {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
            } else {
                szMessageArray[wMessageIndex++] = cszSpace;
                szMessageArray[wMessageIndex++] = cszSpace;
            }
            dwMessageData[dwMessageDataBytes++] =       // recent data
                pLineStruct->lnNewTime.LowPart;
            dwMessageData[dwMessageDataBytes++] =       // recent data
                pLineStruct->lnNewTime.HighPart;
            dwMessageData[dwMessageDataBytes++] =       // previous data
                pLineStruct->lnOldTime.LowPart;
            dwMessageData[dwMessageDataBytes++] =       // previous data
                pLineStruct->lnOldTime.HighPart;
            dwMessageDataBytes *= sizeof(DWORD); // convert index to size
            ReportEvent (hEventLog,
                EVENTLOG_ERROR_TYPE,        // error type
                0,                          // category (not used)
                (DWORD)PERFMON_ERROR_NEGATIVE_TIME, // event,
                NULL,                       // SID (not used),
                wMessageIndex,             // number of strings
                dwMessageDataBytes,         // sizeof raw data
                szMessageArray,             // message text array
                (LPVOID)&dwMessageData[0]); // raw data
        }
        return (FLOAT)0.0f;
    } else {
        eTimeDiff = (FLOAT)llTimeDiff;
    }

    // Get the current and previous counts.

    if (dwFlags & CQLFLAGS_LARGE) {
        llDifference = pLineStruct->lnaCounterValue[0].QuadPart -
                pLineStruct->lnaOldCounterValue[0].QuadPart;
    } else {
        llDifference = (LONGLONG)(pLineStruct->lnaCounterValue[0].LowPart -
                pLineStruct->lnaOldCounterValue[0].LowPart);
    }

    eDifference = (FLOAT)llDifference;

    if (eDifference < 0.0f) {
        if (bReportEvents) {
            wMessageIndex = 0;
            dwMessageDataBytes = 0;
            szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
            if (pLineStruct->lnInstanceName != NULL){
                if (pLineStruct->lnPINName != NULL) {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                } else {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
            } else {
                szMessageArray[wMessageIndex++] = cszSpace;
                szMessageArray[wMessageIndex++] = cszSpace;
            }
            if (!(dwFlags & CQLFLAGS_LARGE)) {
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnaCounterValue[0].LowPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnaOldCounterValue[0].LowPart;
            } else {  // 8 byte counter values
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnaCounterValue[0].LowPart;
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnaCounterValue[0].HighPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnaOldCounterValue[0].LowPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnaOldCounterValue[0].HighPart;
            }
            dwMessageDataBytes *= sizeof(DWORD); // convert index to size
            ReportEvent (hEventLog,
                EVENTLOG_WARNING_TYPE,      // error type
                0,                          // category (not used)
                (DWORD)PERFMON_ERROR_NEGATIVE_VALUE, // event,
                NULL,                       // SID (not used),
                wMessageIndex,              // number of strings
                dwMessageDataBytes,         // sizeof raw data
                szMessageArray,             // message text array
                (LPVOID)&dwMessageData[0]); // raw data
        }
        eCount = 0.0f ;
    } else {
        eCount = eDifference / eTimeDiff;
    }

    return(eCount) ;

}

FLOAT Counter_Delta(PLINESTRUCT pLineStruct, BOOL bLargeData)
/*++

Routine Description:

    Take the difference between the current and previous counts,

Arguments:

    IN pLineStruct
        Line structure containing data to perform computations on

Return Value:

    Floating point representation of outcome
--*/
{
    FLOAT   eDifference;
    LONGLONG    llDifference;
    ULONGLONG   ullThisValue, ullPrevValue;

    // Get the current and previous counts.

    if (!bLargeData) {
        // then clear the high part of the word
        ullThisValue = (ULONGLONG)pLineStruct->lnaCounterValue[0].LowPart;
        ullPrevValue = (ULONGLONG)pLineStruct->lnaOldCounterValue[0].LowPart;
    } else {
        ullThisValue = (ULONGLONG)pLineStruct->lnaCounterValue[0].QuadPart;
        ullPrevValue = (ULONGLONG)pLineStruct->lnaOldCounterValue[0].QuadPart;
    }

    if (ullThisValue > ullPrevValue) {
        llDifference = (LONGLONG)(ullThisValue - ullPrevValue);
        eDifference = (FLOAT)llDifference;
    } else {
        // the new value is smaller than or equal to the old value
        // and negative numbers are not allowed.
        if ((ullThisValue < ullPrevValue) && bReportEvents) {
            wMessageIndex = 0;
            dwMessageDataBytes = 0;
            szMessageArray[wMessageIndex++] = pLineStruct->lnSystemName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnObjectName;
            szMessageArray[wMessageIndex++] = pLineStruct->lnCounterName;
            if (pLineStruct->lnInstanceName != NULL){
                if (pLineStruct->lnPINName != NULL) {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnPINName;
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                } else {
                    szMessageArray[wMessageIndex++] = pLineStruct->lnInstanceName;
                    szMessageArray[wMessageIndex++] = cszSpace;
                }
            } else {
                szMessageArray[wMessageIndex++] = cszSpace;
                szMessageArray[wMessageIndex++] = cszSpace;
            }
            if (!bLargeData) {
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnaCounterValue[0].LowPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnaOldCounterValue[0].LowPart;
            } else {  // 8 byte counter values
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnaCounterValue[0].LowPart;
                dwMessageData[dwMessageDataBytes++] =       // recent data
                    pLineStruct->lnaCounterValue[0].HighPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnaOldCounterValue[0].LowPart;
                dwMessageData[dwMessageDataBytes++] =       // previous data
                    pLineStruct->lnaOldCounterValue[0].HighPart;
            }
            dwMessageDataBytes *= sizeof(DWORD); // convert index to size
            ReportEvent (hEventLog,
                EVENTLOG_WARNING_TYPE,      // error type
                0,                          // category (not used)
                (DWORD)PERFMON_ERROR_NEGATIVE_VALUE, // event,
                NULL,                       // SID (not used),
                wMessageIndex,              // number of strings
                dwMessageDataBytes,         // sizeof raw data
                szMessageArray,             // message text array
                (LPVOID)&dwMessageData[0]); // raw data
        }
        eDifference = 0.0f;
    }

    return(eDifference) ;

}

/*****************************************************************************
 * Counter_Null - The counters that return nothing go here.
 ****************************************************************************/
#define Counter_Null(pline)        \
        ((FLOAT) 0.0)


FLOAT
CounterEntry (
    PLINESTRUCT pLine
)
{
    FLOAT fReturn;

#ifdef DBG_COUNTER_DATA
    PLINESTRUCT	pLineStruct = pLine;
    WCHAR    szBuffer[512];
    WCHAR    szBuffer2[512];

    swprintf (szBuffer2, L"\nPERFMON:CALC\t%s\\%s",
	pLineStruct->lnSystemName,
	pLineStruct->lnObjectName);
    lstrcpyW (szBuffer, szBuffer2);

    if (pLineStruct->lnInstanceName != NULL){
        if (pLineStruct->lnPINName != NULL) {
	   swprintf (szBuffer2, L"\\(%s/%s)",
		pLineStruct->lnPINName,
		pLineStruct->lnInstanceName);
        } else {
	   swprintf (szBuffer2, L"\\(%s)",
		pLineStruct->lnInstanceName);
        }
        lstrcatW (szBuffer, szBuffer2);
    }

    swprintf (szBuffer2, L"\\%s\t%u\t%I64u\t%I64u\t%I64u",
	pLineStruct->lnCounterName,
	pLineStruct->lnCounterType,
	pLineStruct->lnNewTime100Ns,
	pLineStruct->lnaCounterValue[0].QuadPart,
	pLineStruct->lnaCounterValue[1].QuadPart);
    lstrcatW (szBuffer, szBuffer2);
	
#endif

    switch (pLine->lnCounterType) {
        case  PERF_COUNTER_COUNTER:
            fReturn = Counter_Counter (pLine);
	    break;

        case  PERF_COUNTER_TIMER:
        case  PERF_PRECISION_SYSTEM_TIMER:  // precision value is not used
            fReturn = Counter_Timer (pLine);
	    break;

        case  PERF_COUNTER_QUEUELEN_TYPE:
            fReturn = Counter_Queuelen(pLine, 0);
	    break;

        case  PERF_COUNTER_LARGE_QUEUELEN_TYPE:
            fReturn = Counter_Queuelen(pLine, CQLFLAGS_LARGE);
	    break;

        case  PERF_COUNTER_100NS_QUEUELEN_TYPE:
            fReturn = Counter_Queuelen(pLine, CQLFLAGS_LARGE | CQLFLAGS_100NS);
	    break;

        case  PERF_COUNTER_BULK_COUNT:
            fReturn = Counter_Bulk (pLine);
	    break;

        case  PERF_COUNTER_RAWCOUNT:
        case  PERF_COUNTER_RAWCOUNT_HEX:
            fReturn = Counter_Rawcount(pLine);
	    break;

        case  PERF_COUNTER_LARGE_RAWCOUNT:
        case  PERF_COUNTER_LARGE_RAWCOUNT_HEX:
            fReturn = Counter_Large_Rawcount(pLine);
	    break;

        case  PERF_SAMPLE_FRACTION:
            fReturn = Sample_Fraction(pLine);
	    break;

        case  PERF_SAMPLE_COUNTER:
            fReturn = Sample_Counter (pLine);
	    break;

        case  PERF_COUNTER_TIMER_INV:
            fReturn = Counter_Timer_Inv (pLine);
	    break;

        case  PERF_AVERAGE_TIMER:
            fReturn = Counter_Average_Timer (pLine);
	    break;

        case  PERF_AVERAGE_BULK:
            fReturn = Counter_Average_Bulk (pLine);
	    break;

        case  PERF_100NSEC_TIMER:
        case  PERF_PRECISION_100NS_TIMER:   // precision value is not used
            fReturn = Counter_Timer100Ns (pLine);
	    break;

        case  PERF_100NSEC_TIMER_INV:
            fReturn = Counter_Timer100Ns_Inv (pLine);
	    break;

        case  PERF_COUNTER_MULTI_TIMER:
            fReturn = Counter_Timer_Multi (pLine);
	    break;

        case  PERF_COUNTER_MULTI_TIMER_INV:
            fReturn = Counter_Timer_Multi_Inv (pLine);
	    break;

        case  PERF_100NSEC_MULTI_TIMER:
            fReturn = Counter_Timer100Ns_Multi (pLine);
	    break;

        case  PERF_100NSEC_MULTI_TIMER_INV:
            fReturn = Counter_Timer100Ns_Multi_Inv (pLine);
	    break;

        case  PERF_RAW_FRACTION:
            fReturn = Counter_Raw_Fraction (pLine, FALSE);
	    break;

        case  PERF_LARGE_RAW_FRACTION:
            fReturn = Counter_Raw_Fraction (pLine, TRUE);
	    break;

        case  PERF_ELAPSED_TIME:
            fReturn = Counter_Elapsed_Time (pLine);
	    break;

        case  PERF_COUNTER_DELTA:
            fReturn = Counter_Delta(pLine, FALSE);
	    break;

        case  PERF_COUNTER_LARGE_DELTA:
            fReturn = Counter_Delta(pLine, TRUE);
	    break;

        case  PERF_COUNTER_TEXT:
        case  PERF_COUNTER_NODATA:
        case  PERF_RAW_BASE:
        case  PERF_LARGE_RAW_BASE:
        case  PERF_COUNTER_MULTI_BASE:
//      case  PERF_SAMPLE_BASE:
//      case  PERF_AVERAGE_BASE:
        default:
            fReturn = Counter_Null (pLine);
	    break;
    }

#ifdef DBG_COUNTER_DATA
    swprintf (szBuffer2, L"\t%g", fReturn);
    lstrcatW (szBuffer, szBuffer2);
    OutputDebugStringW(szBuffer);
#endif
	
    return fReturn;
}


BOOL
IsCounterSupported (
    DWORD dwCounterType
)
{
    switch (dwCounterType) {
// supported counters
        case  PERF_COUNTER_COUNTER:
        case  PERF_COUNTER_TIMER:
        case  PERF_COUNTER_QUEUELEN_TYPE:
        case  PERF_COUNTER_LARGE_QUEUELEN_TYPE:
        case  PERF_COUNTER_100NS_QUEUELEN_TYPE:
        case  PERF_COUNTER_BULK_COUNT:
        case  PERF_COUNTER_RAWCOUNT:
        case  PERF_COUNTER_RAWCOUNT_HEX:
        case  PERF_COUNTER_LARGE_RAWCOUNT:
        case  PERF_COUNTER_LARGE_RAWCOUNT_HEX:
        case  PERF_SAMPLE_FRACTION:
        case  PERF_SAMPLE_COUNTER:
        case  PERF_COUNTER_TIMER_INV:
        case  PERF_AVERAGE_TIMER:
        case  PERF_AVERAGE_BULK:
        case  PERF_100NSEC_TIMER:
        case  PERF_100NSEC_TIMER_INV:
        case  PERF_COUNTER_MULTI_TIMER:
        case  PERF_COUNTER_MULTI_TIMER_INV:
        case  PERF_100NSEC_MULTI_TIMER:
        case  PERF_100NSEC_MULTI_TIMER_INV:
        case  PERF_RAW_FRACTION:
        case  PERF_ELAPSED_TIME:
        case  PERF_COUNTER_DELTA:
        case  PERF_COUNTER_LARGE_DELTA:
        case  PERF_PRECISION_100NS_TIMER:
        case  PERF_PRECISION_SYSTEM_TIMER:
        case  PERF_LARGE_RAW_FRACTION:
            return TRUE;

// unsupported counters
        case  PERF_COUNTER_TEXT:
        case  PERF_COUNTER_NODATA:
        case  PERF_RAW_BASE:
        case  PERF_LARGE_RAW_BASE:
//      case  PERF_SAMPLE_BASE:
//      case  PERF_AVERAGE_BASE:
        case  PERF_COUNTER_MULTI_BASE:
//        case  PERF_PRECISION_TIMESTAMP:
        default:
            return FALSE;

    }
}