INFO: PDH Sample Code to Enumerate Performance Counters and Instances (287157)



The information in this article applies to:

  • Microsoft Win32 Application Programming Interface (API), when used with:
    • the operating system: Microsoft Windows NT 4.0
    • the operating system: Microsoft Windows 2000
    • the operating system: Microsoft Windows XP

This article was previously published under Q287157

SUMMARY

Performance Data Helper (PDH) APIs can be used to enumerate performance counters and instances of a specified object on a specified computer. This article explains the sequence of PDH API calls needed.

MORE INFORMATION

The sample code below demonstrates the how to obtain the performance counters and instances of a specified object. After the list is obtained, a counter path is constructed for each instance and the performance data value is obtained once.
#include <windows.h>
#include <pdh.h>
#include <pdhmsg.h>
#include <stdio.h>

void WINAPI GetCounterValues(LPTSTR serverName, LPTSTR objectName,
                             LPTSTR counterList, LPTSTR instanceList);
void WINAPI EnumerateExistingInstances(LPTSTR serverName, LPTSTR objectName);

void main(int argc, char *argv[])
{
    if (argc > 1)
    {
        // argv[1] - Server Name
        EnumerateExistingInstances(argv[1], "Thread");
    }
    else
    {
        // Local System
        EnumerateExistingInstances(NULL, "Thread");
    }
}

/////////////////////////////////////////////////////////////////////////////// 
// 
//  FUNCTION:     GetCounterValues - This function constructs a counter path
//                from the counter and instance names obtained. The constructed
//                counter path is then used to get the performance data using
//                PdhCollectQueryData.
// 
//  RETURN VALUE: none
// 
/////////////////////////////////////////////////////////////////////////////// 
void WINAPI GetCounterValues(LPTSTR serverName, LPTSTR objectName,
                             LPTSTR counterList, LPTSTR instanceList)
{
    PDH_STATUS s;

    HQUERY hQuery;

    PDH_COUNTER_PATH_ELEMENTS *cpe = NULL;
    PDH_COUNTER_PATH_ELEMENTS *cpeBeg;

    DWORD  nCounters;
    DWORD  nInstances;

    HCOUNTER *hCounter = NULL;
    HCOUNTER *hCounterPtr;

    char *counterPtr, *instancePtr;

    char szFullPath[MAX_PATH];
    DWORD cbPathSize;
    DWORD   i, j;

    BOOL  ret = FALSE;

    PDH_FMT_COUNTERVALUE counterValue;

    // Scan through the counter names to find the number of counters.
    nCounters = 0;
    counterPtr = counterList;
    while (*counterPtr)
    {
        counterPtr += strlen(counterPtr) + 1;
        nCounters++;
    }

    // Scan through the instance names to find the number of instances.
    nInstances = 0;
    instancePtr = instanceList;
    while (*instancePtr)
    {
        instancePtr += strlen(instancePtr) + 1;
        nInstances++;
    }

    if (!nCounters || !nInstances) return;

    __try
    {
        cpe = (PDH_COUNTER_PATH_ELEMENTS *)HeapAlloc(GetProcessHeap(), 0, 
                    sizeof(PDH_COUNTER_PATH_ELEMENTS) * nCounters * nInstances);
        hCounter = (HCOUNTER *)HeapAlloc(GetProcessHeap(), 0, 
                    sizeof(HCOUNTER) * nCounters * nInstances);

        if (!cpe || !hCounter) __leave;

        // Only do this once to create a query.
        if ((s = PdhOpenQuery(NULL, 0, &hQuery)) != ERROR_SUCCESS)
        {
            fprintf(stderr, "POQ failed %08x\n", s);
            __leave;
        }

        // For each instance name in the list, construct a counter path.
        cpeBeg = cpe;
        hCounterPtr = hCounter;
        for (i = 0, counterPtr = counterList; i < nCounters;
                i++, counterPtr += strlen(counterPtr) + 1)
        {
            for (j = 0, instancePtr = instanceList; j < nInstances;
                    j++,
                    instancePtr += strlen(instancePtr) + 1,
                    cpeBeg++,
                    hCounterPtr++)
            {
                cbPathSize = sizeof(szFullPath);

                cpeBeg->szMachineName = serverName;
                cpeBeg->szObjectName = objectName;
                cpeBeg->szInstanceName = instancePtr;
                cpeBeg->szParentInstance = NULL;
                cpeBeg->dwInstanceIndex = -1;
                cpeBeg->szCounterName = counterPtr;

                if ((s = PdhMakeCounterPath(cpeBeg,
                    szFullPath, &cbPathSize, 0)) != ERROR_SUCCESS)
                {
                    fprintf(stderr,"MCP failed %08x\n", s);
                    __leave;
                }

                // Add the counter path to the query.
                if ((s = PdhAddCounter(hQuery, szFullPath, 0, hCounterPtr))
                        != ERROR_SUCCESS)
                {
                    fprintf(stderr, "PAC failed %08x\n", s);
                    __leave;
                }
            }
        }

        for (i = 0; i < 2; i++)
        {
            Sleep(100);

            // Collect data as often as you need to.
            if ((s = PdhCollectQueryData(hQuery)) != ERROR_SUCCESS)
            {
                fprintf(stderr, "PCQD failed %08x\n", s);
                __leave;
            }

            if (i == 0) continue;

            // Display the performance data value corresponding to each instance.
            cpeBeg = cpe;
            hCounterPtr = hCounter;
            for (i = 0, counterPtr = counterList; i < nCounters;
                    i++, counterPtr += strlen(counterPtr) + 1)
            {
                for (j = 0, instancePtr = instanceList; j < nInstances;
                        j++,
                        instancePtr += strlen(instancePtr) + 1,
                        cpeBeg++,
                        hCounterPtr++)
                {
                    if ((s = PdhGetFormattedCounterValue(*hCounterPtr,
                        PDH_FMT_DOUBLE,
                        NULL, &counterValue)) != ERROR_SUCCESS)
                    {
                        fprintf(stderr, "PGFCV failed %08x\n", s);
                        continue;
                    }
                    printf("%s\\%s\\%s\t\t : [%3.3f]\n",
                        cpeBeg->szObjectName,
                        cpeBeg->szCounterName,
                        cpeBeg->szInstanceName,
                        counterValue.doubleValue);
                }
            }
        }

        // Remove all the performance counters from the query.
        hCounterPtr = hCounter;
        for (i = 0; i < nCounters; i++)
        {
            for (j = 0; j < nInstances; j++)
            {
                PdhRemoveCounter(*hCounterPtr);
            }
        }
    }
    __finally
    {
        HeapFree(GetProcessHeap(), 0, cpe);
        HeapFree(GetProcessHeap(), 0, hCounter);

        // Only do this cleanup once.
        PdhCloseQuery(hQuery);
    }

    return;
}

/////////////////////////////////////////////////////////////////////////////// 
// 
//  FUNCTION:     EnumerateExistingInstances - This function displays the names
//                of all the instances that exist on ServerName. PDH is used
//                to enumerate the performance counter and instance names.
// 
//  RETURN VALUE: none
// 
/////////////////////////////////////////////////////////////////////////////// 

void WINAPI EnumerateExistingInstances(LPTSTR serverName, LPTSTR objectName)
{
    TCHAR       mszEmptyList[2];  // An empty list contains 2 NULL characters
    LPTSTR      mszInstanceList = NULL;
    LPTSTR      szInstanceName;
    DWORD       cchInstanceList;
    LPTSTR      mszCounterList = NULL;
    DWORD       cchCounterList;
    PDH_STATUS  pdhStatus;

    __try
    {
        mszCounterList = NULL;
        cchCounterList = 0;
        // Refresh the list of performance objects available on the specified
        // computer by making a call to PdhEnumObjects with bRefresh set to TRUE.
        pdhStatus = PdhEnumObjects(NULL, serverName, mszCounterList,
            &cchCounterList, PERF_DETAIL_WIZARD, TRUE);

        mszCounterList = NULL;
        cchCounterList = 0;
        // Determine the required size for the buffer containing counter names
        // and instance names by calling PdhEnumObjectItems.
        cchInstanceList = sizeof(mszEmptyList);
        pdhStatus = PdhEnumObjectItems(NULL, serverName,
            objectName, mszCounterList, 
            &cchCounterList, mszEmptyList,
            &cchInstanceList, PERF_DETAIL_WIZARD, 0);

        if (pdhStatus == ERROR_SUCCESS)
            return;  // The list is empty so do nothing.
        else if (pdhStatus != PDH_MORE_DATA)
        {
            fprintf(stderr, "PEOI failed %08x\n", pdhStatus);
            return;
        }

        // Allocate a buffer for the counter names.
        mszCounterList = (LPTSTR)HeapAlloc(GetProcessHeap(),
            HEAP_ZERO_MEMORY, cchCounterList);
        if (!mszCounterList)
        {
            fprintf(stderr, "HA failed %08x\n", GetLastError());
            return;
        }

        // Allocate a buffer for the instance names.
        mszInstanceList = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
            cchInstanceList);
        if (!mszInstanceList)
        {
            fprintf(stderr, "HA failed %08x\n", GetLastError());
            return;
        }

        __try
        {
            // Enumerate to get the list of Counters and Instances provided by
            // the specified object on the specified computer.
            pdhStatus = PdhEnumObjectItems(NULL, serverName,
                objectName, mszCounterList, 
                &cchCounterList, mszInstanceList,
                &cchInstanceList, PERF_DETAIL_WIZARD, 0);
            if (pdhStatus != ERROR_SUCCESS)
            {
                fprintf(stderr, "PEOI failed %08x\n", pdhStatus);
                return;
            }

            // Display the items from the buffer.
            szInstanceName = mszInstanceList;
            while (*szInstanceName)
            {
                printf("%s\n", szInstanceName);
                szInstanceName += strlen(szInstanceName) + 1;
            }

            GetCounterValues(serverName, objectName, mszCounterList,
                mszInstanceList);
        }
        __finally
        {
        }

    }
    __finally
    {
        // Free the buffers.
        if (mszInstanceList) HeapFree(GetProcessHeap(), 0, mszInstanceList);
        if (mszCounterList) HeapFree(GetProcessHeap(), 0, mszCounterList);
    }

    return;   
}
				

REFERENCES

PDH APIs are implemented in the Pdh.dll file that is included with the Windows 2000 operating system. For the Windows NT 4.0 operating system, you can download a separate redistributable Pdh.dll version.

For additional information about how to obtain the redistributable version of Pdh.dll for Windows NT 4.0, click the following article number to view the article in the Microsoft Knowledge Base:

284996 FILE: Latest redistributable PDH.DLL for Windows NT 4.0

For additional information about the PDH APIs, see the following topic in the MSDN Library Web site:

Modification Type:MajorLast Reviewed:10/20/2003
Keywords:kbAPI kbinfo kbKernBase kbPerfMon KB287157