INFO: Collecting Performance Data Using PDH APIs from Visual Basic (296526)



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 Q296526

SUMMARY

Performance Data Helper (PDH) APIs can be used to collect performance data from various performance counters or instances that are available on the system. This article demonstrates the PDH API calls needed to collect performance data of a given performance object, counter, and instance name from Microsoft Visual Basic.

MORE INFORMATION

The following sample code has a function called ConstructCounterPath() that constructs the counter path as required by the PdhAddCounter() API for the specified performance object, counter, and instance names. Command1_Click below constructs an array of counter paths that an application is interested in collecting the performance data from and then calls the CollectAndDisplayPerformanceData Visual Basic helper procedure.

CollectAndDisplayPerformanceData interfaces with PDH APIs to collect/display performance data values.

The hex error codes returned by PDH APIs are defined in the Pdhmsg.h header file.
Option Explicit

' PDH API Success Status
Private Const ERROR_SUCCESS = 0

Const PERF_SIZE_LARGE = &H100

Const PDH_FMT_LONG = &H100

Private Type PDH_COUNTER_INFO
    dwLength As Long
    dwType As Long
    CVersion As Long
    CStatus As Long
    lScale As Long
    lDefaultScale As Long
    dwUserData As Long
    dwQueryUserData As Long
    szFullPath As Long
    szMachineName As Long
    szObjectName As Long
    szInstanceName As Long
    szParentInstance As Long
    dwInstanceIndex As Long
    szCounterName As Long
    szExplainText As Long
    DataBuffer As Long
End Type

Private Type PDH_FMT_COUNTERVALUE
    CStatus As Long
    unionPadding As Long
    longValue As Long
    extraBytes As Long
End Type

Private Declare Function PdhOpenQuery Lib "pdh" Alias "PdhVbOpenQuery" _
        (ByRef hQuery As Long) As Long
Private Declare Function PdhCloseQuery Lib "pdh" _
        (ByVal hQuery As Long) As Long
Private Declare Function PdhCollectQueryData Lib "pdh" _
        (ByVal hQuery As Long) As Long
Private Declare Function PdhAddCounter Lib "pdh" Alias "PdhVbAddCounter" _
        (ByVal hQuery As Long, _
            ByVal counterPath As String, _
            ByRef hCounter As Long) As Long
Private Declare Function PdhRemoveCounter Lib "pdh" _
        (ByVal hCounter As Long) As Long
Private Declare Function PdhVbGetDoubleCounterValue Lib "pdh" _
        (ByVal hCounter As Long, _
        ByRef CounterStatus As Long) As Double
Private Declare Function PdhVbIsGoodStatus Lib "pdh" _
        (ByVal StatusValue As Long) As Long
Private Declare Function PdhGetFormattedCounterValue Lib "pdh" _
        (ByVal hCounter As Long, _
        ByVal dwFormat As Long, _
        lpdwType As Long, _
        pValue As PDH_FMT_COUNTERVALUE) As Long
Private Declare Function PdhGetCounterInfo Lib "pdh" Alias "PdhGetCounterInfoA" _
        (ByVal hCounter As Long, _
        ByVal bRetrieveExplainText As Long, _
        pdwBufferSize As Long, _
        lpBuffer As Any) As Long

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
         (hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long)
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMillseconds As Long)

Private Sub CollectAndDisplayPerformanceData(ByRef counterPath() As String)

Dim ret As Long
Dim hQuery As Long
Dim index As Long
Dim maxIndex As Long
Dim value As Double
Dim counterValue As PDH_FMT_COUNTERVALUE
Dim bufferSize As Long
Dim counterInfo As PDH_COUNTER_INFO
Dim dwType As Long

hQuery = 0

maxIndex = UBound(counterPath)
ReDim hCounters(0 To maxIndex)
ReDim counterTypes(0 To maxIndex)

' Create a PDH Query
ret = PdhOpenQuery(hQuery)
If ret <> ERROR_SUCCESS Then
    Debug.Print "PdhOpenQuery failed with error code : " & Hex(ret)
    Exit Sub
End If

' Add a counter to the query for the counterPath specified.
' If there are multiple counters to be monitored add them to
' created PDH query
For index = 0 To maxIndex
    ret = PdhAddCounter(hQuery, counterPath(index), hCounters(index))
    If ret <> ERROR_SUCCESS Then
        Debug.Print "PdhAddCounter failed with error code : " & Hex(ret)
        GoTo Cleanup
    End If
    
    'Get the counter type information for the counter created
    bufferSize = 0
    ret = PdhGetCounterInfo(hCounters(index), 0, bufferSize, ByVal 0)
    ReDim lpBuffer(bufferSize)
    ret = PdhGetCounterInfo(hCounters(index), 0, bufferSize, lpBuffer(0))
    If ret <> ERROR_SUCCESS Then
        Debug.Print "PdhGetCounterInfo failed with error code : " & Hex(ret)
        GoTo Cleanup
    End If
    CopyMemory counterInfo, lpBuffer(0), Len(counterInfo)
    counterTypes(index) = counterInfo.dwType
Next

' Collect the first snapshot of performance data for all counters.
' We ignore the first snapshot as most performance counters require two
' snapshots to get a calculated value
ret = PdhCollectQueryData(hQuery)
If ret <> ERROR_SUCCESS Then
    Debug.Print "PdhCollectQueryData failed with error code : " & Hex(ret)
    GoTo Cleanup
End If

Dim nMaxCollections As Long
Dim nCollection As Long

nCollection = 0
nMaxCollections = 10
For nCollection = 1 To nMaxCollections
    
    'Sleep for a while between each collection
    Sleep (1000)
    
    ' Collect the next snapshot of performance data.
    ret = PdhCollectQueryData(hQuery)
    If ret <> ERROR_SUCCESS Then
        Debug.Print "PdhCollectQueryData failed with error code : " & Hex(ret)
        GoTo Cleanup
    End If
    
    ' Get Calculated performance data values for each counter and display them
    For index = 0 To maxIndex
        ' Check if it is a LARGE counter type.
        If counterTypes(index) And PERF_SIZE_LARGE Then
            value = PdhVbGetDoubleCounterValue(hCounters(index), ret)
            If PdhVbIsGoodStatus(ret) = 0 Then
                Debug.Print "PdhVbGetDoubleCounterValue " & counterPath(index) & " failed with error code : " & Hex(ret)
            Else
                Debug.Print counterPath(index) & " [" & value & "]"
            End If
        Else
            ret = PdhGetFormattedCounterValue(hCounters(index), _
                        PDH_FMT_LONG, dwType, counterValue)
            If ret <> ERROR_SUCCESS Then
                Debug.Print "PdhGetFormattedCounterValue " & counterPath(index) & " failed with error code : " & Hex(ret)
            Else
                Debug.Print counterPath(index) & " [" & counterValue.longValue & "]"
            End If
        End If
    Next

Next

Cleanup:

' If hQuery has been created
If hQuery <> 0 Then
    For index = 0 To maxIndex
        ' Remove all the counters that have been added successfully, from the query
        If (hCounters(index) <> 0) Then
            ret = PdhRemoveCounter(hCounters(index))
            If ret <> ERROR_SUCCESS Then
                Debug.Print "PdhRemoveCounter failed with error code : " & Hex(ret)
            End If
        End If
    Next
    ret = PdhCloseQuery(hQuery)
    If (ret <> ERROR_SUCCESS) Then
        Debug.Print "PdhCloseQuery failed with error code : " & Hex(ret)
    End If
End If

End Sub

Private Function ConstructCounterPath(ByVal szMachineName As String, _
        ByVal szObjectName As String, ByVal szCounterName As String, ByVal szInstanceName As String) As String
        
Dim counterPath As String
    
counterPath = ""

' If szMachineName is specified
If Len(szMachineName) Then
    counterPath = "\\" + szMachineName
End If

counterPath = counterPath + "\" + szObjectName

' If szInstanceName is specified
If Len(szInstanceName) Then
    counterPath = counterPath + "(" + szInstanceName + ")"
End If

counterPath = counterPath + "\" + szCounterName

ConstructCounterPath = counterPath

End Function

Private Sub Command1_Click()

Dim counterPath(0 To 5) As String

'Performance Counters that have instance names
counterPath(0) = ConstructCounterPath("PRABAGAR4", "Processor", "% Processor Time", "0")
counterPath(1) = ConstructCounterPath("PRABAGAR4", "Process", "% Processor Time", "csrss")

'Performance Counters that DOES NOT have instance names
counterPath(2) = ConstructCounterPath("SERVERNAME", "System", "Processes", vbNullString)
counterPath(3) = ConstructCounterPath("SERVERNAME", "System", "Threads", vbNullString)
counterPath(4) = ConstructCounterPath("SERVERNAME", "Objects", "Events", vbNullString)
counterPath(5) = ConstructCounterPath("SERVERNAME", "Objects", "Mutexes", vbNullString)

CollectAndDisplayPerformanceData counterPath

End Sub
				

REFERENCES

PDH APIs are implemented in PDH.dll, and this DLL comes with the Windows 2000 operating system.

For Windows NT 4.0, there is a separate redistributable PDH.dll version.

For additional information about how to obtain this redistributable version 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, visit the following MSDN Library Web site (or see the Microsoft Platform SDK):

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