/*++

Copyright (C) 1996-1999 Microsoft Corporation

Module Name:

    log_wmi.c

Abstract:

    <abstract>

--*/

#include <stdio.h>
#include <stdlib.h>
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <assert.h>
#include <pdh.h>
#include "pdhidef.h"
#include "perfdata.h"
#include "log_bin.h"
#include "log_wmi.h"
#include "strings.h"
#include "pdhmsg.h"
#pragma warning ( disable : 4201 )
#include <initguid.h>
#include <wmistr.h>
#include <ntwmi.h>
#pragma warning ( default : 4201 )

GUID PdhTransactionGuid = // 933f3bb3-943e-490d-9ced-3cbb14c14479
{ 0x933f3bb3, 0x943e, 0x490d, 0x9c, 0xed, 0x3c, 0xbb, 0x14, 0xc1, 0x44, 0x79 };

PDHI_BINARY_LOG_RECORD_HEADER PdhNullCounterHeader = {
        BINLOG_TYPE_DATA_PSEUDO, 48 };
PDH_RAW_COUNTER PdhNullCounter = {
        0,          // CStatus
        { 0, 0 },   // TimeStamp
        0,          // FirstValue
        0,          // SecondValue
        0           // MultiCount
};

#define PDH_MAX_LOGFILES               32
#define PDH_MAX_PATH                 1024
#define PDH_BLOCK_BUFFER_SIZE        8000
#define PDH_WMI_BUFFER_SIZE          64
#define PDH_WMI_BUFFER_SIZE_BYTE     64 * 1024

#define PDH_LOG_HEADER_EVENT         0x20
#define PDH_LOG_DATA_BLOCK_EVENT     0x22
#define PDH_LOG_CATALOG_LIST_EVENT   0x23
#define PDH_LOG_COUNTER_STRING_EVENT 0x24
#define PDH_EVENT_VERSION              60
#define PDH_WMI_MAX_BUFFERS           512
#define PDH_WMI_DEFAULT_BUFFERS        32
#define PDH_WMI_BUFFER_INCREMENT       16

#define PDH_RESOURCE_NAME L"MofResource"

#define TIME_DELTA                 100000

TRACEHANDLE PdhTraceRegistrationHandle = (TRACEHANDLE) 0;

// For PDH WMI event trace logfile output
//
typedef struct _PDH_WMI_LOG_INFO {
    DWORD       dwLogVersion;       // version stamp
    DWORD       dwFlags;            // option flags
} PDH_WMI_LOG_INFO, *PPDH_WMI_LOG_INFO;

typedef struct _PDH_EVENT_TRACE_PROPERTIES {
    EVENT_TRACE_PROPERTIES LoggerInfo;
    WCHAR                  LoggerName[PDH_MAX_PATH];
    WCHAR                  LogFileName[PDH_MAX_PATH];
    GUID                   LogFileGuid;
    LONGLONG               TimeStamp;
    LPWSTR                 MachineList;
    DWORD                  MachineListSize;
    BOOLEAN                bHeaderEvent;
} PDH_EVENT_TRACE_PROPERTIES, * PPDH_EVENT_TRACE_PROPERTIES;

typedef struct _PDH_WMI_EVENT_TRACE {
    EVENT_TRACE_HEADER EventHeader;
    MOF_FIELD          MofFields[4];
} PDH_WMI_EVENT_TRACE, * PPDH_WMI_EVENT_TRACE;

// For PDH WMI event trace logfile input
//
typedef enum _PDH_PROCESS_TRACE_STATE {
    PdhProcessTraceNormal,
    PdhProcessTraceRewind,
    PdhProcessTraceComplete,
    PdhProcessTraceEnd,
    PdhProcessTraceExit
} PDH_PROCESS_TRACE_STATE;

typedef struct _PDH_WMI_PERF_MACHINE {
    LIST_ENTRY Entry;
    LIST_ENTRY  LogObjectList;
    LPWSTR     pszBuffer;
    DWORD      dwLastId;
    DWORD      dwBufSize;
    LPWSTR   * ptrStrAry;
} PDH_WMI_PERF_MACHINE, * PPDH_WMI_PERF_MACHINE;

typedef struct _PDH_WMI_PERF_OBJECT {
    LIST_ENTRY       Entry;
    DWORD            dwObjectId;
    LPWSTR           szObjectName;
    PVOID            ptrBuffer;
} PDH_WMI_PERF_OBJECT, * PPDH_WMI_PERF_OBJECT;

typedef struct _PDH_COUNTER_PATH {
    LIST_ENTRY  Entry;
    ULONGLONG   TimeStamp;
    PVOID       CounterPathBuffer;
    ULONG       CounterPathSize;
    ULONG       CounterCount;
} PDH_COUNTER_PATH, * PPDH_COUNTER_PATH;

typedef struct _PDH_WMI_LOGFILE_INFO {
    GUID        LogFileGuid;
    LIST_ENTRY  CounterPathList;
    LIST_ENTRY  PerfMachineList;
    ULONGLONG   TimeStart;
    ULONGLONG   TimeEnd;
    ULONGLONG   TimeFreq;
    ULONGLONG   TimePrev;
    PVOID       DataBlock;
    ULONG       ValidEntries;
    ULONG       DataBlockSize;
    ULONG       DataBlockAlloc;
    ULONG       ulNumDataBlocks;
    ULONG       ulDataBlocksCopied;
    ULONG       ulCounters;
} PDH_WMI_LOGFILE_INFO, * PPDH_WMI_LOGFILE_INFO;

typedef struct _PDH_LOGGER_CONTEXT {
    PDH_WMI_LOGFILE_INFO    LogInfo[PDH_MAX_LOGFILES];
    TRACEHANDLE             LogFileHandle[PDH_MAX_LOGFILES];
    LPWSTR                  LogFileName[PDH_MAX_LOGFILES];
    ULONGLONG               TimeFreq;
    HANDLE                  hThreadWork;
    HANDLE                  hSyncPDH;
    HANDLE                  hSyncWMI;
    PVOID                   CounterPathBuffer;
    PVOID                   tmpBuffer;
    PDH_PROCESS_TRACE_STATE LoggerState;
    ULONG                   LogFileCount;
    ULONG                   LoggerCount;
    ULONG                   RefCount;
    DWORD                   dwThread;
    BOOLEAN                 bCounterPathChanged;
    BOOLEAN                 bFirstDataBlockRead;
    BOOLEAN                 bDataBlockProcessed;
    BOOLEAN                 bFirstRun;
} PDH_LOGGER_CONTEXT, * PPDH_LOGGER_CONTEXT;

PPDH_LOGGER_CONTEXT ContextTable[PDH_MAX_LOGFILES];
DWORD               ContextCount = 0;
HANDLE              hPdhContextMutex;

typedef struct _PDH_DATA_BLOCK_TRANSFER {
    ULONGLONG  CurrentTime;
    PVOID      pRecord;
    DWORD      dwCurrentSize;
    PDH_STATUS Status;
} PDH_DATA_BLOCK_TRANSFER, * PPDH_DATA_BLOCK_TRANSFER;

PDH_DATA_BLOCK_TRANSFER DataBlockInfo = { 0, NULL, 0, ERROR_SUCCESS };

void
GuidToString(PWCHAR s, LPGUID piid)
{
    swprintf(s, L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
             piid->Data1,
             piid->Data2,
             piid->Data3,
             piid->Data4[0],
             piid->Data4[1],
             piid->Data4[2],
             piid->Data4[3],
             piid->Data4[4],
             piid->Data4[5],
             piid->Data4[6],
             piid->Data4[7]);
}

PPDH_LOGGER_CONTEXT
PdhWmiGetCurrentContext(
    IN DWORD dwLine)
{
    PPDH_LOGGER_CONTEXT CurrentContext = NULL;
    DWORD               dwThread       = GetCurrentThreadId();
    DWORD               i;

    if (WAIT_FOR_AND_LOCK_MUTEX(hPdhContextMutex) == ERROR_SUCCESS) {
        for (i = 0; i < ContextCount; i ++) {
            if (ContextTable[i]->dwThread == dwThread) {
                CurrentContext = ContextTable[i];
                break;
            }
        }
        RELEASE_MUTEX(hPdhContextMutex);
    }

    return CurrentContext;
}

DWORD
PdhWmiGetLoggerContext(
    IN DWORD               dwLine,
    IN PPDH_LOGGER_CONTEXT pLoggerContext)
{
    DWORD i = ContextCount;

    if (pLoggerContext != NULL) {
        if (WAIT_FOR_AND_LOCK_MUTEX(hPdhContextMutex) == ERROR_SUCCESS) {
            for (i = 0; i < ContextCount; i ++) {
                if (ContextTable[i] == pLoggerContext) {
                    break;
                }
            }
            RELEASE_MUTEX(hPdhContextMutex);
        }
    }

    return i;
}

#define GetCurrentContext() PdhWmiGetCurrentContext(__LINE__)
#define GetLoggerContext(X) PdhWmiGetLoggerContext(__LINE__,X)

#if 0
PDH_FUNCTION
PdhiBuildEmptyBlock(
    IN  PPDHI_LOG   pLog,
    IN  LONGLONG    TimeStamp,
    OUT ULONG     * lenMofData,
    OUT PVOID     * ptrMofData)
{
    PDH_STATUS     pdhStatus    = ERROR_SUCCESS;
    PPDHI_COUNTER  pThisCounter;

    PPDHI_BINARY_LOG_RECORD_HEADER pLogCounterBuffer = NULL;
    PPDHI_BINARY_LOG_RECORD_HEADER pThisLogCounter   = NULL;
    PPDH_RAW_COUNTER               pSingleCounter;
    PPDHI_RAW_COUNTER_ITEM_BLOCK   pMultiCounter;
    PPERF_DATA_BLOCK               pObjectCounter;
    DWORD                          dwBufSize         = 0;

    if (pLog->pQuery == NULL || pLog->pQuery->pCounterListHead == NULL) {
        pdhStatus = PDH_NO_DATA;
        goto Cleanup;
    }

    pThisCounter = pLog->pQuery->pCounterListHead;
    do {

        DWORD   dwType  = (pThisCounter->dwFlags & PDHIC_COUNTER_OBJECT)
                        ? (PDHIC_COUNTER_OBJECT)
                        : (  (pThisCounter->dwFlags & PDHIC_MULTI_INSTANCE)
                           ? (PDHIC_MULTI_INSTANCE) : (0));
        DWORD   dwCtrBufSize = sizeof(PDHI_BINARY_LOG_RECORD_HEADER);
        DWORD   dwNewSize;
        int     nItem;

        switch (dwType) {
        case PDHIC_MULTI_INSTANCE:
            dwCtrBufSize += sizeof(PDHI_RAW_COUNTER_ITEM_BLOCK);
            break;

        case PDHIC_COUNTER_OBJECT:
            dwCtrBufSize += sizeof(PERF_DATA_BLOCK);
            break;

        default:
            dwCtrBufSize += sizeof(PDH_RAW_COUNTER);
            break;
        }

        if (pLogCounterBuffer == NULL) {
            dwBufSize = (dwCtrBufSize + sizeof(PDHI_BINARY_LOG_RECORD_HEADER));
            pLogCounterBuffer = G_ALLOC(dwBufSize);

            if (pLogCounterBuffer == NULL) {
                pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE;
                break;
            }
            pThisLogCounter = (PPDHI_BINARY_LOG_RECORD_HEADER)
                              (  ((PUCHAR) pLogCounterBuffer)
                               + sizeof(PDHI_BINARY_LOG_RECORD_HEADER));
        }
        else {
            PPDHI_BINARY_LOG_RECORD_HEADER ptrTemp = pLogCounterBuffer;
            dwNewSize         = (dwBufSize + dwCtrBufSize);
            pLogCounterBuffer = G_REALLOC(ptrTemp, dwNewSize);
            if (pLogCounterBuffer == NULL) {
                G_FREE(ptrTemp);
                pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE;
                break;
            }
            pThisLogCounter   = (PPDHI_BINARY_LOG_RECORD_HEADER)
                                ((LPBYTE) pLogCounterBuffer + dwBufSize);
            dwBufSize        += (USHORT) dwCtrBufSize;
        }

        assert (dwCtrBufSize < 0x00010000);
        pThisLogCounter->dwLength = LOWORD(dwCtrBufSize);

        if (dwType == PDHIC_COUNTER_OBJECT) {
            FILETIME tmpFileTime;
            pThisLogCounter->dwType = BINLOG_TYPE_DATA_LOC_OBJECT;
            pObjectCounter = (PPERF_DATA_BLOCK)
                             ((LPBYTE) pThisLogCounter +
                                       sizeof(PDHI_BINARY_LOG_RECORD_HEADER));
            pObjectCounter->Signature[0]     = L'P';
            pObjectCounter->Signature[1]     = L'E';
            pObjectCounter->Signature[2]     = L'R';
            pObjectCounter->Signature[3]     = L'F';
            pObjectCounter->LittleEndian     = TRUE;
            pObjectCounter->Version          = PERF_DATA_VERSION;
            pObjectCounter->Revision         = PERF_DATA_REVISION;
            pObjectCounter->TotalByteLength  = sizeof(PERF_DATA_BLOCK);
            pObjectCounter->NumObjectTypes   = 1;
            pObjectCounter->DefaultObject    = pThisCounter->plCounterInfo.dwObjectId;
            pObjectCounter->SystemNameLength = 0;
            pObjectCounter->SystemNameOffset = 0;
            pObjectCounter->HeaderLength     = sizeof(PERF_DATA_BLOCK);

            pObjectCounter->PerfTime.QuadPart         = 0;
            pObjectCounter->PerfFreq.QuadPart         = 0;
            pObjectCounter->PerfTime100nSec.QuadPart  = 0;

            tmpFileTime.dwLowDateTime  = LODWORD(TimeStamp);
            tmpFileTime.dwHighDateTime = HIDWORD(TimeStamp);
            FileTimeToSystemTime(& tmpFileTime, & pObjectCounter->SystemTime);
        }
        else if (dwType == PDHIC_MULTI_INSTANCE) {
            pThisLogCounter->dwType = BINLOG_TYPE_DATA_MULTI;
            pMultiCounter = (PPDHI_RAW_COUNTER_ITEM_BLOCK)
                            ((LPBYTE) pThisLogCounter +
                                      sizeof(PDHI_BINARY_LOG_RECORD_HEADER));
            ZeroMemory(pMultiCounter, sizeof(PDHI_RAW_COUNTER_ITEM_BLOCK));
            pMultiCounter->CStatus   = ERROR_SUCCESS;
            pMultiCounter->TimeStamp.dwLowDateTime  = LODWORD(TimeStamp);
            pMultiCounter->TimeStamp.dwHighDateTime = HIDWORD(TimeStamp);
        }
        else {
            pThisLogCounter->dwType = BINLOG_TYPE_DATA_SINGLE;
            pSingleCounter = (PPDH_RAW_COUNTER)
                             ((LPBYTE) pThisLogCounter +
                                       sizeof(PDHI_BINARY_LOG_RECORD_HEADER));
            ZeroMemory(pSingleCounter, sizeof(PDH_RAW_COUNTER));
            pSingleCounter->CStatus   = ERROR_SUCCESS;
            pSingleCounter->TimeStamp.dwLowDateTime  = LODWORD(TimeStamp);
            pSingleCounter->TimeStamp.dwHighDateTime = HIDWORD(TimeStamp);
        }

        pThisCounter = pThisCounter->next.flink; // go to next in list

    } while (pThisCounter != pLog->pQuery->pCounterListHead);

    if (pLogCounterBuffer) {
        pLogCounterBuffer->dwType   = BINLOG_TYPE_DATA;
        pLogCounterBuffer->dwLength = dwBufSize;
    }

Cleanup:
    * lenMofData = dwBufSize;
    * ptrMofData = pLogCounterBuffer;
    return pdhStatus;
}
#endif

PDH_FUNCTION
PdhWmiTraceEvent(
    IN PPDHI_LOG pLog,
    IN ULONG     PdhEventType,
    IN ULONGLONG TimeStamp,
    IN ULONG     lenMofData,
    IN PVOID     ptrMofData);

PDH_FUNCTION
PdhiBuildPerfCounterList(
    IN  PPDHI_LOG  pLog,
    IN  PPDH_EVENT_TRACE_PROPERTIES LoggerInfo,
    IN  PPDHI_COUNTER               pCounter)
{
    PDH_STATUS      Status    = ERROR_SUCCESS;
    PPERF_MACHINE   pMachine  = pCounter->pQMachine->pMachine;
    LPWSTR        * pString   = pMachine->szPerfStrings;
    BYTE          * pType     = pMachine->typePerfStrings;
    DWORD           dwLastId  = pMachine->dwLastPerfString;
    DWORD           i;
    DWORD           dwBufSize = 0;
    LPWSTR          pszBuffer = NULL;
    LPWSTR          pszCurrent;
    BOOLEAN         bNewEvent = TRUE;

    if (LoggerInfo->MachineList != NULL) {
        pszCurrent = LoggerInfo->MachineList;
        while (* pszCurrent != L'\0') {
            if (lstrcmpiW(pszCurrent, pMachine->szName) == 0) {
                // Machine Perf Counter List already there, bail out.
                //
                goto Cleanup;
            }
            pszCurrent += (lstrlenW(pszCurrent) + 1);
        }
    }

    if (LoggerInfo->MachineList != NULL) {
        LPWSTR pszTemp = LoggerInfo->MachineList;
        LoggerInfo->MachineList = G_REALLOC( pszTemp,
                sizeof(WCHAR) * (LoggerInfo->MachineListSize + (lstrlenW(pMachine->szName) + 1)));
        if (LoggerInfo->MachineList != NULL) {
            pszCurrent = LoggerInfo->MachineList
                       + (LoggerInfo->MachineListSize - 1);
            LoggerInfo->MachineListSize += (lstrlenW(pMachine->szName) + 1);
        }
        else {
            G_FREE(pszTemp);
        }
    }
    else {
        LoggerInfo->MachineListSize = (lstrlenW(pMachine->szName) + 2);
        LoggerInfo->MachineList =
                        G_ALLOC(sizeof(WCHAR) * LoggerInfo->MachineListSize);
        if (LoggerInfo->MachineList != NULL) {
            pszCurrent = LoggerInfo->MachineList;
        }
    }
    if (LoggerInfo->MachineList == NULL) {
        Status = PDH_MEMORY_ALLOCATION_FAILURE;
        goto Cleanup;
    }

    lstrcpyW(pszCurrent, pMachine->szName);

    dwBufSize = sizeof(WCHAR) * (lstrlenW(pMachine->szName) + 1);
    pszBuffer = (LPWSTR) G_ALLOC(PDH_BLOCK_BUFFER_SIZE);
    if (pszBuffer == NULL) {
        Status = PDH_MEMORY_ALLOCATION_FAILURE;
        goto Cleanup;
    }

    __try {
        WCHAR szIndex[10];
        DWORD dwNewSize = 0;

        for (i = 0; i < dwLastId; i ++) {

            if (pString[i] == NULL || pType[i] != STR_COUNTER) continue;
            ZeroMemory(szIndex, sizeof(WCHAR) * 10);
            _ltow(i, szIndex, 10);
            dwNewSize = sizeof(WCHAR) * (  lstrlenW(szIndex) + 1
                                        + lstrlenW(pString[i]) + 1);

            if (dwBufSize + dwNewSize >= PDH_BLOCK_BUFFER_SIZE) {
                Status = PdhWmiTraceEvent(pLog,
                                          PDH_LOG_COUNTER_STRING_EVENT,
                                          LoggerInfo->TimeStamp - 1,
                                          dwBufSize,
                                          pszBuffer);
                bNewEvent = TRUE;
            }
            if (bNewEvent) {
                ZeroMemory(pszBuffer, PDH_BLOCK_BUFFER_SIZE);
                lstrcpyW(pszBuffer, pMachine->szName);
                pszCurrent  = pszBuffer + (lstrlenW(pszBuffer) + 1);
                dwBufSize   = sizeof(WCHAR) * (lstrlenW(pMachine->szName) + 1);
                bNewEvent   = FALSE;
            }
            lstrcpyW(pszCurrent, szIndex);
            pszCurrent += (lstrlenW(pszCurrent) + 1);
            lstrcpyW(pszCurrent, pString[i]);
            pszCurrent += (lstrlenW(pszCurrent) + 1);
            dwBufSize  += dwNewSize;
        }
        Status = PdhWmiTraceEvent(pLog,
                                  PDH_LOG_COUNTER_STRING_EVENT,
                                  LoggerInfo->TimeStamp - 1,
                                  dwBufSize,
                                  pszBuffer);
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        Status = GetExceptionCode();
    }

Cleanup:
    if (pszBuffer != NULL) {
        G_FREE(pszBuffer);
    }
    return Status;
}

PDH_FUNCTION
PdhiBuildLogHeaderBlock(
    IN  PPDHI_LOG   pLog,
    IN  PPDH_EVENT_TRACE_PROPERTIES LoggerInfo)
{
    PDH_STATUS         Status       = ERROR_SUCCESS;
    PVOID              ptrOutBuffer = NULL;
    PVOID              ptrTemp;
    PPDH_WMI_LOG_INFO  pLogInfo     = NULL;
    DWORD              SizeAlloc    = sizeof(PDH_WMI_LOG_INFO);
    DWORD              NewSizeAlloc;

    PPDHI_COUNTER          pThisCounter;
    PPDHI_COUNTER          pThisCounterHead;
    DWORD                  dwPathBufSize;
    PPDHI_LOG_COUNTER_PATH pLogCounter;
    LONG                   lBufOffset;
    PWCHAR                 pBufferBase;

    ptrOutBuffer = G_ALLOC(sizeof(PDH_WMI_LOG_INFO));
    if (ptrOutBuffer == NULL) {
        Status = PDH_MEMORY_ALLOCATION_FAILURE;
        goto Cleanup;
    }
    pLogInfo = (PPDH_WMI_LOG_INFO) ptrOutBuffer;
    pLogInfo->dwLogVersion = WMILOG_VERSION;
    pLogInfo->dwFlags      = pLog->dwLogFormat;

    pThisCounter = pThisCounterHead = (pLog->pQuery)
                                    ? (pLog->pQuery->pCounterListHead)
                                    : (NULL);
    if (pThisCounter != NULL) {
        do {
            dwPathBufSize = sizeof(PDHI_LOG_COUNTER_PATH) + sizeof(DWORD);
            if (pThisCounter->pCounterPath->szMachineName != NULL) {
                dwPathBufSize += sizeof(WCHAR) * (1 +
                        lstrlenW(pThisCounter->pCounterPath->szMachineName));
            }
            if (pThisCounter->pCounterPath->szObjectName != NULL) {
                dwPathBufSize += sizeof(WCHAR) * (1 +
                        lstrlenW(pThisCounter->pCounterPath->szObjectName));
            }
            if (pThisCounter->dwFlags & PDHIC_COUNTER_OBJECT) {
                Status = PdhiBuildPerfCounterList(
                                 pLog, LoggerInfo, pThisCounter);
                if (Status != ERROR_SUCCESS) {
                    goto Cleanup;
                }
            }
            if (pThisCounter->pCounterPath->szInstanceName != NULL) {
                dwPathBufSize += sizeof(WCHAR) * (1 +
                    lstrlenW(pThisCounter->pCounterPath->szInstanceName));
            }
            if (pThisCounter->pCounterPath->szParentName != NULL) {
                dwPathBufSize += sizeof(WCHAR) * (1 +
                    lstrlenW(pThisCounter->pCounterPath->szParentName));
            }
            if (pThisCounter->pCounterPath->szCounterName != NULL) {
                dwPathBufSize += sizeof(WCHAR) * (1 +
                    lstrlenW(pThisCounter->pCounterPath->szCounterName));
            }
            dwPathBufSize = QWORD_MULTIPLE(dwPathBufSize);

            NewSizeAlloc  = SizeAlloc + dwPathBufSize;
            ptrTemp       = ptrOutBuffer;
            ptrOutBuffer  = G_REALLOC(ptrTemp, NewSizeAlloc);
            if (ptrOutBuffer == NULL) {
                G_FREE(ptrTemp);
                Status = PDH_MEMORY_ALLOCATION_FAILURE;
                goto Cleanup;
            }
            pLogCounter   = (PPDHI_LOG_COUNTER_PATH)
                            (((LPBYTE) ptrOutBuffer) + SizeAlloc);
            SizeAlloc     = NewSizeAlloc;

            RtlZeroMemory(pLogCounter, dwPathBufSize);
            pLogCounter->dwLength      = dwPathBufSize;
            pLogCounter->dwFlags       = pThisCounter->dwFlags;
            pLogCounter->dwUserData    = pThisCounter->dwUserData;
            if (pThisCounter->dwFlags & PDHIC_COUNTER_OBJECT) {
                pLogCounter->dwCounterType = PDHIC_COUNTER_OBJECT;
                pLogCounter->lDefaultScale = 0;
                pLogCounter->dwIndex       = 0;
            }
            else {
                pLogCounter->dwCounterType =
                                pThisCounter->plCounterInfo.dwCounterType;
                pLogCounter->lDefaultScale =
                                pThisCounter->plCounterInfo.lDefaultScale;
                pLogCounter->dwIndex       =
                                pThisCounter->pCounterPath->dwIndex;
            }
            pLogCounter->llTimeBase    = pThisCounter->TimeBase;

            // if this is a wild card path, then move the strings up
            // 1 dword in the buffer allowing the first DWORD of the
            // the buffer to contain the offset into the catalog
            // of the instances found in this log file. This list
            // will be built after the log is closed.

            lBufOffset = 0; // in WORDS (not bytes)
            if (pThisCounter->pCounterPath->szInstanceName != NULL) {
                if (* pThisCounter->pCounterPath->szInstanceName == SPLAT_L) {
                    lBufOffset = sizeof(DWORD);
                }
            }
#if DBG
            if (lBufOffset > 0)
                * (LPDWORD) (& pLogCounter->Buffer[0]) = 0x12345678;
#endif

            if (pThisCounter->pCounterPath->szMachineName != NULL) {
                pLogCounter->lMachineNameOffset = lBufOffset;
                pBufferBase = (PWCHAR)
                        ((LPBYTE) & pLogCounter->Buffer[0] + lBufOffset);
                lstrcpyW(pBufferBase,
                         pThisCounter->pCounterPath->szMachineName);
                lBufOffset += sizeof(WCHAR) * (1 +
                        lstrlenW(pThisCounter->pCounterPath->szMachineName));
            }
            else {
                pLogCounter->lMachineNameOffset = (LONG) -1;
            }
            if (pThisCounter->pCounterPath->szObjectName != NULL) {
                pLogCounter->lObjectNameOffset = lBufOffset;
                pBufferBase = (PWCHAR)
                        ((LPBYTE) & pLogCounter->Buffer[0] + lBufOffset);
                lstrcpyW(pBufferBase,
                         pThisCounter->pCounterPath->szObjectName);
                lBufOffset += sizeof(WCHAR) * (1 +
                        lstrlenW(pThisCounter->pCounterPath->szObjectName));
            }
            else {
                pLogCounter->lObjectNameOffset = (LONG) -1;
            }
            if (pThisCounter->pCounterPath->szInstanceName != NULL) {
                pLogCounter->lInstanceOffset = lBufOffset;
                pBufferBase = (PWCHAR)
                        ((LPBYTE) & pLogCounter->Buffer[0] + lBufOffset);
                lstrcpyW(pBufferBase,
                         pThisCounter->pCounterPath->szInstanceName);
                lBufOffset += sizeof(WCHAR) * (1 +
                        lstrlenW(pThisCounter->pCounterPath->szInstanceName));
            }
            else {
                pLogCounter->lInstanceOffset = (LONG) -1;
            }
            if (pThisCounter->pCounterPath->szParentName != NULL) {
                pLogCounter->lParentOffset = lBufOffset;
                pBufferBase = (PWCHAR)
                        ((LPBYTE) & pLogCounter->Buffer[0] + lBufOffset);
                lstrcpyW(pBufferBase,
                         pThisCounter->pCounterPath->szParentName);
                lBufOffset += sizeof(WCHAR) * (1 +
                        lstrlenW(pThisCounter->pCounterPath->szParentName));
            }
            else {
                pLogCounter->lParentOffset = (LONG) -1;
            }

            if (pThisCounter->pCounterPath->szCounterName != NULL) {
                pLogCounter->lCounterOffset = lBufOffset;
                pBufferBase = (PWCHAR)
                        ((LPBYTE) & pLogCounter->Buffer[0] + lBufOffset);
                lstrcpyW(pBufferBase,
                         pThisCounter->pCounterPath->szCounterName);
                lBufOffset += sizeof(WCHAR) * (1 +
                        lstrlenW(pThisCounter->pCounterPath->szCounterName));
            }
            else {
                pLogCounter->lCounterOffset = (LONG) -1;
            }

            pThisCounter = pThisCounter->next.flink;

        } while (pThisCounter != pThisCounterHead);
    }

    if (Status == ERROR_SUCCESS) {
        Status = PdhWmiTraceEvent(pLog,
                                  PDH_LOG_HEADER_EVENT,
                                  LoggerInfo->TimeStamp - 1,
                                  SizeAlloc,
                                  ptrOutBuffer);
    }

Cleanup:
    if (ptrOutBuffer) {
        G_FREE(ptrOutBuffer);
    }
    return Status;
}

PDH_FUNCTION
PdhWmiTraceEvent(
    IN PPDHI_LOG pLog,
    IN ULONG     PdhEventType,
    IN ULONGLONG TimeStamp,
    IN ULONG     lenMofData,
    IN PVOID     ptrMofData)
{
    PDH_STATUS Status = PDH_INVALID_HANDLE;

    if (   (pLog->dwLogFormat & PDH_LOG_WRITE_ACCESS)
        && ((TRACEHANDLE) pLog->hLogFileHandle != (TRACEHANDLE) 0)) {

        PDH_WMI_EVENT_TRACE         Wnode;
        PPDH_EVENT_TRACE_PROPERTIES LoggerInfo =
                (PPDH_EVENT_TRACE_PROPERTIES) pLog->lpMappedFileBase;
        DWORD dwNumEvents     = (lenMofData / PDH_BLOCK_BUFFER_SIZE);
        DWORD dwCurrentEvent  = 1;
        PVOID ptrCurrentMof   = ptrMofData;
        BOOL  bIncreaseBuffer = TRUE;

        if (lenMofData > PDH_BLOCK_BUFFER_SIZE * dwNumEvents) {
            dwNumEvents ++;
        }

        for (Status = ERROR_SUCCESS, dwCurrentEvent = 1;
             (Status == ERROR_SUCCESS) && (dwCurrentEvent <= dwNumEvents);
             dwCurrentEvent ++) {
            USHORT sMofLen = (lenMofData > PDH_BLOCK_BUFFER_SIZE)
                           ? (PDH_BLOCK_BUFFER_SIZE) : ((USHORT) lenMofData);
            RtlZeroMemory(& Wnode, sizeof(PDH_WMI_EVENT_TRACE));
            Wnode.EventHeader.Size       = sizeof(PDH_WMI_EVENT_TRACE);
            Wnode.EventHeader.Class.Type = (UCHAR) PdhEventType;
            Wnode.EventHeader.Class.Version = PDH_EVENT_VERSION;
            Wnode.EventHeader.GuidPtr    = (ULONGLONG) & PdhTransactionGuid;
            Wnode.EventHeader.Flags      = WNODE_FLAG_TRACED_GUID
                                         | WNODE_FLAG_USE_GUID_PTR
                                         | WNODE_FLAG_USE_MOF_PTR
                                         | WNODE_FLAG_USE_TIMESTAMP;
            if (   PdhEventType == PDH_LOG_HEADER_EVENT
                || PdhEventType == PDH_LOG_COUNTER_STRING_EVENT) {
                Wnode.EventHeader.Flags  |= WNODE_FLAG_PERSIST_EVENT;
            }
            Wnode.EventHeader.TimeStamp.QuadPart = TimeStamp;
            Wnode.MofFields[0].Length  = sizeof(GUID);
            Wnode.MofFields[0].DataPtr = (ULONGLONG) & LoggerInfo->LogFileGuid;
            Wnode.MofFields[1].Length  = sizeof(DWORD);
            Wnode.MofFields[1].DataPtr = (ULONGLONG) & dwCurrentEvent;
            Wnode.MofFields[2].Length  = sizeof(DWORD);
            Wnode.MofFields[2].DataPtr = (ULONGLONG) & dwNumEvents;
            Wnode.MofFields[3].Length  = sMofLen;
            Wnode.MofFields[3].DataPtr = (ULONGLONG) ptrCurrentMof;

            bIncreaseBuffer = TRUE;
            while (bIncreaseBuffer == TRUE) {
                Status = TraceEvent((TRACEHANDLE) pLog->hLogFileHandle,
                                    (PEVENT_TRACE_HEADER) & Wnode);
                if (Status == ERROR_NOT_ENOUGH_MEMORY) {
                    if (LoggerInfo->LoggerInfo.MaximumBuffers >= PDH_WMI_MAX_BUFFERS) {
                        bIncreaseBuffer = FALSE;
                    }
                    else {
                        EVENT_TRACE_PROPERTIES tmpLoggerInfo;
                        LoggerInfo->LoggerInfo.MaximumBuffers += PDH_WMI_BUFFER_INCREMENT;
                        RtlCopyMemory(& tmpLoggerInfo,
                                      & LoggerInfo->LoggerInfo,
                                      sizeof(EVENT_TRACE_PROPERTIES));
                        tmpLoggerInfo.Wnode.BufferSize  = sizeof(EVENT_TRACE_PROPERTIES);
                        tmpLoggerInfo.LoggerNameOffset  = 0;
                        tmpLoggerInfo.LogFileNameOffset = 0;
                        Status = ControlTraceW((TRACEHANDLE) pLog->hLogFileHandle,
                                               LoggerInfo->LoggerName,
                                               (PEVENT_TRACE_PROPERTIES) & tmpLoggerInfo,
                                               EVENT_TRACE_CONTROL_UPDATE);
                        DebugPrint((1, "UpdateTrace(0x%08X,%d)(%d,0x%08X)\n",
                                pLog->hLogFileHandle,
                                LoggerInfo->LoggerInfo.MaximumBuffers,
                                Status,
                                Status));
                        bIncreaseBuffer = (Status == ERROR_SUCCESS || Status == ERROR_MORE_DATA) ? (TRUE) : (FALSE);
                    }
                }
                else {
                    bIncreaseBuffer = FALSE;
                }
            }
            if (Status != ERROR_SUCCESS) {
                DebugPrint((1, "PdhWmiTraceEvent(0x%08X,%d,%d/%d,%d,0x%08X,%I64d) fails (%d,0x%08X)\n",
                        pLog->hLogFileHandle,
                        PdhEventType,
                        dwCurrentEvent,
                        dwNumEvents,
                        sMofLen,
                        Wnode.EventHeader.Flags,
                        Wnode.EventHeader.TimeStamp.QuadPart,
                        Status,
                        Status));
            }

            if (Status == ERROR_SUCCESS) {
                lenMofData -= sMofLen;
                ptrCurrentMof = (PVOID) (((LPBYTE) ptrCurrentMof) + sMofLen);
            }
        }
    }
    return Status;
}

ULONG whextoi(WCHAR *s)
{
    long len;
    ULONG num, base, hex;

    if (s == NULL || s[0] == L'\0') {
        return 0;
    }
    len = (long) wcslen(s); // we expect all strings to be less than MAXSTR
    if (len == 0) {
        return 0;
    }

    hex  = 0;
    base = 1;
    num  = 0;
    while (-- len >= 0) {
        if (s[len] >= L'0' && s[len] <= L'9')
            num = s[len] - L'0';
        else if (s[len] >= L'a' && s[len] <= L'f')
            num = (s[len] - L'a') + 10;
        else if (s[len] >= L'A' && s[len] <= L'F')
            num = (s[len] - L'A') + 10;
        else
            continue;

        hex += num * base;
        base = base * 16;
    }
    return hex;
}

PDH_FUNCTION
PdhiCheckWmiLogFileType(
    IN  LPCWSTR LogFileName,
    IN  LPDWORD LogFileType)
{
    PDH_STATUS pdhStatus = ERROR_SUCCESS;
    HANDLE     hFile     = NULL;

    hFile = CreateFileW(LogFileName,
                        GENERIC_READ,
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL,
                        OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL);
    if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) {
        pdhStatus = PDH_LOG_FILE_OPEN_ERROR;
    }
    else {
        BYTE  TraceBuffer[PDH_WMI_BUFFER_SIZE_BYTE];
        ULONG ByteRead = 0;
        INT   bResult = ReadFile(hFile,
                                 (LPVOID) TraceBuffer,
                                 PDH_WMI_BUFFER_SIZE_BYTE,
                                 & ByteRead,
                                 NULL);
        if (bResult > 0 && ByteRead == PDH_WMI_BUFFER_SIZE_BYTE) {
            PWMI_BUFFER_HEADER    BufferHeader;
            PTRACE_LOGFILE_HEADER LogFileHeader;

            BufferHeader  = (PWMI_BUFFER_HEADER) TraceBuffer;
            LogFileHeader = (PTRACE_LOGFILE_HEADER)
                            (TraceBuffer + sizeof(WMI_BUFFER_HEADER)
                                         + sizeof(SYSTEM_TRACE_HEADER));
            if (   BufferHeader->Wnode.BufferSize == PDH_WMI_BUFFER_SIZE_BYTE
                && LogFileHeader->BufferSize == PDH_WMI_BUFFER_SIZE_BYTE) {
                // preassume that this is PDH event trace counter logfile
                //
                * LogFileType = PDH_LOG_TYPE_BINARY;
            }
            else {
                * LogFileType = PDH_LOG_TYPE_UNDEFINED;
            }
        }
    }
    return pdhStatus;
}

PDH_FUNCTION
PdhWmiGetLoggerName(
    IN PPDHI_LOG                   pLog,
    IN PPDH_EVENT_TRACE_PROPERTIES LoggerInfo)
{
    PDH_STATUS Status = ERROR_SUCCESS;
    BYTE       TraceBuffer[PDH_WMI_BUFFER_SIZE_BYTE]; // read in the first trace buffer
    HANDLE     hFile;
    ULONG      ByteRead = 0;

    hFile = CreateFileW(LoggerInfo->LogFileName,
                        GENERIC_READ,
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL,
                        OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL);
    if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) {
        Status = PDH_LOG_FILE_OPEN_ERROR;
    }
    else {
        INT bResult =
            ReadFile(hFile, (LPVOID) TraceBuffer, PDH_WMI_BUFFER_SIZE_BYTE, & ByteRead, NULL);
        if (bResult == 0 || ByteRead != PDH_WMI_BUFFER_SIZE_BYTE) {
            Status = PDH_LOG_FILE_OPEN_ERROR;
        }
        CloseHandle(hFile);
    }

    if (Status == ERROR_SUCCESS) {
        PTRACE_LOGFILE_HEADER pHeader = (PTRACE_LOGFILE_HEADER)
                        (TraceBuffer + sizeof(WMI_BUFFER_HEADER) + sizeof(SYSTEM_TRACE_HEADER));
        if (pHeader->BuffersWritten > 1) {
            UINT    i;
            WCHAR   strTmp[PDH_MAX_PATH];
            LPWSTR  LoggerName = (LPWSTR) (((LPBYTE) pHeader) + sizeof(TRACE_LOGFILE_HEADER));

            lstrcpynW(LoggerInfo->LoggerName, LoggerName, PDH_MAX_PATH);
            wcsncpy(strTmp, LoggerName, 8);
            strTmp[8] = L'\0';
            LoggerInfo->LogFileGuid.Data1 = whextoi(strTmp);

            wcsncpy(strTmp, & LoggerName[9], 4);
            strTmp[4] = L'\0';
            LoggerInfo->LogFileGuid.Data2 = (USHORT) whextoi(strTmp);

            wcsncpy(strTmp, & LoggerName[14], 4);
            strTmp[4] = L'\0';
            LoggerInfo->LogFileGuid.Data3 = (USHORT) whextoi(strTmp);

            for (i = 0; i < 2; i ++) {
                wcsncpy(strTmp, & LoggerName[19 + (i * 2)], 2);
                strTmp[2] = L'\0';
                LoggerInfo->LogFileGuid.Data4[i] = (UCHAR) whextoi(strTmp);
            }

            for (i = 2; i < 8; i ++) {
                wcsncpy(strTmp, & LoggerName[20 + (i * 2)], 2);
                strTmp[2] = L'\0';
                LoggerInfo->LogFileGuid.Data4[i] = (UCHAR) whextoi(strTmp);
            }
        }
        else {
            // Only 1 trace buffer written, no PDH events yet.
            // It is safe to discard this one and create a new one.
            //
            Status = PDH_LOG_FILE_OPEN_ERROR;
            DebugPrint((1,"PdhWmiGetLoggerName(0x%08X,0x%08X,0x%08X,\"%ws\")\n",
                    Status, pLog, LoggerInfo, LoggerInfo->LogFileName));
        }
    }
    return Status;
}

PDH_FUNCTION
PdhiOpenOutputWmiLog(
    IN PPDHI_LOG pLog)
{
    PDH_STATUS                  Status = PDH_LOG_FILE_CREATE_ERROR;
    PPDH_EVENT_TRACE_PROPERTIES LoggerInfo;

    pLog->lpMappedFileBase = G_ALLOC(sizeof(PDH_EVENT_TRACE_PROPERTIES));
    if (pLog->lpMappedFileBase == NULL) {
        Status = PDH_MEMORY_ALLOCATION_FAILURE;
        goto Cleanup;
    }
    LoggerInfo = (PPDH_EVENT_TRACE_PROPERTIES) pLog->lpMappedFileBase;
    RtlZeroMemory(LoggerInfo, sizeof(PDH_EVENT_TRACE_PROPERTIES));

    // Start PDH kernel logger
    //
    LoggerInfo->LoggerInfo.Wnode.BufferSize  =
                                    sizeof(PDH_EVENT_TRACE_PROPERTIES);
    LoggerInfo->LoggerInfo.Wnode.Flags       = WNODE_FLAG_TRACED_GUID;
    LoggerInfo->LoggerInfo.Wnode.ClientContext = EVENT_TRACE_CLOCK_SYSTEMTIME;
    LoggerInfo->LoggerInfo.BufferSize        = PDH_WMI_BUFFER_SIZE;
    LoggerInfo->LoggerInfo.LoggerNameOffset  = sizeof(EVENT_TRACE_PROPERTIES);
    LoggerInfo->LoggerInfo.LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES)
                                             + sizeof(WCHAR) * PDH_MAX_PATH;
    _wfullpath(LoggerInfo->LogFileName, pLog->szLogFileName, PDH_MAX_PATH);
    if (   !(pLog->dwLogFormat & PDH_LOG_OPT_CIRCULAR)
        &&  (pLog->dwLogFormat & PDH_LOG_OPT_APPEND)) {
        Status = PdhWmiGetLoggerName(pLog, LoggerInfo);
        if (Status != ERROR_SUCCESS) {
            // if cannot get LogFileGuid from logfile, erase the old one
            // and create new one
            //
            RPC_STATUS rpcStatus = UuidCreate(& LoggerInfo->LogFileGuid);
            GuidToString(LoggerInfo->LoggerName, & LoggerInfo->LogFileGuid);
        }
        else {
            LoggerInfo->LoggerInfo.LogFileMode |= EVENT_TRACE_FILE_MODE_APPEND;
        }
    }
    else {
        RPC_STATUS rpcStatus = UuidCreate(& LoggerInfo->LogFileGuid);
        GuidToString(LoggerInfo->LoggerName, & LoggerInfo->LogFileGuid);
    }

    if (pLog->dwLogFormat & PDH_LOG_OPT_CIRCULAR) {
        LoggerInfo->LoggerInfo.LogFileMode |= EVENT_TRACE_FILE_MODE_CIRCULAR;
    }
    else {
        LoggerInfo->LoggerInfo.LogFileMode |= EVENT_TRACE_FILE_MODE_SEQUENTIAL;
    }
    LoggerInfo->LoggerInfo.LogFileMode |= EVENT_TRACE_USE_PAGED_MEMORY;

    if (pLog->llMaxSize == 0) {
        LoggerInfo->LoggerInfo.MaximumFileSize = 0;
    }
    else {
        LoggerInfo->LoggerInfo.MaximumFileSize =
                               (ULONG) ((pLog->llMaxSize / 1024) / 1024);
    }
    LoggerInfo->LoggerInfo.MaximumBuffers = PDH_WMI_DEFAULT_BUFFERS;

    LoggerInfo->bHeaderEvent = FALSE;
    Status = StartTraceW((PTRACEHANDLE) & pLog->hLogFileHandle,
                         LoggerInfo->LoggerName,
                         (PEVENT_TRACE_PROPERTIES) LoggerInfo);

    if (Status != ERROR_SUCCESS) {
        DebugPrint((1, "StartTraceW(%ws,%ws,0x%08X,%I64d) fails (0x%08X,%c)\n",
                    LoggerInfo->LoggerName,
                    LoggerInfo->LogFileName,
                    LoggerInfo->LoggerInfo.LogFileMode,
                    pLog->llMaxSize,
                    Status,
                    LoggerInfo->bHeaderEvent ? 'T' : 'F'));
    }

Cleanup:
    if (Status != ERROR_SUCCESS) {
        if (pLog->lpMappedFileBase) {
            G_FREE(pLog->lpMappedFileBase);
            pLog->lpMappedFileBase = NULL;
        }
        Status = PDH_LOG_FILE_CREATE_ERROR;
    }
    return Status;
}

PDH_FUNCTION
PdhiWriteWmiLogRecord (
    IN  PPDHI_LOG     pLog,
    IN  SYSTEMTIME  * stTimeStamp,
    IN  LPCWSTR       szUserString
)
{
    PDH_STATUS                     pdhStatus         = ERROR_SUCCESS;
    PPDHI_BINARY_LOG_RECORD_HEADER pLogCounterBuffer = NULL;
    PPDHI_BINARY_LOG_RECORD_HEADER pThisLogCounter   = NULL;
    PPDH_RAW_COUNTER               pSingleCounter;
    PPDHI_RAW_COUNTER_ITEM_BLOCK   pMultiCounter;
    PPDHI_COUNTER                  pThisCounter;
    PPERF_DATA_BLOCK               pObjectCounter;
    DWORD                          dwBufSize         = 0;
    ULONGLONG                      TimeStamp         = 0;
    FILETIME                       tFileTime;

    PPDH_EVENT_TRACE_PROPERTIES LoggerInfo =
            (PPDH_EVENT_TRACE_PROPERTIES) pLog->lpMappedFileBase;

    DBG_UNREFERENCED_PARAMETER(szUserString);

    SystemTimeToFileTime(stTimeStamp, & tFileTime);
    pThisCounter = pLog->pQuery ? pLog->pQuery->pCounterListHead : NULL;
    if (pThisCounter == NULL) {
        return PDH_NO_DATA;
    }

    do {
        DWORD   dwType  = (pThisCounter->dwFlags & PDHIC_COUNTER_OBJECT)
                        ? (PDHIC_COUNTER_OBJECT)
                        : (  (pThisCounter->dwFlags & PDHIC_MULTI_INSTANCE)
                           ? (PDHIC_MULTI_INSTANCE) : (0));
        DWORD   dwCtrBufSize = sizeof(PDHI_BINARY_LOG_RECORD_HEADER);
        DWORD   dwNewSize;
        int     nItem;

        switch (dwType) {
        case PDHIC_MULTI_INSTANCE:
            if (pThisCounter->pThisRawItemList) {
                dwCtrBufSize += pThisCounter->pThisRawItemList->dwLength;
            }
            else {
                dwCtrBufSize += sizeof(PDHI_RAW_COUNTER_ITEM_BLOCK);
            }
            break;

        case PDHIC_COUNTER_OBJECT:
            if (pThisCounter->pThisObject) {
                dwCtrBufSize += pThisCounter->pThisObject->TotalByteLength;
            }
            else {
                dwCtrBufSize += sizeof(PERF_DATA_BLOCK);
            }
            break;

        default:
            dwCtrBufSize += sizeof(PDH_RAW_COUNTER);
            break;
        }

        if (dwCtrBufSize > 0) {
            // extend buffer to accomodate this new counter
            //
            if (pLogCounterBuffer == NULL) {
                // add in room for the master record header
                // then allocate the first one
                //
                dwBufSize = (dwCtrBufSize + sizeof(PDHI_BINARY_LOG_RECORD_HEADER));
                pLogCounterBuffer = G_ALLOC(dwBufSize);

                // set counter data pointer to just after the master
                // record header
                //
                if (pLogCounterBuffer == NULL) {
                    pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE;
                    break;
                }
                pThisLogCounter = (PPDHI_BINARY_LOG_RECORD_HEADER)
                                  (  ((PUCHAR) pLogCounterBuffer)
                                   + sizeof(PDHI_BINARY_LOG_RECORD_HEADER));
                assert (dwCtrBufSize < 0x00010000);
                pThisLogCounter->dwLength = LOWORD(dwCtrBufSize);
            }
            else  {
                PPDHI_BINARY_LOG_RECORD_HEADER ptrTemp = pLogCounterBuffer;
                dwNewSize         = (dwBufSize + dwCtrBufSize);
                pLogCounterBuffer = G_REALLOC(ptrTemp, dwNewSize);
                if (pLogCounterBuffer == NULL) {
                    G_FREE(ptrTemp);
                    pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE;
                    break;
                }
                pThisLogCounter   = (PPDHI_BINARY_LOG_RECORD_HEADER)
                                    ((LPBYTE) pLogCounterBuffer + dwBufSize);
                dwBufSize        += dwCtrBufSize;
                assert (dwCtrBufSize < 0x00010000);
                pThisLogCounter->dwLength = LOWORD(dwCtrBufSize);
            }
        }

        if (dwType == PDHIC_COUNTER_OBJECT) {
            FILETIME LocFileTime;
            pThisLogCounter->dwType = BINLOG_TYPE_DATA_LOC_OBJECT;
            pObjectCounter = (PPERF_DATA_BLOCK)
                             ((LPBYTE) pThisLogCounter +
                              sizeof(PDHI_BINARY_LOG_RECORD_HEADER));
            if (pThisCounter->pThisObject) {
                RtlCopyMemory(pObjectCounter,
                              pThisCounter->pThisObject,
                              pThisCounter->pThisObject->TotalByteLength);
                SystemTimeToFileTime(& pThisCounter->pThisObject->SystemTime,
                                     & LocFileTime);
            }
            else {
                if (TimeStamp != 0) {
                    LocFileTime.dwLowDateTime  = LODWORD(TimeStamp);
                    LocFileTime.dwHighDateTime = HIDWORD(TimeStamp);
                }
                else {
                    LocFileTime = tFileTime;
                }

                pObjectCounter->Signature[0]     = L'P';
                pObjectCounter->Signature[1]     = L'E';
                pObjectCounter->Signature[2]     = L'R';
                pObjectCounter->Signature[3]     = L'F';
                pObjectCounter->LittleEndian     = TRUE;
                pObjectCounter->Version          = PERF_DATA_VERSION;
                pObjectCounter->Revision         = PERF_DATA_REVISION;
                pObjectCounter->TotalByteLength  = sizeof(PERF_DATA_BLOCK);
                pObjectCounter->NumObjectTypes   = 1;
                pObjectCounter->DefaultObject    = pThisCounter->plCounterInfo.dwObjectId;
                pObjectCounter->SystemNameLength = 0;
                pObjectCounter->SystemNameOffset = 0;
                pObjectCounter->HeaderLength     = sizeof(PERF_DATA_BLOCK);

                pObjectCounter->PerfTime.QuadPart         = 0;
                pObjectCounter->PerfFreq.QuadPart         = 0;
                pObjectCounter->PerfTime100nSec.QuadPart  = 0;
                FileTimeToSystemTime(& LocFileTime,
                                     & pObjectCounter->SystemTime);
            }
            TimeStamp = MAKELONGLONG(LocFileTime.dwLowDateTime,
                                     LocFileTime.dwHighDateTime);
        }
        else if (dwType == PDHIC_MULTI_INSTANCE) {
            // multiple counter
            //
            pThisLogCounter->dwType = BINLOG_TYPE_DATA_MULTI;
            pMultiCounter = (PPDHI_RAW_COUNTER_ITEM_BLOCK)
                            ((LPBYTE) pThisLogCounter +
                                  sizeof(PDHI_BINARY_LOG_RECORD_HEADER));
            if (pThisCounter->pThisRawItemList) {
                RtlCopyMemory(pMultiCounter,
                              pThisCounter->pThisRawItemList,
                              pThisCounter->pThisRawItemList->dwLength);
            }
            else {
                FILETIME LocFileTime;
                if (TimeStamp != 0) {
                    LocFileTime.dwLowDateTime  = LODWORD(TimeStamp);
                    LocFileTime.dwHighDateTime = HIDWORD(TimeStamp);
                }
                else {
                    LocFileTime = tFileTime;
                }
                ZeroMemory(pMultiCounter,
                           sizeof(PDHI_RAW_COUNTER_ITEM_BLOCK));
                pMultiCounter->CStatus = PDH_CSTATUS_INVALID_DATA;
                pMultiCounter->TimeStamp.dwLowDateTime =
                                        LocFileTime.dwLowDateTime;
                pMultiCounter->TimeStamp.dwHighDateTime =
                                        LocFileTime.dwHighDateTime;
            }
            TimeStamp = MAKELONGLONG(pMultiCounter->TimeStamp.dwLowDateTime,
                                 pMultiCounter->TimeStamp.dwHighDateTime);
        }
        else {
            pThisLogCounter->dwType = BINLOG_TYPE_DATA_SINGLE;
            pSingleCounter = (PPDH_RAW_COUNTER)
                             ((LPBYTE) pThisLogCounter +
                                 sizeof(PDHI_BINARY_LOG_RECORD_HEADER));
            RtlCopyMemory(  pSingleCounter,
                          & pThisCounter->ThisValue,
                            sizeof(PDH_RAW_COUNTER));
            if (pSingleCounter->CStatus != ERROR_SUCCESS) {
                if (TimeStamp != 0) {
                    pSingleCounter->TimeStamp.dwLowDateTime =
                                    LODWORD(TimeStamp);
                    pSingleCounter->TimeStamp.dwHighDateTime =
                                    HIDWORD(TimeStamp);
                }
                else {
                    pSingleCounter->TimeStamp.dwLowDateTime =
                                    tFileTime.dwLowDateTime;
                    pSingleCounter->TimeStamp.dwHighDateTime =
                                    tFileTime.dwHighDateTime;
                }
            }
            TimeStamp = (ULONGLONG) MAKELONGLONG(
                            pSingleCounter->TimeStamp.dwLowDateTime,
                            pSingleCounter->TimeStamp.dwHighDateTime);
        }
        pThisCounter = pThisCounter->next.flink; // go to next in list

    } while (pThisCounter != pLog->pQuery->pCounterListHead);

    if (TimeStamp == 0) {
        TimeStamp = MAKELONGLONG(tFileTime.dwLowDateTime,
                                 tFileTime.dwHighDateTime);
    }

    if (pdhStatus == ERROR_SUCCESS && pLogCounterBuffer) {
        pLogCounterBuffer->dwType   = BINLOG_TYPE_DATA;
        pLogCounterBuffer->dwLength = dwBufSize;
        LoggerInfo->TimeStamp = TimeStamp;

        if (! LoggerInfo->bHeaderEvent) {
            ULONG HeaderMofLength = 0;
            PVOID HeaderMofData   = NULL;
            ULONG EventType;

            if (LoggerInfo->LoggerInfo.LogFileMode
                                & EVENT_TRACE_FILE_MODE_APPEND) {
#if 0
                pdhStatus = PdhiBuildEmptyBlock(pLog,
                                                LoggerInfo->TimeStamp - 1,
                                                & HeaderMofLength,
                                                & HeaderMofData);
                if (pdhStatus == ERROR_SUCCESS) {
                    pdhStatus = PdhWmiTraceEvent(pLog,
                                                 PDH_LOG_DATA_BLOCK_EVENT,
                                                 LoggerInfo->TimeStamp - 1,
                                                 HeaderMofLength,
                                                 HeaderMofData);
                }
                if (HeaderMofData) {
                    G_FREE(HeaderMofData);
                }
#endif
            }
            else {
                pdhStatus = PdhiBuildLogHeaderBlock(pLog, LoggerInfo);
            }
            LoggerInfo->bHeaderEvent = TRUE;
        }
    }

    if (pdhStatus == ERROR_SUCCESS && pLogCounterBuffer) {
        pdhStatus = PdhWmiTraceEvent(pLog,
                                     PDH_LOG_DATA_BLOCK_EVENT,
                                     TimeStamp,
                                     dwBufSize,
                                     (PVOID)  pLogCounterBuffer);
    }
    if (pLogCounterBuffer != NULL) {
        G_FREE(pLogCounterBuffer);
    }

    return pdhStatus;
}

PDH_FUNCTION
PdhiWriteWmiLogHeader (
    IN  PPDHI_LOG   pLog,
    IN  LPCWSTR     szUserCaption)
{
    UNREFERENCED_PARAMETER (pLog);
    UNREFERENCED_PARAMETER (szUserCaption);
    return ERROR_SUCCESS;
}


PDH_FUNCTION
PdhiCloseWmiLog(
    IN  PPDHI_LOG   pLog,
    IN  DWORD       dwFlags)
{
    PDH_STATUS Status = PDH_INVALID_ARGUMENT;

    UNREFERENCED_PARAMETER (pLog);
    UNREFERENCED_PARAMETER (dwFlags);

    if (pLog->dwLogFormat & PDH_LOG_WRITE_ACCESS) {

        if ((TRACEHANDLE) pLog->hLogFileHandle != (TRACEHANDLE) 0) {
            PPDH_EVENT_TRACE_PROPERTIES LoggerInfo =
                        (PPDH_EVENT_TRACE_PROPERTIES) pLog->lpMappedFileBase;
            if (LoggerInfo == NULL) {
                Status = PDH_INVALID_HANDLE;
            }
            else {
                ULONG HeaderMofLength = 0;
                PVOID HeaderMofData   = NULL;
#if 0
                Status = PdhiBuildEmptyBlock(pLog,
                                             LoggerInfo->TimeStamp,
                                             & HeaderMofLength,
                                             & HeaderMofData);
                if (Status == ERROR_SUCCESS) {
                    Status = PdhWmiTraceEvent(pLog,
                                              PDH_LOG_DATA_BLOCK_EVENT,
                                              LoggerInfo->TimeStamp + 1,
                                              HeaderMofLength,
                                              HeaderMofData);
                }
                if (HeaderMofData) {
                    G_FREE(HeaderMofData);
                }
#endif
                Status = ControlTraceW((TRACEHANDLE) pLog->hLogFileHandle,
                                       LoggerInfo->LoggerName,
                                       (PEVENT_TRACE_PROPERTIES) LoggerInfo,
                                       EVENT_TRACE_CONTROL_STOP);
                if (Status != ERROR_SUCCESS) {
                    DebugPrint((1, "StopTrace(0x%08X,%ws,%ws) fails (%d)\n",
                            pLog->hLogFileHandle,
                            LoggerInfo->LoggerName,
                            LoggerInfo->LogFileName,
                            Status,
                            Status));
                }
                if (LoggerInfo->MachineList != NULL) {
                    G_FREE(LoggerInfo->MachineList);
                }
                G_FREE(pLog->lpMappedFileBase);
                pLog->lpMappedFileBase = NULL;
            }

            pLog->hLogFileHandle = INVALID_HANDLE_VALUE;

            if (PdhTraceRegistrationHandle != (TRACEHANDLE) 0) {
                Status = UnregisterTraceGuids(PdhTraceRegistrationHandle);
                if (Status != ERROR_SUCCESS) {
                    DebugPrint((4,
                            "UnregisterTraceGuid(0x%08X) fails (%d,0x%08X)\n",
                            PdhTraceRegistrationHandle,
                            Status,
                            Status));
                }
            }
        }
        else {
            Status = PDH_INVALID_HANDLE;
        }
    }
    else if (pLog->dwLogFormat & PDH_LOG_READ_ACCESS) {
        ULONG               i;
        DWORD               dwExitCode;
        PPDH_LOGGER_CONTEXT CurrentContext =
                                (PPDH_LOGGER_CONTEXT) pLog->lpMappedFileBase;
        DWORD               dwContext = GetLoggerContext(CurrentContext);

        if (dwContext < ContextCount) {
            Status = WAIT_FOR_AND_LOCK_MUTEX(hPdhContextMutex);
            if (Status == ERROR_SUCCESS) {
                CurrentContext->RefCount --;
                if (CurrentContext->RefCount > 0) {
                    RELEASE_MUTEX(hPdhContextMutex);
                    return ERROR_SUCCESS;
                }
            }
            else {
                return Status;
            }
            if (dwContext != ContextCount - 1) {
                ContextTable[dwContext] = ContextTable[ContextCount - 1];
            }
            ContextCount --;
            ContextTable[ContextCount] = NULL;
            RELEASE_MUTEX(hPdhContextMutex);

            if (CurrentContext->hThreadWork != NULL
                && CurrentContext->hThreadWork != INVALID_HANDLE_VALUE) {
                if (GetExitCodeThread(CurrentContext->hThreadWork, & dwExitCode)
                        && dwExitCode == STILL_ACTIVE) {
                    CurrentContext->LoggerState = PdhProcessTraceEnd;
                    SetEvent(CurrentContext->hSyncWMI);
                    while (CurrentContext->LoggerState != PdhProcessTraceExit) {
                        _sleep(1);
                    }
                    if (TerminateThread(CurrentContext->hThreadWork, 0)) {
                        WaitForSingleObject(CurrentContext->hThreadWork, INFINITE);
                    }
                }
            }

            if (CurrentContext->hSyncWMI != NULL) {
                CloseHandle(CurrentContext->hSyncWMI);
            }
            if (CurrentContext->hSyncPDH != NULL) {
                CloseHandle(CurrentContext->hSyncPDH);
            }

            for (i = 0; i < CurrentContext->LoggerCount; i ++) {
                if (! IsListEmpty(& CurrentContext->LogInfo[i].CounterPathList)) {
                    PLIST_ENTRY PathHead =
                                & CurrentContext->LogInfo[i].CounterPathList;
                    PLIST_ENTRY PathNext = PathHead->Flink;
                    PPDH_COUNTER_PATH pCounterPath;

                    while (PathHead != PathNext) {
                        pCounterPath = CONTAINING_RECORD(PathNext,
                                                         PDH_COUNTER_PATH,
                                                         Entry);
                        PathNext     = PathNext->Flink;
                        RemoveEntryList(& pCounterPath->Entry);
                        G_FREE(pCounterPath->CounterPathBuffer);
                        G_FREE(pCounterPath);
                    }
                }
                if (! IsListEmpty(& CurrentContext->LogInfo[i].PerfMachineList)) {
                    PLIST_ENTRY PathHead =
                                & CurrentContext->LogInfo[i].PerfMachineList;
                    PLIST_ENTRY PathNext = PathHead->Flink;
                    PPDH_WMI_PERF_MACHINE pPerfMachine;

                    while (PathHead != PathNext) {
                        pPerfMachine = CONTAINING_RECORD(PathNext,
                                                         PDH_WMI_PERF_MACHINE,
                                                         Entry);
                        PathNext     = PathNext->Flink;
                        RemoveEntryList(& pPerfMachine->Entry);
                        if (! IsListEmpty(& pPerfMachine->LogObjectList)) {
                            PLIST_ENTRY pHead =
                                & pPerfMachine->LogObjectList;
                            PLIST_ENTRY pNext = pHead->Flink;
                            PPDH_WMI_PERF_OBJECT pPerfObject;

                            while (pHead != pNext) {
                                pPerfObject = CONTAINING_RECORD(
                                    pNext, PDH_WMI_PERF_OBJECT, Entry);
                                pNext       = pNext->Flink;
                                RemoveEntryList(& pPerfObject->Entry);
                                G_FREE(pPerfObject->ptrBuffer);
                                G_FREE(pPerfObject);
                            }
                        }
                        G_FREE(pPerfMachine->pszBuffer);
                        G_FREE(pPerfMachine->ptrStrAry);
                        G_FREE(pPerfMachine);
                    }
                }
                if (CurrentContext->LogInfo[i].DataBlock) {
                    G_FREE(CurrentContext->LogInfo[i].DataBlock);
                }
            }
            if (CurrentContext->tmpBuffer) {
                G_FREE(CurrentContext->tmpBuffer);
            }
            if (CurrentContext->CounterPathBuffer) {
                G_FREE(CurrentContext->CounterPathBuffer);
            }
            G_FREE(pLog->lpMappedFileBase);
            pLog->lpMappedFileBase = NULL;
            pLog->pLastRecordRead  = NULL;

            Status = ERROR_SUCCESS;
        }
        else {
            Status = PDH_INVALID_HANDLE;
        }
    }
    return Status;
}

PDH_FUNCTION
PdhiAddWmiLogFileGuid(
    PPDH_LOGGER_CONTEXT CurrentContext,
    LPGUID              pGuid)
{
    PDH_STATUS Status = ERROR_SUCCESS;
    ULONG      i;

    for (i = 0; i < CurrentContext->LoggerCount; i ++) {
        if (IsEqualGUID(pGuid, & CurrentContext->LogInfo[i].LogFileGuid))
            break;
    }
    if (i == CurrentContext->LoggerCount) {
        CurrentContext->LogInfo[i].LogFileGuid = * pGuid;
        CurrentContext->LoggerCount ++;
        if (CurrentContext->LoggerCount > CurrentContext->LogFileCount) {
            DebugPrint((4, "LogFileCount(%d) LoggerCount(%d) mismatch\n",
                    CurrentContext->LoggerCount,
                    CurrentContext->LogFileCount));
        }
    }
    return Status;
}

ULONG
PdhiFindLogFileGuid(
    PPDH_LOGGER_CONTEXT CurrentContext,
    LPGUID              pGuid)
{
    ULONG i;

    for (i = 0; i < CurrentContext->LoggerCount; i ++) {
        if (IsEqualGUID(pGuid, & CurrentContext->LogInfo[i].LogFileGuid))
            break;
    }
    return i;
}

ULONGLONG
PdhWmiGetDataBlockTimeStamp(
    PPDH_LOGGER_CONTEXT CurrentContext,
    PEVENT_TRACE        pEvent,
    BOOLEAN             bFirstRun)
{
    PVOID     pDataBlock = pEvent->MofData;
    ULONGLONG TimeStamp  = pEvent->Header.TimeStamp.QuadPart;

    if (TimeStamp > 0) {
        LPGUID pLogFileGuid = (LPGUID) pDataBlock;
        ULONG  i;

        for (i = 0; i < CurrentContext->LoggerCount; i ++) {
            if (IsEqualGUID(  pLogFileGuid,
                            & CurrentContext->LogInfo[i].LogFileGuid)) {
                break;
            }
        }

        if (i < CurrentContext->LoggerCount) {
            if (bFirstRun) {
                if (   pEvent->Header.Class.Version < PDH_EVENT_VERSION
                    || CurrentContext->LogInfo[i].TimePrev < TimeStamp) {
                    CurrentContext->LogInfo[i].ValidEntries ++;
                }
                if (CurrentContext->LogInfo[i].TimeStart == 0) {
                    CurrentContext->LogInfo[i].TimeStart = TimeStamp;
                    CurrentContext->LogInfo[i].TimePrev  = TimeStamp;
                }
                else {
                    // no need to update StartTime.
                    // Always assume the first trace event has the StartTime.
                    //
                    if (CurrentContext->LogInfo[i].TimeEnd < TimeStamp) {
                        CurrentContext->LogInfo[i].TimeEnd = TimeStamp;
                    }

                    if (  (TimeStamp - CurrentContext->LogInfo[i].TimePrev)
                        < (CurrentContext->LogInfo[i].TimeFreq)) {
                        CurrentContext->LogInfo[i].TimeFreq =
                                TimeStamp - CurrentContext->LogInfo[i].TimePrev;
                    }
                    CurrentContext->LogInfo[i].TimePrev = TimeStamp;
                }
            }
            else {
                assert(CurrentContext->LogInfo[i].TimePrev  <= TimeStamp);
                assert(CurrentContext->LogInfo[i].TimeStart <= TimeStamp);
                assert(CurrentContext->LogInfo[i].TimeEnd   >= TimeStamp);
                CurrentContext->LogInfo[i].TimePrev = TimeStamp;
            }
        }
    }
    return TimeStamp;
}

PDH_FUNCTION
PdhiWmiComputeCounterBlocks(
    PPDH_LOGGER_CONTEXT CurrentContext,
    PEVENT_TRACE        pEvent)
{
    LPGUID     pLogFileGuid;
    PDH_STATUS Status       = ERROR_SUCCESS;
    PVOID      pDataBlock;
    ULONG      i;
    ULONG      ulNumDataBlocks;
    ULONG      ulDataBlocksCopied;
    ULONG      ulBufferSize;
    ULONG      ulBlockIndex;
    BOOLEAN    bOldEvent;

    if (CurrentContext == NULL || pEvent == NULL) {
        return PDH_INVALID_DATA;
    }

    bOldEvent    = (pEvent->Header.Class.Version < PDH_EVENT_VERSION);
    pLogFileGuid = (LPGUID) pEvent->MofData;
    if (pLogFileGuid == NULL) {
        return PDH_INVALID_DATA;
    }
    for (i = 0; i < CurrentContext->LoggerCount; i ++) {
        if (IsEqualGUID(pLogFileGuid,
                        & CurrentContext->LogInfo[i].LogFileGuid)) {
            break;
        }
    }
    if (i == CurrentContext->LoggerCount) {
        return PDH_INVALID_DATA;
    }

    ulNumDataBlocks    = CurrentContext->LogInfo[i].ulNumDataBlocks;
    ulDataBlocksCopied = CurrentContext->LogInfo[i].ulDataBlocksCopied;

    if (ulNumDataBlocks > 0 && ulNumDataBlocks == ulDataBlocksCopied) {
        if (CurrentContext->LogInfo[i].DataBlock != NULL) {
            G_FREE(CurrentContext->LogInfo[i].DataBlock);
            CurrentContext->LogInfo[i].DataBlock = NULL;
        }
        goto Cleanup;
    }

    if (ulNumDataBlocks == 0) {
        if (bOldEvent) {
            ulNumDataBlocks = 1;
            ulBufferSize    = pEvent->MofLength;
        }
        else {
            ulNumDataBlocks = * (DWORD *) (  ((LPBYTE) pEvent->MofData)
                                           + sizeof(GUID) + sizeof(DWORD));
            ulBufferSize    = ulNumDataBlocks * PDH_BLOCK_BUFFER_SIZE
                            + sizeof(GUID);
        }

        if (CurrentContext->LogInfo[i].DataBlock != NULL) {
            G_FREE(CurrentContext->LogInfo[i].DataBlock);
            CurrentContext->LogInfo[i].DataBlock = NULL;
        }
        CurrentContext->LogInfo[i].DataBlock = G_ALLOC(ulBufferSize);
        if (CurrentContext->LogInfo[i].DataBlock == NULL) {
            Status = PDH_MEMORY_ALLOCATION_FAILURE;
            goto Cleanup;
        }

        CurrentContext->LogInfo[i].ulNumDataBlocks    = ulNumDataBlocks;
        CurrentContext->LogInfo[i].ulDataBlocksCopied = 0;
        CurrentContext->LogInfo[i].DataBlockAlloc     = ulBufferSize;
    }

    if (bOldEvent) {
        RtlCopyMemory(CurrentContext->LogInfo[i].DataBlock,
                      pEvent->MofData,
                      pEvent->MofLength);
    }
    else {
        ulBlockIndex = * (DWORD *) (((LPBYTE) pEvent->MofData) + sizeof(GUID));
        pDataBlock = (PVOID)
                     (  ((LPBYTE) CurrentContext->LogInfo[i].DataBlock)
                      + sizeof(GUID)
                      + PDH_BLOCK_BUFFER_SIZE * (ulBlockIndex - 1));
        RtlCopyMemory(pDataBlock,
                      (PVOID) (((LPBYTE) pEvent->MofData)
                              + sizeof(GUID) + sizeof(DWORD) + sizeof(DWORD)),
                      pEvent->MofLength - sizeof(GUID)
                              - sizeof(DWORD) - sizeof(DWORD));
    }
    CurrentContext->LogInfo[i].ulDataBlocksCopied ++;

    if (CurrentContext->LogInfo[i].ulDataBlocksCopied >=
            CurrentContext->LogInfo[i].ulNumDataBlocks) {
        DWORD   dwTotal;
        DWORD   dwCurrent;
        DWORD   dwCounters  = 0;
        BOOLEAN bValidBlock = TRUE;
        PPDHI_BINARY_LOG_RECORD_HEADER pCurrent;

        PPDHI_BINARY_LOG_RECORD_HEADER pMasterRec;
        pDataBlock = CurrentContext->LogInfo[i].DataBlock;
        pMasterRec = (PPDHI_BINARY_LOG_RECORD_HEADER)
                     ((LPBYTE) pDataBlock + sizeof(GUID));
        dwTotal    = pMasterRec->dwLength;
        pCurrent   = pMasterRec;
        dwCurrent  = sizeof(PDHI_BINARY_LOG_RECORD_HEADER);
        while (bValidBlock && dwCurrent < dwTotal) {
            pCurrent   = (PPDHI_BINARY_LOG_RECORD_HEADER)
                         ((LPBYTE) pMasterRec + dwCurrent);
            if (LOWORD(pCurrent->dwType) != BINLOG_START_WORD) {
                bValidBlock = FALSE;
            }
            else {
                dwCurrent += pCurrent->dwLength;
                dwCounters ++;
            }
        }
        if (bValidBlock) {
            CurrentContext->LogInfo[i].ulCounters += dwCounters;
        }
    }

Cleanup:
    return Status;
}

PDH_FUNCTION
PdhiAddCounterPathRecord(
    PPDH_LOGGER_CONTEXT CurrentContext,
    PEVENT_TRACE        pEvent,
    LPGUID              pLogFileGuid,
    ULONG               BufferSize,
    PVOID               pBuffer,
    ULONGLONG           TimeStamp,
    DWORD               dwIndex,
    DWORD               dwCount,
    BOOLEAN           * pNeedUpdate)
{
    PPDH_COUNTER_PATH pNewCounter;
    ULONG             i;
    PVOID             pCounterPath;

    i = PdhiFindLogFileGuid(CurrentContext, pLogFileGuid);
    if (i == CurrentContext->LoggerCount) {
        return PDH_INVALID_DATA;
    }

    if (pEvent->Header.Class.Version >= PDH_EVENT_VERSION) {
        if (   CurrentContext->LogInfo[i].DataBlock
            && CurrentContext->LogInfo[i].ulNumDataBlocks != dwCount) {
            G_FREE(CurrentContext->LogInfo[i].DataBlock);
            CurrentContext->LogInfo[i].DataBlock = NULL;
        }
        if (CurrentContext->LogInfo[i].DataBlock == NULL) {
            CurrentContext->LogInfo[i].DataBlock =
                    G_ALLOC(PDH_BLOCK_BUFFER_SIZE * dwCount);
            if (CurrentContext->LogInfo[i].DataBlock == NULL) {
                return PDH_MEMORY_ALLOCATION_FAILURE;
            }
            CurrentContext->LogInfo[i].DataBlockAlloc     = PDH_BLOCK_BUFFER_SIZE * dwCount;
            CurrentContext->LogInfo[i].DataBlockSize      = 0;
            CurrentContext->LogInfo[i].ulNumDataBlocks    = dwCount;
            CurrentContext->LogInfo[i].ulDataBlocksCopied = 0;
        }

        pCounterPath = (PVOID) (  ((LPBYTE) CurrentContext->LogInfo[i].DataBlock)
                                + (dwIndex - 1) * PDH_BLOCK_BUFFER_SIZE);
        if (BufferSize > PDH_BLOCK_BUFFER_SIZE) {
            BufferSize = PDH_BLOCK_BUFFER_SIZE;
        }
        RtlCopyMemory(pCounterPath, pBuffer, BufferSize);
        CurrentContext->LogInfo[i].ulDataBlocksCopied ++;
        CurrentContext->LogInfo[i].DataBlockSize += BufferSize;

        if (  CurrentContext->LogInfo[i].ulDataBlocksCopied
            < CurrentContext->LogInfo[i].ulNumDataBlocks) {
            return ERROR_SUCCESS;
        }

        pCounterPath = (PVOID) (((LPBYTE) CurrentContext->LogInfo[i].DataBlock)
                                + sizeof(PDH_WMI_LOG_INFO));
        BufferSize   = CurrentContext->LogInfo[i].DataBlockSize
                                - sizeof(PDH_WMI_LOG_INFO);
    }

    if (! IsListEmpty(& CurrentContext->LogInfo[i].CounterPathList)) {
        PLIST_ENTRY PathHead = & CurrentContext->LogInfo[i].CounterPathList;
        PLIST_ENTRY PathNext = PathHead->Flink;

        while (PathHead != PathNext) {
            pNewCounter = CONTAINING_RECORD(PathNext,
                                            PDH_COUNTER_PATH,
                                            Entry);
            PathNext    = PathNext->Flink;
            if (TimeStamp == pNewCounter->TimeStamp) {
                // CounterPath record is already in the list
                //
                return ERROR_SUCCESS;
            }
        }
    }

    pNewCounter = G_ALLOC(sizeof(PDH_COUNTER_PATH));
    if (pNewCounter == NULL) {
        return PDH_MEMORY_ALLOCATION_FAILURE;
    }

    pNewCounter->TimeStamp   = TimeStamp;
    pNewCounter->CounterPathBuffer = G_ALLOC(BufferSize);
    if (pNewCounter->CounterPathBuffer == NULL) {
        G_FREE(pNewCounter);
        return PDH_MEMORY_ALLOCATION_FAILURE;
    }
    pNewCounter->CounterPathSize = BufferSize;
    if (pEvent->Header.Class.Version >= PDH_EVENT_VERSION) {
        RtlCopyMemory(pNewCounter->CounterPathBuffer, pCounterPath, BufferSize);
    }
    else {
        RtlCopyMemory(pNewCounter->CounterPathBuffer, pBuffer, BufferSize);
    }

    pNewCounter->CounterCount = 0;
    {
        PPDHI_LOG_COUNTER_PATH pPath = pNewCounter->CounterPathBuffer;
        ULONG dwProcessed = 0;

        while (dwProcessed < BufferSize) {
            pNewCounter->CounterCount ++;
            dwProcessed += pPath->dwLength;
            pPath = (PPDHI_LOG_COUNTER_PATH)
                    (((LPBYTE) pPath) + pPath->dwLength);
        }
    }

    InsertTailList(& CurrentContext->LogInfo[i].CounterPathList,
                   & pNewCounter->Entry);
    * pNeedUpdate = TRUE;

    if (pEvent->Header.Class.Version >= PDH_EVENT_VERSION) {
        G_FREE(CurrentContext->LogInfo[i].DataBlock);
        CurrentContext->LogInfo[i].DataBlock          = NULL;
        CurrentContext->LogInfo[i].DataBlockAlloc     = 0;
        CurrentContext->LogInfo[i].DataBlockSize      = 0;
        CurrentContext->LogInfo[i].ulNumDataBlocks    = 0;
        CurrentContext->LogInfo[i].ulDataBlocksCopied = 0;
    }

    return ERROR_SUCCESS;
}

PDH_FUNCTION
PdhiAddPerfMachine(
    IN PPDH_LOGGER_CONTEXT CurrentContext,
    IN PVOID               pMofDataBlock,
    IN DWORD               dwMofLength)
{
    PDH_STATUS Status         = ERROR_SUCCESS;
    LPGUID     pLogFileGuid   = (LPGUID) pMofDataBlock;
    LPWSTR     pszMachineName = (LPWSTR) (((LPBYTE) pMofDataBlock)
                              + sizeof(GUID) + sizeof(DWORD) + sizeof(DWORD));
    DWORD      dwBufSize      = dwMofLength
                              - sizeof(GUID) - sizeof(DWORD) - sizeof(DWORD);
    ULONG      i;
    LPWSTR     pszTmpBuffer;
    DWORD      dwThisId;
    DWORD      dwBufUsed;

    PPDH_WMI_PERF_MACHINE pCurrentMachine = NULL;
    BOOLEAN               bNewMachine     = TRUE;

    i = PdhiFindLogFileGuid(CurrentContext, pLogFileGuid);
    if (i == CurrentContext->LoggerCount) {
        Status = PDH_INVALID_DATA;
        goto Cleanup;
    }

    if (! IsListEmpty(& CurrentContext->LogInfo[i].PerfMachineList)) {
        PLIST_ENTRY pHead = & CurrentContext->LogInfo[i].PerfMachineList;
        PLIST_ENTRY pNext = pHead->Flink;
        while (pNext != pHead) {
            PPDH_WMI_PERF_MACHINE pMachine =
                    CONTAINING_RECORD(pNext, PDH_WMI_PERF_MACHINE, Entry);
            if (lstrcmpiW(pMachine->pszBuffer, pszMachineName) == 0) {
                pCurrentMachine = pMachine;
                bNewMachine     = FALSE;
                break;
            }
            pNext = pNext->Flink;
        }
    }

    if (bNewMachine) {
        pCurrentMachine = G_ALLOC(sizeof(PDH_WMI_PERF_MACHINE));
        if (pCurrentMachine == NULL) {
            Status = PDH_MEMORY_ALLOCATION_FAILURE;
            goto Cleanup;
        }
        InsertTailList(& CurrentContext->LogInfo[i].PerfMachineList,
                       & pCurrentMachine->Entry);
        pCurrentMachine->pszBuffer = G_ALLOC(dwBufSize);
        if (pCurrentMachine->pszBuffer == NULL) {
            Status = PDH_MEMORY_ALLOCATION_FAILURE;
            goto Cleanup;
        }
        RtlCopyMemory(pCurrentMachine->pszBuffer, pszMachineName, dwBufSize);
        pCurrentMachine->dwBufSize = dwBufSize;
        InitializeListHead(& pCurrentMachine->LogObjectList);
    }
    else {
        pszTmpBuffer    = pCurrentMachine->pszBuffer;
        dwBufSize      -= (sizeof(WCHAR) * (lstrlenW(pszMachineName) + 1));
        pszMachineName += (lstrlenW(pszMachineName) + 1);
        pCurrentMachine->pszBuffer = G_ALLOC(
                pCurrentMachine->dwBufSize + dwBufSize);
        if (pCurrentMachine->pszBuffer == NULL) {
            pCurrentMachine->pszBuffer = pszTmpBuffer;
            Status = PDH_MEMORY_ALLOCATION_FAILURE;
            goto Cleanup;
        }
        RtlCopyMemory(pCurrentMachine->pszBuffer,
                      pszTmpBuffer,
                      pCurrentMachine->dwBufSize);
        G_FREE(pszTmpBuffer);
        pszTmpBuffer = (LPWSTR)
                       (  ((LPBYTE) pCurrentMachine->pszBuffer)
                        + pCurrentMachine->dwBufSize);
        RtlCopyMemory(pszTmpBuffer, pszMachineName, dwBufSize);
        pCurrentMachine->dwBufSize += dwBufSize;
    }
    dwBufSize = pCurrentMachine->dwBufSize;

    pCurrentMachine->dwLastId = 0;
    i            = lstrlenW(pCurrentMachine->pszBuffer) + 1;
    pszTmpBuffer = pCurrentMachine->pszBuffer + i;
    dwBufUsed    = sizeof(WCHAR) * i;
    while ((dwBufUsed < dwBufSize) && (* pszTmpBuffer)) {
        do {
            dwThisId = wcstoul(pszTmpBuffer, NULL, 10);
            if (dwThisId > pCurrentMachine->dwLastId) {
                pCurrentMachine->dwLastId = dwThisId;
            }
            i = lstrlenW(pszTmpBuffer) + 1;
            dwBufUsed += (sizeof(WCHAR) * i);
            if (dwBufUsed < dwBufSize) {
                pszTmpBuffer += i;
            }
        }
        while ((dwThisId == 0) && (* pszTmpBuffer)
                               && dwBufUsed < dwBufSize);

        i = lstrlenW(pszTmpBuffer) + 1;
        dwBufUsed += (sizeof(WCHAR) * i);
        if ((* pszTmpBuffer) && (dwBufUsed < dwBufSize)) {
            pszTmpBuffer += i;
        }
    }

    if (pCurrentMachine->dwLastId == 0) {
        Status = PDH_CANNOT_READ_NAME_STRINGS;
        goto Cleanup;
    }

    if (! bNewMachine) {
        G_FREE(pCurrentMachine->ptrStrAry);
        pCurrentMachine->ptrStrAry = NULL;
    }
    pCurrentMachine->ptrStrAry =
                G_ALLOC(sizeof(LPWSTR) * (pCurrentMachine->dwLastId + 1));
    if (pCurrentMachine->ptrStrAry == NULL) {
        Status = PDH_CANNOT_READ_NAME_STRINGS;
        goto Cleanup;
    }

    i            = lstrlenW(pCurrentMachine->pszBuffer) + 1;
    pszTmpBuffer = pCurrentMachine->pszBuffer + i;
    dwBufUsed    = sizeof(WCHAR) * i;
    while ((dwBufUsed < dwBufSize) && (* pszTmpBuffer)) {
        do {
            dwThisId = wcstoul(pszTmpBuffer, NULL, 10);
            i = lstrlenW(pszTmpBuffer) + 1;
            dwBufUsed += (sizeof(WCHAR) * i);
            if (dwBufUsed < dwBufSize) {
                pszTmpBuffer += i;
            }
        }
        while ((dwThisId == 0) && (* pszTmpBuffer)
                               && dwBufUsed < dwBufSize);

        if (dwThisId > 0 && dwThisId <= pCurrentMachine->dwLastId) {
            pCurrentMachine->ptrStrAry[dwThisId] = pszTmpBuffer;
        }

        i = lstrlenW(pszTmpBuffer) + 1;
        dwBufUsed += (sizeof(WCHAR) * i);
        if ((* pszTmpBuffer) && (dwBufUsed < dwBufSize)) {
            pszTmpBuffer += i;
        }
    }

Cleanup:
    if (Status != ERROR_SUCCESS) {
        if (bNewMachine) {
            if (pCurrentMachine != NULL) {
                if (pCurrentMachine->pszBuffer != NULL) {
                    G_FREE(pCurrentMachine->pszBuffer);
                }
                RemoveEntryList(& pCurrentMachine->Entry);
                G_FREE(pCurrentMachine);
            }
        }
        else if (pCurrentMachine != NULL) {
            if (pCurrentMachine->pszBuffer != NULL) {
                G_FREE(pCurrentMachine->pszBuffer);
            }
            RemoveEntryList(& pCurrentMachine->Entry);
            G_FREE(pCurrentMachine);
        }
    }
    return Status;
}

PDH_FUNCTION
PdhiGetCounterPathRecord(
    IN PPDH_LOGGER_CONTEXT CurrentContext,
    IN PVOID               pRecord,
    IN ULONG               dwMaxSize)
{
    PDH_STATUS Status = ERROR_SUCCESS;
    DWORD CurrentSize = sizeof(PDHI_BINARY_LOG_HEADER_RECORD);

    if (CurrentContext->bCounterPathChanged) {
        PPDHI_BINARY_LOG_HEADER_RECORD pBinLogHeader = NULL;
        PVOID pCounterPath;
        ULONG i;

        if (CurrentContext->CounterPathBuffer) {
            G_FREE(CurrentContext->CounterPathBuffer);
        }
        CurrentContext->CounterPathBuffer = G_ALLOC(CurrentSize);
        if (CurrentContext->CounterPathBuffer == NULL) {
            Status = PDH_MEMORY_ALLOCATION_FAILURE;
            goto Cleanup;
        }

        for (i = 0; i < CurrentContext->LoggerCount; i ++) {
            if (! IsListEmpty(& CurrentContext->LogInfo[i].CounterPathList)) {
                PLIST_ENTRY  PathHead =
                                & CurrentContext->LogInfo[i].CounterPathList;
                PLIST_ENTRY  PathNext = PathHead->Flink;
                PPDH_COUNTER_PATH pCurrentPath;

                while (Status == ERROR_SUCCESS && PathNext != PathHead) {
                    PVOID ptrTemp;

                    pCurrentPath = CONTAINING_RECORD(PathNext,
                                                     PDH_COUNTER_PATH,
                                                     Entry);
                    PathNext = PathNext->Flink;

                    ptrTemp = CurrentContext->CounterPathBuffer;
                    CurrentContext->CounterPathBuffer = G_REALLOC(
                            ptrTemp, CurrentSize + pCurrentPath->CounterPathSize);
                    if (CurrentContext->CounterPathBuffer == NULL) {
                        G_FREE(ptrTemp);
                        Status = PDH_MEMORY_ALLOCATION_FAILURE;
                        goto Cleanup;
                    }

                    pCounterPath  = (PVOID) (((PUCHAR)
                            CurrentContext->CounterPathBuffer) + CurrentSize);
                    RtlCopyMemory(pCounterPath,
                                  pCurrentPath->CounterPathBuffer,
                                  pCurrentPath->CounterPathSize);
                    CurrentSize += pCurrentPath->CounterPathSize;
                }
            }
        }
        pBinLogHeader = (PPDHI_BINARY_LOG_HEADER_RECORD)
                        CurrentContext->CounterPathBuffer;
        RtlZeroMemory(pBinLogHeader, sizeof(PDHI_BINARY_LOG_HEADER_RECORD));
        pBinLogHeader->RecHeader.dwType  = BINLOG_TYPE_CATALOG_LIST;
        pBinLogHeader->Info.dwLogVersion = BINLOG_VERSION;

        pBinLogHeader->RecHeader.dwLength = CurrentSize;
        CurrentContext->bCounterPathChanged = FALSE;
    }
    else if (CurrentContext->CounterPathBuffer == NULL) {
        return PDH_ENTRY_NOT_IN_LOG_FILE;
    }
    else {
        CurrentSize = ((PPDHI_BINARY_LOG_HEADER_RECORD)
                        CurrentContext->CounterPathBuffer)->RecHeader.dwLength;
    }

    if (pRecord != NULL) {
        if (dwMaxSize < CurrentSize) {
            CurrentSize = dwMaxSize;
            Status = PDH_MORE_DATA;
        }
        RtlCopyMemory(pRecord, CurrentContext->CounterPathBuffer, CurrentSize);
    }

Cleanup:
    return Status;
}

ULONG
WINAPI
PdhWmiFirstBufferCallback(
    PEVENT_TRACE_LOGFILEW LogFile)
{
    UNREFERENCED_PARAMETER(LogFile);
    return TRUE;
}

ULONG
WINAPI
PdhWmiBufferCallback(
    PEVENT_TRACE_LOGFILEW LogFile)
{
    ULONG               bResult;
    PPDH_LOGGER_CONTEXT CurrentContext;

    UNREFERENCED_PARAMETER(LogFile);
    CurrentContext = GetCurrentContext();
    bResult  = (CurrentContext && CurrentContext->LoggerState == PdhProcessTraceNormal)
             ? (TRUE) : (FALSE);
    return bResult;
}

void
WINAPI
PdhWmiFirstEventCallback(
    PEVENT_TRACE pEvent)
{
    DWORD               dwMofHeader;
    DWORD               dwCurrentBlock;
    DWORD               dwTotalBlocks;
    PPDH_LOGGER_CONTEXT CurrentContext;

    CurrentContext = GetCurrentContext();
    if (pEvent == NULL) {
        goto Cleanup;
    }
    else if (IsEqualGUID(& pEvent->Header.Guid, & EventTraceGuid)) {
        goto Cleanup;
    }
    else if (! IsEqualGUID(& pEvent->Header.Guid, & PdhTransactionGuid)) {
        goto Cleanup;
    }
    else if (CurrentContext == NULL) {
        goto Cleanup;
    }

    dwMofHeader = (pEvent->Header.Class.Version >= PDH_EVENT_VERSION)
                ? (sizeof(GUID) + sizeof(DWORD) + sizeof(DWORD))
                : (sizeof(GUID));

    switch (pEvent->Header.Class.Type) {
    case PDH_LOG_HEADER_EVENT:
        assert(pEvent->MofLength >= sizeof(PDH_WMI_LOG_INFO) + sizeof(GUID));
        PdhiAddWmiLogFileGuid(CurrentContext, (LPGUID) pEvent->MofData);
        if (pEvent->Header.Class.Version >= PDH_EVENT_VERSION) {
            dwCurrentBlock = * (DWORD *)
                    (((LPBYTE) pEvent->MofData) + sizeof(GUID));
            dwTotalBlocks  = * (DWORD *)
                    (((LPBYTE) pEvent->MofData) + sizeof(GUID) + sizeof(DWORD));
            PdhiAddCounterPathRecord(
                    CurrentContext,
                    pEvent,
                    (LPGUID) pEvent->MofData,
                    pEvent->MofLength - dwMofHeader,
                    (PVOID) (((PUCHAR) pEvent->MofData) + dwMofHeader),
                    pEvent->Header.TimeStamp.QuadPart,
                    dwCurrentBlock,
                    dwTotalBlocks,
                    & CurrentContext->bCounterPathChanged);
        }
        else {
            dwCurrentBlock = dwTotalBlocks = 1;
            PdhiAddCounterPathRecord(
                    CurrentContext,
                    pEvent,
                    (LPGUID) pEvent->MofData,
                    pEvent->MofLength - sizeof(GUID) - sizeof(PDH_WMI_LOG_INFO),
                    (PVOID) (((PUCHAR) pEvent->MofData)
                            + sizeof(GUID) + sizeof(PDH_WMI_LOG_INFO)),
                    pEvent->Header.TimeStamp.QuadPart,
                    dwCurrentBlock,
                    dwTotalBlocks,
                    & CurrentContext->bCounterPathChanged);
        }
        break;

    case PDH_LOG_DATA_BLOCK_EVENT:
        PdhWmiGetDataBlockTimeStamp(CurrentContext, pEvent, TRUE);
        PdhiWmiComputeCounterBlocks(CurrentContext, pEvent);
        break;

    case PDH_LOG_COUNTER_STRING_EVENT:
        PdhiAddWmiLogFileGuid(CurrentContext, (LPGUID) pEvent->MofData);
        PdhiAddPerfMachine(CurrentContext, pEvent->MofData, pEvent->MofLength);
        break;

    default:
        DebugPrint((4, "PdhWmiFirstEventCallback(), unknown EventType %d\n",
                pEvent->Header.Class.Type));
        break;
    }

Cleanup:
    return;
}

void
WINAPI
PdhWmiEventCallback(
    PEVENT_TRACE pEvent)
{
    LPGUID              pLogFileGuid;
    ULONG               iLogFile;
    BOOLEAN             bNotifyPDH      = FALSE;
    ULONGLONG           EventTime       = 0;
    ULONGLONG           EventTimePrev   = 0;
    DWORD               dwNumDataBlocks = 0;
    DWORD               dwBlockIndex    = 0;
    DWORD               dwBufferSize    = 0;
    PPDH_LOGGER_CONTEXT CurrentContext  = GetCurrentContext();

    if (CurrentContext == NULL) {
        goto Cleanup;
    }

    if (pEvent == NULL) {
        DebugPrint((4, "PdhWmiEventCallback() with NULL PEVENT_TRACE\n"));
        goto Cleanup;
    }
    else if (IsEqualGUID(& pEvent->Header.Guid, & EventTraceGuid)) {
        goto Cleanup;
    }
    else if ((CurrentContext->LoggerState != PdhProcessTraceNormal) ||
             (! IsEqualGUID(& pEvent->Header.Guid, & PdhTransactionGuid))) {
        goto Cleanup;
    }

    switch (pEvent->Header.Class.Type) {
    case PDH_LOG_HEADER_EVENT:
    case PDH_LOG_COUNTER_STRING_EVENT:
        // counter path information has been collected during the first run
        //
        break;

    case PDH_LOG_DATA_BLOCK_EVENT:
        pLogFileGuid  = (LPGUID) pEvent->MofData;
        iLogFile      = PdhiFindLogFileGuid(CurrentContext, pLogFileGuid);

        if (iLogFile >= CurrentContext->LoggerCount) {
            break;
        }

        EventTimePrev = CurrentContext->LogInfo[iLogFile].TimePrev;
        EventTime     = PdhWmiGetDataBlockTimeStamp(CurrentContext, pEvent, FALSE);
        if (EventTime == 0 || EventTimePrev > EventTime) {
            break;
        }

        if (   pEvent->Header.Class.Version < PDH_EVENT_VERSION
            || EventTimePrev < EventTime) {
            dwNumDataBlocks = * (DWORD *) (  ((LPBYTE) pEvent->MofData)
                                           + sizeof(GUID) + sizeof(DWORD));
            dwBufferSize = (pEvent->Header.Class.Version >= PDH_EVENT_VERSION)
                    ? (dwNumDataBlocks * PDH_BLOCK_BUFFER_SIZE + sizeof(GUID))
                    : (pEvent->MofLength);

            if (CurrentContext->LogInfo[iLogFile].DataBlock == NULL) {
                CurrentContext->LogInfo[iLogFile].DataBlock = G_ALLOC(dwBufferSize);
            }
            else if (   CurrentContext->LogInfo[iLogFile].DataBlockAlloc
                     < dwBufferSize) {
                PVOID ptrTemp = CurrentContext->LogInfo[iLogFile].DataBlock;
                CurrentContext->LogInfo[iLogFile].DataBlock = G_REALLOC(ptrTemp, dwBufferSize);
                if (CurrentContext->LogInfo[iLogFile].DataBlock == NULL) {
                    G_FREE(ptrTemp);
                }
            }
            if (   (CurrentContext->LogInfo[iLogFile].DataBlock != NULL)
                && (pEvent->Header.Class.Version >= PDH_EVENT_VERSION)) {
                RtlCopyMemory(CurrentContext->LogInfo[iLogFile].DataBlock,
                              pLogFileGuid,
                              sizeof(GUID));
                CurrentContext->LogInfo[iLogFile].DataBlockSize   = sizeof(GUID);
                CurrentContext->LogInfo[iLogFile].DataBlockAlloc  = dwBufferSize;
                CurrentContext->LogInfo[iLogFile].ulNumDataBlocks = dwNumDataBlocks;
                CurrentContext->LogInfo[iLogFile].ulDataBlocksCopied = 0;
            }
        }

        dwBlockIndex = * (DWORD *) (((LPBYTE) pEvent->MofData) + sizeof(GUID));
        if (CurrentContext->LogInfo[iLogFile].DataBlock != NULL) {
            if (pEvent->Header.Class.Version >= PDH_EVENT_VERSION) {
                PVOID ptrDataBlock = (PVOID)
                        (  ((LPBYTE) CurrentContext->LogInfo[iLogFile].DataBlock)
                         + sizeof(GUID)
                         + PDH_BLOCK_BUFFER_SIZE * (dwBlockIndex - 1));
                RtlCopyMemory(ptrDataBlock,
                              (PVOID) (((LPBYTE) pEvent->MofData)
                                  + sizeof(GUID) + sizeof(DWORD) + sizeof(DWORD)),
                              pEvent->MofLength - sizeof(GUID)
                                  - sizeof(DWORD) - sizeof(DWORD));
                CurrentContext->LogInfo[iLogFile].ulDataBlocksCopied ++;
                CurrentContext->LogInfo[iLogFile].DataBlockSize +=
                        pEvent->MofLength - sizeof(GUID)
                                          - sizeof(DWORD) - sizeof(DWORD);
            }
            else {
                RtlCopyMemory(CurrentContext->LogInfo[iLogFile].DataBlock,
                              pEvent->MofData,
                              pEvent->MofLength);
                CurrentContext->LogInfo[iLogFile].DataBlockSize = pEvent->MofLength;
            }
        }

        if (pEvent->Header.Class.Version < PDH_EVENT_VERSION) {
            if (DataBlockInfo.CurrentTime == (ULONGLONG) 0) {
                // no CurrentTime comparison, just get the data block
                //
                DataBlockInfo.CurrentTime = EventTime;
            }

            if (DataBlockInfo.CurrentTime <= EventTime) {
                DataBlockInfo.pRecord =
                        CurrentContext->LogInfo[iLogFile].DataBlock;
                DataBlockInfo.dwCurrentSize = pEvent->MofLength;
                DataBlockInfo.Status        = ERROR_SUCCESS;
                bNotifyPDH = TRUE;
                CurrentContext->bDataBlockProcessed = FALSE;
            }
        }
        else if (   CurrentContext->LogInfo[iLogFile].ulDataBlocksCopied
                 >= CurrentContext->LogInfo[iLogFile].ulNumDataBlocks) {
            if (DataBlockInfo.CurrentTime == (ULONGLONG) 0) {
                // no CurrentTime comparison, just get the data block
                //
                DataBlockInfo.CurrentTime = EventTime;
            }

            if (DataBlockInfo.CurrentTime <= EventTime) {
                DataBlockInfo.pRecord       =
                        CurrentContext->LogInfo[iLogFile].DataBlock;
                DataBlockInfo.dwCurrentSize =
                        CurrentContext->LogInfo[iLogFile].DataBlockSize;
                DataBlockInfo.Status        = ERROR_SUCCESS;
                bNotifyPDH = TRUE;
                CurrentContext->bDataBlockProcessed = FALSE;
            }
        }
        break;

    default:
        DebugPrint((4, "PdhWmiEventCallback(), unknown EventType %d\n",
                pEvent->Header.Class.Type));
        break;
    }

Cleanup:
    if (bNotifyPDH) {
        // Signal that we get the current DataBlock event, then wait for next
        // DataBlock requests.
        //
        SetEvent(CurrentContext->hSyncPDH);
        WaitForSingleObject(CurrentContext->hSyncWMI, INFINITE);
    }
}

PDH_FUNCTION
PdhProcessLog(
    PPDH_LOGGER_CONTEXT CurrentContext)
{
    PDH_STATUS  Status = ERROR_SUCCESS;
    LONG        i;

    if (GetLoggerContext(CurrentContext) >= ContextCount) {
        return PDH_INVALID_HANDLE;
    }
    CurrentContext->bFirstRun = TRUE;
    CurrentContext->dwThread  = GetCurrentThreadId();

    do {
        CurrentContext->LoggerState         = PdhProcessTraceNormal;
        CurrentContext->bFirstDataBlockRead = FALSE;
        CurrentContext->bDataBlockProcessed = FALSE;
        for (i = 0; i < (LONG) CurrentContext->LoggerCount; i ++) {
            CurrentContext->LogInfo[i].TimePrev = 0;
            if (CurrentContext->LogInfo[i].DataBlock) {
                G_FREE(CurrentContext->LogInfo[i].DataBlock);
                CurrentContext->LogInfo[i].DataBlock = NULL;
            }
            CurrentContext->LogInfo[i].DataBlockSize = 0;
        }

        RtlZeroMemory(& DataBlockInfo, sizeof(PDH_DATA_BLOCK_TRANSFER));
        Status = ProcessTrace(CurrentContext->LogFileHandle,
                              CurrentContext->LogFileCount,
                              NULL,
                              NULL);
        if (Status != ERROR_SUCCESS && Status != ERROR_CANCELLED) {
            DebugPrint((4, "ProcessTrace(0x%08X,%d) fails (%d,0x%08X)\n",
                    CurrentContext,
                    CurrentContext->LogFileCount,
                    Status,
                    Status));
        }

        if (CurrentContext->bFirstRun) {
            CurrentContext->bFirstRun   = FALSE;
            CurrentContext->LoggerState = PdhProcessTraceRewind;
            CurrentContext->TimeFreq    = CurrentContext->LogInfo[0].TimeFreq;
            for (i = 1; i < (LONG) CurrentContext->LogFileCount; i ++) {
                if (  CurrentContext->TimeFreq
                    > CurrentContext->LogInfo[i].TimeFreq) {
                    CurrentContext->TimeFreq =
                                    CurrentContext->LogInfo[i].TimeFreq;
                }
            }
        }
        else if (CurrentContext->LoggerState == PdhProcessTraceNormal) {
            CurrentContext->LoggerState = PdhProcessTraceComplete;
            DataBlockInfo.Status = PDH_END_OF_LOG_FILE;

            // Wake up PDH main thread so that PdhiReadNextWmiRecord() will
            // notice END_OF_LOG_FILE condition. Wait PDH main thread to wake
            // me up and rewind logger. After wake up, LoggerState should
            // be reset to PdhProcessTraceNormal.
            //
            SetEvent(CurrentContext->hSyncPDH);
            Status = WaitForSingleObject(CurrentContext->hSyncWMI,
                                         INFINITE);
            assert(CurrentContext->LoggerState == PdhProcessTraceRewind);
        }

        for (i = 0; i < (LONG) CurrentContext->LogFileCount; i ++) {
            Status = CloseTrace(CurrentContext->LogFileHandle[i]);
            if (Status != ERROR_SUCCESS) {
                DebugPrint((4, "CloseTrace(%d,%ws) fails (%d,0x%08X)\n",
                        CurrentContext->LogFileHandle[i],
                        CurrentContext->LogFileName[i],
                        Status,
                        Status));
            }
        }

        if (CurrentContext->LoggerState == PdhProcessTraceRewind) {
            EVENT_TRACE_LOGFILEW EvmFile;

            for (i = 0; i < (LONG) CurrentContext->LogFileCount; i ++) {
                RtlZeroMemory(& EvmFile, sizeof(EVENT_TRACE_LOGFILE));
                EvmFile.BufferCallback = PdhWmiBufferCallback;
                EvmFile.EventCallback  = PdhWmiEventCallback;
                EvmFile.LogFileName    = CurrentContext->LogFileName[i];

                CurrentContext->LogFileHandle[i] = OpenTraceW(& EvmFile);

                if (   CurrentContext->LogFileHandle[i] == 0
                    || CurrentContext->LogFileHandle[i] ==
                                (TRACEHANDLE) INVALID_HANDLE_VALUE) {
                    DebugPrint((4,"OpenTraceW(%d,%ws) fails\n",
                            i,
                            CurrentContext->LogFileName[i]));

                    while (--i >= 0) {
                        CloseTrace(CurrentContext->LogFileHandle[i]);
                    }

                    Status = PDH_LOG_FILE_OPEN_ERROR;
                    DataBlockInfo.Status = PDH_LOG_FILE_OPEN_ERROR;
                    SetEvent(CurrentContext->hSyncPDH);
                    goto Cleanup;
                }
            }
        }

    } while (CurrentContext->LoggerState == PdhProcessTraceRewind);

Cleanup:
    CurrentContext->LoggerState = PdhProcessTraceExit;
    return Status;
}

PDH_FUNCTION
PdhiOpenInputWmiLog(
    IN PPDHI_LOG pLog)
{
    PDH_STATUS           Status         = ERROR_SUCCESS;
    PPDHI_LOG            pLogCurrent    = pLog;
    PPDH_LOGGER_CONTEXT  CurrentContext;
    EVENT_TRACE_LOGFILEW EvmFile;
    WCHAR                LogFileName[PDH_MAX_PATH];
    LONG                 i;
    DWORD                ThreadId;

    CurrentContext = (PPDH_LOGGER_CONTEXT) pLog->lpMappedFileBase;
    if (CurrentContext != NULL) {
        if (GetLoggerContext(CurrentContext) < ContextCount) {
            CurrentContext->RefCount ++;
            pLog->lpMappedFileBase = (PVOID) CurrentContext;
            return ERROR_SUCCESS;
        }
        else {
            DebugPrint((4, "PdhiOpenInputWmiLog(0x%08X,0x%08X,0x%08X)\n",
                    pLog,
                    pLog->lpMappedFileBase,
                    CurrentContext));
            return PDH_INVALID_ARGUMENT;
        }
    }

    CurrentContext = (PPDH_LOGGER_CONTEXT) G_ALLOC(sizeof(PDH_LOGGER_CONTEXT));
    if (CurrentContext == NULL) {
        Status = PDH_MEMORY_ALLOCATION_FAILURE;
        goto Cleanup;
    }
    pLog->lpMappedFileBase = (PVOID) CurrentContext;
    RtlZeroMemory(CurrentContext, sizeof(PDH_LOGGER_CONTEXT));


    for (i = 0; i < PDH_MAX_LOGFILES && pLogCurrent; i ++) {
        CurrentContext->LogFileName[i] = pLogCurrent->szLogFileName;
        pLogCurrent                    = pLogCurrent->NextLog;
    }
    CurrentContext->LogFileCount = i;
    CurrentContext->LoggerCount  = 0;

    for (i = 0; i < (LONG) CurrentContext->LogFileCount; i ++) {
        InitializeListHead(& CurrentContext->LogInfo[i].CounterPathList);
        InitializeListHead(& CurrentContext->LogInfo[i].PerfMachineList);
    }
    CurrentContext->RefCount = 1;
    CurrentContext->hSyncWMI = CreateEvent(NULL, FALSE, FALSE, NULL);
    CurrentContext->hSyncPDH = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (CurrentContext->hSyncWMI == NULL || CurrentContext->hSyncPDH == NULL) {
        Status = PDH_INVALID_HANDLE;
        goto Cleanup;
    }

    Status = WAIT_FOR_AND_LOCK_MUTEX(hPdhContextMutex);
    if (Status == ERROR_SUCCESS) {
        if (ContextCount < PDH_MAX_LOGFILES) {
            ContextTable[ContextCount] = CurrentContext;
            ContextCount ++;
        }
        else {
            Status = PDH_MEMORY_ALLOCATION_FAILURE;
        }
        RELEASE_MUTEX(hPdhContextMutex);
    }
    if (Status != ERROR_SUCCESS) {
        goto Cleanup;
    }

    for (i = 0; i < (LONG) CurrentContext->LogFileCount; i ++) {
        RtlZeroMemory(& EvmFile, sizeof(EVENT_TRACE_LOGFILE));
        EvmFile.BufferCallback = PdhWmiFirstBufferCallback;
        EvmFile.EventCallback  = PdhWmiFirstEventCallback;
        EvmFile.LogFileName    = LogFileName;
        _wfullpath(EvmFile.LogFileName,
                   CurrentContext->LogFileName[i],
                   PDH_MAX_PATH);
        CurrentContext->LogFileHandle[i] = OpenTraceW(& EvmFile);
        if (   CurrentContext->LogFileHandle[i] == 0
            || CurrentContext->LogFileHandle[i] ==
                       (TRACEHANDLE) INVALID_HANDLE_VALUE) {
            DebugPrint((4,"OpenTraceW(%d,%ws) fails\n",
                    i,
                    CurrentContext->LogFileName[i]));

            while (--i >= 0) {
                CloseTrace(CurrentContext->LogFileHandle[i]);
            }

            Status = PDH_LOG_FILE_OPEN_ERROR;
            goto Cleanup;
        }
    }

    CurrentContext->hThreadWork = CreateThread(
                     NULL,
                     0,
                     (LPTHREAD_START_ROUTINE) PdhProcessLog,
                     CurrentContext,
                     0,
                     (LPDWORD) & ThreadId);
    if (CurrentContext->hThreadWork == NULL) {
        Status = GetLastError();
        DebugPrint((4, "CreateThread() fails (%d,0x%08X)\n",
                Status, Status));
        for (i = 0; i < (LONG) CurrentContext->LogFileCount; i ++) {
            Status = CloseTrace(CurrentContext->LogFileHandle[i]);
            if (Status != ERROR_SUCCESS) {
                DebugPrint((4, "CloseTrace(0x%08X,%ws) fails (%d,0x%08X)\n",
                        CurrentContext->LogFileHandle[i],
                        CurrentContext->LogFileName[i],
                        Status,
                        Status));
            }
        }
        goto Cleanup;
    }

    WaitForSingleObject(CurrentContext->hSyncPDH, INFINITE);

Cleanup:
    if (Status != ERROR_SUCCESS) {
        if (CurrentContext != NULL) {
            DWORD dwContext = GetLoggerContext(CurrentContext);
            if (dwContext < ContextCount) {
                if (   WAIT_FOR_AND_LOCK_MUTEX(hPdhContextMutex)
                    == ERROR_SUCCESS) {
                    if (dwContext != ContextCount - 1) {
                        ContextTable[dwContext] = ContextTable[ContextCount - 1];
                    }
                    ContextTable[ContextCount - 1] = NULL;
                    ContextCount --;
                    RELEASE_MUTEX(hPdhContextMutex);
                }
                else {
                    ContextTable[dwContext] = NULL;
                }
            }
            if (CurrentContext->hSyncWMI) {
                CloseHandle(CurrentContext->hSyncWMI);
            }
            if (CurrentContext->hSyncPDH) {
                CloseHandle(CurrentContext->hSyncPDH);
            }
            G_FREE(CurrentContext);
            CurrentContext = NULL;
        }
        pLog->lpMappedFileBase = NULL;
    }
    return Status;
}

PDH_FUNCTION
PdhiRewindWmiLog(
    IN PPDHI_LOG pLog)
{
    PDH_STATUS          Status  = PDH_INVALID_HANDLE;
    PPDH_LOGGER_CONTEXT CurrentContext;

    CurrentContext = (PPDH_LOGGER_CONTEXT) pLog->lpMappedFileBase;
    if (GetLoggerContext(CurrentContext) < ContextCount) {
        CurrentContext->LoggerState = PdhProcessTraceRewind;
        SetEvent(CurrentContext->hSyncWMI);
        WaitForSingleObject(CurrentContext->hSyncPDH, INFINITE);
        Status = ERROR_SUCCESS;
    }

    return Status;
}

PDH_FUNCTION
PdhiReadWmiHeaderRecord(
    IN PPDHI_LOG pLog,
    IN LPVOID    pRecord,
    IN DWORD     dwMaxSize)
{
    PDH_STATUS          Status   = ERROR_SUCCESS;
    PPDH_LOGGER_CONTEXT CurrentContext = (PPDH_LOGGER_CONTEXT) pLog->lpMappedFileBase;

    if (GetLoggerContext(CurrentContext) >= ContextCount) {
        return PDH_INVALID_HANDLE;
    }

    // Wait until logfiles are scaned first to collect
    // 1) Counter Path information
    // 2) Time Range information
    //
    while (CurrentContext->bFirstRun) {
        _sleep(1);
    }

    Status = PdhiGetCounterPathRecord(CurrentContext, pRecord, dwMaxSize);
    pLog->pLastRecordRead = CurrentContext->CounterPathBuffer;
    return Status;
}

PDH_FUNCTION
PdhiBuildDataBlock(
    IN PPDHI_LOG pLog,
    IN ULONGLONG TimeStamp)
{
    PDH_STATUS pdhStatus = ERROR_SUCCESS;
    LONG       Offset    = sizeof(PDHI_BINARY_LOG_RECORD_HEADER);
    LONG       CopySize;
    LONG       CheckSize;
    ULONG      i;
    LONG       CurrentSize = PDH_WMI_BUFFER_SIZE_BYTE;

    PPDH_LOGGER_CONTEXT            CurrentContext;
    PPDHI_BINARY_LOG_RECORD_HEADER pHeader;
    PVOID                          pBlock;
    PPDHI_BINARY_LOG_RECORD_HEADER pCounterHeader;
    PPDH_RAW_COUNTER               pSingleCounter;
    PPDHI_LOG_COUNTER_PATH         pCounterPath;

    BOOLEAN bValidBlock;

    CurrentContext = (PPDH_LOGGER_CONTEXT) pLog->lpMappedFileBase;
    if (GetLoggerContext(CurrentContext) >= ContextCount) {
        return PDH_INVALID_HANDLE;
    }

    if (CurrentContext->tmpBuffer != NULL) {
        G_FREE(CurrentContext->tmpBuffer);
        CurrentContext->tmpBuffer = NULL;
    }
    pHeader = (PPDHI_BINARY_LOG_RECORD_HEADER) 
              G_ALLOC(PDH_WMI_BUFFER_SIZE_BYTE);
    if (pHeader == NULL) {
        return PDH_MEMORY_ALLOCATION_FAILURE;
    }
    pHeader->dwType = BINLOG_TYPE_DATA;

    for (i = 0; i < CurrentContext->LoggerCount; i ++) {
        CopySize = CurrentContext->LogInfo[i].DataBlockSize
                 - sizeof(GUID) - sizeof(PDHI_BINARY_LOG_RECORD_HEADER);

        bValidBlock = TRUE;

        if (Offset + CopySize > CurrentSize) {
            while (Offset + CopySize > CurrentSize) {
                CurrentSize += PDH_WMI_BUFFER_SIZE_BYTE;
            }
            CurrentContext->tmpBuffer = pHeader;
            pHeader = G_REALLOC(CurrentContext->tmpBuffer, CurrentSize);
            if (pHeader == NULL) {
                G_FREE(CurrentContext->tmpBuffer);
                CurrentContext->tmpBuffer = NULL;
                return PDH_MEMORY_ALLOCATION_FAILURE;
            }
            CurrentContext->tmpBuffer = NULL;
        }
        pBlock   = (PVOID) (((PUCHAR) pHeader) + Offset);

        if (   (CurrentContext->LogInfo[i].DataBlock)
            && (CopySize > 0)
            && (   DataBlockInfo.CurrentTime
                <= CurrentContext->LogInfo[i].TimeEnd)) {

            CheckSize = sizeof(GUID);
            while (bValidBlock && CheckSize < CopySize) {
                pCounterHeader = (PPDHI_BINARY_LOG_RECORD_HEADER)
                        (((PUCHAR) CurrentContext->LogInfo[i].DataBlock) + CheckSize);
                if (LOWORD(pCounterHeader->dwType) == BINLOG_START_WORD) {
                    CheckSize += pCounterHeader->dwLength;
                }
                else {
                    bValidBlock = FALSE;
                }
            }
        }
        else {
            bValidBlock = FALSE;
        }

        if (bValidBlock == TRUE) {
            RtlCopyMemory(
                pBlock,
                (PVOID) (  ((PUCHAR) CurrentContext->LogInfo[i].DataBlock)
                         + sizeof(GUID)
                         + sizeof(PDHI_BINARY_LOG_RECORD_HEADER)),
                CopySize);
            Offset += CopySize;
        }
        else {
            // need to sneak in pseudo counter block
            //
            PVOID             pCounterBlock;
            ULONG             BlockSize = 0;
            ULONG             j;

            for (j = 0; j < CurrentContext->LogInfo[i].ulCounters; j ++) {
                pBlock       = (PVOID) (((PUCHAR) pHeader) + Offset);
                RtlCopyMemory(pBlock,
                              & PdhNullCounterHeader,
                              sizeof(PDHI_BINARY_LOG_RECORD_HEADER));
                pSingleCounter = (PPDH_RAW_COUNTER) (((PUCHAR) pBlock)
                                + sizeof(PDHI_BINARY_LOG_RECORD_HEADER));
                RtlCopyMemory(pSingleCounter,
                              & PdhNullCounter,
                              sizeof(PDH_RAW_COUNTER));
                pSingleCounter->TimeStamp.dwLowDateTime  = LODWORD(TimeStamp);
                pSingleCounter->TimeStamp.dwHighDateTime = HIDWORD(TimeStamp);
                Offset         = Offset + sizeof(PDH_RAW_COUNTER)
                               + sizeof(PDHI_BINARY_LOG_RECORD_HEADER);
            }
        }
    }
    pHeader->dwLength = Offset;
    CurrentContext->tmpBuffer = pHeader;

    return pdhStatus;
}

PDH_FUNCTION
PdhiReadNextWmiRecord(
    IN PPDHI_LOG pLog,
    IN LPVOID    pRecord,
    IN DWORD     dwMaxSize,
    IN BOOLEAN   bAllCounter)
{
    PDH_STATUS          Status = ERROR_SUCCESS;
    PPDH_LOGGER_CONTEXT CurrentContext;
    ULONGLONG           CurrentTime;

    CurrentContext = (PPDH_LOGGER_CONTEXT) pLog->lpMappedFileBase;
    if (GetLoggerContext(CurrentContext) >= ContextCount) {
        Status = PDH_INVALID_HANDLE;
        goto Cleanup;
    }

    // Wait until logfiles are scaned first to collect
    // 1) Counter Path information
    // 2) Time Range information
    //
    while (CurrentContext->bFirstRun) {
        _sleep(1);
    }

    if (CurrentContext->LoggerState == PdhProcessTraceComplete) {
        CurrentContext->LoggerState = PdhProcessTraceRewind;
        SetEvent(CurrentContext->hSyncWMI);
        WaitForSingleObject(CurrentContext->hSyncPDH, INFINITE);
        Status = PDH_END_OF_LOG_FILE;
        goto Cleanup;
    }

    if (! CurrentContext->bFirstDataBlockRead) {
        CurrentContext->bFirstDataBlockRead = TRUE;
        CurrentContext->bDataBlockProcessed = TRUE;
    }
    else if (! CurrentContext->bDataBlockProcessed) {
        CurrentContext->bDataBlockProcessed = TRUE;
    }
    else {
        DataBlockInfo.CurrentTime = (ULONGLONG) 0;
        SetEvent(CurrentContext->hSyncWMI);
        WaitForSingleObject(CurrentContext->hSyncPDH, INFINITE);
    }

    if (CurrentContext->LoggerState == PdhProcessTraceComplete) {
        CurrentContext->LoggerState = PdhProcessTraceRewind;
        SetEvent(CurrentContext->hSyncWMI);
        WaitForSingleObject(CurrentContext->hSyncPDH, INFINITE);
        Status = PDH_END_OF_LOG_FILE;
        goto Cleanup;
    }

    if (bAllCounter && CurrentContext->LogFileCount > 1) {
        CurrentTime = DataBlockInfo.CurrentTime;

        while (   (CurrentContext->LoggerState != PdhProcessTraceComplete)
               && (DataBlockInfo.CurrentTime - CurrentTime <= TIME_DELTA)) {
            DataBlockInfo.CurrentTime = (ULONGLONG) 0;
            SetEvent(CurrentContext->hSyncWMI);
            WaitForSingleObject(CurrentContext->hSyncPDH, INFINITE);
        }

        CurrentContext->bDataBlockProcessed = FALSE;
        Status = DataBlockInfo.Status;
        if (Status == ERROR_SUCCESS) {
            Status = PdhiBuildDataBlock(pLog, CurrentTime);
        }
        if (Status == ERROR_SUCCESS) {
            pLog->pLastRecordRead = CurrentContext->tmpBuffer;
        }
    }
    else {
        if (bAllCounter) {
            pLog->pLastRecordRead =
                    (((PUCHAR) DataBlockInfo.pRecord) + sizeof(GUID));
        }
        else {
            pLog->pLastRecordRead = ((PUCHAR) DataBlockInfo.pRecord);
        }
        CurrentContext->bDataBlockProcessed = TRUE;
        Status = DataBlockInfo.Status;
    }

    if (Status == ERROR_SUCCESS) {
        if (dwMaxSize < DataBlockInfo.dwCurrentSize - sizeof(GUID)) {
            Status = PDH_MORE_DATA;
        }
        if (pRecord) {
            RtlCopyMemory(pRecord,
                     pLog->pLastRecordRead,
                     (Status == ERROR_SUCCESS) ? (DataBlockInfo.dwCurrentSize)
                                               : (dwMaxSize));
        }
    }

Cleanup:
    return Status;
}

PDH_FUNCTION
PdhiReadTimeWmiRecord(
    IN PPDHI_LOG pLog,
    IN ULONGLONG TimeStamp,
    IN LPVOID    pRecord,
    IN DWORD     dwMaxSize)
{
    PDH_STATUS          Status      = ERROR_SUCCESS;
    PPDH_LOGGER_CONTEXT CurrentContext;
    BOOLEAN             TimeInRange = FALSE;
    BOOLEAN             bRewind     = TRUE;
    ULONG               i;

    CurrentContext = (PPDH_LOGGER_CONTEXT) pLog->lpMappedFileBase;
    if (GetLoggerContext(CurrentContext) >= ContextCount) {
        Status = PDH_INVALID_HANDLE;
        goto Cleanup;
    }

    if (TimeStamp == MIN_TIME_VALUE) {
        TimeStamp = CurrentContext->LogInfo[0].TimeStart;
    }
    if (TimeStamp == MAX_TIME_VALUE) {
        TimeStamp = CurrentContext->LogInfo[0].TimeEnd;
    }
    for (i = 0; i < (ULONG) CurrentContext->LoggerCount; i ++) {
        if (   TimeStamp >= CurrentContext->LogInfo[i].TimeStart
            && TimeStamp <= CurrentContext->LogInfo[i].TimeEnd) {
            TimeInRange = TRUE;
            break;
        }
    }

    if (! TimeInRange) {
        Status = PDH_INVALID_ARGUMENT;
        goto Cleanup;
    }

    // Wait until logfiles are scaned first to collect
    // 1) Counter Path information
    // 2) Time Range information
    //
    while (CurrentContext->bFirstRun) {
        _sleep(1);
    }

    if (CurrentContext->LoggerState == PdhProcessTraceComplete) {
        CurrentContext->LoggerState = PdhProcessTraceRewind;
        SetEvent(CurrentContext->hSyncWMI);
        WaitForSingleObject(CurrentContext->hSyncPDH, INFINITE);
        Status = PDH_END_OF_LOG_FILE;
        goto Cleanup;
    }

ReScan:
    if (! CurrentContext->bFirstDataBlockRead) {
        CurrentContext->bFirstDataBlockRead = TRUE;
        CurrentContext->bDataBlockProcessed = TRUE;
    }
    else if (! CurrentContext->bDataBlockProcessed) {
        CurrentContext->bDataBlockProcessed = TRUE;
    }
    else {
        DataBlockInfo.CurrentTime = 0;
        SetEvent(CurrentContext->hSyncWMI);
        WaitForSingleObject(CurrentContext->hSyncPDH, INFINITE);
    }

    if (CurrentContext->LoggerState == PdhProcessTraceComplete) {
        CurrentContext->LoggerState = PdhProcessTraceRewind;
        SetEvent(CurrentContext->hSyncWMI);
        WaitForSingleObject(CurrentContext->hSyncPDH, INFINITE);
        Status = PDH_END_OF_LOG_FILE;
        goto Cleanup;
    }
    else if (DataBlockInfo.CurrentTime > TimeStamp) {
        if (bRewind) {
            bRewind = FALSE;
            CurrentContext->LoggerState = PdhProcessTraceRewind;
            SetEvent(CurrentContext->hSyncWMI);
            WaitForSingleObject(CurrentContext->hSyncPDH, INFINITE);
            goto ReScan;
        }
    }

    while (   (CurrentContext->LoggerState != PdhProcessTraceComplete)
           && ((  ((LONGLONG) TimeStamp)
                - ((LONGLONG) DataBlockInfo.CurrentTime))
                > TIME_DELTA)) {
        DataBlockInfo.CurrentTime = (ULONGLONG) 0;
        SetEvent(CurrentContext->hSyncWMI);
        WaitForSingleObject(CurrentContext->hSyncPDH, INFINITE);
    }

    CurrentContext->bDataBlockProcessed = TRUE;
    Status = DataBlockInfo.Status;
    if (Status == ERROR_SUCCESS) {
        Status = PdhiBuildDataBlock(pLog, TimeStamp);
    }
    if (Status == ERROR_SUCCESS) {
        pLog->pLastRecordRead = CurrentContext->tmpBuffer;
    }

    if (Status == ERROR_SUCCESS) {
        if (dwMaxSize < DataBlockInfo.dwCurrentSize - sizeof(GUID)) {
            Status = PDH_MORE_DATA;
        }
        if (pRecord) {
            RtlCopyMemory(pRecord,
                     (((PUCHAR) DataBlockInfo.pRecord) + sizeof(GUID)),
                     (Status == ERROR_SUCCESS) ? (DataBlockInfo.dwCurrentSize)
                                               : (dwMaxSize));
        }
    }

Cleanup:
    return Status;
}

PDH_FUNCTION
PdhiGetTimeRangeFromWmiLog (
    IN PPDHI_LOG      pLog,
    IN LPDWORD        pdwNumEntries,
    IN PPDH_TIME_INFO pInfo,
    IN LPDWORD        pdwBufferSize)
{
    PDH_STATUS          Status = ERROR_SUCCESS;
    PPDH_LOGGER_CONTEXT CurrentContext;
    ULONG               i;
    ULONGLONG           StartTime;
    ULONGLONG           EndTime;
    ULONG               EntryCount;

    CurrentContext = (PPDH_LOGGER_CONTEXT) pLog->lpMappedFileBase;
    if (GetLoggerContext(CurrentContext) >= ContextCount) {
        return PDH_INVALID_HANDLE;
    }

    // Wait until logfiles are scaned first to collect
    // 1) Counter Path information
    // 2) Time Range information
    //
    while (CurrentContext->bFirstRun) {
        _sleep(1);
    }

    for (StartTime  = CurrentContext->LogInfo[0].TimeStart,
         EndTime    = CurrentContext->LogInfo[0].TimeEnd,
         EntryCount = CurrentContext->LogInfo[0].ValidEntries,
         i = 1;
         i < CurrentContext->LoggerCount;
         i ++) {
        if (   StartTime == 0
            || (   CurrentContext->LogInfo[i].TimeStart != 0
                && StartTime > CurrentContext->LogInfo[i].TimeStart)) {
            StartTime = CurrentContext->LogInfo[i].TimeStart;
        }
        if (EndTime   < CurrentContext->LogInfo[i].TimeEnd)
            EndTime   = CurrentContext->LogInfo[i].TimeEnd;
        EntryCount   += CurrentContext->LogInfo[i].ValidEntries;
    }

    if (* pdwBufferSize >=  sizeof(PDH_TIME_INFO)) {
        * (LONGLONG *) (& pInfo->StartTime) = StartTime;
        * (LONGLONG *) (& pInfo->EndTime)   = EndTime;
        pInfo->SampleCount                  = EntryCount;
        * pdwBufferSize                     = sizeof(PDH_TIME_INFO);
        * pdwNumEntries                     = 1;
    }
    else {
        Status = PDH_MORE_DATA;
    }

    return Status;
}

PPDH_WMI_PERF_MACHINE
PdhWmiGetLogNameTable(
    IN  PPDHI_LOG pLog,
    IN  LPCWSTR   szMachineName,
    IN  DWORD     dwLangId
)
{
    PPDH_WMI_PERF_MACHINE pReturnMachine = NULL;
    PPDH_LOGGER_CONTEXT   CurrentContext;
    DWORD                 i;

    CurrentContext = (PPDH_LOGGER_CONTEXT) pLog->lpMappedFileBase;
    if (GetLoggerContext(CurrentContext) >= ContextCount) {
        return NULL;
    }

    for (i = 0;
         pReturnMachine == NULL && i < CurrentContext->LoggerCount;
         i ++) {
        if (! IsListEmpty(& CurrentContext->LogInfo[i].PerfMachineList)) {
            PLIST_ENTRY pHead = & CurrentContext->LogInfo[i].PerfMachineList;
            PLIST_ENTRY pNext = pHead->Flink;
            while (pNext != pHead) {
                PPDH_WMI_PERF_MACHINE pMachine =
                        CONTAINING_RECORD(pNext, PDH_WMI_PERF_MACHINE, Entry);
                if (lstrcmpiW(pMachine->pszBuffer, szMachineName) == 0) {
                    pReturnMachine = pMachine;
                    break;
                }
                pNext = pNext->Flink;
            }
        }
    }
    return pReturnMachine;
}

PPDH_WMI_PERF_OBJECT
PdhWmiAddPerfObject(
    IN  PPDHI_LOG        pLog,
    IN  LPCWSTR          szMachineName,
    IN  DWORD            dwLangId,
    IN  LPCWSTR          szObjectName,
    IN  DWORD            dwObjectId,
    IN  PPERF_DATA_BLOCK pDataBlock
)
{
    PPDH_WMI_PERF_OBJECT  pPerfObject  = NULL;
    PPDH_WMI_PERF_MACHINE pPerfMachine =
                    PdhWmiGetLogNameTable(pLog, szMachineName, dwLangId);

    PLIST_ENTRY           pHead;
    PLIST_ENTRY           pNext;
    PPDH_WMI_PERF_OBJECT  pCurrentObj;

    if (pPerfMachine == NULL) {
        SetLastError(PDH_ENTRY_NOT_IN_LOG_FILE);
        goto Cleanup;
    }

    pHead = & pPerfMachine->LogObjectList;
    pNext = pHead->Flink;
    while (pNext != pHead) {
        pCurrentObj = CONTAINING_RECORD(pNext, PDH_WMI_PERF_OBJECT, Entry);
        if (lstrcmpiW(pCurrentObj->szObjectName, szObjectName) == 0) {
            pPerfObject = pCurrentObj;
            break;
        }
        pNext = pNext->Flink;
    }

    if (pPerfObject != NULL) {
        goto Cleanup;
    }

    pPerfObject = G_ALLOC(sizeof(PDH_WMI_PERF_OBJECT));
    if (pPerfObject == NULL) {
        SetLastError(PDH_MEMORY_ALLOCATION_FAILURE);
        goto Cleanup;
    }
    pPerfObject->ptrBuffer = G_ALLOC(pDataBlock->TotalByteLength
                           + sizeof(WCHAR) * (lstrlenW(szObjectName) + 1));
    if (pPerfObject->ptrBuffer == NULL) {
        G_FREE(pPerfObject);
        SetLastError(PDH_MEMORY_ALLOCATION_FAILURE);
        goto Cleanup;
    }
    pPerfObject->dwObjectId = dwObjectId;
    RtlCopyMemory(pPerfObject->ptrBuffer,
                  pDataBlock,
                  pDataBlock->TotalByteLength);
    pPerfObject->szObjectName = (LPWSTR)
            (((LPBYTE) pPerfObject->ptrBuffer) + pDataBlock->TotalByteLength);
    lstrcpyW(pPerfObject->szObjectName, szObjectName);
    InsertTailList(& pPerfMachine->LogObjectList, & pPerfObject->Entry);

Cleanup:
    return pPerfObject;
}

DWORD
PdhWmiGetLogPerfIndexByName(
    IN  PPDHI_LOG pLog,
    IN  LPCWSTR   szMachineName,
    IN  DWORD     dwLangId,
    IN  LPCWSTR   szNameBuffer
)
{
    PPDH_WMI_PERF_MACHINE pMachine;
    DWORD                 dwLastIndex;
    LPWSTR              * pNameArray;
    DWORD                 dwIndex;

    SetLastError(ERROR_SUCCESS);

    pMachine = PdhWmiGetLogNameTable(pLog, szMachineName, dwLangId);
    if (pMachine != NULL && pMachine->ptrStrAry != NULL) {
        dwLastIndex = pMachine->dwLastId;
        pNameArray  = pMachine->ptrStrAry;

        for (dwIndex = 1; dwIndex <= dwLastIndex; dwIndex ++) {
            if (lstrcmpiW(szNameBuffer, pNameArray[dwIndex]) == 0) {
                if ((dwIndex & 0x00000001) == 0) {
                    // counter name index should be even integer
                    //
                    break;
                }
            }
        }

        if (dwIndex > dwLastIndex) {
            SetLastError(PDH_STRING_NOT_FOUND);
            dwIndex = 0;
        }
    }
    else {
        SetLastError(PDH_ENTRY_NOT_IN_LOG_FILE);
        dwIndex = 0;
    }

    return dwIndex;
}

LPWSTR
PdhWmiGetLogPerfNameByIndex (
    IN  PPDHI_LOG pLog,
    IN  LPCWSTR   szMachineName,
    IN  DWORD     dwLangId,
    IN  DWORD     dwIndex
)
{
    PPDH_WMI_PERF_MACHINE pMachine;
    LPWSTR                pszReturnName = NULL;
    LPWSTR              * pNameArray;
    static WCHAR          szNumber[16];

    pMachine = PdhWmiGetLogNameTable(pLog, szMachineName, dwLangId);
    if (pMachine != NULL && pMachine->ptrStrAry != NULL
                         && dwIndex < pMachine->dwLastId) {
        pNameArray    = pMachine->ptrStrAry;
        pszReturnName = pNameArray[dwIndex];
    }

    if (pszReturnName == NULL) {
        // unable to find name string, return numeric index string
        //
        memset(szNumber, 0, sizeof (szNumber));
        _ltow(dwIndex, szNumber, 10);
        pszReturnName = szNumber;
    }
    return pszReturnName;
}

PPDHI_BINARY_LOG_RECORD_HEADER
PdhiGetWmiSubRecord(
    IN PPDHI_LOG                      pLog,
    IN PPDHI_BINARY_LOG_RECORD_HEADER pRecord,
    IN DWORD                          dwRecordId,
    IN LPGUID                         LogFileGuid)
{
    PPDHI_BINARY_LOG_RECORD_HEADER pThisRecord;
    PPDH_LOGGER_CONTEXT            CurrentContext;

    DWORD dwRecordType;
    DWORD dwRecordLength;
    DWORD dwBytesProcessed;
    DWORD dwThisSubRecordId;
    DWORD dwLocalIndex;
    ULONG i;

    CurrentContext = (PPDH_LOGGER_CONTEXT) pLog->lpMappedFileBase;
    if (GetLoggerContext(CurrentContext) >= ContextCount) {
        return (NULL);
    }

    dwLocalIndex = dwRecordId;
    for (i = 0; i < CurrentContext->LoggerCount; i ++) {
        PLIST_ENTRY pHead = & CurrentContext->LogInfo[i].CounterPathList;
        if (! IsListEmpty(pHead)) {
            PLIST_ENTRY pNext = pHead->Flink;
            PPDH_COUNTER_PATH pCounterPath =
                    CONTAINING_RECORD(pNext, PDH_COUNTER_PATH, Entry);
            if (dwLocalIndex <= pCounterPath->CounterCount) {
                break;
            }
            dwLocalIndex -= pCounterPath->CounterCount;
        }
    }
    assert(i < CurrentContext->LoggerCount);

    __try {
        if (   (i >= CurrentContext->LoggerCount)
            || (! IsEqualGUID(  LogFileGuid,
                              & CurrentContext->LogInfo[i].LogFileGuid))) {
            // binary log record does not contain intended object's counter
            //
            return NULL;
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER) {
        return NULL;
    }

    dwRecordType   = ((PPDHI_BINARY_LOG_RECORD_HEADER) pRecord)->dwType;
    dwRecordLength = ((PPDHI_BINARY_LOG_RECORD_HEADER) pRecord)->dwLength;

    pThisRecord = (PPDHI_BINARY_LOG_RECORD_HEADER)
                  (((LPBYTE) pRecord) + sizeof(PDHI_BINARY_LOG_RECORD_HEADER));
    dwBytesProcessed = sizeof(PDHI_BINARY_LOG_RECORD_HEADER);

    if (dwBytesProcessed < dwRecordLength) {
        dwThisSubRecordId = 1;
        while (dwThisSubRecordId < dwLocalIndex) {
            if ((WORD)(pThisRecord->dwType & 0x0000FFFF) == BINLOG_START_WORD) {
                dwBytesProcessed += pThisRecord->dwLength;
                pThisRecord = (PPDHI_BINARY_LOG_RECORD_HEADER)
                              (((LPBYTE)pThisRecord) + pThisRecord->dwLength);
                if (dwBytesProcessed >= dwRecordLength) {
                    break;
                }
                else {
                    dwThisSubRecordId ++;
                }
            }
            else {
                break;
            }
        }
    }
    else {
        dwThisSubRecordId = 0;
    }

    if (dwThisSubRecordId == dwLocalIndex) {
        if ((WORD)(pThisRecord->dwType & 0x0000FFFF) != BINLOG_START_WORD) {
            pThisRecord = NULL;
        }
    }
    else {
        pThisRecord = NULL;
    }

    return pThisRecord;
}

PDH_FUNCTION
PdhWmiEnumObjectItemsFromDataBlock(
    IN PPDHI_LOG          pLog,
    IN PPERF_DATA_BLOCK   pDataBlock,
    IN LPCWSTR            szMachineName,
    IN LPCWSTR            szObjectName,
    IN DWORD              dwObjectId,
    IN DWORD              dwLangId,
    IN PDHI_COUNTER_TABLE CounterTable
)
{
    PDH_STATUS         Status     = ERROR_SUCCESS;
    PERF_OBJECT_TYPE * pObjectDef = GetObjectDefByTitleIndex(pDataBlock, dwObjectId);
    PERF_COUNTER_DEFINITION  * pCountDef;
    PERF_INSTANCE_DEFINITION * pInstDef;

    DWORD  dwItems;
    LPWSTR szItemName;
    DWORD  dwItemLen;
    WCHAR  szInstanceName[1024];

    PPDHI_INST_LIST pInstList      = NULL;
    PPDHI_INSTANCE  pInstance      = NULL;
    PPDHI_INST_LIST pFirstInstList = NULL;

    PPDH_WMI_PERF_OBJECT pPerfObj;

    if (pObjectDef == NULL) {
        Status = PDH_ENTRY_NOT_IN_LOG_FILE;
        goto Cleanup;
    }
    pPerfObj = PdhWmiAddPerfObject(pLog,
                                   szMachineName,
                                   9,
                                   szObjectName,
                                   dwObjectId,
                                   pDataBlock);
    if (pPerfObj == NULL) {
        Status = GetLastError();
        goto Cleanup;
    }

    dwItems   = 0;
    pCountDef = FirstCounter(pObjectDef);
    while (dwItems < (DWORD) pObjectDef->NumCounters) {
        szItemName = PdhWmiGetLogPerfNameByIndex(
                        pLog,
                        szMachineName,
                        dwLangId,
                        pCountDef->CounterNameTitleIndex);
        Status = PdhiFindCounterInstList(
                CounterTable, szItemName, & pInstList);
        if (Status == ERROR_SUCCESS && pFirstInstList == NULL
                                    && pInstList != NULL) {
            pFirstInstList = pInstList;
        }
        dwItems ++;
        pCountDef  = NextCounter(pCountDef);
    }

    if (pFirstInstList == NULL) {
        Status = PDH_NO_COUNTERS;
        goto Cleanup;
    }

    if (pObjectDef->NumInstances != PERF_NO_INSTANCES) {
        dwItems  = 0;
        pInstDef = FirstInstance(pObjectDef);
        while (dwItems < (DWORD) pObjectDef->NumInstances) {
            ZeroMemory(szInstanceName, sizeof(WCHAR) * 1024);
            dwItemLen = GetFullInstanceNameStr(
                            pDataBlock,
                            pObjectDef,
                            pInstDef,
                            szInstanceName);
            if (dwItemLen > 0) {
                Status = PdhiFindInstance(
                         & pFirstInstList->InstList,
                         szInstanceName,
                         (lstrcmpiW(szInstanceName, L"_Total") == 0) ? FALSE : TRUE,
                         & pInstance);
            }
            dwItems ++;
            pInstDef  = NextInstance(pInstDef);
        }
    }

Cleanup:
    return Status;
}

PDH_FUNCTION
PdhiEnumObjectItemsFromWmiLog (
    IN PPDHI_LOG          pLog,
    IN LPCWSTR            szMachineName,
    IN LPCWSTR            szObjectName,
    IN PDHI_COUNTER_TABLE CounterTable,
    IN DWORD              dwDetailLevel,
    IN DWORD              dwFlags
)
{
    DWORD       dwTempBufferSize;
    LPVOID      pTempBuffer = NULL;
    LPVOID      ptrTemp;
    PDH_STATUS  pdhStatus   = ERROR_SUCCESS;

    PPDHI_BINARY_LOG_HEADER_RECORD  pHeader;
    PPDHI_LOG_COUNTER_PATH          pPath;
    PPDHI_BINARY_LOG_RECORD_HEADER  pThisMasterRecord;
    PPDHI_BINARY_LOG_RECORD_HEADER  pThisSubRecord;
    PPDHI_RAW_COUNTER_ITEM_BLOCK    pDataBlock;
    PPDHI_RAW_COUNTER_ITEM          pDataItem;

    DWORD       dwBytesProcessed;
    LONG        nItemCount = 0;
    LPBYTE      pFirstChar;
    LPWSTR      szThisMachineName  = NULL;
    LPWSTR      szThisObjectName   = NULL;
    LPWSTR      szThisCounterName  = NULL;
    LPWSTR      szThisInstanceName = NULL;
    LPWSTR      szThisParentName;
    WCHAR       szCompositeInstance[1024];
    DWORD       dwRecordLength;
    BOOL        bCopyThisObject;
    BOOL        bMachineDataBlockScaned = FALSE;
    BOOL        bInstanceListScanned = FALSE;
    DWORD       dwIndex;
    DWORD       dwDataItemIndex;

    DWORD            dwObjectId;
    PPERF_DATA_BLOCK pPerfBlock;

    PPDHI_INST_LIST pInstList      = NULL;
    PPDHI_INSTANCE  pInstance      = NULL;

    PPDH_LOGGER_CONTEXT CurrentContext;

    UNREFERENCED_PARAMETER (dwDetailLevel);
    UNREFERENCED_PARAMETER (dwFlags);

    CurrentContext = (PPDH_LOGGER_CONTEXT) pLog->lpMappedFileBase;
    if (GetLoggerContext(CurrentContext) >= ContextCount) {
        pdhStatus = PDH_INVALID_ARGUMENT;
        goto Cleanup;
    }

    if (pLog->dwMaxRecordSize == 0) {
        // no size is defined so start with 64K
        pLog->dwMaxRecordSize = 0x010000;
    }

    dwTempBufferSize = pLog->dwMaxRecordSize;
    pTempBuffer = G_ALLOC(dwTempBufferSize);
    if (pTempBuffer == NULL) {
        pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE;
        goto Cleanup;
    }

    // read in the catalog record

    pdhStatus = PdhiReadWmiHeaderRecord(pLog, pTempBuffer, dwTempBufferSize);
    while (pdhStatus == PDH_MORE_DATA) {
        if (* (WORD *) pTempBuffer == BINLOG_START_WORD) {
            dwTempBufferSize = ((DWORD *) pTempBuffer)[1];
            if (dwTempBufferSize < pLog->dwMaxRecordSize) {
                pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE;
            }
            else {
                pLog->dwMaxRecordSize = dwTempBufferSize;
            }
        }
        else {
            pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE;
        }

        ptrTemp     = pTempBuffer;
        pTempBuffer = G_REALLOC(ptrTemp, dwTempBufferSize);
        if (pTempBuffer == NULL) {
            G_FREE(ptrTemp);
            pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE;
        }
        if (pdhStatus == PDH_MORE_DATA) {
            pdhStatus = PdhiReadWmiHeaderRecord(
                                pLog, pTempBuffer, dwTempBufferSize);
        }
    }

    if (pdhStatus != ERROR_SUCCESS) {
        goto Cleanup;
    }

    pHeader        = (PPDHI_BINARY_LOG_HEADER_RECORD) pTempBuffer;
    dwRecordLength = ((PPDHI_BINARY_LOG_RECORD_HEADER) pTempBuffer)->dwLength;

    pPath = (PPDHI_LOG_COUNTER_PATH)
            ((LPBYTE) pTempBuffer + sizeof(PDHI_BINARY_LOG_HEADER_RECORD));
    dwBytesProcessed = sizeof(PDHI_BINARY_LOG_HEADER_RECORD);

    dwIndex = 0;
    while (dwBytesProcessed < dwRecordLength) {
        bCopyThisObject  = FALSE;
        szThisObjectName = NULL;
        dwIndex ++;
        pFirstChar       = (LPBYTE) & pPath->Buffer[0];

        if (pPath->lMachineNameOffset >= 0L) {
            szThisMachineName = (LPWSTR) (  (LPBYTE) pFirstChar
                                          + pPath->lMachineNameOffset);
            if (lstrcmpiW(szThisMachineName, szMachineName) == 0) {
                if (pPath->dwFlags & PDHIC_COUNTER_BLOCK) {
                    if (bMachineDataBlockScaned == FALSE) {
                        bCopyThisObject         = TRUE;
                        bMachineDataBlockScaned = TRUE;
                    }
                }
                else if (pPath->lObjectNameOffset >= 0) {
                    szThisObjectName = (LPWSTR) (  (LPBYTE) pFirstChar
                                                 + pPath->lObjectNameOffset);
                    if (lstrcmpiW(szThisObjectName, szObjectName) == 0) {
                        bCopyThisObject = TRUE;
                    }
                }
            }
        }
        else if (pPath->lObjectNameOffset >= 0) {
            szThisObjectName = (LPWSTR) (  (LPBYTE) pFirstChar
                                         + pPath->lObjectNameOffset);
            if (lstrcmpiW(szThisObjectName, szObjectName) == 0) {
                bCopyThisObject = TRUE;
            }
        }

        dwObjectId = 0;
        if (bCopyThisObject) {
            if (   (pPath->dwFlags & PDHIC_COUNTER_OBJECT)
                || (pPath->dwFlags & PDHIC_COUNTER_BLOCK)) {
                dwObjectId = PdhWmiGetLogPerfIndexByName(
                        pLog, szMachineName, 9, szObjectName);
                if (dwObjectId == 0) {
                    dwObjectId = wcstoul(szObjectName, NULL, 10);
                    if (dwObjectId == 0) {
                        szThisCounterName = NULL;
                        bCopyThisObject   = FALSE;
                    }
                }
            }
            else if (pPath->lCounterOffset > 0) {
                szThisCounterName = (LPWSTR) (  (LPBYTE)pFirstChar
                                              + pPath->lCounterOffset);
            }
            else {
                szThisCounterName = NULL;
                bCopyThisObject   = FALSE;
            }
        }

        if (bCopyThisObject) {
            if (dwObjectId == 0) {
                pdhStatus = PdhiFindCounterInstList(
                            CounterTable,
                            szThisCounterName,
                            & pInstList);
                if (pdhStatus == ERROR_SUCCESS && pInstList != NULL) {
                    nItemCount ++;
                }
            }

            if (pPath->lInstanceOffset >= 0) {
                szThisInstanceName = (LPWSTR) (  (LPBYTE) pFirstChar
                                               + pPath->lInstanceOffset);
            }

            if (   dwObjectId > 0
                || (pInstList != NULL && szThisInstanceName != NULL)) {
                if (szThisInstanceName && * szThisInstanceName != SPLAT_L) {
                    if (pPath->lParentOffset >= 0) {
                        szThisParentName = (LPWSTR)
                            ((LPBYTE) pFirstChar + pPath->lParentOffset);
                        lstrcpyW(szCompositeInstance, szThisParentName);
                        lstrcatW(szCompositeInstance, cszSlash);
                        lstrcatW(szCompositeInstance, szThisInstanceName);
                    }
                    else {
                        lstrcpyW(szCompositeInstance, szThisInstanceName);
                    }

                    if (pPath->dwIndex > 0 && pPath->dwIndex != PERF_NO_UNIQUE_ID) {
                            lstrcatW(szCompositeInstance, L"#");
                            _ltow(pPath->dwIndex,
                                  (LPWSTR)(  szCompositeInstance
                                           + lstrlenW(szCompositeInstance)),
                                  10L);
                    }
                    pdhStatus = PdhiFindInstance(
                            & pInstList->InstList,
                            szCompositeInstance,
                            (lstrcmpiW(szCompositeInstance, L"_Total") == 0)
                                            ? FALSE : TRUE,
                            & pInstance);
                    if (pdhStatus == ERROR_SUCCESS && pInstance != NULL) {
                        nItemCount ++;
                    }
                }
                else if (dwObjectId > 0 || !bInstanceListScanned) {
                    pdhStatus = PdhiRewindWmiLog(pLog);
                    if (pdhStatus == ERROR_SUCCESS) {
                        pdhStatus = PdhiReadNextWmiRecord(
                                                pLog, NULL, 0, FALSE);
                        while (   pdhStatus == ERROR_SUCCESS
                               || pdhStatus == PDH_MORE_DATA) {
                            PdhiResetInstanceCount(CounterTable);
                            pdhStatus = ERROR_SUCCESS;
                            pThisMasterRecord =
                                    (PPDHI_BINARY_LOG_RECORD_HEADER)
                                    (  ((PUCHAR) pLog->pLastRecordRead)
                                     + sizeof(GUID));
                            pThisSubRecord = PdhiGetWmiSubRecord(
                                    pLog,
                                    pThisMasterRecord,
                                    dwIndex,
                                    (LPGUID)(pLog->pLastRecordRead));

                            if (pThisSubRecord == NULL) {
                                // this data record does not contain
                                // counter record for selected object,
                                // skip to next one.
                                //
                                pdhStatus = PdhiReadNextWmiRecord(
                                                pLog, NULL, 0, FALSE);
                                continue;
                            }

                            if (pThisSubRecord->dwType == BINLOG_TYPE_DATA_OBJECT
                                    || pThisSubRecord->dwType == BINLOG_TYPE_DATA_LOC_OBJECT) {
                                pPerfBlock = (PPERF_DATA_BLOCK)
                                        ((LPBYTE)pThisSubRecord +
                                    sizeof (PDHI_BINARY_LOG_RECORD_HEADER));

                                pdhStatus = PdhWmiEnumObjectItemsFromDataBlock(
                                                pLog,
                                                pPerfBlock,
                                                szMachineName,
                                                szObjectName,
                                                dwObjectId,
                                                9,
                                                CounterTable);
                                if (pdhStatus == PDH_ENTRY_NOT_IN_LOG_FILE) {
                                    pdhStatus = ERROR_SUCCESS;
                                }
                            }
                            else {
                                pDataBlock = (PPDHI_RAW_COUNTER_ITEM_BLOCK)
                                        ((LPBYTE)pThisSubRecord +
                                    sizeof (PDHI_BINARY_LOG_RECORD_HEADER));

                                if (pDataBlock->dwLength > 0) {
                                    for (dwDataItemIndex = 0;
                                         dwDataItemIndex < pDataBlock->dwItemCount;
                                         dwDataItemIndex++) {
                                         pDataItem = & pDataBlock->pItemArray[dwDataItemIndex];
                                         szThisInstanceName = (LPWSTR)
                                                 (((LPBYTE) pDataBlock) + pDataItem->szName);
                                        pdhStatus = PdhiFindInstance(
                                                & pInstList->InstList,
                                                szThisInstanceName,
                                                (lstrcmpiW(szThisInstanceName, L"_Total") == 0)
                                                                ? FALSE : TRUE,
                                                & pInstance);
                                        if (pdhStatus == ERROR_SUCCESS) {
                                            nItemCount++;
                                        }
                                    }
                                }
                            }

                            if (pdhStatus != ERROR_SUCCESS) {
                                break;
                            }
                            else {
                                pdhStatus = PdhiReadNextWmiRecord(pLog, NULL, 0, FALSE);
                            }
                        }
                        if (pdhStatus == PDH_END_OF_LOG_FILE) {
                            pdhStatus = ERROR_SUCCESS;
                        }
                        if (pdhStatus == ERROR_SUCCESS) {
                            bInstanceListScanned = TRUE;
                        }
                    }
                }
            }
        }
        ZeroMemory(szCompositeInstance, sizeof(szCompositeInstance));
        dwBytesProcessed += pPath->dwLength;
        pPath             = (PPDHI_LOG_COUNTER_PATH) (  (LPBYTE) pPath
                                                      + pPath->dwLength);
    }

    if ((nItemCount > 0) && (pdhStatus != PDH_INSUFFICIENT_BUFFER)
                         && (pdhStatus != PDH_MORE_DATA)) {
            pdhStatus = ERROR_SUCCESS;
    }

Cleanup:
    if (pTempBuffer != NULL)
        G_FREE(pTempBuffer);

    return pdhStatus;
}

PDH_FUNCTION
PdhiGetWmiLogCounterInfo(
    IN  PPDHI_LOG       pLog,
    IN  PPDHI_COUNTER   pCounter)
{
    PDH_STATUS Status          = ERROR_SUCCESS;
    DWORD      dwObjectId      = PdhWmiGetLogPerfIndexByName(
                                         pLog,
                                         pCounter->pCounterPath->szMachineName,
                                         9,
                                         pCounter->pCounterPath->szObjectName);
    DWORD      dwCounterId     = wcstoul(pCounter->pCounterPath->szCounterName,
                                         NULL, 10);
    PPDH_WMI_PERF_MACHINE      pMachine    = NULL;
    PPDH_WMI_PERF_OBJECT       pObject     = NULL;
    PLIST_ENTRY                pHead;
    PLIST_ENTRY                pNext;
    PPERF_DATA_BLOCK           pDataBlock  = NULL;
    PERF_OBJECT_TYPE         * pPerfObject = NULL;
    DWORD                      dwItems     = 0;
    PERF_COUNTER_DEFINITION  * pPerfCounter;
    PERF_INSTANCE_DEFINITION * pPerfInstance;
    PPDH_LOGGER_CONTEXT        CurrentContext;

    BOOL                  bNeedEnumerate = TRUE;

    CurrentContext = (PPDH_LOGGER_CONTEXT) pLog->lpMappedFileBase;
    if (GetLoggerContext(CurrentContext) < ContextCount) {
        pMachine = PdhWmiGetLogNameTable(pLog,
                                         pCounter->pCounterPath->szMachineName,
                                         9);
        if (pMachine == NULL) {
            Status = PDH_ENTRY_NOT_IN_LOG_FILE;
            goto Cleanup;
        }
        pHead = & pMachine->LogObjectList;
        pNext = pHead->Flink;
        while (pNext != pHead) {
            pObject = CONTAINING_RECORD(pNext, PDH_WMI_PERF_OBJECT, Entry);
            if (pObject->dwObjectId == dwObjectId) {
                bNeedEnumerate = FALSE;
                break;
            }
            pNext = pNext->Flink;
        }

        if (bNeedEnumerate) {
            DWORD dwCounterSize  = 0;
            DWORD dwInstanceSize = 0;
            Status = PdhiEnumLoggedObjectItems(
                        (HLOG) pLog,
                        pCounter->pCounterPath->szMachineName,
                        pCounter->pCounterPath->szObjectName,
                        NULL,
                        & dwCounterSize,
                        NULL,
                        & dwInstanceSize,
                        0,
                        0,
                        TRUE);
            if (Status != ERROR_SUCCESS && Status != PDH_MORE_DATA
                                        && Status != PDH_INSUFFICIENT_BUFFER) {
                goto Cleanup;
            }
            Status = ERROR_SUCCESS;
        }
    }
    else {
        Status = PDH_INVALID_ARGUMENT;
        goto Cleanup;
    }

    if (dwObjectId == 0) {
        dwObjectId = wcstoul(pCounter->pCounterPath->szObjectName, NULL, 10);
        if (dwObjectId != 0) {
            Status = ERROR_SUCCESS;
        }
        else {
            Status = PDH_ENTRY_NOT_IN_LOG_FILE;
        }
    }
    else {
        Status = ERROR_SUCCESS;
    }

    if (Status != ERROR_SUCCESS) {
        goto Cleanup;
    }

    pMachine    = PdhWmiGetLogNameTable(pLog,
                                        pCounter->pCounterPath->szMachineName,
                                        9);
    if (pMachine == NULL) {
        Status = PDH_ENTRY_NOT_IN_LOG_FILE;
        goto Cleanup;
    }
    pHead = & pMachine->LogObjectList;
    pNext = pHead->Flink;
    while (pNext != pHead) {
        PPDH_WMI_PERF_OBJECT pThisObject = CONTAINING_RECORD(
                pNext, PDH_WMI_PERF_OBJECT, Entry);
        if (pThisObject->dwObjectId == dwObjectId) {
            pObject = pThisObject;
            break;
        }
        pNext = pNext->Flink;
    }
    if (pObject == NULL) {
        Status = PDH_ENTRY_NOT_IN_LOG_FILE;
        goto Cleanup;
    }

    pDataBlock = (PPERF_DATA_BLOCK) pObject->ptrBuffer;
    if (pDataBlock == NULL) {
        Status = PDH_ENTRY_NOT_IN_LOG_FILE;
        goto Cleanup;
    }
    pPerfObject = GetObjectDefByTitleIndex(pDataBlock, dwObjectId);
    if (pPerfObject == NULL) {
        Status = PDH_CSTATUS_NO_OBJECT;
        goto Cleanup;
    }

    dwItems      = 0;
    pPerfCounter = FirstCounter(pPerfObject);
    while (dwItems < pPerfObject->NumCounters) {
        if (   pPerfCounter->CounterNameTitleIndex > 0
            && pPerfCounter->CounterNameTitleIndex <= pMachine->dwLastId) {
            if (lstrcmpiW(pCounter->pCounterPath->szCounterName,
                      pMachine->ptrStrAry[pPerfCounter->CounterNameTitleIndex])
                    == 0) {
                break;
            }
            if (   dwCounterId != 0
                && dwCounterId == pPerfCounter->CounterNameTitleIndex) {
                break;
            }
        }
        dwItems ++;
        if (dwItems < pPerfObject->NumCounters) {
            pPerfCounter = NextCounter(pPerfCounter);
            if (pPerfCounter == NULL) {
                break;
            }
        }
        else {
            pPerfCounter = NULL;
        }
    }
    if (dwItems == pPerfObject->NumCounters) {
        pPerfCounter = NULL;
    }
    if (pPerfCounter == NULL) {
        Status = PDH_CSTATUS_NO_OBJECT;
        goto Cleanup;
    }

    pCounter->plCounterInfo.dwObjectId    = dwObjectId;
    pCounter->plCounterInfo.dwCounterId   = pPerfCounter->CounterNameTitleIndex;
    pCounter->plCounterInfo.dwCounterType = pPerfCounter->CounterType;
    pCounter->plCounterInfo.dwCounterSize = pPerfCounter->CounterSize;
    pCounter->plCounterInfo.lDefaultScale = pPerfCounter->DefaultScale;
    if (pCounter->plCounterInfo.dwCounterType  & PERF_TIMER_100NS) {
        pCounter->TimeBase = (LONGLONG) 10000000;
    }
    else if (pCounter->plCounterInfo.dwCounterType  & PERF_OBJECT_TIMER) {
        pCounter->TimeBase = pPerfObject->PerfFreq.QuadPart;
    }
    else {
        pCounter->TimeBase = pDataBlock->PerfFreq.QuadPart;
    }

    if (pPerfObject->NumInstances == PERF_NO_INSTANCES) {
        pCounter->plCounterInfo.lInstanceId          = 0;
        pCounter->plCounterInfo.szInstanceName       = NULL;
        pCounter->plCounterInfo.dwParentObjectId     = 0;
        pCounter->plCounterInfo.szParentInstanceName = NULL;
    }
    else {
        pPerfInstance = FirstInstance(pPerfObject);
        if (pPerfInstance->UniqueID == PERF_NO_UNIQUE_ID) {
            pCounter->plCounterInfo.lInstanceId    = PERF_NO_UNIQUE_ID;
            pCounter->plCounterInfo.szInstanceName = pCounter->pCounterPath->szInstanceName;
            pCounter->plCounterInfo.dwParentObjectId = (DWORD)PERF_NO_UNIQUE_ID;
            pCounter->plCounterInfo.szParentInstanceName = pCounter->pCounterPath->szParentName;
        }
        else {
            LONG    lTempId;
            if (pCounter->pCounterPath->szInstanceName != NULL) {
                lTempId = wcstoul(pCounter->pCounterPath->szInstanceName,
                                  NULL,
                                  10);
            }
            else {
                lTempId = 0;
            }
            pCounter->plCounterInfo.lInstanceId    = lTempId;
            pCounter->plCounterInfo.szInstanceName = NULL;

            if (pCounter->pCounterPath->szParentName != NULL) {
                lTempId = wcstoul(pCounter->pCounterPath->szParentName,
                                  NULL,
                                  10);
            }
            else {
                lTempId = 0;
            }
            pCounter->plCounterInfo.dwParentObjectId = lTempId;
            pCounter->plCounterInfo.szParentInstanceName = NULL;
        }
    }

Cleanup:
    return Status;
}

PDH_FUNCTION
PdhiGetWmiLogFileSize(
    IN PPDHI_LOG  pLog,
    IN LONGLONG * llSize)
{
    PDH_STATUS          pdhStatus = ERROR_SUCCESS;
    LONGLONG            SizeSum   = 0;
    DWORD               dwFileSizeLow;
    DWORD               dwFileSizeHigh;
    DWORD               dwError;
    HANDLE              hFile;

    if (pLog->dwLogFormat & PDH_LOG_WRITE_ACCESS) {
        PPDH_EVENT_TRACE_PROPERTIES LoggerInfo =
                        (PPDH_EVENT_TRACE_PROPERTIES) pLog->lpMappedFileBase;
        if (LoggerInfo != NULL && LoggerInfo->LogFileName != NULL
                               && LoggerInfo->LogFileName[0] != L'\0') {
            hFile = CreateFileW(
                    LoggerInfo->LogFileName,
                    GENERIC_READ,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL,
                    NULL);
            if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) {
                DWORD Win32Error = GetLastError();
                switch (Win32Error) {
                case ERROR_FILE_NOT_FOUND:
                    pdhStatus = PDH_FILE_NOT_FOUND;
                    break;

                case ERROR_ALREADY_EXISTS:
                    pdhStatus = PDH_FILE_ALREADY_EXISTS;
                    break;

                default:
                    pdhStatus = PDH_LOG_FILE_OPEN_ERROR;
                    break;
                }
            }
            else {
                dwFileSizeLow = GetFileSize(hFile, & dwFileSizeHigh);
                if (   (dwFileSizeLow == 0xFFFFFFFF)
                    && ((dwError = GetLastError()) != NO_ERROR)) {
                    pdhStatus = PDH_LOG_FILE_OPEN_ERROR;
                }
                else {
                    if (dwFileSizeHigh != 0) {
                        SizeSum += (dwFileSizeHigh << (sizeof(DWORD) * 8));
                    }
                    SizeSum += dwFileSizeLow;
                }
                CloseHandle(hFile);
            }
        }
        else {
            pdhStatus = PDH_INVALID_ARGUMENT;
        }
    }
    else {
        PPDH_LOGGER_CONTEXT CurrentContext =
                        (PPDH_LOGGER_CONTEXT) pLog->lpMappedFileBase;
        if (GetLoggerContext(CurrentContext) < ContextCount) {
            LONG     i;

            for (i = 0, hFile = NULL, dwFileSizeLow = 0, dwFileSizeHigh = 0;
                 (pdhStatus == ERROR_SUCCESS)
                        && (i < (LONG) CurrentContext->LogFileCount);
                  i ++, hFile = NULL, dwFileSizeLow = 0, dwFileSizeHigh = 0) {
                hFile = CreateFileW(
                        CurrentContext->LogFileName[i],
                        GENERIC_READ,
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL,
                        OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL);
                if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) {
                    DWORD Win32Error = GetLastError();
                    switch (Win32Error) {
                    case ERROR_FILE_NOT_FOUND:
                        pdhStatus = PDH_FILE_NOT_FOUND;
                        break;

                    case ERROR_ALREADY_EXISTS:
                        pdhStatus = PDH_FILE_ALREADY_EXISTS;
                        break;

                    default:
                        pdhStatus = PDH_LOG_FILE_OPEN_ERROR;
                        break;
                    }
                    break;
                }
                dwFileSizeLow = GetFileSize(hFile, & dwFileSizeHigh);
                if (   (dwFileSizeLow == 0xFFFFFFFF)
                    && ((dwError = GetLastError()) != NO_ERROR)) {
                    pdhStatus = PDH_LOG_FILE_OPEN_ERROR;
                }
                else {
                    if (dwFileSizeHigh != 0) {
                        SizeSum = SizeSum + (dwFileSizeHigh << (sizeof(DWORD) * 8));
                    }
                    SizeSum += dwFileSizeLow;
                }
                CloseHandle(hFile);
            }
        }
        else {
            pdhStatus = PDH_INVALID_ARGUMENT;
        }
    }

    if (pdhStatus == ERROR_SUCCESS) {
        __try {
            * llSize = SizeSum;
        }
        __except (EXCEPTION_EXECUTE_HANDLER) {
            pdhStatus = PDH_INVALID_ARGUMENT;
        }
    }
    return pdhStatus;
}