PerformanceCounters (PDH)

PDH Performance Counters

Image and video hosting by TinyPic   
(note that the above dialog appears differently on Vista+)

New ObjectBase interface!!
*x64 and Localization Issues have been resolved!

*Download and License agreement are at the bottom of this post!

About The Project:
Performance Counters gather all kind of performance data about the PC and the Network using the standard PDH.DLL module (standard since Windows 2000).

Among the performance data you can collect and monitor are:
  • Processor Stats (including total usage)
  • Process (programs) Stats  (including CPU usage)
  • Disk Stats (speed/access)
  • TCP, UDP, IP Connection Counts, Speed/Error Stats
  • Network Stats
  • ..and so on
Note that one example is a WIP. 'TaskManager' mockup UDF, I'm looking at you.

You can jump right in, download the two AutoIT files (below) and run one of the following to see what the PDH Performance Counters project can get you:
  • TestPDH_PerformanceCounters - this is where you should start! It allows you to visually experiment with most all of the Counters available on your PC and Network.
    - Currently the 'Refresh' rate is set to 1/2 second - this can be changed by altering the Adlib() function frequency
    - New Counters that come into existence during monitoring will *not* be added to the list (this would be a function I've yet to add)
    - Counters that become invalid are simply given a prefix of '[Dead Counter Handle]:' 
Image and video hosting by TinyPic
  • TestPDH_ProcessLoop - this basically repeatedly shows an extended Process-information list for all processes. NOTE: You must hit 'ESC' to exit the loop
Image and video hosting by TinyPic
  • TestPDH_TaskManager - this gathers and displays most everything you'd see in the Process and Performance sections of Task Manager.
    - The screen does NOT update, and the UDF is a MESS.  This is due to a number of reasons - the biggest one being laziness on my behalf.  Plus I need to figure out how best to manage ListView changes.
    - TWO fields need Windows XP+ to display correctly (or a version of psapi.dll that supports 'GetPerformanceInfo'): Commit Charge (Peak) and System Cache
Image and video hosting by TinyPic
  • TestPDH_ProcessGetRelatives - this shows how the PDH Performance Counters can be used to get 'parent' and 'children' process information.  There are more practical means of getting this info of course (a few you'll see in my 'Process Functions' module, but hey - its just yet another example of what can be done.
  • TestPDH_ObjectTests - this is more for reading/understanding the code than anything.  It is there to show how the new 'ObjectBase' Interface works to make coding Performance Counters *much* easier.
  • Multipile extra 'TestPDH*' examples
At its most basic, interacting with Performance Counters is as such:
  1. _PDH_Init() must be called to Initialize the PDH Performance Counters. This is a *MUST*.
    This does the following:
    - Verifies Performance Counters aren't disabled in the Registry. If they *ARE*, it tries to enable them.  Note this may require Admin authorization as the keys are located in the HKLM branch
    - Loads the PDH.DLL, which in turn generates a file in the @TempDir named something like 'Perflib_Perfdata_xxx.dat' (around ~16KB)
  2. _PDH_GetNewQueryHandle() needs to be called to get a PDH Query Handle.  This handle is used to add Counters to, and everytime this is updated, all Counters attached will be updated.
  3. _PDH_AddCounter() needs to be called to add Counters to the PDH Query Handle (for as many counters as need to be added).  These can be in Localized ("\\PCNAME\Object(Instance)\Counter") or non-localized format (":\##\##\(Instance)\PCNAME"), and can contain a Wildcard ("*") in place of one of the parts of the path (Object, Instance, or Counter)
  4. _PDH_UpdateCounter(), _PDH_UpdateCounters(), or _PDH_UpdateWildcardCounter() should be called to get the Counter values.  Call this at regular intervals to get updated Performance Information.
  5. _PDH_FreeQueryHandle() should be called for each Query handle obtained.  This automatically releases the Counters that were attached, as well.
  6. _PDH_UnInit() must be called to cleanup before exiting.
The brand new ObjectBase Interface removes some of the difficulty in interacting with Performance Counters, and works like this:
  1. _PDH_Init() as usual
  2. _PDH_ObjectBaseCreate(), _PDH_ProcessObjectCreate(), or _PDH_ProcessAlInit() must be called to create a special 'ObjectBase' Counter Array.
  3. _PDH_ObjectBaseAddCounters(), _PDH_ProcessObjectAddCounters(), or _PDH_ProcessAllAddCounters() must be called to add Counters to the ObjectBase.  These can be passed by index # or Local string form in a multiple-Counter string, and can optionally be added on Object Creation (except for ProcessObjects).
  4. _PDH_ObjectBaseUpdateCounters(), _PDH_ProcessObjectUpdateCounters(), or _PDH_ProcessAllUpdateCounters() must be called to update the ObjectBase Counters and retrieve current performance data.  Call these at regular intervals to get updated Performance Information.
  5. _PDH_ObjectBaseDestroy(), _PDH_ProcessObjectDestroy(), or _PDH_ProcessAllUnInit() must be called to Destroy the 'ObjectBase' array (and free its Query handle)
  6. _PDH_UnInit() as usual
I've put a LOT of work into this project, and all I ask is that you follow my License Agreement when using the code (very easy, see below). Any feedback is welcome. I apologize for the unpolished GUI interfaces (especially the unfinished one),  but I will get to that TaskManager GUI one day, hah.  Enough chatter -now  go on and experience the awesome power of Performance Counters :lmao:

Download the ZIP here

NOTE: Bundled in the ZIP (and included in the License agreement)  are other UDF's I wrote that are necessary to run some of the 'Test' programs:
_WinAPI_GetSystemInfo.au3, _WinAPI_GetPerformanceInfo.au3, _WinTimeFunctions.au3, and the unnecessary, but provided for those who are interested in the _WinTimeFunctions 'filetime' usage, program: TestWinTimeFunctions.au3.

Ascend4nt's AutoIT Code License agreement:
While I provide this source code freely, if you do use the code in your projects, all I ask is that:
  1. If you provide source, keep the header as I have put it, OR, if you expand it, then at least acknowledge me as the original author, and any other authors I credit
  2. If the program is released, acknowledge me in your credits (it doesn't have to state which functions came from me, though  again if the source is provided - see #1)
  3. The source on it's own (as opposed to part of a project) can not be posted unless a link to the page(s) where the code were retrieved from is provided and a message stating that the latest updates will be available on the page(s) linked to.
  4. Pieces of the code can however be discussed on the threads where Ascend4nt has posted the code without worrying about further linking.


  • 10/12/2012:
    - Fix: For systems with >64 processors, wrong results were returned for the # of CPU's. A new test is run using 'GetActiveProcessorCount' (Win7+/2008R2+) to get the total number of logical processors in ALL processor groups
  • 6/2/2011:
  • - MS Bugs found: _PDH_BrowseCounters() on Win7 causes around 40 DLL's to STAY loaded after the API call.
      Plus, reference count to PDH.DLL is increased by 2.
  •  *Workaround: Don't use this function unless ABSOLUTELY necessary!
  •   However, using _PDH_ReloadDLL() can cut down on the # of DLL's that are loaded (to around 10 on my system)
  •  but note that subsequent calls to _PDH_BrowseCounter() will be slower!
  • - Added: _PDH_ReloadDLL(). I found a bug with these API calls (Win2000->Win7):
  •   * PdhExpandWildCardPath [_PDH_GetCounterList()]
  •   * PdhValidatePathW [_PDH_ValidatePath()]
        Neither of the above recognize new object instances unless PDH.DLL is unloaded! Hence, PDH_ReloadDLL() is there to fix this issue.
  •   *NOTE: Use of this will cause subsequent _PDH_BrowseCounters() call's to take a long time to load, unless previous calls weren't made.
  • - Added: Internal function __PDH_ForceFreeDLL() -> to support _PDH_ReloadDLL(), and maybe fix 'Browse Counters Dialog' problems in the future
  • - Changed: TestPDH_PerformanceCounters() now calls _PDH_ReloadDLL() to allow it to 'see' new instances when Manual Entry is used.
  • - Changed: TestPDH_PerformanceCounters() now recognizes all-counters-dead scenarios.
  • - Fix: _PDH_GetCounterInfo() on Win2000 was broken.
  • - Fix: _WinTime_Format* functions handling of Milliseconds - was padding to 4 character instead of 3 characters long (Thx Joakim)
  • 5/28/2011:
    - Fixed: Bug introduced in last version: Single-item selection in 'Browse Counters Dialog' returning cut-off strings.
    - Fixed: _ProcessAll* UDF function had misspelling
    - Fixed: The 'Browse Counters Dialog' doesn't display instances in some weird cases. TestPDH_PerformanceCounters now deals with this properly
    - Change: TestPDH_TaskManager title reflects static nature of UDF.
    - Change: Removed PC Name parameter from _PDH_Init(). Don't know what I was thinking.
    - Change: DEBUG information defaults to error-only mode now. Set $PDH_DEBUG to 2 to show ALL Debug info
    - Change: All examples (save for one) now follow the 'baseline-sleep' model for collecting data (not always needed)
    - Added: _PDH_ObjectBaseCollectQueryData(), _PDH_ProcessObjectCollectQueryData(), & _PDH_ProcessAllCollectQueryData()   (to better support baseline-sleep)
  • 5/22/2011
    - New UDF: PDH_HardDiskUsageExample.au3
    - _PDH_PerformanceCounters.au3 UDF changes:
    -  Added:
     _PDH_RegistryCheck(), _PDH_RegistryEnable(), and _PDH_RegistryDisable() -> to allow checking & altering Performance Counter availability on local OR Remote PC. Note that ADMIN rights are req'd for making changes.
    -  Changed:
    _PDH_Init() & _PDH_UnInit altered to work with new Registry modification code
    _PDH_Init() now takes 3 optional parameters regarding Registry state (force registry on, restore on exit, PC Name)
    _PDH_BrowseCounters() - added new parameters to allow customization, now allows multiple selections to be returned in an array
    -  Fixed:
    _PDH_BrowseCounters() - default title was not showing for Win7
    _PDH_GetCounterList() - Wildcard sanity-check fixed for special cases where a * is part of the Instance name

    - Fixed: TestPDH_PerformanceCounters -> Non-localized + PC-Name output wasn't displaying correctly in certain cases.
  • 8/19/2010:
    - Fixed: _PDH_UnInit() was looking for parameters when called On-Exit
    - Fixed: _PDH_ObjectBaseCreate -> Logic error if Couldn't get counters from given PCNAME
    - Fixed: Extra Registry value found that could disable Performance Counters, changed registry save/restore code
    - Updated: _PDH_PerformanceCounter_Notes.txt (requirements)
    - Changed: Core Module: _PDH_Init() registers _PDH_UnInit() as an on-exit function (and _PDH_UnInit() unregisters itself)
    - Added: Core: _PDH_ConnectMachine() - connects to the given machine (really only adds the name to the dropdown box in BrowseCounters calls)
    - Added: _PDH_ProcessCounters Module: two wrapper functions: _PDH_ProcessObjectGetPID() and _PDH_ProcessObjectGetName()
    -> More Examples Added <-:
    TestPDH_ProcessCounter, TestPDH_ProcessCounterPoll, TestPDH_ProcessMultipleInstanceMonitor, TestPDH_HDDWaitIdle, TestPDH_CPUUsage, TestPDH_NetworkUsage
  • 7/2/2010 (MASSIVE Update):
    _PDH_PerformanceCounters core module:
     - Fixed: PCRE "% Processor Usage" detection in 'Update' functions to work with nonlocalized counter strings.  NOTE: will not work on localized non-English Counters (forcing it with a 'path' of -2 works here)
    - Fixed: _PDH_GetCounterInfo now works correctly on Windows 2000 (return message discrepancy)
    - Changed: _PDH_UpdateCounterArray now retrieves null-term strings the (better) way.
    - Changed: _PDH_FreeQueryHandle now returns False when there is an error condition returned,
        and also if successful now invalidates the passed Query Handle.
        NOTE _PDH_UnInit() will *not* invalidate any (optional) passed Query handles - they will retain their values
    - Changed: _PDH_RemoveCounter also now invalidates the passed Counter Handle
    - Changed: _PDH_UpdateCounterArray => renamed to => _PDH_UpdateWildcardCounter
    - Changed: __PDH_LocalizeCounter() now works with non-localized counters ending in slash but with no PC name
    - Removed: Comments and error codes extracted and put in two separate text files:
       _PDH_Error_Codes.txt (the PDH codes you'll see in the console window and in @extended returns)
       _PDH_PerformanceCounter_Notes.txt - Some personal notes/references about the PDH process project
    - Removed extraneous Wrapper functions (if you need these, see the older version):
       _PDH_AddCountersTo2DArray()    ; needless wrapper function
       _PDH_GetCounterValuesByPathArrays()    ; needless wrapper function
    TestPDH_PerformanceCounters GUI:
    - Changed default 'convert to all instances' behavior to instead work only on what is returned from the Browse Counter Dialoag box.  If you still want *all* instances, just select 'All instances' from the Dialog box.
    _PDH_ProcessStats module removed.  The 3 'ObjectBase' modules can be used in place of this.
    TestPDH_Multi => renamed to => TestPDH_ProcessLoop (also utilizes new interface)
    _PDH_TaskMgrPerfStats => renamed to => _PDH_TaskMgrSysStats (with some changes)
    - Changed: utilizes <_PDH_ObjectBaseCounters.au3> now
    - Added: _PDH_ProcessList() -> simple list that should report on ANY PC (but mind the '.exe' issue)
      (see TestPDH_ProcessLoop on how to get the *local* PC Processes info)
    - Return style changed for both _PDH_ProcessGetParent() and _PDH_ProcessGetChildren(), to keep consistency in returns. ([0]=name,[1]=PID#)
    _PDH_AddProcessCounter module removed.  Instead use <_PDH_ProcessCounters.au3>
    Addition of 3 new modules (plus a simple example) to make adding Counters with the same Base Object easier:
    _PDH_ObjectBaseCounters -> for Counters that have the same base and use an Instance name or Wildcard
    _PDH_ProcessCounters    -> for single processes, 1 or more Counters [Local PC only]
      These functions will monitor 'Instance' changes and readjust Counters as needed.
       (MUCH easier and safer implementation than previous _PDH_AddProcessCounter UDF)
    _PDH_ProcessAllCounters -> for ALL processes, 1 or more Counters [Local PC only]
    + minor misc. changes, alterations in some UDF's to work with new interfaces.
  • 6/20/2010:
    - Added new module: _PDH_AddProcessCounter.au3, to make adding single Process Counters easier
    - Added a test of the new module 'TestPDH_ProcessCounter.au3'.  This will have to be manually edited to test other Processes other than 'autoit3.exe'
    - Updated TestPDH_PerformanceCounters to now display "Non-Localized Counter Path *with* PC Name"
  • 5/18/2010: Localization issues should be fixed. New function _PDH_CounterNameToNonLocalStr() creates a special string that can be used to create counters for other languages.
    - Two support functions were added in addition to the above
    - _PDH_GetCounterInfo() now returns an array.
    - TestPDH_PerformanceCounters now provides better output on the bottom.
      * Also Fixed PCRE issue with multiple parentheses.
  • 5/14/2010: x64 Compatibility Is Fixed!
  • 5/9/2010: Releasing W-I-P since I feel the need for this is out there, even if the project is not complete.
    Note that there are extra modules includes for the 'Test' programs (_WinAPI_GetPerformanceInfo.au3, _WinAPI_GetSystemInfo.au3, _WinTimeFunctions.au3, and the unnecessary TestWinTimeFunctions.au3)
  • 5/09-5/2010 Updates: Added extra _PDH extended info modules, more functions in general (lost track, sorry), new Test programs, etc.
    - Bottom edit box now displays appropriate 'GetCounterInfo' for selected Counter Handle in listview.
  • 4/1/09 Update: Wildcard Counters support.
  • 3/7/09:
    !! CPU Usage now reports correct % (based on comparisons with Task Manager)!!
    New functionality:
    TEST_PDH_PerformanceCounters now allows Manual entry for more experimentation
    Added functions:
    Also added from another UDF (just to keep file count down):
    _WinAPI_GetSystemInfo() - for CPU count
    Altered functions:
    PDH_Init() now initializes CPU totals
    PDH_UpdateCounters() changed to test for CPU usage Counters and adjust according to # of CPU's
    (PDH_UpdateCounter() has the same functionality)
    _PDH_AddCountersByArray() altered slightly in that the 'bottom rows' parameter is a count rather than a bool.
    - *misc* other changes
A Scendant,
Oct 18, 2012, 4:53 AM