Figure 1   MSJPDH.BAS


 Attribute VB_Name = "Module1"
 '------------------------------------------------------------------------------
 ' WINPERF.H / PDH.DLL constants and defines
 ' Matt Pietrek - March 1998 MSJ
 '------------------------------------------------------------------------------
 
 Enum PERF_DETAIL
 PERF_DETAIL_NOVICE = 100      ' The uninformed can understand it
 PERF_DETAIL_ADVANCED = 200    ' For the advanced user
 PERF_DETAIL_EXPERT = 300      ' For the expert user
 PERF_DETAIL_WIZARD = 400      ' For the system designer
 End Enum
 
 Enum PDH_STATUS
 PDH_CSTATUS_VALID_DATA = &H0
 PDH_CSTATUS_NEW_DATA = &H1
 PDH_CSTATUS_NO_MACHINE = &H800007D0
 PDH_CSTATUS_NO_INSTANCE = &H800007D1
 PDH_MORE_DATA = &H800007D2
 PDH_CSTATUS_ITEM_NOT_VALIDATED = &H800007D3
 PDH_RETRY = &H800007D4
 PDH_NO_DATA = &H800007D5
 PDH_CALC_NEGATIVE_DENOMINATOR = &H800007D6
 PDH_CALC_NEGATIVE_TIMEBASE = &H800007D7
 PDH_CALC_NEGATIVE_VALUE = &H800007D8
 PDH_DIALOG_CANCELLED = &H800007D9
 PDH_CSTATUS_NO_OBJECT = &HC0000BB8
 PDH_CSTATUS_NO_COUNTER = &HC0000BB9
 PDH_CSTATUS_INVALID_DATA = &HC0000BBA
 PDH_MEMORY_ALLOCATION_FAILURE = &HC0000BBB
 PDH_INVALID_HANDLE = &HC0000BBC
 PDH_INVALID_ARGUMENT = &HC0000BBD
 PDH_FUNCTION_NOT_FOUND = &HC0000BBE
 PDH_CSTATUS_NO_COUNTERNAME = &HC0000BBF
 PDH_CSTATUS_BAD_COUNTERNAME = &HC0000BC0
 PDH_INVALID_BUFFER = &HC0000BC1
 PDH_INSUFFICIENT_BUFFER = &HC0000BC2
 PDH_CANNOT_CONNECT_MACHINE = &HC0000BC3
 PDH_INVALID_PATH = &HC0000BC4
 PDH_INVALID_INSTANCE = &HC0000BC5
 PDH_INVALID_DATA = &HC0000BC6
 PDH_NO_DIALOG_DATA = &HC0000BC7
 PDH_CANNOT_READ_NAME_STRINGS = &HC0000BC8
 End Enum
 
 Global Const ERROR_SUCCESS = 0
 
 Declare Function PdhVbGetOneCounterPath _
     Lib "PDH.DLL" _
     (ByVal PathString As String, _
     ByVal PathLength As Long, _
     ByVal DetailLevel As Long, _
     ByVal CaptionString As String) _
     As Long
     
 Declare Function PdhVbCreateCounterPathList _
     Lib "PDH.DLL" _
     (ByVal PERF_DETAIL As Long, _
     ByVal CaptionString As String) _
     As Long
 
 Declare Function PdhVbGetCounterPathFromList _
     Lib "PDH.DLL" _
     (ByVal Index As Long, _
     ByVal Buffer As String, _
     ByVal BufferLength As Long) _
     As Long
 
 Declare Function PdhOpenQuery _
     Lib "PDH.DLL" _
     (ByVal Reserved As Long, _
     ByVal dwUserData As Long, _
     ByRef hQuery As Long) _
     As PDH_STATUS
 
 Declare Function PdhCloseQuery _
     Lib "PDH.DLL" _
     (ByVal hQuery As Long) _
     As PDH_STATUS
 
 Declare Function PdhVbAddCounter _
     Lib "PDH.DLL" _
     (ByVal QueryHandle As Long, _
     ByVal CounterPath As String, _
     ByRef CounterHandle As Long) _
     As PDH_STATUS
 
 Declare Function PdhCollectQueryData _
     Lib "PDH.DLL" _
     (ByVal QueryHandle As Long) _
     As PDH_STATUS
     
 Declare Function PdhVbIsGoodStatus _
     Lib "PDH.DLL" _
     (ByVal StatusValue As Long) _
     As Long
     
 Declare Function PdhVbGetDoubleCounterValue _
     Lib "PDH.DLL" _
     (ByVal CounterHandle As Long, _
     ByRef CounterStatus As Long) _
     As Double
  

Figure 4   PDH_VBDemo.frm


 VERSION 5.00
 Begin VB.Form Form1 
   Caption         =   "PDH_VBDemo - Matt Pietrek 1998, for MSJ"
   ClientHeight    =   2895
   ClientLeft      =   2445
   ClientTop       =   1515
   ClientWidth     =   6810
   LinkTopic       =   "Form1"
   ScaleHeight     =   2895
   ScaleWidth      =   6810
   Begin VB.OptionButton PerfDetail 
      Caption         =   "Wizard"
      Height          =   255
      Index           =   3
      Left            =   5400
      TabIndex        =   5
      Top             =   1080
      Value           =   -1  'True
      Width           =   1215
   End
   Begin VB.OptionButton PerfDetail 
      Caption         =   "Expert"
      Height          =   255
      Index           =   2
      Left            =   5400
      TabIndex        =   4
      Top             =   840
      Width           =   1215
   End
   Begin VB.OptionButton PerfDetail 
      Caption         =   "Advanced"
      Height          =   255
      Index           =   1
      Left            =   5400
      TabIndex        =   3
      Top             =   600
      Width           =   1215
   End
   Begin VB.OptionButton PerfDetail 
      Caption         =   "Novice"
      Height          =   255
      Index           =   0
      Left            =   5400
      TabIndex        =   2
      Top             =   360
      Width           =   1215
   End
   Begin VB.Frame Frame1 
      Caption         =   "Detail Level"
      Height          =   1335
      Left            =   5280
      TabIndex        =   6
      Top             =   120
      Width           =   1455
   End
   Begin VB.CommandButton cmdAddCounter 
      Caption         =   "Add Counter..."
      Height          =   495
      Left            =   5280
      TabIndex        =   1
      Top             =   2280
      Width           =   1335
   End
   Begin VB.Timer Timer1 
      Enabled         =   0   'False
      Interval        =   1000
      Left            =   120
      Top             =   2400
   End
   Begin VB.Label Label1 
      Height          =   2535
      Left            =   120
      TabIndex        =   0
      Top             =   120
      Width           =   4935
   End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
'--------------------------------------------------------------
' Matt Pietrek
' Microsoft Systems Journal, March 1998
' Program: PDH_VBDemo
' FILE: PDH_VBDemo.FRM
'--------------------------------------------------------------

Option Explicit

' Declare a structure to correlate a counter path to its
' associated handle values
Private Type CounterInfo
    hCounter As Long
    strName As String
End Type

Dim hQuery As Long

' Make room for 100 counters.  Yeah, it probably should be a
' collection or something...
Dim Counters(0 To 99) As CounterInfo

' The array index where the next CounterInfo will be added
Dim currentCounterIdx As Long

' The current level of geeky counters to show in the
' PdhVbGetOneCounterPath dialog box
Dim iPerformanceDetail As PERF_DETAIL

'-------------------------------------------------------------
' Calls PdhVbAddCounter, then appends the counter path and
' handle to the Counters array.
'-------------------------------------------------------------
Public Sub AddCounter(strCounterName As String, hQuery As Long)
    Dim pdhStatus As PDH_STATUS
    Dim hCounter As Long
    
    ' Associate the counter path to the query
    pdhStatus = PdhVbAddCounter(hQuery, strCounterName, hCounter)
    
    ' Append the path and handle to the Counters array
    Counters(currentCounterIdx).hCounter = hCounter
    Counters(currentCounterIdx).strName = strCounterName
    
    ' Bump up index to be ready for the next addition
    currentCounterIdx = currentCounterIdx + 1
End Sub

'--------------------------------------------------------------
' Displays the counter names and values for each added counter.
' The display string is built by iterating through all of the
' added counters, and using CR-LF to split counter value
' out one to a line.  The final result is dumped into a label
' (static text control.)
'--------------------------------------------------------------
Private Sub UpdateValues()
    Dim dblCounterValue As Double
    Dim pdhStatus As Long
    Dim strInfo As String
    Dim i As Long
    
    ' Get the most recent value for all the added counters
    PdhCollectQueryData (hQuery)
    
    ' For each added counter...
    For i = 0 To (currentCounterIdx - 1)

        ' Get the counter value as a double
        dblCounterValue = _
            PdhVbGetDoubleCounterValue(Counters(i).hCounter, pdhStatus)
                
        ' Verify that when we queried, the returned value was valid
        If (pdhStatus = PDH_CSTATUS_VALID_DATA) _
        Or (pdhStatus = PDH_CSTATUS_NEW_DATA) Then
            
            ' The counter value is valid, so append the counter path and
            ' its value onto the end of a string.  Note the second argument
            ' to Format$, which ensures that fractional part of the number
            ' will be discarded.  This is somewhat cheesy, but it's a lot
            ' more work to get something that's nicer in all cases.
            strInfo = strInfo + Counters(i).strName + " = " + _
                        Format$(dblCounterValue, "0") + Chr$(13) + Chr$(10)
        End If
        
    Next i
       
    Label1 = strInfo    ' Blast the finished string into the text control
End Sub

Private Sub Form_Load()
    Dim pdhStatus As PDH_STATUS
    
    currentCounterIdx = 0   ' Not necesary, but C++ habits die hard
    
    ' Create the query, and verify that the request succeeded.
    ' Bail out if not.
    pdhStatus = PdhOpenQuery(0, 1, hQuery)
    If pdhStatus <> ERROR_SUCCESS Then
        MsgBox "OpenQuery failed"
        End
    End If
    
    ' Add some interesting counters as defaults
    AddCounter "\Process(_total)\Thread Count", hQuery
    AddCounter "\Memory\Committed Bytes", hQuery
    AddCounter "\Processor(0)\% Processor Time", hQuery
    AddCounter "\System\Context Switches/Sec", hQuery
    
    UpdateValues    ' Force an immediate display of the counter values
    
    PerfDetail_Click (3)    ' Fake a click on the "Wizard" radio button
    
    ' Now that everything is set up, we can enable the 1 second timer.
    ' It's disabled up to this point.
    Timer1.Enabled = True
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Timer1.Enabled = False  ' Don't need periodic updates anymore
    
    PdhCloseQuery (hQuery)  ' Free the query
End Sub

Private Sub cmdAddCounter_Click()
    Dim cbLen As Long
    Dim strCounterPath As String * 256
    Dim strCounterPath2 As String
    
    ' Display the dialog to pick one counter.  strCounterPath is a buffer
    ' of 256 charaters
    cbLen = PdhVbGetOneCounterPath(strCounterPath, 256, iPerformanceDetail, _
                                    "MSJ PDH.DLL Demo - Matt Pietrek, 1998")
    
    ' If nothing was selected, bail out
    If cbLen = 0 Then Exit Sub
    
    ' Make a string that contains only the valid characters that were written
    ' to by the call to PdhVbGetOneCounterPath
    strCounterPath2 = Left$(strCounterPath, cbLen)
    
    ' Add the new counter to the query, and to the Counters array
    AddCounter strCounterPath2, hQuery
    
    UpdateValues    ' Force an immediate display update
End Sub

Private Sub PerfDetail_Click(Index As Integer)
    ' Based upon the radio button that was pressed, set the
    ' iPerformanceDetail variable to the proper enum
    Select Case Index
        Case 0
            iPerformanceDetail = PERF_DETAIL_NOVICE
        Case 1
            iPerformanceDetail = PERF_DETAIL_ADVANCED
        Case 2
            iPerformanceDetail = PERF_DETAIL_EXPERT
        Case 3
            iPerformanceDetail = PERF_DETAIL_WIZARD
End Select
End Sub

Private Sub Timer1_Timer()
    ' Set to trigger once a second.  Update the counter display
    UpdateValues
End Sub