Data hosted with ♥ by Pastebin.com - Download Raw - See Original
  1. #include-once
  2. ;~ #obfuscator_ignore_funcs _PDH_UnInit     ; Currently Obfuscator ignores this in #include's
  3. #include <_WinAPI_GetSystemInfo.au3>    ; _WinAPI_GetSystemInfo(6) -> CPU count
  4. ; ===============================================================================================================================
  5. ; <_PDH_PerformanceCounters.au3>
  6. ;
  7. ; Functions to Initialize, Get, Update, Collect Values, and Uninitialize PDH Performance Counters
  8. ;   Performance Counters are performance information reported from the current PC or a Networked PC
  9. ;   encompassing Process, Processor, Network, System, .NET, Hardware, and other Performance Data
  10. ;
  11. ;   version 2011.06.02
  12. ;
  13. ; AutoIT version requirements: Minimum: AutoIT v3.3.6.0 (3.2.12.1 Unicode if alter DLLCall/Struct types)
  14. ;   O/S: Windows 2000+
  15. ;
  16. ; TO DO: Test Registry functions on Remote PC's. Find a workaround for the 'Browse Counters Dialog' DLL-hell bug (thanx MS!)
  17. ;
  18. ; Functions:
  19. ;   _PDH_RegistryCheck()    ; Checks if Performance Counters are enabled in the Registry (Returns True if they are)
  20. ;
  21. ; * NOTE * - The following 2 functions require ADMINISTRATOR privileges and *MAY* require a reboot depending on O/S
  22. ;   _PDH_RegistryEnable()   ; Enables Performance Counters via the Registry (requires ADMIN privileges)
  23. ;   _PDH_RegistryDisable()  ; Disables Performance Counters via the Registry (requires ADMIN privileges)
  24. ;
  25. ;   _PDH_Init()             ; Initialize PDH.DLL handle, get CPU count, & verify registry key settings are correct
  26. ;   _PDH_UnInit()           ; Release PDH.DLL handle & restore registry key settings
  27. ;   _PDH_ReloadDLL()        ; Forces PDH.DLL to be released and then reloads it.  This is a workaround for 2 bugs:
  28. ;                           ;  1. In the 'PdhBrowseCounters' API call, which winds up loading 40 DLL's and keeping them
  29. ;                           ;     loaded, as WELL as increasing the Reference count on pdh.dll! (Win 7 + .NET 4)
  30. ;                           ;  2. The PdhExpandWildCardPath API call (_PDH_GetCounterList()) doesn't recognize new
  31. ;                           ;     object instances unless PDH.DLL is unloaded first!
  32. ;                           ; NOTE: In BOTH cases, ALL Query handles will be destroyed & invalid!
  33. ;                           ; Also, a subsequent call to _PDH_BrowseCounters() will be SLOW to start up.
  34. ;   _PDH_ValidatePath()     ; Function to check if a Counter path is valid or not
  35. ;                           ; NOTE: Needs PDH.DLL reloaded in order to see new instances
  36. ;   _PDH_ConnectMachine()   ; Connects to another PC/Machine.  Not really required unless adding to the dropdown list for
  37. ;                           ;  _PDH_BrowseCounters(). Otherwise, the Browse Counters dialog does accept manual PC entry.
  38. ;   _PDH_BrowseCounters()   ; Displays the "Browse Performance Counters" dialog box and allows selection
  39. ;   _PDH_GetCounterList()   ; Returns an array OR 'multi-string' of counter paths (used in calling _PDH_AddCounter())
  40. ;                           ; NOTE: Needs PDH.DLL reloaded in order to see new instances
  41. ;   _PDH_GetNewQueryHandle()    ; Get a new PDH Query Handle
  42. ;   _PDH_FreeQueryHandle()      ; Free PDH Query Handle
  43. ;   _PDH_AddCounter()           ; Add a Counter to a PDH Query Handle
  44. ;   _PDH_AddCountersByArray()   ; Adds Counters to a PDH Query Handle using paths in an array
  45. ;                               ; (as returned by _PDH_GetCounterList()), returns 2-D array [path & Counter Handle]
  46. ;   _PDH_AddCountersByMultiStr()    ; Adds Counters to a PDH Query Handle using paths passed in a 'Multi String'
  47. ;                                   ;  (i.e. a string with multiple paths, separated by $sSepChar (ex: 'str1|str2|str3')
  48. ;   _PDH_AddCountersByWildcardPath() ; Wrapper - one call achieves GetCounterList() & AddCountersByArray()
  49. ;   _PDH_RemoveCounter()            ; Removes a Counter Handle from a PDH Query Handle
  50. ;   _PDH_CounterNameToNonLocalStr() ; Converts a *full* Counter path to a non-localized generic string for use
  51. ;                                   ;  with _PDH_AddCounter*() and _PDH_GetCounterList() functions.
  52. ;                                   ; RESOLVES LOCALIZED<->NONLOCALIZED COUNTER PORTABILITY ISSUES
  53. ;   _PDH_GetCounterInfo()       ; Retrieves a formatted string of information about the given PDH Counter
  54. ;   _PDH_CollectQueryData()     ; Function used before or during 'UpdateCounter' calls (this updates Counter values *internally*)
  55. ;                               ;  Use *outside* of 'UpdateCounter' functions allows one to grab each Counter separately
  56. ;                               ;  without having to re-collect the data (see $bGrabValueOnly in 'UpdateCounter' functions)
  57. ;   _PDH_UpdateCounter()        ; Update Counter(s) attached to a PDH Query Handle and Returns new Value of *ONE* Counter
  58. ;   _PDH_UpdateWildcardCounter(); Updates Wildcard Counter(s) attached to a PDH Query Handle, returns 2D array of new values
  59. ;   _PDH_UpdateCounters()       ; Updates Array of Counters attached to a PDH Query Handle inserting current Values,
  60. ;                               ;  and optionally, Delta Values.  A 2D array must be passed by Reference.
  61. ;                               ;  The only required columns are 'Counter Handle' and 'Value', both of which can be in any column
  62. ;                               ;   same as the start/end rows.  'Counter Name' and 'Delta Value' columns are also optional
  63. ;   _PDH_GetCounterValueByPath()    ; Wrapper - Adds a Counter, Grabs the counter value, Removes Counter, Returns Value
  64. ;
  65. ; *Mostly* for Internal-Use functions (but can be called given the caller knows what's what):
  66. ;   _PDH_GetCounterNameByIndex() ; Gets a Counter or Object name by Index #.  (used by _PDH_AddCounter*, _PDH_GetCounterList)
  67. ;   _PDH_GetCounterIndex()      ; Gets the Counter or Object Index #. (used by _PDH_CounterNameToNonLocalStr())
  68. ;
  69. ; INTERNAL-ONLY functions:
  70. ;   _PDH_DebugWrite()               ; Writes debug output to Console if $PDH_DEBUGLOG>=1
  71. ;   _PDH_DebugWriteErr()            ; Writes debug error output to Console if $PDH_DEBUGLOG>=2
  72. ;   __PDH_RegistryToggle()          ; Sets, clears, or checks state of Registry values pertaining to Performance Counters' availability
  73. ;   __PDH_LocalizeCounter()         ; Converts 'non-localized' string to a Full (localized) Counter Path. Used by 3 functions
  74. ;   __PDH_StringGetNullTermMemStr() ; Grabs a zero-terminated string where the length is unknown
  75. ;   __PDH_ForceFreeDLL()    ; Attempts to forcibly free the DLL (keeps decrementing its count, up to $iDecCount).
  76. ;                           ;   See _PDH_ReloadDLL() and _PDH_BrowseCounters()
  77. ;
  78. ; Dependencies:
  79. ;   <_WinAPI_GetSystemInfo.au3>     ; System basic info, used for CPU count info
  80. ;
  81. ; See also:
  82. ;   <TestPDH_PerformanceCounters.au3>   ; GUI interface to much of <_PDH_PerformanceCounters.au3>
  83. ;   <_PDH_ObjectBaseCounters.au3>   ; Special Object-based Interface for interacting with Counters
  84. ;   <_PDH_ProcessCounters.au3>      ; Individual *Local* Process Object Interface
  85. ;   <_PDH_ProcessAllCounters.au3>   ; *ALL* Local-Processes Object Interface
  86. ;   <TestPDH_ObjectTests.au3>       ; Example use/tests of the 3 Object Counters modules
  87. ;   <TestPDH_ProcessLoop.au3>       ; Example use of <_PDH_ProcessAllCounters.au3>
  88. ;                                   ;  (for displaying Process Info in a loop)
  89. ;   <_PDH_ProcessGetRelatives.au3>  ; Uses ObjectBaseCounters to give 'ProcessList' & Child/Parent Process Info
  90. ;   <TestPDH_ProcessGetRelatives.au3>   ; Test of the <_PDH_ProcessGetRelatives.au3>
  91. ;   <_PDH_TaskMgrSysStats.au3>      ; System Statistics Counters useful in gathering Task-Manager type info
  92. ;   <TestPDH_TaskManager.au3>       ; Test of the <_PDH_TaskMgrSysStats.au3> and <_PDH_ProcessAllCounters.au3>
  93. ;                                   ;  ** A very messy W.I.P. currently **;
  94. ;   <_ProcessRegPerfCounters.au3>   ; started, but never finished (too slow and complex)
  95. ;   [CpuUsage_src].zip              ; Registry Performance Counters C++ code
  96. ;
  97. ; NOTES:
  98. ;   Performance Counters Registry Key Values which affect Performance Counter availability:
  99. ;
  100. ;    Key: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib
  101. ;    Value: Disable Performance Counters
  102. ;
  103. ;    Key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PerfOS\Performance
  104. ;     Value: Disable Performance Counters
  105. ;
  106. ;    Key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PerfProc\Performance
  107. ;     Value: Disable Performance Counters
  108. ;
  109. ;   If _PDH_Init() doesn't have write access to HKLM branch and one/all are set to non-zero,
  110. ;    functions would probably return 'PDH_CSTATUS_NO_OBJECT' if these Values are set to 1,
  111. ;       and _PDH_Init() wasn't able to change them. (It may take a reboot between changing them though...)
  112. ;
  113. ; Additional:
  114. ;   Processes ending in .EXE are reported\looked up *without* the ending .EXE
  115. ;       (i.e. svchost.exe = svchost, or svchost#0 (and #1 etc - depending on # of instances) )
  116. ;   Processes ending in *ANYTHING* else RETAIN their .ext (example: more.com, scrnsave.scr,Creative_Audio_Engine_Cleanup.0001)
  117. ;       Wildcard Counters may help, but there are modules bundled now that handle Process Counters effectively.
  118. ;
  119. ;   There's a service you can start at the 'Run' prompt named 'perfmon.msc'.. doesn't seem to do a whole lot, though..?
  120. ;
  121. ; References (see _PDH_PerformanceCounter_Notes.txt and _PDH_Error_Codes.txt for PDH error codes)
  122. ;
  123. ; Author: Ascend4nt
  124. ; ===============================================================================================================================
  125.  
  126.  
  127. ; ===============================================================================================================================
  128. ;  --------------------  GLOBAL PDH VALUES  --------------------
  129. ; ===============================================================================================================================
  130.  
  131. ; Used in all _PDH calls (though 1st 2 not necessary for _PDH_BrowseCounters(),_PDH_GetCounterList())
  132. Global $_PDH_hDLLHandle=-1,$_PDH_bInit=False,$_PDH_iLastError=0
  133.  
  134. ; Available after a call to _PDH_UpdateCounters()
  135. Global $_PDH_aInvalidHandles[1]=[0]
  136.  
  137. ; Registry modification variables
  138. Global $_PDH_REG_MODIFIED,$_PDH_RESTORE_REG
  139.  
  140. ; Used by _PDH_UpdateCounters (and other functions that adjust Counter results by # CPU's). Initialized by Init()
  141. Global $_PDH_iCPUCount
  142.  
  143. ; Used to toggle Console-Output to one of 3 modes: 0=completely Off, 1=Report Errors only, >1=Report ALL
  144. Global $PDH_DEBUGLOG=1
  145.  
  146.  
  147. ; ===================================================================================================================
  148. ;   --------------------    MAIN FUNCTIONS  --------------------
  149. ; ===================================================================================================================
  150.  
  151.  
  152. ; ===============================================================================================================================
  153. ; Func _PDH_RegistryCheck($sPCName='')
  154. ;
  155. ; Checks to see if Performance Counters are enabled in the registry.
  156. ;
  157. ; $sPCName = *optional* value -> the machine name, prefixed as normal with '\\' (Example: '\\PCNAME')
  158. ;
  159. ; Returns:
  160. ;   Success: True = enabled, False with @error=0 = disabled
  161. ;   Failure: False, with @error set:
  162. ;       @error =  1 = invalid parameter ($sPCName)
  163. ;
  164. ; Author: Ascend4nt
  165. ; ===============================================================================================================================
  166.  
  167. Func _PDH_RegistryCheck($sPCName='')
  168.     If __PDH_RegistryToggle(-1,$sPCName) Then Return True
  169.     Return SetError(@error,@extended,False)
  170. EndFunc
  171.  
  172.  
  173. ; ===============================================================================================================================
  174. ; Func _PDH_RegistryEnable($sPCName='')
  175. ;
  176. ; Enables Performance Counters via the Registry. (default on most machines is enabled)
  177. ;   NOTE: Requires ADMINISTRATOR Privileges, as it alters keys in the HKLM branch.
  178. ;
  179. ; $sPCName = *optional* value -> the machine name, prefixed as normal with '\\' (Example: '\\PCNAME')
  180. ;
  181. ; Returns:
  182. ;   Success: True with @extended = # of changes
  183. ;   Failure: False and @error set:
  184. ;       @error =  1 = invalid parameter ($sPCName)
  185. ;       @error = 16 = error setting/clearing a registry value that needed to be altered. @extended contains RegWrite() error
  186. ;
  187. ; Author: Ascend4nt
  188. ; ===============================================================================================================================
  189.  
  190. Func _PDH_RegistryEnable($sPCName='')
  191.     Local $vRet=__PDH_RegistryToggle(1,$sPCName)
  192.     Return SetError(@error,@extended,$vRet)
  193. EndFunc
  194.  
  195.  
  196. ; ===============================================================================================================================
  197. ; Func _PDH_RegistryDisable($sPCName='')
  198. ;
  199. ; Disables Performance Counters via the Registry.
  200. ;   NOTE: Requires ADMINISTRATOR Privileges, as it alters keys in the HKLM branch.
  201. ;
  202. ; $sPCName = *optional* value -> the machine name, prefixed as normal with '\\' (Example: '\\PCNAME')
  203. ;
  204. ; Returns:
  205. ;   Success: True with @extended = # of changes
  206. ;   Failure: False and @error set:
  207. ;       @error =  1 = invalid parameter ($sPCName)
  208. ;       @error = 16 = error setting/clearing a registry value that needed to be altered. @extended contains RegWrite() error
  209. ;
  210. ; Author: Ascend4nt
  211. ; ===============================================================================================================================
  212.  
  213. Func _PDH_RegistryDisable($sPCName='')
  214.     Local $vRet=__PDH_RegistryToggle(0,$sPCName)
  215.     Return SetError(@error,@extended,$vRet)
  216. EndFunc
  217.  
  218.  
  219. ; ===============================================================================================================================
  220. ; Func _PDH_Init($bForceCountersOn=False,$bRestoreRegStateOnExit=False)
  221. ;
  222. ; Simple call to 'initialize' PDH calling. Basically just Loads and sets the handle to the PDH.DLL,
  223. ;   AND clears any 'Disable Performance Counter' Registry values
  224. ;   REQUIRED since all handles obtained via PDH calls will be invalidated if it is not DLLOpen()'ed,
  225. ;   and probably won't work if those Reg values are set to non-zero
  226. ;
  227. ; $bForceCountersOn = If True, will attempt to enable Performance Counters through the registry.
  228. ;   Note that this requires ADMIN rights, and may require a reboot on certain O/S's.
  229. ;   If False, the function will fail if Performance Counters are disabled in the Registry.
  230. ; $bRestoreRegStateOnExit = If True, and $bForceCountersOn = True, the state of the Registry is restored on exit
  231. ;   If either is False, this parameter isn't used.
  232. ;
  233. ; NOTE: Opening the PDH.DLL and obtaining a PDH Query Handle creates a small file in the @TempDir named something like:
  234. ;   Perflib_Perfdata_xxx.dat
  235. ;  This file is typically small (~16 KB), so it's not a concern. And it is automatically deleted after _PDH_UnInit().
  236. ;
  237. ; Returns:
  238. ;   Success: True
  239. ;   Failure: False, with @error set:
  240. ;       @error = 16 = Could not initialize (due to Registry settings). If $bForceCountersOn=True, might not be running as ADMIN
  241. ;           If Running as ADMIN, and $bForceCountersOn=True, then @extended=RegWrite() error
  242. ;       @error = 32 = Could not open PDH.DLL
  243. ;
  244. ; Author: Ascend4nt
  245. ; ===============================================================================================================================
  246.  
  247. Func _PDH_Init($bForceCountersOn=False,$bRestoreRegStateOnExit=False)
  248.     ; Already initialized?
  249.     If $_PDH_bInit Then Return True
  250.  
  251.     ; Clear Reg Flags
  252.     $_PDH_REG_MODIFIED=0
  253.     $_PDH_RESTORE_REG=0
  254.     ; Check if Performance Counters are enabled in the Registry:
  255.     If Not __PDH_RegistryToggle(-1) Then
  256. ;~      _PDH_DebugWriteErr("Performance Counters found to be disabled in registry..")
  257.         If Not $bForceCountersOn Or Not IsAdmin() Then Return SetError(16,0,False)
  258.         If Not __PDH_RegistryToggle(1) Then Return SetError(@error,@extended,False)
  259.         $_PDH_REG_MODIFIED=1
  260.         If $bRestoreRegStateOnExit Then $_PDH_RESTORE_REG=1
  261.     EndIf
  262.  
  263. ;~  Performance Counters are enabled... Now grab a handle to PDH.DLL
  264.  
  265.     ; NOTE: It is ABSOLUTELY REQUIRED to Open this handle while using PDH counter collection data
  266.     ;   Otherwise, all handles retrieved by Queries become invalid!
  267.     $_PDH_hDLLHandle=DllOpen("pdh.dll")
  268.     ; Error opening?
  269.     If $_PDH_hDLLHandle=-1 Then
  270.         ; Reset Registry if modified
  271.         If $_PDH_RESTORE_REG Then
  272. ;~          _PDH_DebugWrite("Resetting registry..")
  273.             If Not __PDH_RegistryToggle(0) Then Return SetError(@error,@extended,False)
  274.         EndIf
  275.         Return SetError(32,0,False)
  276.     EndIf
  277.  
  278.     ; GET # of CPU's
  279. #cs
  280.     ; Win 7+ and Win Server 2008 R2+ 64bit O/S's support more than 64 Processors on a computer
  281.     ;   To make use of >64 Processors, the O/S separates them into logical groups of <=64, and these groups cause
  282.     ;   individual processes to be confined to one group or the other.
  283.     ; While 1 process can't spread itself across more than 64 processors or change groups, it CAN 'see' if there
  284.     ;   are more than one group, and what the real total of logical processors are (again, Win7+/2008R2+ 64-bit O/S's)
  285. #ce
  286.     ; The following call will only succeed on Win7+/2008R2+ 64-bit O/S's (@error otherwise):
  287.     Local $aRet = DllCall("kernel32.dll", "dword", "GetActiveProcessorCount", "word", 0xFFFF)   ; ALL_PROCESSOR_GROUPS = 0xFFFF
  288.     If Not @error Then
  289.         $_PDH_iCPUCount=$aRet[0]
  290.         ;MsgBox(0, "GetActiveProcessorCount return", "Total # Logical Processors = " & $_PDH_iCPUCount & @CRLF)
  291.     Else
  292.         $_PDH_iCPUCount=_WinAPI_GetSystemInfo(6)
  293.         If @error Then
  294.             $_PDH_iCPUCount=EnvGet("NUMBER_OF_PROCESSORS")
  295.             If $_PDH_iCPUCount="" Then
  296.                 $_PDH_iCPUCount=1
  297.             Else
  298.                 $_PDH_iCPUCount=Int($_PDH_iCPUCount)
  299.             EndIf
  300.         EndIf
  301.     EndIf
  302.     _PDH_DebugWrite("CPU count result:" & $_PDH_iCPUCount)
  303.  
  304.     ; Good to go - set 'initialized' flag to True
  305.     $_PDH_bInit=True
  306.     ; And setup exit routine in case programmer forgets to call it/exits unexpectedly
  307.     OnAutoItExitRegister("_PDH_UnInit")
  308.     Return True
  309. EndFunc
  310.  
  311.  
  312. ; ===============================================================================================================================
  313. ;  Func _PDH_UnInit($hPDHQueryHandle=-1)
  314. ;
  315. ; Simple call to 'uninitialize' all PDH data and disable it from being used (another 'Init' will be required otherwise)
  316. ;   Basically 1. Closes the given PDH Query Handle (if valid) and 2. Unloads and resets the PDH.DLL handle.
  317. ;   This is not *technically* required since AutoIT automatically closes any open DLL's, which also invalidates
  318. ;   the PDH Query Handle, but it's good practice to clean up like this.
  319. ;   Oh, and 3. Resets the relevant Registry values if they were changed (which is *not* done automatically by AutoIT)
  320. ;
  321. ; $hPDHQueryHandle = (optional) PDH Query Handle to close/free before closing DLL. -1 = none
  322. ;   Note that this is *NOT* set to 0 (invalidated) like a direct call to _PDH_FreeQueryHandle() would do
  323. ;
  324. ; Returns: None (assume success!)
  325. ;
  326. ; Author: Ascend4nt
  327. ; ===============================================================================================================================
  328.  
  329. Func _PDH_UnInit($hPDHQueryHandle=-1)
  330.     If Not $_PDH_bInit Then Return True ; If not initialized, nothing to Uninitialize
  331.  
  332.     ; Free Query Handle (if valid). We don't care if there was an error, since we will be closing DLL
  333.     If @NumParams Then _PDH_FreeQueryHandle($hPDHQueryHandle)   ; The check for @NumParams is important in an On-Exit function!
  334.  
  335.     DllClose($_PDH_hDLLHandle)  ; Close the DLL
  336.  
  337.     ; If requested, reset any Performance Counter registry keys that were modified. No error checking here
  338.     If $_PDH_RESTORE_REG Then __PDH_RegistryToggle(0)
  339.  
  340.     ; Reset variables
  341.     $_PDH_hDLLHandle=-1
  342.     $_PDH_bInit=False
  343.  
  344.     OnAutoItExitUnregister("_PDH_UnInit")   ; Unregister self as an on-exit function
  345.     Return
  346. EndFunc
  347.  
  348.  
  349. ; ===============================================================================================================================
  350. ; Func _PDH_ReloadDLL($hPDHQueryHandle=-1)
  351. ;
  352. ; Forces PDH.DLL to be released and then reloads it.  This is a workaround for 2 bugs:
  353. ; 1. In the 'PdhBrowseCounters' API call, which winds up loading 40 DLL's and keeping them loaded,
  354. ;   as WELL as increasing the Reference count on pdh.dll!
  355. ; 2. The PdhExpandWildCardPath API call (_PDH_GetCounterList()) doesn't recognize new object instances
  356. ;   unless PDH.DLL is unloaded first!
  357. ; NOTE: In BOTH cases, ALL Query handles will be destroyed & invalid!
  358. ;  - Also, a subsequent call to _PDH_BrowseCounters() will be SLOW to start up.
  359. ;
  360. ; $hPDHQueryHandle = (optional) PDH Query Handle to close/free before freeing the DLL. -1 = none
  361. ;   Note that this is *NOT* set to 0 (invalidated) like a direct call to _PDH_FreeQueryHandle() would do
  362. ;
  363. ; Returns:
  364. ;   Success: True - DLL loaded (or if _PDH_Init() wasn't called before, it was now, and is successful)
  365. ;   Failure: False, with @error set:
  366. ;       @error = 16 = Could not initialize (due to Registry settings). If $bForceCountersOn=True, might not be running as ADMIN
  367. ;           If Running as ADMIN, and $bForceCountersOn=True, then @extended=RegWrite() error
  368. ;       @error = 32 = Could not open/re-open PDH.DLL
  369. ;
  370. ; Author: Ascend4nt
  371. ; ===============================================================================================================================
  372.  
  373. Func _PDH_ReloadDLL($hPDHQueryHandle=-1)
  374.     ; Migh as well run Init() if it wasn't done already
  375.     If Not $_PDH_bInit Then
  376.         If Not _PDH_Init() Then Return SetError(@error,@extended,False)
  377.     Else
  378.         ; It's initialized, so free the query handle, close the DLL and reopen it
  379.         _PDH_FreeQueryHandle($hPDHQueryHandle)
  380.     EndIf
  381.  
  382.     ; 1st close the pseudo-handle AutoIt gave us. This decrements load-count by 1, and usually unloads the DLL.
  383.     DllClose($_PDH_hDLLHandle)
  384.  
  385.     ; Force all instances to unload, if its still around (PdhBrowseCounters can cause this issue)
  386.     __PDH_ForceFreeDLL('pdh.dll',999)
  387.  
  388.     ; Now re-load/open
  389.     $_PDH_hDLLHandle=DllOpen("pdh.dll")
  390.  
  391.     ; Error opening?
  392.     If $_PDH_hDLLHandle=-1 Then
  393.         _PDH_UnInit($hPDHQueryHandle)
  394.         Return SetError(32,0,False)
  395.     EndIf
  396.     Return True
  397. EndFunc
  398.  
  399.  
  400. ; ===============================================================================================================================
  401. ; Func _PDH_ValidatePath($sCounterPath)
  402. ;
  403. ; Function to check a Counter Path and return whether it is valid or not.
  404. ;
  405. ; $sCounterPath = Counter Path (of the format returned by _PDH_BrowseCounters(). Examples:
  406. ;   Format with network path: "\\<PCNAME>\Process(ProcessName)\% Processor Time")
  407. ;   Actual Value (on local PC): "\Process(Idle)\% Processor Time"
  408. ;
  409. ;   NOTE: The PdhValidatePath API call (_PDH_ValidatePath()) doesn't recognize new object instances on Vista+ O/S's
  410. ;     *unless* PDH.DLL is unloaded first, or _PDH_BrowseCounters() is called [NOT recommended - see notes on THAT function]
  411. ;     The fix: use _PDH_ReloadDLL(), OR use Wildcard counters, which seem to recognize new object instances
  412. ;     [Error returned in these cases: 0x800007D1 (PDH_CSTATUS_NO_INSTANCE)]
  413. ;
  414. ; Return:
  415. ;   Success: True (or False if invalid, with @error=0, @extended=PDH return code [as does $_PDH_iLastError)
  416. ;   Failure: False with @error set:
  417. ;       @error = 1 = invalid parameter
  418. ;       @error = 2 = DLL Call error, @extended=DLLCall error (see AutoIt Help)
  419. ;       @error = 7 = non-localized string passed, but invalid format
  420. ;
  421. ; Author: Ascend4nt
  422. ; ===============================================================================================================================
  423.  
  424. Func _PDH_ValidatePath($sCounterPath)
  425.     Local $aRet,$tErr,$hPDHDLL
  426.  
  427.     If Not IsString($sCounterPath) Then Return SetError(1,0,False)
  428.  
  429.     ; Unlike other functions, getting a counter list doesn't require initialization,
  430.     ;   though it doesn't hurt (especially if Disable Performance Counters is set)
  431.     If Not $_PDH_bInit Then
  432.         $hPDHDLL="pdh.dll"
  433.     Else
  434.         $hPDHDLL=$_PDH_hDLLHandle
  435.     EndIf
  436.  
  437.     ; Non-localized string? Create localized string and add it.
  438.     If StringLeft($sCounterPath,1)=':' Then
  439.         $sCounterPath=__PDH_LocalizeCounter($sCounterPath)
  440.         If @error Then Return SetError(@error,0,"")
  441.         _PDH_DebugWrite("Localized counter (from non-localized string):"&$sCounterPath)
  442.     EndIf
  443.  
  444.     $aRet=DllCall($_PDH_hDLLHandle,"long","PdhValidatePathW","wstr",$sCounterPath)
  445.  
  446.     ; DLL call error?
  447.     If @error Then
  448.         ; Only needed to allow _PDH_DebugWrite() msg (which resets @error)
  449.         $tErr=@error
  450.         _PDH_DebugWriteErr("Path '"&$sCounterPath&"' caused a DLL-Call error")
  451.         Return SetError(2,$tErr,False)
  452.     EndIf
  453.  
  454.     ; PDH return code? Then *most* likely not a valid path (there are some rare exceptions but they aren't likely to occur)
  455.     If $aRet[0] Then
  456.         $_PDH_iLastError=$aRet[0]
  457.         _PDH_DebugWriteErr("Path '"&$sCounterPath&"', per PdhValidatePathW, is invalid (or has too many wildcards)! [Return code: "&Hex($_PDH_iLastError)&']')
  458.         Return SetExtended($_PDH_iLastError, False)
  459.     EndIf
  460.     Return True
  461. EndFunc
  462.  
  463.  
  464. ; ===============================================================================================================================
  465. ; Func _PDH_ConnectMachine($sPCName)
  466. ;
  467. ; Connects to a given machine (PC).  Not necessary for any function really, because the PC Name can be part of the path.
  468. ;   However, _PDH_BrowseCounters() will not show other PC's in its drop-down list unless they are connected to
  469. ;    first using this function.  (An alternative of course is just to manually enter the PC Name in the PC box)
  470. ;
  471. ; $sPCName =  PC/Machine name of the format: "\\PCNAME"
  472. ;
  473. ; Returns:
  474. ;   Success: True, @error=0
  475. ;   Failure: False, and @error set:
  476. ;       @error = 16 = not initialized, and _PDH_Init() call failed
  477. ;       @error = 2 = DLL Call error, @extended=DLLCall error (see AutoIt Help)
  478. ;       @error = 3 = PDH error (@extended contains error, also $_PDH_iLastError)
  479. ;
  480. ; Author: Ascend4nt
  481. ; ===============================================================================================================================
  482.  
  483. Func _PDH_ConnectMachine($sPCName)
  484.     ; Check that PDH handle has been initialized
  485.     If Not $_PDH_bInit Then Return SetError(16,0,False)
  486.     If Not IsString($sPCName) Or $sPCName="" Then Return SetError(1,0,False)
  487.     Local $aRet=DllCall($_PDH_hDLLHandle,"long","PdhConnectMachineW","wstr",$sPCName)
  488.     If @error Then Return SetError(2,@error,False)
  489.     ; PDH return code? Then PDH error (could be invalid PC name, or PC doesn't support remote registry connections/performancec monitoring)
  490.     If $aRet[0] Then
  491.         $_PDH_iLastError=$aRet[0]
  492.         _PDH_DebugWriteErr("Machine '"&$sPCName&"' was not able to be connected to. [Return code: "&Hex($_PDH_iLastError)&']')
  493.         Return SetError(3,$_PDH_iLastError, False)
  494.     EndIf
  495.     Return True
  496. EndFunc
  497.  
  498.  
  499. ; ====================================================================================================================================
  500. ; Func _PDH_BrowseCounters($sTitle="",$hWnd=0,$iDetailLvl=4,$bAllowRemotePCs=True,$bAllowMultipleSel=False,$bAllowCostlyObjects=False)
  501. ;
  502. ; Displays a Browse Counters Dialog Box, allowing user to select one or more counters to add to the query. (MSDN)
  503. ;   Returns a Counter Selection String, or "" if user cancelled out of Dialog (or error occurred)
  504. ;
  505. ; $sTitle = If not "", then the Title to display on top of the "Browse Performance Counters" box
  506. ; $hWnd = (optional) Parent Window
  507. ; $iDetailLvl = Initial Browser Display mode: 1 (NOVICE), 2 (ADVANCED), 3 (EXPERT), 4 (WIZARD)
  508. ; $bAllowRemotePCs = If True/non-zero, allows selection of other PC's. If False/0, only allows counter lookup on current PC
  509. ; $bAllowMultipleSel = If True, an array will be returned of selection(s). If False, only the first (topmost) selection is returned
  510. ;   NOTE: The ability to limit a selection to one item was broken with Vista+ O/S's (see info regarding flag 2 below), so
  511. ;    we have to allow the user to select multiple items, but with this parameter set to False, we disregard the others
  512. ; $bAllowCostlyObjects = If True, resource-intensive counters will be displayed. Default is False.
  513. ;   NOTE that these objects may require heavy CPU/memory usage!
  514. ;
  515. ; Returns:
  516. ;   Success: If $bAllowMultipleSel=False, the topmost selected Counter string is returned (wildcard or not). (@error=0)
  517. ;       If $bAllowMultipleSel=True, an array is returned (@error=0):
  518. ;       [0] = # of Counters
  519. ;       [x] = Counter path(s)
  520. ;   Failure: "" Empty String, and @error set:
  521. ;       @error = -1 = Nothing returned, even though API call returned as though a selection had been made
  522. ;       @error = 0  = User cancelled out of Dialog Box
  523. ;       @error = 2  = DLL Call error, @extended=DLLCall error (see AutoIt Help)
  524. ;       @error = 3  = PDH error code, with @extended set, as well as $_PDH_iLastError
  525. ;
  526. ; Author: Ascend4nt
  527. ; ====================================================================================================================================
  528.  
  529. Func _PDH_BrowseCounters($sTitle="",$hWnd=0,$iDetailLvl=4,$bAllowRemotePCs=True,$bAllowMultipleSel=False,$bAllowCostlyObjects=False)
  530.     Local $hPDHDLL,$aRet,$iBCFlags,$hPDHBCCallback,$sReturnStr="",$aReturnArr[2]
  531.     Local $stCounterSelectBuffer,$stBrowseDlgConfig,$stTitle
  532.  
  533.     ; Unlike other functions, getting a counter list doesn't require initialization,
  534.     ;   though it doesn't hurt (especially if Disable Performance Counters is set)
  535.     If Not $_PDH_bInit Then
  536.         $hPDHDLL="pdh.dll"
  537.     Else
  538.         $hPDHDLL=$_PDH_hDLLHandle
  539.     EndIf
  540.  
  541. ;~  _PDH_DebugWrite("_PDH_BrowseCounters() call, PDH DLL 'handle' (or just 'pdh.dll'):" & $hPDHDLL)
  542. #cs
  543.     ; ------------------------------------------------------------------------------------------------------------------
  544.     ; - Browse Counters Flags -
  545.     ;
  546.     ; 1 = Include Instance Index (#x and/or /x) for multiple instances that occur of a given object
  547.     ; 2 = Allow only a single counter selection/add (NOTE: Vista+: prevents wildcarding last part of path!)
  548.     ; 4 = Allow only a single counter selection per dialog, prevents 'Add' and 'Close' buttons from being displayed
  549.     ; 8 = Local counters only (local computer). Otherwise, this allows selection of network counters
  550.     ; 16 = Wildcard Instances (allows the user to select 'All Instances' which returns a wildcard string)
  551.     ; 32 = Hide Detail Box (not set = allows user to change the detail level of counters displayed)
  552.     ; 64 = Initialize Path - uses szReturnPath's value (not set here) to highlight an initial selection
  553.     ; 128 = Disable Machine Selection - If set, user cannot access remote machine counters
  554.     ; 256 = Include Costly Objects - If set, Counters that take a LOT of time/memory to process will be shown
  555.     ; 512 = If set, a. Only Performance Objects will be shown (Instances and Individual Counters are not shown)
  556.     ;       b. Return will include wildcard characters for instance name and counter IF the
  557.     ;         object is a multi-instance object
  558.     ; MSDN (on +512):
  559.     ;  ..For example, if the "Process" object is selected, the dialog returns the string "\Process(*)\*".
  560.     ;  If the object is a single instance object, the path contains a wildcard character for counter only.
  561.     ;  For example, "\System\*". You can then pass the path to PdhExpandWildCardPath to retrieve a list
  562.     ;  of actual paths for the object.
  563.     ; ------------------------------------------------------------------------------------------------------------------
  564. ;
  565.     ; ------------------------------------------------------------------------------------------------------------------
  566.     ; Notes:
  567.     ; * Vista+ O/S: adding 2 (single counter selection/add) causes blank-string returns (with 'success' result)
  568.     ;     for counters with wildcard at end of path
  569.     ; * not adding 16 (Wildcard Instances) causes crashes (wth!?) when wildcards are selected (at least on Win7..)
  570.     ; * not adding 4 (multi-counter selection toggle) causes failure on return, because a Callback function must
  571.     ;     be in place which processes each string individually.  This can be done, but would be better if user could
  572.     ;     see & interact with (including removing) chosen counters. Too much work IMO for this function.
  573.     ; * +64 (Initialize Path) - fails in practice, at least Vista+, so it's useless (would be nice if it worked though!)
  574.     ; * +256 (costly objects) - not recommended
  575.     ; ------------------------------------------------------------------------------------------------------------------
  576. #ce
  577.     $iBCFlags=1+4+16    ; cross-O/S-safe values
  578.     If Not $bAllowRemotePCs Then $iBCFlags+=8   ; forces user to select counters from the local PC only
  579.     If $bAllowCostlyObjects Then $iBCFlags+=256 ; requires more resources (possibly CPU/memory) to monitor these!
  580.  
  581.     ; Create the return buffer. PDH_MAX_COUNTER_PATH size is (2048). We create a buffer of 65536 (64K) to allow for multiple paths (default)
  582.     $stCounterSelectBuffer=DllStructCreate("byte[131072]")  ; 65536 wchars
  583. #cs
  584.     ; Set an initial path? - Fails, at least on Vista+
  585.     If $sDefCounter<>'' Then
  586. ;~      DllStructSetData($stCounterSelectBuffer,1,$sDefCounter)
  587.         ; For "byte[]" structure definitions, this would be needed:
  588.         DllStructSetData($stCounterSelectBuffer,1,StringToBinary($sDefCounter,2))
  589.         $iBCFlags+=64   ; flag that an initial path is in the output buffer
  590.     EndIf
  591. #ce
  592.     ; Create the main PDH_BROWSE_DLG_CONFIG structure:
  593.     ;   iFlagMask,hWndOwner,szDataSource,szReturnPathBuffer,cchReturnPathLength,pCallBack,dwCallBackArg,CallBackStatus,szDialogBoxCaption
  594.     $stBrowseDlgConfig=DllStructCreate("dword;hwnd;ptr;ptr;dword;ptr;dword_ptr;long;dword;ptr")
  595.     ; Set the data members appropriately (structure is zero-filled on creation, so no need to re-set the ones commented out)
  596.     DllStructSetData($stBrowseDlgConfig,1,$iBCFlags) ; Flags
  597.     DllStructSetData($stBrowseDlgConfig,2,$hWnd)    ; HWndOwner (NULL or parent GUI)
  598. ;~  Log files aren't currently supported, as I have no need for them, nor has anyone requested this functionality
  599. ;~  DllStructSetData($stBrowseDlgConfig,3,0)        ; DataSource (NULL = don't use log file)
  600.     DllStructSetData($stBrowseDlgConfig,4,DllStructGetPtr($stCounterSelectBuffer))  ; (out) ReturnPathBuffer
  601.     DllStructSetData($stBrowseDlgConfig,5,2048)     ; cchReturnPathLen max
  602. ;~  DllStructSetData($stBrowseDlgConfig,6,0)    ; Callback function (NULL is fine)
  603. ;~  DllStructSetData($stBrowseDlgConfig,7,0)    ; callback parameter
  604. ;~  DllStructSetData($stBrowseDlgConfig,8,0)    ; (out) Callback Status (PDH_STATUS=ERROR_SUCCESS(0))
  605.  
  606. ;  -  Detail Levels  -  (these only affect *Initial* view, user can then change to another level)
  607.     Switch $iDetailLvl
  608.         Case 1
  609.             $iDetailLvl=0x64    ; NOVICE (PERF_DETAIL_NOVICE)
  610.         Case 2
  611.             $iDetailLvl=0xC8    ; ADVANCED (PERF_DETAIL_ADVANCED)
  612.         Case 3
  613.             $iDetailLvl=0x012C  ; EXPERT (PERF_DETAIL_EXPERT)
  614.         Case Else               ; aka 'Case 4' (default if invalid param)
  615.             $iDetailLvl=0x0190  ; WIZARD (PERF_DETAIL_WIZARD)
  616.     EndSwitch
  617.  
  618.     DllStructSetData($stBrowseDlgConfig,9,$iDetailLvl)
  619.  
  620.     ; User didn't specify a caption?  On most O/S's, setting DialogBoxCaption to NULL resulted in the following string by default,
  621.     ;   but with Win7 it doesn't appear to put anything but "s" (?!), so now it is forced to the old default string:
  622.     If $sTitle='' Then $sTitle="Browse Performance Counters"
  623.  
  624.     $stTitle=DllStructCreate("wchar["&(StringLen($sTitle)+1)&']')
  625.     DllStructSetData($stTitle,1,$sTitle)
  626.     DllStructSetData($stBrowseDlgConfig,10,DllStructGetPtr($stTitle))   ; Dialog Box caption
  627.  
  628.     ; Make the call
  629.     $aRet=DllCall($hPDHDLL,"long","PdhBrowseCountersW","ptr",DllStructGetPtr($stBrowseDlgConfig))
  630.     If @error Then Return SetError(2,@error,'')
  631.  
  632. #cs
  633.     ; ------------------------------------------------------------------------------------------------------------------
  634.     ; YET More 'Browse Counter Dialog' BUGS (f'in Microsoft!):
  635.     ;------------------------------------------------------------
  636.     ; On some systems (at least Windows 7 w/.NET 4), the 'PdhBrowseCounters' call causes 'pdh.dll' to stay fixed
  637.     ;  in memory, with a remaining load count of 2 or 3 (2 if _PDH_Init() wasn't called).
  638.     ;  ADDITIONALLY, around 40 (count them, *FORTY!!*) other DLL's remain loaded after the call!!!
  639.     ; - The workaround - force an unload of the two DLL's we KNOW import PDH.DLL:
  640.     ;   1. PDHUI.DLL (the UI for 'Browse Counter Dialog'), which SHOULD be unloaded after the call, and
  641.     ;   2. 'perfcounter.dll' - a .NET 4.0 DLL.
  642.     ; Here's the problem with that: it CRASHES, hard. OR, if not, 6 other DLL's remain loaded, and the reference
  643.     ;   count on rasman.dll increments on each call to 'PdhBrowseCountersW'.  Not a huge deal, but I'd like to find
  644.     ;   out the order to unload these DLL's properly (and safely), so that this function doesn't leave a 'mess' behind.
  645.     ;   (Microsoft I hate you!)
  646.     ;
  647.     ;   (NOTE: These DLL's seem to be commonly loaded among regular PDH use: ntmarta.dll, WLDAP32.DLL, perfproc.dll)
  648.     ; ------------------------------------------------------------------------------------------------------------------
  649. #ce
  650. #cs
  651. ;~  _PDH_ReloadDLL()
  652. #ce
  653.  
  654.     If $aRet[0] Then
  655.         ; PDH_DIALOG_CANCELLED (0x800007D9)? - Then no selection was made
  656.         If $aRet[0]=0x800007D9 Then
  657.             _PDH_DebugWriteErr("_PDH_Browse_Counters Dialog Cancelled, returning empty string")
  658.             Return ''
  659.         EndIf
  660.         ; Otherwise unknown error
  661.         $_PDH_iLastError=$aRet[0]
  662.         _PDH_DebugWriteErr("PdhBrowseCountersW non-zero error code:" & Hex($_PDH_iLastError))
  663.         Return SetError(3,$_PDH_iLastError,'')
  664.     EndIf
  665.     ; Grab the selection string(s). User can select multiple strings due to inability to use flag 2 on Vista+ O/S's
  666.     ;  Steps: convert Binary to Unicode string, then get rid of double-Unicode-null's, and replace single ones with @LF
  667.     $sReturnStr=StringReplace(StringReplace(BinaryToString(DllStructGetData($stCounterSelectBuffer,1),2),ChrW(0)&ChrW(0),''),ChrW(0),@LF)
  668.     ;  Strip last @LF (present if multiple items selected)
  669.     If StringRight($sReturnStr,1)=@LF Then $sReturnStr=StringTrimRight($sReturnStr,1)
  670.     ; DEBUG
  671.     _PDH_DebugWrite("Selected Counter(s) from _PDH_Browse_Counters:"&@CRLF&$sReturnStr)
  672.     If $sReturnStr='' Then Return SetError(-1,0,'')     ; Unknown problem if string is blank
  673.     $aReturnArr=StringSplit($sReturnStr,@LF,1)
  674.     ; Return array or (first) Counter string?
  675.     If $bAllowMultipleSel Then Return $aReturnArr
  676.     Return $aReturnArr[1]
  677. EndFunc
  678.  
  679.  
  680. ; ===============================================================================================================================
  681. ; Func _PDH_GetCounterList($sCounterWildcardPath,$bReturnAsString=False)
  682. ;
  683. ; Return an array of Counters, OR a string (based on 2nd param) of them, based on matches to the Wildcard path.
  684. ;   NOTES:
  685. ;   - If a PC Name was not part of the Wildcard path, the local PC Name may be prefixed to expanded strings
  686. ;   - The PdhExpandWildCardPath API call (_PDH_GetCounterList()) doesn't recognize new object instances on Vista+ O/S's
  687. ;     *unless* PDH.DLL is unloaded first, or _PDH_BrowseCounters() is called [NOT recommended - see notes on THAT function]
  688. ;     The fix: use _PDH_ReloadDLL(), OR use Wildcard counters, which seem to recognize new object instances
  689. ;
  690. ; $sCounterWildcardPath = The Wildcard path, obviously.
  691. ;       Example use: "\Process(*)\% Processor Time"
  692. ; $bReturnAsString = If True, the List will be returned as a string, If False (default), as an array
  693. ;   NOTE: The returned string will contain NULL terms - except for the last element of the string.
  694. ;       This makes it easy enough to do a StringSplit() of your own (using ChrW(0))
  695. ;
  696. ; Returns:
  697. ;   Success: Either a 1D array of Counter paths, with bottom element equaling total count. @error=0,
  698. ;       OR (if $bReturnAsString==True), a string of NULL-term separated Counter paths (last NULL is stripped though)
  699. ;   Failure: A 1-element array containing '0', with @error set:
  700. ;       @error = 1 = invalid parameter
  701. ;       @error = 2 = DLL Call error, @extended=DLLCall error (see AutoIt Help)
  702. ;       @error = 3 = 1st DLL call PDH error. @extended contains error, as well as $_PDH_iLastError
  703. ;       @error = 4 = 2nd DLL call PDH error. @extended contains error, as well as $_PDH_iLastError
  704. ;       @error = 5 = 2nd DLL call successful, but data returned is invalid
  705. ;       @error = 7 = non-localized string passed, but invalid format
  706. ;
  707. ; Author: Ascend4nt
  708. ; ===============================================================================================================================
  709.  
  710. Func _PDH_GetCounterList($sCounterWildcardPath,$bReturnAsString=False)
  711.     Local $aRet,$stExpandedPathList
  712.     Local $hPDHDLL,$iBufSize,$sCounterList,$aCounterList[1]=[0]
  713.  
  714.     If Not IsString($sCounterWildcardPath) Then Return SetError(1,0,$aCounterList)
  715.  
  716.     ; Unlike other functions, getting a counter list doesn't require initialization,
  717.     ;   though it doesn't hurt (especially if Disable Performance Counters is set)
  718.     If Not $_PDH_bInit Then
  719.         $hPDHDLL="pdh.dll"
  720.     Else
  721.         $hPDHDLL=$_PDH_hDLLHandle
  722.     EndIf
  723.  
  724.     _PDH_DebugWrite("_PDH_GetCounterList() call, $sCounterWildcardPath='" & $sCounterWildcardPath & _
  725.         "', PDH DLL 'handle' (or just 'pdh.dll'):" & $hPDHDLL)
  726.  
  727.     ; Non-localized string? Create localized string and add it.
  728.     If StringLeft($sCounterWildcardPath,1)=':' Then
  729.         $sCounterWildcardPath=__PDH_LocalizeCounter($sCounterWildcardPath)
  730.         If @error Then Return SetError(@error,0,"")
  731.         _PDH_DebugWrite("Localized *wildcard* counter (from non-localized string):"&$sCounterWildcardPath)
  732.     EndIf
  733.  
  734.     ; 1st call to PdhExpandWildCardPathW - get required buffer size
  735.     $aRet=DllCall($hPDHDLL,"long","PdhExpandWildCardPathW","ptr",ChrW(0), _
  736.         "wstr",$sCounterWildcardPath,"ptr",ChrW(0),"dword*",$iBufSize,"dword",0)
  737.     If @error Then Return SetError(2,@error,$aCounterList)  ; DLL Call error
  738.  
  739.     ; Return should be PDH_MORE_DATA (0x800007D2)
  740.     If $aRet[0]<>0x800007D2 Then
  741.         $_PDH_iLastError=$aRet[0]
  742.         ; DEBUG
  743.         _PDH_DebugWriteErr("PdhExpandWildCardPathW 1st call unsuccessful, return:" & Hex($_PDH_iLastError))
  744.         Return SetError(3,$_PDH_iLastError,$aCounterList)
  745.     EndIf
  746.  
  747.     ; Grab required buffer size
  748.     $iBufSize=$aRet[4]
  749.  
  750.     ; For Win2000 (no SP), from tests, the buffer size reported on 1st call is 1 less than what it is on 2nd call!
  751.     ;   New MSDN info: 'Note  You must add one to the required size on Windows XP and Windows 2000.' - XP too?! hmm..
  752.     If @OSVersion="WIN_2000" Or StringLeft(@OSVersion,6)="WIN_XP" Then $iBufSize+=1
  753.  
  754.     ; DEBUG
  755.     ;_PDH_DebugWrite("_PDH_GetCounterList: PdhExpandWildCardPathW initial call successful, BufSize required:" & $iBufSize)
  756.  
  757.     ; Setup a buffer (bytes because pulling a multi-NULL-terminated Unicode string out is impossible with wchars)
  758.     $stExpandedPathList=DllStructCreate("byte["&($iBufSize*2)&']')
  759.  
  760.     ; 2nd call to PdhExpandWildCardPathW - fill buffer with expanded PDH Paths
  761.     $aRet=DllCall($hPDHDLL,"long","PdhExpandWildCardPathW","ptr",ChrW(0), _
  762.         "wstr",$sCounterWildcardPath,"ptr",DllStructGetPtr($stExpandedPathList),"dword*",$iBufSize,"dword",0)
  763.     If @error Then Return SetError(2,@error,$aCounterList)  ; DLL Call Error
  764.  
  765.     ; PDH Error?
  766.     If $aRet[0] Then
  767.         $_PDH_iLastError=$aRet[0]
  768.         ; DEBUG
  769.         _PDH_DebugWriteErr("PdhExpandWildCardPathW 2nd call unsuccessful, return:" & Hex($_PDH_iLastError))
  770.         ; This shouldn't occur on 2nd call (but did originally with Win2000 due to issues with PDH.DLL)
  771.         ;   should be fixed now (*unless* perhaps theres a rare case where more counters were added between calls?)
  772.         If $aRet[0]=0x800007D2 Then
  773.             _PDH_DebugWriteErr("PdhExpandWildCardPathW 2nd call returned 'PDH_MORE_DATA'. 1st reported Bufsize:" & $iBufSize & _
  774.                 ", 2nd call's Bufsize:" & $aRet[4])
  775.         EndIf
  776.         Return SetError(4,$_PDH_iLastError,$aCounterList)
  777.     EndIf
  778.  
  779.     ; GET UNICODE STRING
  780.     $sCounterList=BinaryToString(DllStructGetData($stExpandedPathList,1),2)
  781.  
  782.     ; DEBUG INFO
  783. ;~  _PDH_DebugWrite("_PDH_GetCounterList: PdhExpandWildCardPathW 2nd Call successful, 1st reported Bufsize (adjusted+1 on Win2K systems):" & $iBufSize & _
  784. ;~      ", 2nd call's Bufsize (should match):" & $aRet[4])
  785.         ; &", String Size:"&StringLen($sCounterList))   ; String Length *always* matches DLL struct's size
  786.  
  787.     ; Are there *still* wildcards left in the pattern [at the end of path sections]? Then it will not work, not even with further expansion
  788.     If StringRegExp($sCounterList,"\*(\)|\\|/|$)") Then
  789.         ; DEBUG
  790.         _PDH_DebugWriteErr("There are still * wildcards left after a call to PdhExpandWildCardPathW!"&@CRLF& _
  791.             "Original wildcard path:"&$sCounterWildcardPath)    ;&", Results:"&@CRLF& _
  792.             ;StringReplace($sCounterList,ChrW(0),@CRLF))
  793.         Return SetError(1,0,$aCounterList)
  794.     EndIf
  795.  
  796.     Local $iStrip=0
  797.     ; Check last 2 characters of string. If they are NULL values, add them to be stripped
  798.     ;   (This saves us from having to ReDim the array later, which would otherwise always happen)
  799.     If StringRight($sCounterList,1)=ChrW(0) Then $iStrip=1
  800.     ; For Win 2000 (non-service-pack) systems, and if they decide to put double NULL-terms later:
  801.     If StringMid($sCounterList,$iBufSize-1,1)=ChrW(0) Then $iStrip+=1
  802.  
  803.     ; Did user opt to be returned a string?
  804.     If $bReturnAsString Then
  805.         Return StringTrimRight($sCounterList,$iStrip)
  806.     Else
  807.         ; SPLIT string by NULL-TERMS and put into ARRAY
  808.         $aCounterList=StringSplit(StringTrimRight($sCounterList,$iStrip),ChrW(0),1)
  809.  
  810.         ; Counter list successfully split into an array?
  811.         If Not @error Then
  812.             _PDH_DebugWrite("_PDH_GetCounterList() call success, returning a "&$aCounterList[0]&"-element array")
  813.             Return $aCounterList
  814.         Else
  815.             ; @error from StringSplit. Must be invalid data
  816.             Dim $aCounterList[1]
  817.             $aCounterList[0]=0
  818.             ; DEBUG
  819.             _PDH_DebugWriteErr("PdhExpandWildCardPathW data invalid")
  820.             Return SetError(5,0,$aCounterList)
  821.         EndIf
  822.     EndIf
  823. EndFunc
  824.  
  825.  
  826. ; ===============================================================================================================================
  827. ; Func _PDH_GetNewQueryHandle($iUserInfo=0)
  828. ;
  829. ; Returns a new PDH Query Handle - necessary for adding Counters to
  830. ;       (which can then be used to get performance info)
  831. ;
  832. ; $iUserInfo = (pointer-sized) Identifier that is retrieved on _PDH_GetCounterInfo calls (dwQueryUserData)
  833. ;
  834. ; Success: Returns non-zero handle
  835. ; Failure: Returns 0 with @error set:
  836. ;   @error = 16 = not initialized, and _PDH_Init() call failed
  837. ;   @error = 2 = DLL Call error, @extended=DLLCall error (see AutoIt Help)
  838. ;   @error = 3 = PDH error (@extended contains error, also $_PDH_iLastError)
  839. ;
  840. ; Author: Ascend4nt
  841. ; ===============================================================================================================================
  842.  
  843. Func _PDH_GetNewQueryHandle($iUserInfo=0)
  844.     ; 1st check that PDH handle has been initialized
  845.     If Not $_PDH_bInit Then
  846.         If Not _PDH_Init() Then Return SetError(@error,0,0)
  847.     EndIf
  848.  
  849.     ; PdhOpenQuery: Create a PDH Query Handle
  850.     Local $aRet=DllCall($_PDH_hDLLHandle,"long","PdhOpenQueryW","ptr",0,"dword_ptr",$iUserInfo,"ptr*",Ptr(0))
  851.     If @error Then Return SetError(2,@error,0)  ; DLL Call Error
  852.     ; PDH error?
  853.     If $aRet[0] Then
  854.         $_PDH_iLastError=$aRet[0]
  855.         _PDH_DebugWriteErr("_PDH_GetNewQueryHandle call returned with PDH error:" & Hex($_PDH_iLastError))
  856.         Return SetError(3,$_PDH_iLastError,0)
  857.     EndIf
  858.     _PDH_DebugWrite("_PDH_GetNewQueryHandle Call succeeded, return:" &$aRet[0]& _
  859.         ",param1:"&$aRet[1]&",param2:"&$aRet[2]&",handle:" & $aRet[3])
  860.     ; Return handle
  861.     Return $aRet[3]
  862. EndFunc
  863.  
  864.  
  865. ; ===============================================================================================================================
  866. ; Func _PDH_FreeQueryHandle(ByRef $hPDHQueryHandle)
  867. ;
  868. ; Function to free/close/release a PDH Query Handle and linked Counter Handles. Also invalidates passed handle (sets to 0)
  869. ;
  870. ; $hPDHQueryHandle = PDH Query Handle
  871. ;
  872. ; Returns:
  873. ;   Success: True, with the passed Query handle invalidated (set to 0)
  874. ;   Failure: False, with @error set:
  875. ;       @error = 16 = not initialized
  876. ;       @error = 1 = Invalid handle (0 or -1)
  877. ;       @error = 2 = DLL Call error, @extended=DLLCall error (see AutoIt Help)
  878. ;       @error = 3 = PDH error, @extended = error code, as well as $_PDH_iLastError
  879. ;
  880. ; Author: Ascend4nt
  881. ; ===============================================================================================================================
  882.  
  883. Func _PDH_FreeQueryHandle(ByRef $hPDHQueryHandle)
  884.     ; Invalid handle?
  885.     If Not IsPtr($hPDHQueryHandle) Then Return SetError(1,0,False)
  886.     ; Check that PDH handle has been initialized
  887.     If Not $_PDH_bInit Then Return SetError(16,0,False)
  888.  
  889.     ; Close the Query Handle
  890.     Local $aRet=DllCall($_PDH_hDLLHandle,"long","PdhCloseQuery","ptr",$hPDHQueryHandle)
  891.     If @error Then Return SetError(2,@error,False)  ; DLL Call error
  892.     ; PDH Error?
  893.     If $aRet[0] Then
  894.         $_PDH_iLastError=$aRet[0]
  895.         _PDH_DebugWriteErr("PdhCloseQuery DLL call unsuccessful for handle: "&Hex($hPDHQueryHandle)&", return:" & Hex($_PDH_iLastError))
  896.         Return SetError(3,$_PDH_iLastError,False)
  897.     EndIf
  898. ;~  _PDH_DebugWrite("PdhCloseQuery DLL call successful")
  899.     $hPDHQueryHandle=0
  900.     Return True
  901. EndFunc
  902.  
  903.  
  904. ; ===============================================================================================================================
  905. ; Func _PDH_AddCounter($hPDHQueryHandle,$sCounterPath,$iUserInfo=0)
  906. ;
  907. ; Adds the given Counter to the PDH Query Handle, and returns the Counter Handle
  908. ;
  909. ; $hPDHQueryHandle = PDH Query Handle, obviously
  910. ; $sCounterPath = Counter Path (of the format returned by _PDH_BrowseCounters(). Examples:
  911. ;   Format with network path: "\\<PCNAME>\Process(ProcessName)\% Processor Time")
  912. ;   Actual Value (on local PC): "\Process(Idle)\% Processor Time"
  913. ;
  914. ;   NOTE: (No longer a warning): Wildcard use is now allowed and in some circumstances, encouraged
  915. ;       Using Wildcards in $sCounterPath will add ALL items matching the wildcard into ONE Counter handle)
  916. ;       Benefits of using wildcards: each update will grow or shrink the # of matching counter data,
  917. ;       automatically discarding dead Counters and adding new ones.
  918. ;       Also, counter names with #x or parentheses (when not paired like "(t)") no longer become an issue
  919. ;       and also there will be no #1, #2, etc appended to them.
  920. ;
  921. ; $iUserInfo = Optional User-defined (pointer-sized) value. This value becomes part of the counter information
  922. ;       and can be retrieved by accessing the dwUserData member of the PDH_COUNTER_INFO structure
  923. ;       returned from a 'PdhGetCounterInfo' call, (per MSDN).
  924. ;
  925. ; Returns:
  926. ;   Success: non-zero Counter Handle, @error=0
  927. ;   Failure: 0, @error set:
  928. ;       @error = 16 = not initialized
  929. ;       @error = 1 = Invalid handle (0 or -1)
  930. ;       @error = 2 = DLL Call error, @extended=DLLCall error (see AutoIt Help)
  931. ;       @error = 3 = PDH error, @extended = error code, as well as $_PDH_iLastError
  932. ;       @error = 7 = non-localized string passed, but invalid format
  933. ;
  934. ; Author: Ascend4nt
  935. ; ===============================================================================================================================
  936.  
  937. Func _PDH_AddCounter($hPDHQueryHandle,$sCounterPath,$iUserInfo=0)
  938.     If Not $_PDH_bInit Then Return SetError(16,0,0)
  939.     ; Check if its a valid Query Handle
  940.     If Not IsPtr($hPDHQueryHandle) Then Return SetError(1,0,0)
  941. ;~  _PDH_DebugWrite("_PDH_AddCounter called w/ Handle (" &$hPDHQueryHandle&") Adding: '" &$sCounterPath & "' w/ param to associate:" & $iParam)
  942.  
  943.     ; Non-localized string? Create localized string and add it.
  944.     If StringLeft($sCounterPath,1)=':' Then
  945.         $sCounterPath=__PDH_LocalizeCounter($sCounterPath)
  946.         If @error Then Return SetError(@error,0,"")
  947.         _PDH_DebugWrite("Localized counter (from non-localized string):"&$sCounterPath)
  948.     EndIf
  949.  
  950.     Local $aRet=DllCall($_PDH_hDLLHandle,"long","PdhAddCounterW","ptr",$hPDHQueryHandle, _
  951.         "wstr",$sCounterPath,"dword_ptr",$iUserInfo,"ptr*",Ptr(0))
  952.     If @error Then Return SetError(2,@error,0)  ; DLL call error?
  953.  
  954.     ; PDH error?
  955.     If $aRet[0] Then
  956.         $_PDH_iLastError=$aRet[0]
  957.         _PDH_DebugWriteErr("PdhAddCounterW error [path:'"&$sCounterPath&"'], return:" & Hex($_PDH_iLastError))
  958.         ; DEBUG CALL (optional) - Validate Path.
  959. ;~      $aRet=DllCall($_PDH_hDLLHandle,"long","PdhValidatePathW","wstr",$sCounterPath)
  960. ;~      If Not @error And IsArray($aRet) And Not $aRet[0] Then _PDH_DebugWrite("Path validated!")
  961.         Return SetError(3,$_PDH_iLastError,0)
  962.     EndIf
  963.     _PDH_DebugWrite("PdhAddCounterW success for path '"&$sCounterPath&"', handle:"&$aRet[4])
  964.  
  965.     ; Return Counter Handle
  966.     Return $aRet[4]
  967. EndFunc
  968.  
  969.  
  970. ; ===============================================================================================================================
  971. ; Func _PDH_AddCountersByArray($hPDHQueryHandle,Const ByRef $aCounterArray,$iStart=0,$iEnd=-1,$iBottomRows=0,$iExtraColumns=0)
  972. ;
  973. ; Function to add counters from a 1-Dimensional array to a PDH Query Handle, and return the results in a 2D array.
  974. ;   Ideally used after a call to _PDH_GetCounterList().
  975. ;
  976. ; $hPDHQueryHandle = PDH Query Handle, obviously
  977. ; $aCounterArray = an array of strings returned by _PDH_GetCounterList() or formatted in the same way so
  978. ;   that they will yield a handle from _PDH_AddCounter() [see notes on $sCounterPath]
  979. ; $iStart = 1st position in array to start at. For arrays with a bottom count element, like those
  980. ;   returned by _PDH_GetCounterList(), this should be 1 (but it can be anywhere, so long as the data there is valid)
  981. ; $iEnd = last position in array to stop at. -1 (default) means end-of-array.
  982. ; $iBottomRows = If non-zero, the # indicates the extra rows that will be created at the bottom of the array
  983. ;   If non-zero, element [0][0] will be set to # Counter Handles
  984. ;   NOTE that the # Counter Handles will *only* equal the array size if $iBottomRows == 1
  985. ; $iExtraColumns = If non-zero, the # indicates the extra columns that will be allocated for the array
  986. ;   (thus saving on any ReDim's outside of the functino)
  987. ;
  988. ; Returns:
  989. ;   Success: 2-Dimensional array, @error=0,@extended=# of failures (if any)
  990. ;       array[x][0] = path, array[x][1] = Counter Handle
  991. ;   Failure: 2-Dimensional array with 1 row, 2 columns, set to 0, and @error set:
  992. ;       @error = 16 = not initialized
  993. ;       @error = 1 = Invalid parameters
  994. ;       @error = 8 = COMPLETE failure for entire array (all calls failed) (@extended=total count)
  995. ;
  996. ; Author: Ascend4nt
  997. ; ===============================================================================================================================
  998.  
  999. Func _PDH_AddCountersByArray($hPDHQueryHandle,Const ByRef $aCounterArray,$iStart=0,$iEnd=-1,$iBottomRows=0,$iColumns=0)
  1000.     Local $iFailures=0,$iReturnIndex,$aCountersReturn[1][2]=[[0,0]]
  1001.     If Not $_PDH_bInit Then Return SetError(16,0,$aCountersReturn)
  1002.     ; Check parameters. 1Dimensional Array?, $iBottomRows<0?
  1003.     If Not IsArray($aCounterArray) Or UBound($aCounterArray,0)>1 Or $iBottomRows<0 Or $iColumns<0 Then Return SetError(1,0,$aCountersReturn)
  1004.     ; Check if its a valid Query Handle
  1005.     If Not IsPtr($hPDHQueryHandle) Then Return SetError(1,0,$aCountersReturn)
  1006.  
  1007.     If $iEnd=-1 Then $iEnd=UBound($aCounterArray)-1
  1008.     ; Everything within bounds and in right order?
  1009.     If $iStart<0 Or $iStart>$iEnd Or $iEnd>UBound($aCounterArray)-1 Then Return SetError(1,0,$aCountersReturn)
  1010.  
  1011.     ; Size the array (including # of bottom rows) & extra columns
  1012.     Dim $aCountersReturn[$iEnd-$iStart+1+$iBottomRows][2+$iColumns]
  1013.     ; Set the count of Counter Handles at [0][0] if additional rows requested
  1014.     ;   NOTE that this will equal the array size *ONLY* if $iBottomRows = 1 !!
  1015.     If $iBottomRows Then $aCountersReturn[0][0]=$iEnd-$iStart+1
  1016.  
  1017.     ; Set the index to start adding data to Return array (0-based offset)
  1018.     $iReturnIndex=$iBottomRows
  1019.     ; Iterate through array, Copying strings and Adding Counter Handles
  1020.     For $i=$iStart To $iEnd
  1021.         $aCountersReturn[$iReturnIndex][0]=$aCounterArray[$i]
  1022.         $aCountersReturn[$iReturnIndex][1]=_PDH_AddCounter($hPDHQueryHandle,$aCounterArray[$i])
  1023.         If @error Then $iFailures+=1
  1024.         $iReturnIndex+=1
  1025.     Next
  1026.     ; COMPLETE failure on ALL 'AddCounter' calls?
  1027.     If $iFailures=($iEnd-$iStart+1) Then
  1028.         Dim $aCountersReturn[1][2]
  1029.         $aCountersReturn[0][0]=0
  1030.         Return SetError(8,$iFailures,$aCountersReturn)
  1031.     EndIf
  1032.     Return SetExtended($iFailures,$aCountersReturn)
  1033. EndFunc
  1034.  
  1035.  
  1036. ; ===============================================================================================================================
  1037. ; Func _PDH_AddCountersByMultiStr($hPDHQueryHandle,Const ByRef $sMultiStr,$sSepChar,$iBottomRows=0,$iColumns=0)
  1038. ;
  1039. ; An alternative way to add Counters to a PDH Query Handle. This function does a StringSplit() based on
  1040. ;   the passed String, then passes this to _PDH_AddCountersByArray(), and returns that function's resultant 2D array.
  1041. ;
  1042. ; $hPDHQueryHandle = PDH Query Handle, obviously
  1043. ; $sMultiStr = String with multiple Counter paths, split by $sSepChar
  1044. ; $sSepChar = the character that separates the counter paths. This character must *NOT* be at the end of the string
  1045. ;   (as this will cause an additional element in the StringSplit() array to be created)
  1046. ;   ChrW(0) would be default, as this is what's returned by a call to _PDH_GetCounterList($sStr,TRUE)
  1047. ; $iBottomRows = If non-zero, the # indicates the extra rows that will be created at the bottom of the resultant array
  1048. ;   If non-zero, element [0][0] will be set to # Counter Handles
  1049. ;   NOTE that the # Counter Handles will *only* equal the array size if $iBottomRows == 1
  1050. ; $iExtraColumns = If non-zero, the # indicates the extra columns that will be allocated for the resultant array
  1051. ;   (thus saving on any ReDim's outside of the functino)
  1052. ;
  1053. ; Returns:
  1054. ;   Success: 2-Dimensional array, @error=0,@extended=# of failures (if any)
  1055. ;       array[x][0] = path, array[x][1] = Counter Handle
  1056. ;   Failure: 2-Dimensional array with 1 row, 2 columns, set to 0, and @error set:
  1057. ;       @error = 16 = not initialized
  1058. ;       @error = 1 = Invalid parameters
  1059. ;       @error = 8 = COMPLETE failure for entire array (all calls failed) (@extended=total count)
  1060. ;
  1061. ; Author: Ascend4nt
  1062. ; ===============================================================================================================================
  1063.  
  1064. Func _PDH_AddCountersByMultiStr($hPDHQueryHandle,Const ByRef $sMultiStr,$sSepChar,$iBottomRows=0,$iColumns=0)
  1065.     Local $aCounterList[1][2]=[[0,0]]
  1066.  
  1067.     ; Check 1 parameter. Rest of checks are done by _PDH_AddCountersByArray()
  1068.     If Not IsString($sMultiStr) Then Return SetError(1,0,$aCounterList)
  1069.  
  1070.     ; SPLIT string by $sSepChar and put into ARRAY
  1071.     $aCounterList=StringSplit($sMultiStr,$sSepChar,1)
  1072.  
  1073.     ; Counter list successfully split into an array?
  1074.     If Not @error Then
  1075.         _PDH_DebugWrite("_PDH_AddCountersByMultiStr() StringSplit() success, returning a "&$aCounterList[0]&"-element array")
  1076.         Return _PDH_AddCountersByArray($hPDHQueryHandle,$aCounterList,1,-1,$iBottomRows,$iColumns)
  1077.     Else
  1078.         ; @error from StringSplit. Must be invalid param
  1079.         Dim $aCounterList[1][2]
  1080.         $aCounterList[0][0]=0
  1081.         ; DEBUG
  1082.         _PDH_DebugWriteErr("_PDH_AddCountersByMultiStr() StringSplit() resulted in no data (invalid param)")
  1083.         Return SetError(1,0,$aCounterList)
  1084.     EndIf
  1085. EndFunc
  1086.  
  1087.  
  1088. ; ===============================================================================================================================
  1089. ; Func _PDH_AddCountersByWildcardPath($hPDHQueryHandle,$sCounterWildcardPath,$iBottomRows=0,$iColumns=0)
  1090. ;
  1091. ; Gets a list of counters, adds them to the PDH Query Handle, and puts them in an array (which it returns)
  1092. ;   (basically a wrapper function for a couple of calls)
  1093. ;
  1094. ; $hPDHQueryHandle = PDH Query Handle, obviously
  1095. ; $sCounterWildcardPath = The Wildcard path, obviously.
  1096. ;       Example use: "\Process(*)\% Processor Time"
  1097. ; $iBottomRows = If non-zero, the # indicates the extra rows that will be created at the bottom of the array
  1098. ;   If non-zero, element [0][0] will be set to # Counter Handles
  1099. ;   NOTE that the # Counter Handles will *only* equal the array size if $iBottomRows == 1
  1100. ; $iExtraColumns = If non-zero, the # indicates the extra columns that will be allocated for the array
  1101. ;   (thus saving on any ReDim's outside of the functino)
  1102. ;
  1103. ; Returns:
  1104. ;   Success: 2-Dimensional array, @error=0,@extended=# of failures (if any)
  1105. ;       array[x][0] = path, array[x][1] = Counter Handle
  1106. ;   Failure: 2-Dimensional array with 1 row, 2 columns, set to 0, and @error set:
  1107. ;           NOTE: error codes 2-5 are specific to the call to _PDH_GetCounterList()
  1108. ;       @error = 16 = not initialized
  1109. ;       @error = 1 = Invalid parameters
  1110. ;       @error = 2 = DLL Call error, @extended=DLLCall error (see AutoIt Help)
  1111. ;       @error = 3 = 1st DLL call PDH error. @extended contains error, as well as $_PDH_iLastError
  1112. ;       @error = 4 = 2nd DLL call PDH error. @extended contains error, as well as $_PDH_iLastError
  1113. ;       @error = 5 = 2nd DLL call successful, but data returned is invalid
  1114. ;           NOTE: error code 8 is specific to _PDH_AddCountersByArray()
  1115. ;       @error = 8 = COMPLETE failure for entire array (all calls failed) (@extended=total count)
  1116. ;
  1117. ; Author: Ascend4nt
  1118. ; ===============================================================================================================================
  1119.  
  1120. Func _PDH_AddCountersByWildcardPath($hPDHQueryHandle,$sCounterWildcardPath,$iBottomRows=0,$iColumns=0)
  1121.     If Not $_PDH_bInit Then Return SetError(16,0,False)
  1122.     ; Check if its a valid Query Handle
  1123.     If Not IsPtr($hPDHQueryHandle) Then Return SetError(1,0,False)
  1124.  
  1125.     Local $aCountersReturn[1][2]=[[0,0]]
  1126.  
  1127.     _PDH_DebugWrite("_PDH_AddCountersByWildcardPath called")
  1128.  
  1129.     Local $aCounterList=_PDH_GetCounterList($sCounterWildcardPath)
  1130.     If @error Or $aCounterList[0]=0 Then
  1131.         Local $iErr=@error,$iExt=@extended  ; (save due to DEBUG-Write call)
  1132.         ; DEBUG
  1133.         _PDH_DebugWriteErr("_PDH_AddCountersByWildcardPath call to PDH_GetCounterList failed")
  1134.         Return SetError($iErr,$iExt,$aCountersReturn)
  1135.     EndIf
  1136.     _PDH_DebugWrite("_PDH_AddCountersByWildcardPath call to PDH_GetCounterList succeeded, now returning _PDH_AddCountersByArray")
  1137.     $aCountersReturn=_PDH_AddCountersByArray($hPDHQueryHandle,$aCounterList,1,-1,$iBottomRows,$iColumns)
  1138.     Return SetError(@error,@extended,$aCountersReturn)
  1139. EndFunc
  1140.  
  1141.  
  1142. ; ===============================================================================================================================
  1143. ; Func _PDH_RemoveCounter(ByRef $hPDHCounterHandle)
  1144. ;
  1145. ; Removes a Counter from its Query Handle, and invalidates it (sets it to 0).
  1146. ;   Note that just the Counter Handle is necessary for the call. (Internally it knows to which Query Handle it belongs)
  1147. ;
  1148. ; $hPDHCounterHandle = Counter Handle, as returned by _PDH_AddCounter() or _PDH_AddCountersByArray()
  1149. ;   NOTE: The handle will be invalidated (set to 0) if successfull
  1150. ;
  1151. ; Return:
  1152. ;   Success: True, @error = 0, and Counter Handle invalidated (set to 0)
  1153. ;   Failure: False, @error set:
  1154. ;       @error = 16 = not initialized
  1155. ;       @error = 1 = invalid handle
  1156. ;       @error = 2 = DLL Call error, @extended=DLLCall error (see AutoIt Help)
  1157. ;       @error = 3 = PDH error, @extended = error code, as well as $_PDH_iLastError
  1158. ;
  1159. ; Author: Ascend4nt
  1160. ; ===============================================================================================================================
  1161.  
  1162. Func _PDH_RemoveCounter(ByRef $hPDHCounterHandle)
  1163.     If Not $_PDH_bInit Then Return SetError(16,0,False)
  1164.     ; Check if its a valid Counter Handle
  1165.     If Not IsPtr($hPDHCounterHandle) Then Return SetError(1,0,False)
  1166.  
  1167. ;~  DEBUG
  1168.     _PDH_DebugWrite("_PDH_RemoveCounter called w/ Counter Handle (" &$hPDHCounterHandle&')')
  1169.  
  1170.     Local $aRet=DllCall($_PDH_hDLLHandle,"long","PdhRemoveCounter","ptr",$hPDHCounterHandle)
  1171.     If @error Then Return SetError(2,@error,False)  ; DLL call error
  1172.  
  1173.     ; PDH error?
  1174.     If $aRet[0] Then
  1175.         $_PDH_iLastError=$aRet[0]
  1176.         _PDH_DebugWriteErr("PdhRemoveCounter error, return:" & Hex($_PDH_iLastError))
  1177.         Return SetError(3,$_PDH_iLastError,False)
  1178.     EndIf
  1179. ;~  DEBUG
  1180.     ;_PDH_DebugWrite("_PDH_RemoveCounter call completed successfully")
  1181.     $hPDHCounterHandle=0    ; invalidate the handle
  1182.     Return True
  1183. EndFunc
  1184.  
  1185.  
  1186. ; ===============================================================================================================================
  1187. ; Func _PDH_CounterNameToNonLocalStr($sCounterPath,$bKeepPCName=False)
  1188. ;
  1189. ; Takes a Counter path string and creates a generic non-localized string from it (using the below format)
  1190. ;   Format output: ':Object#\Counter#\' + optional '(Instance)' + optional '\PCNAME'
  1191. ;       Examples: ':230\6\(Idle)\PCNAME' (based on English counter path '\\PCNAME\Process(Idle)\% Processor Time')
  1192. ;           ':238\6\(0)' (based on English counter path '\Processor(0)\% Processor Time')
  1193. ;           ':2\250\' (based on English counter path '\System\Threads'
  1194. ;           ':2\250\\PCNAME'  (same as above, but with PCNAME [2nd backslash mandatory, 3rd is only needed when PCNAME added])
  1195. ;
  1196. ; $sCounterPath = *FULL* Counter path (with or without PC NAME). Format on entry must be of the standard types:
  1197. ;   "\\PCNAME\Object(Instance)\Counter', '\Object(Instance)\Counter', or '\Object\Counter'
  1198. ;   NOTES: 'Instance' can be '*' for wildcard. It can also optionally have a #x appended or /x prefixed (for extra instances)
  1199. ;     'Object' and 'Counter' can also be a wildcard, though I don't believe 'Object(Instance)' can both be wildcards.
  1200. ; $bKeepPCName = If True, the PC Name will remain a part of the 'non-localized' string.  Not recommended for portability reasons.
  1201. ;
  1202. ; Returns:
  1203. ;   Success: 'Non-localized' string (see above for format)
  1204. ;   Failure: "" with @error set:
  1205. ;       @error = 1 = invalid path
  1206. ;       @error = 2 = DLL call error, @extended = DLLCall error code (see AutoIT help)
  1207. ;       @error = 3 = PDH error, @extended = error code, as well as $_PDH_iLastError
  1208. ;
  1209. ; Author: Ascend4nt
  1210. ; ===============================================================================================================================
  1211.  
  1212. Func _PDH_CounterNameToNonLocalStr($sCounterPath,$bKeepPCName=False)
  1213.     Local $aCounterElements,$sNeutralStr
  1214.     ; Original PCRE for below [with look-ahead] (but now proper path format is enforced, so the starting \ is required)
  1215.     ;   "(\\\\[^\\]+(?=\\))?\\?([^\\\(]+)?\(?([^\)]+)?\)?\\(.*)",1)
  1216.  
  1217.     ; Break counter path down to 4 elements: Machine name (optional), Object Name, Instance Name (optional), Counter Name
  1218.     $aCounterElements=StringRegExp($sCounterPath,"(\\\\[^\\]+)?\\([^\\\(]+)?(\([^\)]+\))?\\(.*)",1)
  1219.     ; Not formatted properly?
  1220.     If @error Then Return SetError(1,0,"")
  1221. #cs
  1222.     ; [0] = Machine Name (or "" if not present)
  1223.     ; [1] = Object Name (MUST be present for real string)
  1224.     ; [2] = Instance Name (or "" if not present) - inside Object Name portion of path (if present)
  1225.     ; [3] = Counter Name (MUST be present)
  1226. #ce
  1227.     $aCounterElements[1]=_PDH_GetCounterIndex($aCounterElements[1],$aCounterElements[0])
  1228.     If @error Then Return SetError(@error,@extended,"")
  1229.     $aCounterElements[3]=_PDH_GetCounterIndex($aCounterElements[3],$aCounterElements[0])
  1230.     If @error Then Return SetError(@error,@extended,"")
  1231.  
  1232.     $sNeutralStr=':'&$aCounterElements[1]&'\'&$aCounterElements[3]&'\'&$aCounterElements[2]
  1233.     ; Strip one '\' off of the PC name before adding it
  1234.     If $bKeepPCName And $aCounterElements[0]<>"" Then $sNeutralStr&=StringTrimLeft($aCounterElements[0],1)
  1235.     Return $sNeutralStr
  1236. EndFunc
  1237.  
  1238.  
  1239. ; ===============================================================================================================================
  1240. ; Func _PDH_GetCounterNameByIndex($iIndex,$sPCName="")
  1241. ;
  1242. ; Function to get the name of a Counter or Object based on its index #
  1243. ;  Ideal for use in place of hardcoded paths when using a different language.
  1244. ;   Note that both the Object AND Counter index need to be queried separately, and then assembled together.
  1245. ;
  1246. ; $iIndex = index of the Counter. (hopefully these remain consistent throughout O/S versions!)
  1247. ; $sPCName = *optional* parameter. If included, the machine name is extracted from the passed
  1248. ;   string (which can be a full Counter path).  Machine name is of the format: "\\PCNAME"
  1249. ;
  1250. ; Return:
  1251. ;   Success: Counter name, @error = 0
  1252. ;   Failure: "", @error set:
  1253. ;       @error = 2 = DLL call error, @extended = DLLCall error code (see AutoIT help)
  1254. ;       @error = 3 = PDH error, @extended = error code, as well as $_PDH_iLastError
  1255. ;
  1256. ; Author: Ascend4nt
  1257. ; ===============================================================================================================================
  1258.  
  1259. Func _PDH_GetCounterNameByIndex($iIndex,$sPCName="")
  1260.     Local $hPDHDLL,$aRet,$sMachineParamType,$vMachineParam
  1261.  
  1262.     ; Unlike other functions, getting a counter index doesn't require initialization,
  1263.     ;   though it doesn't hurt (especially if Disable Performance Counters is set)
  1264.     If Not $_PDH_bInit Then
  1265.         $hPDHDLL="pdh.dll"
  1266.     Else
  1267.         $hPDHDLL=$_PDH_hDLLHandle
  1268.     EndIf
  1269.     _PDH_DebugWrite("_PDH_GetCounterNameByIndex() call, Index:"&$iIndex&" [optional] Machine Name:"&$sPCName&", PDH DLL 'handle' (or just 'pdh.dll'):" &$hPDHDLL)
  1270.  
  1271.     ; Extract Machine Name (optional). All other parts of path (if included) are cleared
  1272.     $vMachineParam=StringRegExpReplace($sPCName,"(\\\\[^\\]+)?(\\?.*)","$1")
  1273.  
  1274.     If $vMachineParam="" Then
  1275.         $vMachineParam=0
  1276.         $sMachineParamType="ptr"
  1277.     Else
  1278.         _PDH_DebugWrite("_PDH_GetCounterNameByIndex Machine name: "&$vMachineParam)
  1279.         $sMachineParamType="wstr"
  1280.     EndIf
  1281.  
  1282.     $aRet=DllCall($hPDHDLL,"long","PdhLookupPerfNameByIndexW",$sMachineParamType,$vMachineParam,"dword",$iIndex,"wstr","","dword*",65536)
  1283.     If @error Then Return SetError(2,@error,"")
  1284.  
  1285.     If $aRet[0] Then
  1286.         $_PDH_iLastError=$aRet[0]
  1287.         _PDH_DebugWriteErr("_PDH_GetCounterNameByIndex non-zero error code:" & Hex($_PDH_iLastError))
  1288.         Return SetError(3,$_PDH_iLastError,"")
  1289.     EndIf
  1290. ;~  _PDH_DebugWrite("_PDHGetCounterIndex results: Index string:"&$aRet[3])
  1291.     Return $aRet[3]
  1292. EndFunc
  1293.  
  1294.  
  1295. ; ===============================================================================================================================
  1296. ; Func _PDH_GetCounterIndex($sCounterOrObject,$sPCName="")
  1297. ;
  1298. ; Function to get the index # of a Counter or an Object.
  1299. ;  Ideal for hardcoded paths that need to be translated to another language.
  1300. ;   !!! Note that the Counter or Object name here must *NOT* be prefixed with a '\'!!!
  1301. ;
  1302. ;   Also note that Counters like '% Processor Time' will give only one numerical value, but can be used
  1303. ;    with multiple Objects (Processor, Process).  This is why this function is better off used indirectly
  1304. ;    via _PDH_CounterNameToNonLocalStr() to convert each object for you.
  1305. ;
  1306. ; $sCounterOrObject = Counter or Object name (*without* '\'. Examples: 'Process', '% Processor Time')
  1307. ; $sPCName = *optional* value -> the machine name, prefixed as normal with '\\' (Example: '\\PCNAME')
  1308. ;
  1309. ; Return:
  1310. ;   Success: Counter #, @error = 0
  1311. ;   Failure: -1, @error set:
  1312. ;       @error = 1 = Counter Path invalid
  1313. ;       @error = 2 = DLL call error, @extended = DLLCall error code (see AutoIT help)
  1314. ;       @error = 3 = PDH error, @extended = error code, as well as $_PDH_iLastError
  1315. ;
  1316. ; Author: Ascend4nt
  1317. ; ===============================================================================================================================
  1318.  
  1319. Func _PDH_GetCounterIndex($sCounterOrObject,$sPCName="")
  1320.     Local $hPDHDLL,$aRet,$sMachineParamType
  1321.  
  1322.     ; Unlike other functions, getting a counter index doesn't require initialization,
  1323.     ;   though it doesn't hurt (especially if Disable Performance Counters is set)
  1324.     If Not $_PDH_bInit Then
  1325.         $hPDHDLL="pdh.dll"
  1326.     Else
  1327.         $hPDHDLL=$_PDH_hDLLHandle
  1328.     EndIf
  1329.     _PDH_DebugWrite("_PDHGetCounterIndex() call, Counter or Object Name:'"&$sCounterOrObject&"', Machine Name (optional):'"&$sPCName&"', PDH DLL 'handle' (or just 'pdh.dll'):" &$hPDHDLL)
  1330.  
  1331.     If $sPCName="" Then
  1332.         $sPCName=0
  1333.         $sMachineParamType="ptr"
  1334.     Else
  1335.         $sMachineParamType="wstr"
  1336.     EndIf
  1337.  
  1338.     $aRet=DllCall($hPDHDLL,"long","PdhLookupPerfIndexByNameW",$sMachineParamType,$sPCName,"wstr",$sCounterOrObject,"dword*",0)
  1339.     If @error Then Return SetError(2,@error,-1)
  1340.  
  1341.     If $aRet[0] Then
  1342.         $_PDH_iLastError=$aRet[0]
  1343.         _PDH_DebugWriteErr("_PDHGetCounterIndex non-zero error code:" & Hex($_PDH_iLastError))
  1344.         Return SetError(3,$_PDH_iLastError,-1)
  1345.     EndIf
  1346.     _PDH_DebugWrite("_PDHGetCounterIndex results: Index:"&$aRet[3])
  1347.     Return $aRet[3]
  1348. EndFunc
  1349.  
  1350.  
  1351. ; ===============================================================================================================================
  1352. ; Func _PDH_GetCounterInfo($hPDHCounterHandle)
  1353. ;
  1354. ; Retrieves a string of information about the PDH Counter passed (by Handle)
  1355. ;
  1356. ; $hPDHCounterHandle = Counter Handle, as returned by _PDH_AddCounter() or _PDH_AddCountersByArray()
  1357. ;
  1358. ; Returns:
  1359. ;   Success: Array of information:
  1360. ;       [0]  = Counter Type
  1361. ;       [1]  = Counter Status
  1362. ;       [2]  = Scale Factor
  1363. ;       [3]  = Default Scale
  1364. ;       [4]  = Counter User Data (passed when _PDH_AddCounter*() called)
  1365. ;       [5]  = Query User (passed when _PDH_GetNewQueryHandle() called)
  1366. ;       [6]  = Full Counter Path
  1367. ;       [7]  = Machine Name (if available)
  1368. ;       [8]  = Object Name
  1369. ;       [9]  = Instance Name (if available)
  1370. ;       [10] = Parent Instance Name (if available)
  1371. ;       [11] = Instance Index (0 means no index)
  1372. ;       [12] = Counter Name
  1373. ;       [13] = Counter Explanation Text
  1374. ;   Failure: "", @error set:
  1375. ;       @error = 16 = not initialized
  1376. ;       @error = 1 = invalid PDH Counter Handle
  1377. ;       @error = 2 = DLL Call error, @extended=DLLCall error (see AutoIt Help)
  1378. ;       @error = 3 = 1st DLL call PDH error. @extended contains error, as well as $_PDH_iLastError
  1379. ;       @error = 4 = 2nd DLL call PDH error. @extended contains error, as well as $_PDH_iLastError
  1380. ;
  1381. ; Author: Ascend4nt
  1382. ; ===============================================================================================================================
  1383.  
  1384. Func _PDH_GetCounterInfo($hPDHCounterHandle)
  1385.     If Not $_PDH_bInit Then Return SetError(16,0,"")
  1386.     If Not IsPtr($hPDHCounterHandle) Then Return SetError(1,0,"")
  1387.  
  1388.     Local $aRet,$iBufSize
  1389.     Local $stCounterBuffer,$stCounterInfo
  1390.  
  1391.     ; Initial call to get size
  1392.     $aRet=DllCall($_PDH_hDLLHandle,"long","PdhGetCounterInfoW","ptr",$hPDHCounterHandle,"bool",True,"dword*",0,"ptr",0)
  1393.     If @error Then Return SetError(2,@error,"") ; DLL Call error
  1394.  
  1395.     ; Return should be PDH_MORE_DATA (0x800007D2) unless on Windows 2000 which returns 0 but sets $aRet[3] with a size
  1396.     If ($aRet[0] And $aRet[0]<>0x800007D2) Or ($aRet[0]=0 And $aRet[3]=0) Then
  1397.         $_PDH_iLastError=$aRet[0]
  1398. ;~      DEBUG
  1399.         _PDH_DebugWriteErr("PdhGetCounterInfoW 1st call unsuccessful, return:" & Hex($_PDH_iLastError))
  1400.         Return SetError(3,$_PDH_iLastError,"")
  1401.     EndIf
  1402.  
  1403.     ; Grab required buffer size (in bytes)
  1404.     $iBufSize=$aRet[3]
  1405. ;~  DEBUG
  1406.     ;_PDH_DebugWrite("PdhGetCounterInfoW 1st call successful, Required Buffer Size:" & $iBufSize)
  1407.  
  1408.     ; Setup buffer (size is in bytes)
  1409.     $stCounterBuffer=DllStructCreate("byte["&$iBufSize&"]")
  1410.  
  1411.     ; 2nd call to get data
  1412.     $aRet=DllCall($_PDH_hDLLHandle,"long","PdhGetCounterInfoW","ptr",$hPDHCounterHandle,"bool",True,"dword*", _
  1413.         $iBufSize,"ptr",DllStructGetPtr($stCounterBuffer))
  1414.     If @error Then Return SetError(2,@error,"") ; DLL Call error
  1415.  
  1416.     ; PDH Error?
  1417.     If $aRet[0] Then
  1418.         $_PDH_iLastError=$aRet[0]
  1419. ;~      DEBUG
  1420.         _PDH_DebugWriteErr("PdhGetCounterInfoW 2nd call unsuccessful, return:" & Hex($_PDH_iLastError))
  1421.         Return SetError(4,$_PDH_iLastError,"")
  1422.     EndIf
  1423.  
  1424. ;~  DEBUG
  1425.     _PDH_DebugWrite("PdhGetCounterInfoW 2nd call successful, 1st Reported BufSize:" & $iBufSize & _
  1426.         ", Return Size (should match)" & $aRet[3])
  1427.  
  1428. #cs
  1429.     ; ----------------------------------------------------------------------------------------------------------
  1430.     ; PDH_COUNTER_INFO structure. First portion is straightforward, until alignment at end of union (on x86)
  1431.     ;  Size, Counter Type, Version (not used [but set]), Counter Status, Scale Factor, Default Scale Factor,
  1432.     ;   Counter User Data, Query User Data, Counter Full Path (ptr), Data Item Path (ptr), Counter Path (ptr),
  1433.     ;   Machine Name (ptr), Object Name (ptr), Instance Name (ptr), Parent Instance (ptr), Instance Index #,
  1434.     ;   Counter Name (ptr), Explanation Text (ptr)
  1435.     ; ----------------------------------------------------------------------------------------------------------
  1436. #ce
  1437.     Local $tagPDH_COUNTER_INFO="dword;dword;dword;long;long;long;dword_ptr;dword_ptr;ptr;ptr;ptr;ptr;ptr;dword;ptr"
  1438.  
  1439.     ; For 32-bit mode, PDH_COUNTER_PATH_ELEMENTS is the largest part of union at 28 bytes, requiring a 'Filler' member
  1440.     ;   but in x64 mode, its PDH_DATA_ITEM_PATH_ELEMENTS (or inline struct [same elements]) at 48 bytes, so the union Filler is not needed
  1441.     If Not @AutoItX64 Then $tagPDH_COUNTER_INFO&=";dword Filler"
  1442.     ;   Final piece of structure before Data Buffer/String Data
  1443.     $tagPDH_COUNTER_INFO&=";ptr ExplainText"
  1444. ;~  _PDH_DebugWrite("PDH_COUNTER_INFO Structure size without appended data:"&DllStructGetSize(DllStructCreate($tagPDH_COUNTER_INFO)))
  1445.  
  1446.     ; Skip the buffer part (we have pointers inside the structure to null-term strings in $stCounterBuffer that we'll use instead)
  1447.     $stCounterInfo=DllStructCreate($tagPDH_COUNTER_INFO,DllStructGetPtr($stCounterBuffer))
  1448.  
  1449.     ; Create and fill the Return structure (order is listed in header)
  1450.     Dim $aCounterInfo[14]
  1451.     $aCounterInfo[0]=DllStructGetData($stCounterInfo,2)
  1452.     For $i=1 To 5
  1453.         $aCounterInfo[$i]=DllStructGetData($stCounterInfo,$i+3)
  1454.     Next
  1455.     ; Grab null-terminated strings using pointers
  1456.     For $i=6 To 10
  1457.         $aCounterInfo[$i]=__PDH_StringGetNullTermMemStr(DllStructGetData($stCounterInfo,$i+3))
  1458.     Next
  1459.     $aCounterInfo[11]=DllStructGetData($stCounterInfo,14)
  1460.     $aCounterInfo[12]=__PDH_StringGetNullTermMemStr(DllStructGetData($stCounterInfo,15))
  1461.     $aCounterInfo[13]=__PDH_StringGetNullTermMemStr(DllStructGetData($stCounterInfo,"ExplainText"))
  1462.  
  1463.     Return $aCounterInfo
  1464. EndFunc
  1465.  
  1466.  
  1467. ; ===============================================================================================================================
  1468. ; Func _PDH_CollectQueryData($hPDHQueryHandle,$bSkipChecks=False)
  1469. ;
  1470. ; Updates the Counters attached to a Query Handle - doesn't actually collect the values.
  1471. ;   Collecting values is done via _PDH_UpdateCounter() or _PDH_UpdateWildcardCounter()
  1472. ;   (using API call PdhGetFormattedCounterValue or PdhGetFormattedCounterArrayW)
  1473. ;
  1474. ; $hPDHQueryHandle = The handle for the Query which has Counter Handle(s) attached to it
  1475. ; $bSkipChecks = **NOTE: FOR INTERNAL USE - DO NOT CHANGE FOR EXTERNAL CALLS!**
  1476. ;       This skips parameter checks. It's original intention was for internal-use only,
  1477. ;       so it was typically always 'True', but since it can be useful outside of other _PDH_ functions,
  1478. ;       the option was added and set to False to allow regular Param/Init checks & extra error info)
  1479. ;
  1480. ; Return:
  1481. ;   Success: True, @error = 0
  1482. ;   Failure: False, @error set:
  1483. ;       @error = 16 = not initialized
  1484. ;       @error = 1 = invalid PDH Query Handle or PDH Counter Handle
  1485. ;       @error = 2 = DLL Call error, @extended=DLLCall error (see AutoIt Help)
  1486. ;       @error = 3 = PDH error. @extended contains error, as well as $_PDH_iLastError
  1487. ;
  1488. ; Author: Ascend4nt
  1489. ; ===============================================================================================================================
  1490.  
  1491. Func _PDH_CollectQueryData($hPDHQueryHandle,$bSkipChecks=False)
  1492.     If Not $bSkipChecks Then
  1493.         ; Properly initialized?
  1494.         If Not $_PDH_bInit Then Return SetError(16,0,False)
  1495.         ; Check if Query Handle is valid
  1496.         If Not IsPtr($hPDHQueryHandle) Then Return SetError(1,0,False)
  1497.     EndIf
  1498.     Local $aRet=DllCall($_PDH_hDLLHandle,"long","PdhCollectQueryData","ptr",$hPDHQueryHandle)
  1499.     If @error Then Return SetError(2,@error,False)  ; DLL Call error
  1500.     ; PDH error?
  1501.     If $aRet[0] Then
  1502.         $_PDH_iLastError=$aRet[0]
  1503.         _PDH_DebugWriteErr("PdhCollectQueryData error, return:" & Hex($_PDH_iLastError))
  1504.         Return SetError(3,$_PDH_iLastError,False)
  1505.     EndIf
  1506.     Return True
  1507. EndFunc
  1508.  
  1509.  
  1510. ; ===============================================================================================================================
  1511. ; Func _PDH_UpdateCounter($hPDHQueryHandle,$hPDHCounterHandle,$sCounterPath="",$bGrabValueOnly=False)
  1512. ;
  1513. ; Function to update (all) counters under the given Query Handle, but only return 1 Counter's Value.
  1514. ;   NOTE: Do not call this multiple times in a row - call _PDH_CollectQueryData() 1st, then call this
  1515. ;     with the last paramter set to True.
  1516. ;
  1517. ; $hPDHQueryHandle = The handle for the Query which has Counter Handle(s) attached to it
  1518. ; $hPDHCounterHandle = Counter Handle, as returned by _PDH_AddCounter() or _PDH_AddCountersByArray()
  1519. ; $sCounterPath = The counter path. If passed as a parameter, it will allow the function to
  1520. ;   perform division on CPU Usage counters, and write debug info with _PDH_DebugWrite. While not necessary,
  1521. ;   its good to set this up for automatic CPU usage division. OPTIONALLY,
  1522. ;   *WHEN* it is known beforehand that CPU usage is being looked for, then this can be set to -2
  1523. ;   which indicates to this function to do the CPU division regardless
  1524. ; $bGrabValueOnly = When a PDHQueryHandle has been updated already, and only individual Counter Handle
  1525. ;   values need to be extracted - use this to avoid additional calls to PdhCollectQueryData
  1526. ;   (avoids sticky issues with multiple Counters under one Query handle, & also speeds things up by avoiding extra calls)
  1527. ;
  1528. ; Returns:
  1529. ;   Success: Counter value, @error = 0
  1530. ;   Failure: -1, @error set:
  1531. ;       @error = 16 = not initialized
  1532. ;       @error = 1 = invalid PDH Query Handle or PDH Counter Handle
  1533. ;       @error = 2 = DLL Call error, @extended=DLLCall error (see AutoIt Help)
  1534. ;       @error = 3 = 1st DLL call PDH error. @extended contains error, as well as $_PDH_iLastError
  1535. ;       @error = 4 = 2nd DLL call PDH error. @extended contains error, as well as $_PDH_iLastError
  1536. ;       @error = -1 = PDH error 'PDH_INVALID_DATA' (0xC0000BC6) Returned
  1537. ;           IMPORTANT: It is recommended to call _PDH_RemoveCounter() when @error = -1 !!
  1538. ;               (It generally means Counter is no longer valid)
  1539. ;
  1540. ; Author: Ascend4nt
  1541. ; ===============================================================================================================================
  1542.  
  1543. Func _PDH_UpdateCounter($hPDHQueryHandle,$hPDHCounterHandle,$sCounterPath="",$bGrabValueOnly=False)
  1544.     ; Properly initialized?
  1545.     If Not $_PDH_bInit Then Return SetError(16,0,-1)
  1546.     ; Check if Counter Handle is valid
  1547.     If Not IsPtr($hPDHCounterHandle) Then Return SetError(1,0,-1)
  1548.  
  1549.     ; Called with request to just grab the counter value only?
  1550.     If Not $bGrabValueOnly Then
  1551.         ; Check if Query Handle is valid
  1552.         If Not IsPtr($hPDHQueryHandle) Then Return SetError(1,0,-1)
  1553.         ; Call CollectQueryData - 'True' means no checks necessary
  1554.         If Not _PDH_CollectQueryData($hPDHQueryHandle,True) Then Return SetError(@error,@extended,-1)
  1555.     EndIf
  1556.  
  1557.     Local $stCounterValue=DllStructCreate("long;int64") ; PDH_FMT_COUNTERVALUE  (can also change to long;double)
  1558. #cs
  1559.     ;-----------------------------------------------------
  1560.     ;  -  Format Options  -
  1561.     ;   PDH_FMT_LONG    0x00000100 (long-32bit)
  1562.     ;   PDH_FMT_DOUBLE  0x00000200 (double float)
  1563.     ;   PDH_FMT_LARGE   0x00000400 <- my choice (int64)
  1564.     ;   ( + )
  1565.     ;   PDH_FMT_NOSCALE     0x00001000
  1566.     ;   PDH_FMT_NOCAP100    0x00008000 <- also my choice (requires division for CPU usage results tho)
  1567.     ;   PDH_FMT_1000        0x00002000
  1568.     ;-----------------------------------------------------
  1569. #ce
  1570.     Local $aRet=DllCall($_PDH_hDLLHandle,"long","PdhGetFormattedCounterValue","ptr",$hPDHCounterHandle,"dword",0x8400, _
  1571.         "dword*",0,"ptr",DllStructGetPtr($stCounterValue))
  1572.     If @error Then Return SetError(2,@error,-1)
  1573.  
  1574.     If $aRet[0] Then
  1575.         $_PDH_iLastError=$aRet[0]
  1576.         ; PDH_INVALID_DATA? (0xC0000BC6). If CStatus also is PDH_CSTATUS_NO_INSTANCE (0x800007D1), counter no longer exists
  1577.         If $aRet[0]=0xC0000BC6 And DllStructGetData($stCounterValue,1)=0x800007D1 Then
  1578.             _PDH_DebugWriteErr("Invalid Data (invalid handle) message for Handle:" & $hPDHCounterHandle & _
  1579.                 ", Path [if passed]:" &$sCounterPath)
  1580.             Return SetError(-1,$_PDH_iLastError,-1)
  1581.         EndIf
  1582.         _PDH_DebugWriteErr("Error Calling PdhGetFormattedCounterValue for Handle:"&$hPDHCounterHandle& _
  1583.             ", Path [if passed]:" &$sCounterPath&", Return:" & Hex($_PDH_iLastError))
  1584.  
  1585.         Return SetError(4,$_PDH_iLastError,-1)
  1586.     EndIf
  1587.     ; Test for % Processor Usage (#6) Counters and adjust accordingly (Thread (#232) or Process (#230) only)
  1588.     If $sCounterPath=-2 Or ($sCounterPath<>"" And StringRegExp($sCounterPath,"(:23(0|2)\\6|(Thread|Process)\([^%]+%)",0)) Then _
  1589.         Return Round(DllStructGetData($stCounterValue,2) / $_PDH_iCPUCount)
  1590.  
  1591.     Return DllStructGetData($stCounterValue,2)
  1592. EndFunc
  1593.  
  1594.  
  1595. ; ===============================================================================================================================
  1596. ; Func _PDH_UpdateWildcardCounter($hPDHQueryHandle,$hPDHCounterHandle,$sCounterWildcardPath="",$bGrabValueOnly=False))
  1597. ;
  1598. ; Accesses an array of Counter values when a Counter Handle was retrieved through a wildcard value,
  1599. ;   returns an array of Counter names & values.
  1600. ;
  1601. ; $hPDHQueryHandle = The handle for the Query which has Counter Handle(s) attached to it
  1602. ; $hPDHCounterHandle = Counter Handle, as returned by _PDH_AddCounter()
  1603. ;   NOTE: !!! MUST BE A WILDCARD COUNTER !!! (a counter initialized with a * in any one of its path's positions)
  1604. ; $sCounterWildcardPath = The wildcard path used for this counter. This isn't necessary, but if not present,
  1605. ;   will not do the necessary division when a CPU usage Counter is involved - however:
  1606. ;   *WHEN* it is known beforehand that CPU usage is being looked for, then this can be set to -2
  1607. ;   which indicates to this function to do the CPU division regardless
  1608. ;   *ALSO*, for bytes-to-KBytes division (or KBytes-to-MBytes), -3 can be passed
  1609. ;       AND -4 for bytes-to-MBytes
  1610. ; $bGrabValueOnly = When a PDHQueryHandle has been updated already, and only individual Counter
  1611. ;   Handle values need to be extracted - use this to avoid additional calls to PdhCollectQueryData
  1612. ;   (can help in cases where the data may change between calls, and also speeds things up by avoiding extra calls)
  1613. ;
  1614. ; Returns:
  1615. ;   Success: Array of Counter values with the following format (@error = 0):
  1616. ;       [0][0]  = count of items
  1617. ;       [$i][0] = Counter Name
  1618. ;       [$i][1] = Counter Value
  1619. ;   Failure: [0][0]=0, @error set:
  1620. ;       @error = 16 = not initialized
  1621. ;       @error = 1 = invalid PDH Query Handle or PDH Counter Handle
  1622. ;       @error = 2 = DLL Call error, @extended=DLLCall error (see AutoIt Help)
  1623. ;       @error = 3 = 1st DLL call PDH error. @extended contains error, as well as $_PDH_iLastError
  1624. ;       @error = 4 = 2nd DLL call PDH error. @extended contains error, as well as $_PDH_iLastError
  1625. ;
  1626. ; Author: Ascend4nt
  1627. ; ===============================================================================================================================
  1628.  
  1629. Func _PDH_UpdateWildcardCounter($hPDHQueryHandle,$hPDHCounterHandle,$sCounterWildcardPath="",$bGrabValueOnly=False)
  1630.     Local $aValueArray[1][2]=[[0,0]]
  1631.     ; Properly initialized?
  1632.     If Not $_PDH_bInit Then Return SetError(16,0,$aValueArray)
  1633.     ; Check if Counter Handle is valid
  1634.     If Not IsPtr($hPDHCounterHandle) Then Return SetError(1,0,$aValueArray)
  1635.     ; Called with request to just grab the counter value only?
  1636.     If Not $bGrabValueOnly Then
  1637.         ; Check if Query Handle is valid
  1638.         If Not IsPtr($hPDHQueryHandle) Then Return SetError(1,0,$aValueArray)
  1639.         ; Call CollectQueryData - 'True' means no checks necessary
  1640.         If Not _PDH_CollectQueryData($hPDHQueryHandle,True) Then Return SetError(@error,@extended,$aValueArray)
  1641. ;~      _PDH_DebugWrite("CollectQueryData succeeded")
  1642.     EndIf
  1643.  
  1644.     Local $aRet,$stCounterBuffer,$stCountValueItem,$iCountValueSz,$pPointer,$iDivisor=0
  1645.  
  1646.     ; 1st call - get required buffer size
  1647.     $aRet=DllCall($_PDH_hDLLHandle,"long","PdhGetFormattedCounterArrayW","ptr",$hPDHCounterHandle, _
  1648.         "dword",0x8400,"dword*",0,"dword*",0,"ptr",0)
  1649.     If @error Then Return SetError(2,@error,$aValueArray)
  1650.  
  1651.     ; Return should be PDH_MORE_DATA (0x800007D2), but on Win2000 'ERROR_SUCCESS' is returned - so check if there's a return size
  1652.     If $aRet[0]<>0x800007D2 And Not ($aRet[0]=0 And $aRet[3] And @OSVersion="WIN_2000") Then
  1653.         $_PDH_iLastError=$aRet[0]
  1654.         ; DEBUG
  1655.         _PDH_DebugWriteErr("PdhGetFormattedCounterArrayW 1st call unsuccessful, return:" & Hex($_PDH_iLastError)&", sz:"&$aRet[3])
  1656.         Return SetError(3,$_PDH_iLastError,$aValueArray)
  1657.     EndIf
  1658.  
  1659.     ; DEBUG:
  1660.     _PDH_DebugWrite("Required buffer size after 1st PdhGetFormattedCounterArrayW call:"&$aRet[3]&" bytes, #counters:"&$aRet[4])
  1661.  
  1662.     ; Set structure size according to results of 1st call
  1663.     $stCounterBuffer=DllStructCreate("byte["&$aRet[3]&']')
  1664.  
  1665.     ; 2nd call - get actual data
  1666.     $aRet=DllCall($_PDH_hDLLHandle,"long","PdhGetFormattedCounterArrayW","ptr",$hPDHCounterHandle, _
  1667.         "dword",0x8400,"dword*",$aRet[3],"dword*",0,"ptr",DllStructGetPtr($stCounterBuffer))
  1668.     If @error Then Return SetError(2,@error,$aValueArray)
  1669.  
  1670.     ; PDH Error? (typically PDH_CSTATUS_INVALID_DATA (0xC0000BBA) is returned for new\updated CPU Usage Counters)
  1671.     If $aRet[0] Then
  1672.         $_PDH_iLastError=$aRet[0]
  1673.         ; DEBUG:
  1674.         _PDH_DebugWriteErr("PdhGetFormattedCounterArrayW 2nd call unsuccessful, return:"&Hex($_PDH_iLastError)&", sz:"&$aRet[3]&", #counters:"&$aRet[4])
  1675.         Return SetError(4,$_PDH_iLastError,$aValueArray)
  1676.     EndIf
  1677.     ;_PDH_DebugWrite("PdhGetFormattedCounterArrayW 2nd call successfull, return size:"&$aRet[3]&" bytes, #counters:"&$aRet[4])
  1678.  
  1679.     ; Size array accordingly. 2 columns: 1 for Instance name, 1 for value [skipping 'status' because it isnt used here]
  1680.     Dim $aValueArray[$aRet[4]+1][2]=[[$aRet[4],0]]
  1681.  
  1682. ;   Determine if we need to do division on the results (better to just pass a number to divide by?)
  1683.     If $sCounterWildcardPath=-2 Or ($sCounterWildcardPath<>"" And StringRegExp($sCounterWildcardPath,"(:23(0|2)\\6|(Thread|Process)\([^%]+%)",0)) Then
  1684.         $iDivisor=$_PDH_iCPUCount   ; CPU Count (for % Processor Usage (#6) Counters [Thread (#232) or Process (#230) only])
  1685.     ElseIf $sCounterWildcardPath=-3 Then
  1686.         $iDivisor=1024      ; Bytes-To-KBytes, or KBytes-to-MBytes
  1687.     ElseIf $sCounterWildcardPath=-4 Then
  1688.         $iDivisor=1048576   ; Bytes-To-MBytes   [1024*1024 = 1048576]  or KByte to GBytes
  1689.     EndIf
  1690.  
  1691.     ; According to MSDN sources, the PDH_FMT_COUNTERVALUE_ITEM structure really should be ptr;dword;int64 (as max sized part of union)
  1692.     ;   However, the embedded PDH_FMT_COUNTERVALUE structure on its own *would* need padding to offset the in64 value, so alone
  1693.     ;   it would need that padding.  In x64 mode, the same situation results
  1694.     $iCountValueSz=DllStructGetSize(DllStructCreate("ptr;long;dword;int64"))    ; get PDH_FMT_COUNTERVALUE_ITEM structure size
  1695.  
  1696.     ; Pointer used in pulling\assigning each new structure in the buffer
  1697.     $pPointer=DllStructGetPtr($stCounterBuffer,1)
  1698.  
  1699.     ; Move through the buffer filled with PDH_FMT_COUNTERVALUE_ITEM structures
  1700.     For $i=1 To $aRet[4]
  1701.         $stCountValueItem=DllStructCreate("ptr;long;dword;int64",$pPointer) ; PDH_FMT_COUNTERVALUE_ITEM (see above)
  1702.  
  1703.         ; Assign Name from pointer
  1704.         $aValueArray[$i][0]=__PDH_StringGetNullTermMemStr(DllStructGetData($stCountValueItem,1))
  1705.  
  1706.         ; Ignoring 2nd *undocumented* member of struct - no idea what it is for
  1707.  
  1708.         ; Assign Counter Value
  1709.         $aValueArray[$i][1]=DllStructGetData($stCountValueItem,4)
  1710.  
  1711. ;~      ; Assign Status [optional]  -> through testing, always 0 *unless* 0xC0000BBA PDH error returned above,
  1712. ;~      ;   in which case the counters would all be Status code: 0xC0000BBA. (which won't happen since we return on error)
  1713. ;~      $aValueArray[$i][2]=DllStructGetData($stCountValueItem,3)
  1714.  
  1715. ;~      ; DEBUG:
  1716. ;~      If DllStructGetData($stCountValueItem,3) Then _
  1717. ;~          _PDH_DebugWrite("Non-zero status found for Counter '"&$aValueArray[$i][0]&"':"&Hex(DllStructGetData($stCountValueItem,3)))
  1718.  
  1719.         If $iDivisor Then $aValueArray[$i][1]=Round($aValueArray[$i][1]/$iDivisor)  ; divide if required
  1720.  
  1721.         ; DEBUG:
  1722. ;~      _PDH_DebugWrite("Name of item#"&$i&":" & $aValueArray[$i][0]&", Status:"&Hex(DllStructGetData($stCountValueItem,3))& _
  1723. ;~      ",Value:"&$aValueArray[$i][1]&", Sizeof stucture:" & DllStructGetSize($stCountValueItem)& _
  1724. ;~          ", Extra bits:"&DllStructGetData($stCountValueItem,2))  ; (extra seems to always = 0...)
  1725.  
  1726.         $pPointer+=$iCountValueSz   ; Move pointer to next counter value (PDH_FMT_COUNTERVALUE)
  1727.     Next
  1728.  
  1729.     ; Return value array
  1730.     Return $aValueArray
  1731. EndFunc
  1732.  
  1733.  
  1734. ; ===============================================================================================================================
  1735. ; Func _PDH_UpdateCounters($hPDHQueryHandle,ByRef $aPDHCountersArray,$iHandleIndex,$iCountIndex,$iStrIndex=-1,$iDeltaIndex=-1, _
  1736. ;   $iStart=1,$iEnd=-1,$bFirstCall=False)
  1737. ;
  1738. ; Updates Counter Values and delta changes for an array of PDH Counters.
  1739. ;   It also adjusts processor usage % to account for # of processors. Should match Task Manager values now
  1740. ;
  1741. ; $hPDHQueryHandle = The handle for the Query which has all the Counter Handles attached to it
  1742. ; $aPDHCountersArray = Array of Counters, Counter Handles, Counter Values, & Delta change data
  1743. ;       This is passed by REFERENCE so that this function can directly alter the values
  1744. ; $iHandleIndex = column index of Counter Handles in array [This is set to 0 and Removed when a Counter becomes invalid!]
  1745. ; $iCountIndex = column index of where Counter value data is stored
  1746. ; $iStrIndex = IF >0, the column index of where the string is (which may be adjusted as below)
  1747. ;   *** SPECIAL OPTIONS *** If passed a negative number instead, this function will do:
  1748. ;   For: -2 value, the Counter will be treated as a CPU usage related counter & adjusted accordingly
  1749. ;   For: -3 value, the Counter will be divided by 1024 (for Byte-to-KByte & KByte-to-MByte op's)
  1750. ;   For: -4 value, the Counter will be divided by 1048576 (for Byte-to-MByte op's)
  1751. ; $iDeltaIndex = IF >0 the column index of where the delta value should be stored
  1752. ; $iStart = 1st position in array to start at. For arrays with a bottom count element, like those
  1753. ;   containing counts/headers, this should be >0 (but it can be anywhere, so long as the data there is valid)
  1754. ; $iEnd = last position in array to stop at. -1 (default) means end-of-array.
  1755. ; $bFirstCall = If True, the 'delta change data' will be initialized/reinitialized to 0,
  1756. ;   otherwise, it contains the change since last call
  1757. ;
  1758. ; Returns:
  1759. ;   Success: True, with @extended = # of changes since last call, @error = # of invalidated handles
  1760. ;       NOTE: any processes that have ended will have their data (columns 1-3) set to 0,
  1761. ;           and 1st column will be prefixed with "[Dead Counter Handle]:"
  1762. ;   Failure: False, with @error set:
  1763. ;       @error = 16 = not initialized
  1764. ;       @error = 1 = Invalid parameters
  1765. ;       @error = 2 = DLL Call error, @extended=DLLCall error (see AutoIt Help)
  1766. ;       @error = 3 = PDH error, @extended = error code, as well as $_PDH_iLastError
  1767. ;
  1768. ; ADDITIONAL: $_PDH_aInvalidHandles[0]=#invalidated handles (or 0 for none), & size = # invalid handles +1 (bottom element)
  1769. ;
  1770. ; Author: Ascend4nt
  1771. ; ===============================================================================================================================
  1772.  
  1773. Func _PDH_UpdateCounters($hPDHQueryHandle,ByRef $aPDHCountersArray,$iHandleIndex,$iCountIndex,$iStrIndex=-1,$iDeltaIndex=-1, _
  1774.     $iStart=1,$iEnd=-1,$bFirstCall=False)
  1775.     ; Set this initially so that immediate Returns can rely on this being 1 element = 0
  1776.     Dim $_PDH_aInvalidHandles[1]=[0]
  1777.     ; Checked by _PDH_CollectQueryData():
  1778.     ;   If Not $_PDH_bInit Then Return SetError(16,0,False)
  1779.  
  1780.     ; Check array parameter. Requires a 2D array.
  1781.     If Not IsArray($aPDHCountersArray) Or UBound($aPDHCountersArray,0)<2 Then Return SetError(1,0,False)
  1782.  
  1783.     Local $iArrayColumns,$aRet,$stCounterValue,$iNewVal,$iChangesCount=0,$iDivisor=0
  1784.  
  1785.     $iArrayColumns=UBound($aPDHCountersArray,2)
  1786.  
  1787.     ; Check indexes are within bounds
  1788.     If $iHandleIndex<0 Or $iHandleIndex>$iArrayColumns Or $iCountIndex<0 Or $iCountIndex>$iArrayColumns Or _
  1789.         $iStrIndex>$iArrayColumns Or $iDeltaIndex>$iArrayColumns Then Return SetError(1,0,False)
  1790.  
  1791.     If $iEnd=-1 Then $iEnd=UBound($aPDHCountersArray)-1
  1792.     ; Everything within bounds and in right order?
  1793.     If $iStart<0 Or $iStart>$iEnd Or $iEnd>UBound($aPDHCountersArray)-1 Then Return SetError(1,0,False)
  1794.  
  1795.     ; Can't collect Query data? ('False' to allow checks for invalid params/Init)
  1796.     If Not _PDH_CollectQueryData($hPDHQueryHandle,False) Then Return SetError(@error,@extended,False)
  1797. #cs
  1798.         Local $iErr=@error,$iExt=@extended,$iDead=0
  1799.         ; Loop through and remove them all if PDH_NO_DATA? (all counters invalid)
  1800.         If $_PDH_iLastError=0x800007D5 Then
  1801.             For $i=$iStart To $iEnd
  1802.                 ;   Not a great way to handle the situation, but the Counter handle is invalidated below
  1803.                 If $iStrIndex>=0 Then $aPDHCountersArray[$i][$iStrIndex]="[Dead Counter Handle]:" & $aPDHCountersArray[$i][$iStrIndex]
  1804.                 ; Make a 'Remove Counter' call to remove it from PDH Query Handle
  1805.                 _PDH_RemoveCounter($aPDHCountersArray[$i][$iHandleIndex])
  1806.                 _PDH_DebugWriteErr("_PDH_RemoveCounter() call completed (for PDH error 0x"&Hex($_PDH_iLastError)&")")
  1807.                 $aPDHCountersArray[$i][$iHandleIndex]=0 ; Invalidate handle
  1808.                 $aPDHCountersArray[$i][$iCountIndex]=0  ; Set Counter value to 0
  1809.             Next
  1810.             _PDH_DebugWriteErr("ALL DEAD!")
  1811.         EndIf
  1812.         Return SetError($iErr,$iExt,False)
  1813.     EndIf
  1814. #ce
  1815.     Dim $_PDH_aInvalidHandles[UBound($aPDHCountersArray)+1]
  1816.     $_PDH_aInvalidHandles[0]=0
  1817.     $stCounterValue=DllStructCreate("long;int64")   ; PDH_FMT_COUNTERVALUE (can also change to long;double)
  1818.  
  1819. ;   Determine if we need to do division on the results (better to just pass a number to divide by?)
  1820.     If $iStrIndex=-2 Then
  1821.         $iDivisor=$_PDH_iCPUCount   ; CPU Count (for % Processor Usage (#6) Counters [Thread (#232) or Process (#230) only])
  1822.     ElseIf $iStrIndex=-3 Then
  1823.         $iDivisor=1024      ; Bytes-To-KBytes, or KBytes-to-MBytes
  1824.     ElseIf $iStrIndex=-4 Then
  1825.         $iDivisor=1048576   ; Bytes-To-MBytes   [1024*1024 = 1048576] or KByte to GBytes
  1826.     EndIf
  1827.  
  1828.     For $i=$iStart To $iEnd
  1829.         ; Make sure it hasn't been invalidated 1st
  1830.         If IsPtr($aPDHCountersArray[$i][$iHandleIndex]) Then
  1831. #cs
  1832.             ;-----------------------------------------------------
  1833.             ;  -  Format Options  -
  1834.             ;   PDH_FMT_LONG    0x00000100 (long-32bit)
  1835.             ;   PDH_FMT_DOUBLE  0x00000200 (double float)
  1836.             ;   PDH_FMT_LARGE   0x00000400 <- my choice (int64)
  1837.             ;   ( + )
  1838.             ;   PDH_FMT_NOSCALE     0x00001000
  1839.             ;   PDH_FMT_NOCAP100    0x00008000 <- also my choice (requires division for CPU usage results tho)
  1840.             ;   PDH_FMT_1000        0x00002000
  1841.             ;-----------------------------------------------------
  1842. #ce
  1843.             $aRet=DllCall($_PDH_hDLLHandle,"long","PdhGetFormattedCounterValue","ptr",$aPDHCountersArray[$i][$iHandleIndex],"dword",0x8400, _
  1844.                 "dword*",0,"ptr",DllStructGetPtr($stCounterValue))
  1845.  
  1846.             If @error Then
  1847.                 ; DLL Call error.. hmm..
  1848.                 If $iStrIndex>=0 Then
  1849.                     _PDH_DebugWriteErr("DLL Call error ("&@error&") at item("&$aPDHCountersArray[$i][$iStrIndex]&") (index #" &$i&")")
  1850.                 Else
  1851.                     _PDH_DebugWriteErr("DLL Call error ("&@error&") at index #" &$i&", Handle:"&$aPDHCountersArray[$i][$iHandleIndex])
  1852.                 EndIf
  1853.             ElseIf $aRet[0] Then
  1854.                 $_PDH_iLastError=$aRet[0]
  1855.                 ; PDH_INVALID_DATA? (0xC0000BC6). If CStatus also is PDH_CSTATUS_NO_INSTANCE (0x800007D1), counter no longer exists
  1856.                 If $aRet[0]=0xC0000BC6 And DllStructGetData($stCounterValue,1)=0x800007D1 Then
  1857.                     $_PDH_aInvalidHandles[0]+=1
  1858.                     If $iStrIndex>=0 Then
  1859.                         _PDH_DebugWriteErr("Invalid Data (invalid handle) message for (" & $aPDHCountersArray[$i][$iStrIndex] & _
  1860.                             "), Total Invalids this call:" & $_PDH_aInvalidHandles[0])
  1861.                         ;   Not a great way to handle the situation, but the Counter handle is invalidated below
  1862.                         $aPDHCountersArray[$i][$iStrIndex]="[Dead Counter Handle]:" & $aPDHCountersArray[$i][$iStrIndex]
  1863.                     Else
  1864.                         _PDH_DebugWriteErr("Invalid data message at index #" &$i&", Handle:"&$aPDHCountersArray[$i][$iHandleIndex])
  1865.                     EndIf
  1866.                     ; Make a 'Remove Counter' call to remove it from PDH Query Handle
  1867.                     _PDH_RemoveCounter($aPDHCountersArray[$i][$iHandleIndex])
  1868.                     _PDH_DebugWriteErr("_PDH_RemoveCounter() call completed (for PDH error 'PDH_INVALID_DATA (0xC0000BC6)')")
  1869.  
  1870.                     $_PDH_aInvalidHandles[$_PDH_aInvalidHandles[0]]=$i
  1871.                     $aPDHCountersArray[$i][$iHandleIndex]=0 ; Invalidate handle
  1872.                     $aPDHCountersArray[$i][$iCountIndex]=0  ; Set Counter value to 0
  1873.                     If $iDeltaIndex>=0 Then $aPDHCountersArray[$i][$iDeltaIndex]=-1 ; Signal that this is a change in status
  1874.                 Else
  1875.                     ; DEBUG
  1876.                     If $iStrIndex>=0 Then
  1877.                         _PDH_DebugWriteErr("Error Calling PdhGetFormattedCounterValue for ("&$aPDHCountersArray[$i][$iStrIndex]& _
  1878.                             "), Return:" & Hex($_PDH_iLastError) & " CStatus:"&Hex(DllStructGetData($stCounterValue,1)))
  1879.                     Else
  1880.                         _PDH_DebugWriteErr("Error calling PdhGetFormattedCounterValue at index #" &$i& _
  1881.                             ", Handle:"&$aPDHCountersArray[$i][$iHandleIndex]&", Return:"&Hex($_PDH_iLastError)& " CStatus:"&Hex(DllStructGetData($stCounterValue,1)))
  1882.                     EndIf
  1883.                 EndIf
  1884.             Else
  1885.                 $iNewVal=DllStructGetData($stCounterValue,2)
  1886.  
  1887.                 ; Common return: PDH_CSTATUS_VALID_DATA (0) = data is valid, but unchanged since last read
  1888.                 ;   Also common:  PDH_CSTATUS_INVALID_DATA  0xC0000BBA
  1889.                 If DllStructGetData($stCounterValue,1) Then _
  1890.                     _PDH_DebugWriteErr("PdhGetFormattedCounterValue non-0 status for handle #" & $i & ":" & Hex(DllStructGetData($stCounterValue,1)))
  1891.                 ; Check for Return of PDH_CSTATUS_NEW_DATA (1)? Nah - never seems to occur!
  1892.  
  1893.                 ; Divide if required
  1894.                 If $iDivisor Then
  1895.                     $iNewVal=Round($iNewVal/$iDivisor)
  1896.                 ElseIf $iStrIndex>=0 And StringRegExp($aPDHCountersArray[$i][$iStrIndex],"(:23(0|2)\\6|(Thread|Process)\([^%]+%)",0) Then
  1897.                     $iNewVal=Round($iNewVal/$_PDH_iCPUCount)
  1898.                 EndIf
  1899.  
  1900.                 If $aPDHCountersArray[$i][$iCountIndex]<>$iNewVal And Not $bFirstCall Then
  1901.                     ; Increase # of changes
  1902.                     $iChangesCount+=1
  1903.                     #cs
  1904.                     ; DEBUG
  1905.                     _PDH_DebugWrite("PdhGetFormattedCounterValue change for (" & $aPDHCountersArray[$i][$iStrIndex]&"):"& _
  1906.                         "Previous Value:" & $aPDHCountersArray[$i][$iCountIndex] & _
  1907.                         ", Delta change:" & ($iNewVal-$aPDHCountersArray[$i][$iCountIndex]) & _
  1908.                         ", New value:" & $iNewVal)
  1909.                     #ce
  1910.                     ; Capture Delta Change
  1911.                     If $iDeltaIndex>=0 Then $aPDHCountersArray[$i][$iDeltaIndex]=$iNewVal-$aPDHCountersArray[$i][$iCountIndex]
  1912.                 Else
  1913.                     ; No change since last call (delta = 0)
  1914.                     If $iDeltaIndex>=0 Then $aPDHCountersArray[$i][$iDeltaIndex]=0
  1915.                 EndIf
  1916.                 ; Capture new value
  1917.                 $aPDHCountersArray[$i][$iCountIndex]=$iNewVal
  1918.             EndIf
  1919.         ElseIf $iDeltaIndex>=0 And $aPDHCountersArray[$i][$iDeltaIndex]=-1 Then
  1920.         ; Counter was invalidated last call, now reset it's delta to 0
  1921.             $aPDHCountersArray[$i][$iDeltaIndex]=0
  1922.             $iChangesCount+=1   ; do we count this? Sure why not
  1923.         EndIf
  1924.     Next
  1925.     ; DEBUG
  1926. ;~  _PDH_DebugWrite("Number of counter changes this call:" & $iChangesCount)
  1927.  
  1928.     ReDim $_PDH_aInvalidHandles[$_PDH_aInvalidHandles[0]+1]
  1929.     $_PDH_aInvalidHandles[0]=$_PDH_aInvalidHandles[0]
  1930.     Return SetError($_PDH_aInvalidHandles[0],$iChangesCount,True)
  1931. EndFunc
  1932.  
  1933.  
  1934. ; ===============================================================================================================================
  1935. ; Func _PDH_GetCounterValueByPath($hPDHQueryHandle,$sCounterPath)
  1936. ;
  1937. ; Function to quickly grab a counter value by a path without having to manually add it and remove it.
  1938. ;   (Basically a wrapper).
  1939. ;   It does the following:
  1940. ;       1. If Query Handle is invalid (-1 or 0), it will make a new one
  1941. ;       2. It will add the Counter (by path) to the Query Handle
  1942. ;       3. It will grab the current value of the Counter
  1943. ;       4. It will either a.) Remove the Counter or B) if it created a new Query Handle, it will destroy the Query Handle
  1944. ;       5. It will return the value obtained from the Counter (or -1 if an error occurred somewhere - see below)
  1945. ;
  1946. ; $hPDHQueryHandle = PDH Query Handle, obviously
  1947. ; $sCounterPath = Counter Path (of the format returned by _PDH_BrowseCounters(). Examples:
  1948. ;   Format with network path: "\\<PCNAME>\Process(ProcessName)\% Processor Time")
  1949. ;   Actual Value (on local PC): "\Process(Idle)\% Processor Time"
  1950. ;
  1951. ; Return:
  1952. ;   Success: Counter value, @error = 0
  1953. ;   Failure: -1, @error set (& possibly @extended):
  1954. ;       @error = 16 = not initialized
  1955. ;       @error = 1 = Invalid handle (0 or -1), and could not create a new one (@extended = 1)
  1956. ;       @error = 2 = DLL Call error, @extended=DLLCall error (see AutoIt Help)
  1957. ;       @error = 3 = PDH error, @extended = error code, as well as $_PDH_iLastError
  1958. ;
  1959. ; Author: Ascend4nt
  1960. ; ===============================================================================================================================
  1961.  
  1962. Func _PDH_GetCounterValueByPath($hPDHQueryHandle,$sCounterPath)
  1963.     If Not $_PDH_bInit Then Return SetError(16,0,-1)
  1964.     Local $tErr,$bNewQueryHandleCreated=False
  1965.     ; Check if its a valid Query Handle. If not, we can try to grab one for this call only
  1966.     If Not IsPtr($hPDHQueryHandle) Then
  1967.         $hPDHQueryHandle=_PDH_GetNewQueryHandle()
  1968.         If @error Then Return SetError(@error,1,-1)
  1969.         $bNewQueryHandleCreated=True
  1970.     EndIf
  1971.     Local $hPDHCounterHandle=_PDH_AddCounter($hPDHQueryHandle,$sCounterPath)
  1972.     If @error Then
  1973.         $tErr=@error
  1974.         If $bNewQueryHandleCreated Then _PDH_FreeQueryHandle($hPDHQueryHandle)
  1975.         Return SetError($tErr,0,-1)
  1976.     EndIf
  1977.     Local $iVal=_PDH_UpdateCounter($hPDHQueryHandle,$hPDHCounterHandle,$sCounterPath)
  1978.     $tErr=@error
  1979.     If $bNewQueryHandleCreated Then
  1980.         _PDH_FreeQueryHandle($hPDHQueryHandle)
  1981.     Else
  1982.         _PDH_RemoveCounter($hPDHCounterHandle)
  1983.     EndIf
  1984.     Return SetError($tErr,0,$iVal)
  1985. EndFunc
  1986.  
  1987.  
  1988. #region PDH_INTERNAL_FUNCS
  1989.  
  1990. ; ===================================================================================================================
  1991. ;   --------------------    INTERNAL FUNCTIONS  --------------------
  1992. ; ===================================================================================================================
  1993.  
  1994.  
  1995. ; ===============================================================================================================================
  1996. ; Func _PDH_DebugWrite($sString,$bAddCRLF=True)
  1997. ; + Func _PDH_DebugWriteErr($sString,$bAddCRLF=True)
  1998. ;
  1999. ; Simple functions to replace 'ConsoleWrite' that allows Debug output to be silenced (see $PDH_DEBUGLOG)
  2000. ;   (also could be saved to a log)
  2001. ;
  2002. ; $sString = string to output (if $PDH_DEBUGLOG=True)
  2003. ; $bAddCRLF = Add @CRLF to output? True in most cases
  2004. ;
  2005. ; Returns: nothing
  2006. ;
  2007. ; Author: Ascend4nt
  2008. ; ===============================================================================================================================
  2009.  
  2010. Func _PDH_DebugWrite($sString,$bAddCRLF=True,$bErr=False)
  2011.     ; Less than 2 -> don't report on non-error related events
  2012.     If $PDH_DEBUGLOG<2 Then Return
  2013.     If $bAddCRLF Then $sString&=@CRLF
  2014.     ConsoleWrite($sString)
  2015. EndFunc
  2016.  
  2017. ;~
  2018.  
  2019. Func _PDH_DebugWriteErr($sString,$bAddCRLF=True)
  2020.     If $PDH_DEBUGLOG<1 Then Return
  2021.     If $bAddCRLF Then $sString&=@CRLF
  2022.     ConsoleWriteError($sString)
  2023. EndFunc
  2024.  
  2025.  
  2026. ; ===================================================================================================================
  2027. ; Func __PDH_ForceFreeDLL($sDLLName,$iDecCount=1)
  2028. ;
  2029. ; Attempts to forcibly free the DLL (keeps decrementing its count, up to $iDecCount).
  2030. ;   NOTE: Not recommended for DLL's that weren't loaded by your code - generally the EXE or other DLL's will
  2031. ;     be referencing the one you are freeing! (which may well cause a crash sooner or later)
  2032. ;     - Mainly use this only on DLL's that have a reference count of 1, which you yourself are aware
  2033. ;       of how it was loaded (or the cause of it being loaded).
  2034. ;
  2035. ; $sDLLName = Name of main .EXE (don't be silly) or a loaded .DLL file name - with or without the path.
  2036. ;   (better w/o for bitmode path compatibility, though that poses a security issue)
  2037. ; $iDecCount = Max # of times to decrement the DLL load count. More than 1 is iffy..(see above NOTE)
  2038. ;
  2039. ; Returns:
  2040. ;   Success: True, with unloaded DLL, @error=0
  2041. ;   Failure: 0, with @error set:
  2042. ;       @error = 1 = invalid parameter
  2043. ;       @error = 2 = DLLCall error, with @extended = DLLCall error code (see AutoIT help)
  2044. ;       @error = 3 = False/Failure returned from API called
  2045. ;
  2046. ; Author: Ascend4nt
  2047. ; ===================================================================================================================
  2048.  
  2049. Func __PDH_ForceFreeDLL($sDLLName,$iDecCount=1)
  2050.     If Not IsString($sDLLName) Or $sDLLName='' Then Return SetError(1,0,0)
  2051.     Local $aRet
  2052.     For $i=1 To $iDecCount
  2053.         $aRet=DllCall('kernel32.dll',"handle","GetModuleHandleW","wstr",$sDLLName)
  2054.         If @error Then Return SetError(2,@error,0)
  2055.         If $aRet[0]=0 Then
  2056.             $aRet=DllCall('kernel32.dll','dword','GetLastError')
  2057.             If @error Then Return SetError(2,@error,0)
  2058.             ; 126 = 'The specified module could not be found' - GOOD, that means it is free/gone
  2059.             If $aRet[0]=126 Then ExitLoop
  2060.             Return SetError(3,0,0)
  2061.         EndIf
  2062.         $aRet=DllCall('kernel32.dll',"bool","FreeLibrary","handle",$aRet[0])
  2063.         If @error Then Return SetError(2,@error,0)
  2064.         If Not $aRet[0] Then Return SetError(3,0,0)
  2065.     Next
  2066.     _PDH_DebugWrite("__PDH_ForceFreeDLL() call succeeded for '"&$sDLLName&"', "&$i&" iterations")
  2067.     Return True
  2068. EndFunc
  2069.  
  2070.  
  2071. ; ===============================================================================================================================
  2072. ; Func __PDH_RegistryToggle($bEnable,$sPCName='')
  2073. ;
  2074. ; Multi-purpose function for testing, setting, or clearing the relevant Registry keys pertaining to Performance Counters.
  2075. ;   A re-boot may be necessary for certain O/S's when Performance Counters are enabled or disabled.
  2076. ;   (That is why this function was separated from the _PDH_Init() code, plus it looks cleaner)
  2077. ;
  2078. ; $bEnable = If 1, enable performance counters. If 0, disable. If -1, check if Performance Counters are enabled
  2079. ; $sPCName = *optional* value -> the machine name, prefixed as normal with '\\' (Example: '\\PCNAME')
  2080. ;
  2081. ; Returns:
  2082. ;   Success: True with @extended = # of changes, or just 1 (if $bEnable=-1) with @extended=0
  2083. ;   Failure: False and @error set:
  2084. ;       @error =  1 = invalid parameter ($sPCName)
  2085. ;       @error = -1 = Performance Counters are disabled (return is a string - only if $bEnable=-1)
  2086. ;       @error = 16 = error setting/clearing a registry value that needed to be altered. @extended contains RegWrite() error
  2087. ;
  2088. ; Author: Ascend4nt
  2089. ; ===============================================================================================================================
  2090.  
  2091. Func __PDH_RegistryToggle($bEnable,$sPCName='')
  2092.     If Not IsString($sPCName) Then Return SetError(1,0,False)
  2093.  
  2094.     If $sPCName<>'' Then $sPCName&='\'
  2095.  
  2096.     ; Performance Counter Registry values. If non-zero for 'Disable Performance Counters', Performance Counters are disabled
  2097.     Local $PDH_RegValues[3][3]=[ [$sPCName&"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib",''], _
  2098.         [$sPCName&"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PerfOS\Performance",''], _
  2099.         [$sPCName&"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PerfProc\Performance",''] ]
  2100.     Local Const $PDH_DisableValue="Disable Performance Counters"
  2101.  
  2102.     Local $iSetVal,$iErr=0,$iChanged=0
  2103.     If $bEnable Then
  2104.         $iSetVal=0
  2105.     Else
  2106.         $iSetVal=1
  2107.     EndIf
  2108.  
  2109.     ; Check important 'Disable Performance Counter' Values, and check/set/clear Performance Counters if possible
  2110.     For $i=0 To 2
  2111.         $PDH_RegValues[$i][1]=RegRead($PDH_RegValues[$i][0],$PDH_DisableValue)
  2112.         If @error Then $PDH_RegValues[$i][1]=0  ; would be set to "", but we'll force an integer
  2113.         ; If ENABLING - check if the key is there and set to a non-zero #, then attempt to set the value to 0
  2114.         ;   If DISABLING - check that either the key *isn't* there, or that it is set to non-zero
  2115.         If ($bEnable And $PDH_RegValues[$i][1] And IsInt($PDH_RegValues[$i][1])) Or ($bEnable=0 And $PDH_RegValues[$i][1]=0) Then
  2116.             ; Just checking? Too bad - Performance counters are disabled somewhere
  2117.             If $bEnable=-1 Then
  2118.                 _PDH_DebugWriteErr("Performance Counters disabled, Registry Key: '"&$PDH_RegValues[$i][0]&"'")
  2119.                 Return SetError(-1,0,False)
  2120.             EndIf
  2121.             ; Write the registry value (either enable or disable)
  2122.             RegWrite($PDH_RegValues[$i][0],$PDH_DisableValue,"REG_DWORD",$iSetVal)
  2123.             $iErr=@error
  2124.             If $iErr Then ExitLoop
  2125.             $iChanged+=1
  2126.         EndIf
  2127.     Next
  2128.  
  2129.     If $bEnable=-1 Then Return 1    ; Called with special 'just-checking' value - return 'Performance Counters enabled'
  2130.  
  2131.     ; @error set by previous loop? - RegWrite() failed
  2132.     If $iErr Then
  2133.         ; Reset any 'Disable Performance Counter' registry keys that were modified
  2134.         For $i=0 To 2
  2135.             ; If the key was there and set to non-zero, we need to restore it to the original value
  2136.             If ($bEnable And $PDH_RegValues[$i][1] And IsInt($PDH_RegValues[$i][1])) Or ($bEnable=0 And $PDH_RegValues[$i][1]=0) Then
  2137.                 RegWrite($PDH_RegValues[$i][0],$PDH_DisableValue,"REG_DWORD",$PDH_RegValues[$i][1])
  2138.             EndIf
  2139.         Next
  2140.         Return SetError(16,$iErr,False)
  2141.     EndIf
  2142.  
  2143.     Return SetExtended($iChanged,True)
  2144. EndFunc
  2145.  
  2146.  
  2147. ; ===============================================================================================================================
  2148. ; Func __PDH_LocalizeCounter($sNonLocalizedCounter)
  2149. ;
  2150. ; Internal function for converting from non-localized counter string to a localized Full Counter Path
  2151. ;   WARNING: Do NOT call directly! It is assumed this will be called by one of the regular functions.
  2152. ;
  2153. ; $sNonLocalizedCounter = Counter in the :##\##(x)\PCNAME format. See _PDH_CounterNameToNonLocalStr
  2154. ;
  2155. ; Returns:
  2156. ;   Success: Full Counter String
  2157. ;   Failure: "" with @error = 7 (invalid format)
  2158. ;
  2159. ; Author: Ascend4nt
  2160. ; ===============================================================================================================================
  2161.  
  2162. Func __PDH_LocalizeCounter($sNonLocalizedCounter)
  2163.     Local $aSplitPath,$sPCName=""
  2164.     ; Split counter up to individual components
  2165.     $aSplitPath=StringSplit(StringTrimLeft($sNonLocalizedCounter,1),"\",0)
  2166.     If @error Or $aSplitPath[0]<3 Then Return SetError(7,0,"")
  2167.     ; Machine name?
  2168.     If $aSplitPath[0]>3 And $aSplitPath[4]<>"" Then $sPCName='\\'&$aSplitPath[4]
  2169.     ; Assemble localized counter name
  2170.     Return $sPCName&'\'&_PDH_GetCounterNameByIndex(Int($aSplitPath[1]),$sPCName)&$aSplitPath[3]& _
  2171.         '\'&_PDH_GetCounterNameByIndex(Int($aSplitPath[2]),$sPCName)
  2172. EndFunc
  2173.  
  2174.  
  2175. ; ===============================================================================================================================
  2176. ; Func __PDH_StringGetNullTermMemStr($pStringPtr)
  2177. ;
  2178. ; Function to grab a null-terminated string from a memory location where the string size and end-of-buffer are undefined.
  2179. ;   This avoids a few problems:
  2180. ;    1. Trying to grab *too* much memory (possibly resulting in access violations)
  2181. ;    2. Converting from a 'byte' buffer to a 'wchar' one (of the right size).
  2182. ;    3. Getting an offset to the data *after* the string.
  2183. ;       Either use:
  2184. ;           '(@extended+1)*2 for # of bytes including null-term. ([stringlen+1(null-term)*2 (*2 for Unicode two-byte size)])
  2185. ;            or if alternate function used, '(StringLen($sRet)+1)*2' (stringlen+1(null-term)*2)
  2186. ;
  2187. ; NOTE: While listed on MSDN as requiring Windows XP/2003+, these function(s) are in fact available on Windows 2000 (no SP).
  2188. ;
  2189. ; Returns:
  2190. ;   Success: String or "" if 0-length, @error=0, @extended=string-length
  2191. ;   Failure: "", with @error set:
  2192. ;       @error = 1 = invalid parameter or pointer is 0
  2193. ;       @error = 2 = DLLCall error (see @extended for DLLCall @error code)
  2194. ;
  2195. ; Author: Ascend4nt
  2196. ; ===============================================================================================================================
  2197.  
  2198. Func __PDH_StringGetNullTermMemStr($pStringPtr)
  2199.     If Not IsPtr($pStringPtr) Or $pStringPtr=0 Then Return SetError(1,0,"")
  2200.     ; Get length of null-terminated string  ; alternative: lstrcpynW (needs a different approach though)
  2201.     Local $aRet=DllCall("kernel32.dll","int","lstrlenW","ptr",$pStringPtr)
  2202.     If @error Then Return SetError(2,@error,"")
  2203.     If $aRet[0]=0 Then Return ""    ; not an error, just a zero-length string
  2204.     ; Set buffer size to string size and use pointer so that we can extract it into a variable
  2205.     Local $stString=DllStructCreate("wchar["&$aRet[0]&"]",$pStringPtr)
  2206.     Return SetExtended($aRet[0],DllStructGetData($stString,1))
  2207. EndFunc
  2208.  
  2209. #endregion PDH_INTERNAL_FUNCS