Jump to content
PsaltyDS

_ProcessListProperties()

Recommended Posts

PsaltyDS

The _ProcessListProperties() function has been floating around General Help and Support long enough. This is it's new home.

This function lists several properties of all processes (default), selected processes by name, or a single process by PID.

===> Important bug note: The SwbemRefresher object has a Microsoft bug that causes a memory leak when called repeatedly. Until a fix is found from Microsoft,

don't use _ProcessListProperties() in a tight loop. <===

Change Log:
05/05/2008  --  v1.0.0  --  First version placed in Example Scripts
05/07/2008  --  v1.0.1  --  Added the calling command line at [n][9]
06/10/2008  --  v2.0.0  --  Removed use of ProcessList() so remote access could work
06/12/2008  --  v2.0.1  --  Added geeky RegExp for WMI date conversion by weaponx
07/02/2008  --  v2.0.2  --  Fixed find by PID or Name, which was broken by removing ProcessList() usage
09/17/2008  --  v2.0.3  --  Added Debug (SeDebugPrivilege) to WMI call to retrieve fuller results
12/15/2008  --  v2.0.3  --  No change to function, cleaned up demo and added COM error handler to it

12/01/2009 -- v2.0.4 -- Fixed check for null $Process so '0' could be used (System Idle Process)

Information is returned in a 2D array similar to the native ProcessList(), in fact the UDF uses ProcessList() internally.

This is the structure of the array:

[0][0] - Number of processes listed (can be 0 if no matches found)

[1][0] - 1st process name

[1][1] - 1st process PID

[1][2] - 1st process Parent PID

[1][3] - 1st process owner

[1][4] - 1st process priority (0 = low, 31 = high)

[1][5] - 1st process executable path

[1][6] - 1st process CPU usage

[1][7] - 1st process memory usage

[1][8] - 1st process creation date/time = "MM/DD/YYY hh:mm:ss"

[1][9] - 1st process command line string

...

[n][0] thru [n][9] - last process properties

Here is the function, with a running demo script:

#include <Array.au3>; Only for _ArrayDisplay()

; Install a custom error handler
Global $oMyError = ObjEvent("AutoIt.Error","MyErrFunc"), $f_COMError = False

; Globals
Global $avRET, $iPID

; Demonstrate listing all processes
$avRET = _ProcessListProperties()
_ArrayDisplay($avRET, "All Processes")

; Demonstrate listing by name
$avRET = _ProcessListProperties("svchost.exe")
_ArrayDisplay($avRET, "By name: 'svchost.exe'")

; Demonstrate listing by PID (integer)
$iPID = ProcessExists("svchost.exe")
$avRET = _ProcessListProperties($iPID)
_ArrayDisplay($avRET, "By int PID: " & $iPID)

; Demonstrate listing by PID (numeric string)
$iPID = String($iPID)
$avRET = _ProcessListProperties($iPID)
_ArrayDisplay($avRET, "By string PID: " & $iPID)

; Demonstrate not found
$avRET = _ProcessListProperties("NoSuchProcess.exe")
_ArrayDisplay($avRET, "Not found: 'NoSuchProcess.exe'")

; This is my custom error handler 
Func MyErrFunc()
    Local $HexNumber=hex($oMyError.number,8) 
    Msgbox(0,"","We intercepted a COM Error !" & @CRLF & _
                "Number is: " & $HexNumber & @CRLF & _
                "WinDescription is: " & $oMyError.windescription & @CRLF & _
                "Source is: " & $oMyError.source & @CRLF & _
                "ScriptLine is: " & $oMyError.scriptline) 
   $f_COMError = True; something to check for when this function returns 
Endfunc


;===============================================================================
; Function Name:    _ProcessListProperties()
; Description:   Get various properties of a process, or all processes
; Call With:       _ProcessListProperties( [$Process [, $sComputer]] )
; Parameter(s):  (optional) $Process - PID or name of a process, default is "" (all)
;          (optional) $sComputer - remote computer to get list from, default is local
; Requirement(s):   AutoIt v3.2.4.9+
; Return Value(s):  On Success - Returns a 2D array of processes, as in ProcessList()
;            with additional columns added:
;            [0][0] - Number of processes listed (can be 0 if no matches found)
;            [1][0] - 1st process name
;            [1][1] - 1st process PID
;            [1][2] - 1st process Parent PID
;            [1][3] - 1st process owner
;            [1][4] - 1st process priority (0 = low, 31 = high)
;            [1][5] - 1st process executable path
;            [1][6] - 1st process CPU usage
;            [1][7] - 1st process memory usage
;            [1][8] - 1st process creation date/time = "MM/DD/YYY hh:mm:ss" (hh = 00 to 23)
;            [1][9] - 1st process command line string
;            ...
;            [n][0] thru [n][9] - last process properties
; On Failure:      Returns array with [0][0] = 0 and sets @Error to non-zero (see code below)
; Author(s):        PsaltyDS at http://www.autoitscript.com/forum
; Date/Version:   12/01/2009  --  v2.0.4
; Notes:            If an integer PID or string process name is provided and no match is found,
;            then [0][0] = 0 and @error = 0 (not treated as an error, same as ProcessList)
;          This function requires admin permissions to the target computer.
;          All properties come from the Win32_Process class in WMI.
;            To get time-base properties (CPU and Memory usage), a 100ms SWbemRefresher is used.
;===============================================================================
Func _ProcessListProperties($Process = "", $sComputer = ".")
    Local $sUserName, $sMsg, $sUserDomain, $avProcs, $dtmDate
    Local $avProcs[1][2] = [[0, ""]], $n = 1

    ; Convert PID if passed as string
    If StringIsInt($Process) Then $Process = Int($Process)

    ; Connect to WMI and get process objects
    $oWMI = ObjGet("winmgmts:{impersonationLevel=impersonate,authenticationLevel=pktPrivacy, (Debug)}!\\" & $sComputer & "\root\cimv2")
    If IsObj($oWMI) Then
        ; Get collection processes from Win32_Process
        If $Process == "" Then
            ; Get all
            $colProcs = $oWMI.ExecQuery("select * from win32_process")
        ElseIf IsInt($Process) Then
            ; Get by PID
            $colProcs = $oWMI.ExecQuery("select * from win32_process where ProcessId = " & $Process)
        Else
            ; Get by Name
            $colProcs = $oWMI.ExecQuery("select * from win32_process where Name = '" & $Process & "'")
        EndIf

        If IsObj($colProcs) Then
            ; Return for no matches
            If $colProcs.count = 0 Then Return $avProcs

            ; Size the array
            ReDim $avProcs[$colProcs.count + 1][10]
            $avProcs[0][0] = UBound($avProcs) - 1

            ; For each process...
            For $oProc In $colProcs
                ; [n][0] = Process name
                $avProcs[$n][0] = $oProc.name
                ; [n][1] = Process PID
                $avProcs[$n][1] = $oProc.ProcessId
                ; [n][2] = Parent PID
                $avProcs[$n][2] = $oProc.ParentProcessId
                ; [n][3] = Owner
                If $oProc.GetOwner($sUserName, $sUserDomain) = 0 Then $avProcs[$n][3] = $sUserDomain & "\" & $sUserName
                ; [n][4] = Priority
                $avProcs[$n][4] = $oProc.Priority
                ; [n][5] = Executable path
                $avProcs[$n][5] = $oProc.ExecutablePath
                ; [n][8] = Creation date/time
                $dtmDate = $oProc.CreationDate
                If $dtmDate <> "" Then
                    ; Back referencing RegExp pattern from weaponx
                    Local $sRegExpPatt = "\A(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:.*)"
                    $dtmDate = StringRegExpReplace($dtmDate, $sRegExpPatt, "$2/$3/$1 $4:$5:$6")
                EndIf
                $avProcs[$n][8] = $dtmDate
                ; [n][9] = Command line string
                $avProcs[$n][9] = $oProc.CommandLine

                ; increment index
                $n += 1
            Next
        Else
            SetError(2); Error getting process collection from WMI
        EndIf
        ; release the collection object
        $colProcs = 0

        ; Get collection of all processes from Win32_PerfFormattedData_PerfProc_Process
        ; Have to use an SWbemRefresher to pull the collection, or all Perf data will be zeros
        Local $oRefresher = ObjCreate("WbemScripting.SWbemRefresher")
        $colProcs = $oRefresher.AddEnum($oWMI, "Win32_PerfFormattedData_PerfProc_Process" ).objectSet
        $oRefresher.Refresh

        ; Time delay before calling refresher
        Local $iTime = TimerInit()
        Do
            Sleep(20)
        Until TimerDiff($iTime) >= 100
        $oRefresher.Refresh

        ; Get PerfProc data
        For $oProc In $colProcs
            ; Find it in the array
            For $n = 1 To $avProcs[0][0]
                If $avProcs[$n][1] = $oProc.IDProcess Then
                    ; [n][6] = CPU usage
                    $avProcs[$n][6] = $oProc.PercentProcessorTime
                    ; [n][7] = memory usage
                    $avProcs[$n][7] = $oProc.WorkingSet
                    ExitLoop
                EndIf
            Next
        Next
    Else
        SetError(1); Error connecting to WMI
    EndIf

    ; Return array
    Return $avProcs
EndFunc  ;==>_ProcessListProperties

Constructive criticism welcome.

:)

Edited by PsaltyDS
  • Like 1

Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law

Share this post


Link to post
Share on other sites
weaponx

Good old reliable. I give this script a 9.5/10.

Share this post


Link to post
Share on other sites
amokoura

Excellent! Thank you!

I was just about to create a WMI script to check out some detailed process information but your script solves the problem.

EDIT:

An addition I made is to use the "CommandLine" property. That way I can validate the parameters the process has got. Psalty, how about adding an element to the process array: "command line parameters"?

Edited by amokoura

Share this post


Link to post
Share on other sites
weaponx

Excellent! Thank you!

I was just about to create a WMI script to check out some detailed process information but your script solves the problem.

EDIT:

An addition I made is to use the "CommandLine" property. That way I can validate the parameters the process has got. Psalty, how about adding an element to the process array: "command line parameters"?

Sounds good to me. Gives it more functionality like Process Explorer. Easy too.

$oProc.CommandLine

Share this post


Link to post
Share on other sites
PsaltyDS

Sounds good to me. Gives it more functionality like Process Explorer. Easy too.

$oProc.CommandLine

Done.

P.S. If you're going to keep poking me in the ribs, at least retract the claws! :)


Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law

Share this post


Link to post
Share on other sites
weaponx

Done.

P.S. If you're going to keep poking me in the ribs, at least retract the claws! :)

MMMmmm...penguin ribs...

Share this post


Link to post
Share on other sites
FreeRider

Well done my friend... This could be useful for me in a near future... Many thanks ... and I give this script a 9,8/10 just to say better than weaponx :)

Proposal of improvement : Make the function to accept wild cards ($avProcProps = _ProcessListProperties("Sy*.*"). If it's possible it could be nice to have this possibility.

Bye,


FreeRiderHonour & Fidelity

Share this post


Link to post
Share on other sites
PsaltyDS

Well done my friend... This could be useful for me in a near future... Many thanks ... and I give this script a 9,8/10 just to say better than weaponx :)

Proposal of improvement : Make the function to accept wild cards ($avProcProps = _ProcessListProperties("Sy*.*"). If it's possible it could be nice to have this possibility.

Bye,

Thanks.

The function starts out by calling the native ProcessList(), and then adds more details to the array. So if ProcessList won't take it, then _ProcessListProperties won't. Besides, that is easy to implement in post-processing of the returned array.

:(


Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law

Share this post


Link to post
Share on other sites
NELyon

MMMmmm...penguin ribs...

Great with BBQ Sauce :)

Awesome script. I can't believe I haven't seen it if it's been floating around for a while.

Share this post


Link to post
Share on other sites
weaponx

My only complaint is that the creation timestamp is in U.S. format instead of Date UDF format (YYYY/MM/DD HH:MM:SS), it would be nice to use _DateDiff() without modifying the output.

There is also a cool way to format timestamps returned from WMI. I just put this after the first ObjGet("winmgmts...

$oDateTime = ObjCreate("WbemScripting.SWbemDateTime")

Then this goes in the process collection loop

If $oProc.CreationDate <> "" Then
               ;Copy process creation timestamp to date/time object
                $oDateTime.Value = $oProc.CreationDate
                $FormatDate = StringFormat("%s/%s/%s %s:%s:%s",$oDateTime.Year, $oDateTime.Month, $oDateTime.Day, $oDateTime.Hours, $oDateTime.Minutes, $oDateTime.Seconds)
                ConsoleWrite("Creation date: " & $FormatDate  & @CRLF)
            EndIf

Share this post


Link to post
Share on other sites
PsaltyDS

My only complaint is that the creation timestamp is in U.S. format instead of Date UDF format (YYYY/MM/DD HH:MM:SS), it would be nice to use _DateDiff() without modifying the output.

There is also a cool way to format timestamps returned from WMI. I just put this after the first ObjGet("winmgmts...

$oDateTime = ObjCreate("WbemScripting.SWbemDateTime")

Then this goes in the process collection loop

If $oProc.CreationDate <> "" Then
              ;Copy process creation timestamp to date/time object
                $oDateTime.Value = $oProc.CreationDate
                $FormatDate = StringFormat("%s/%s/%s %s:%s:%s",$oDateTime.Year, $oDateTime.Month, $oDateTime.Day, $oDateTime.Hours, $oDateTime.Minutes, $oDateTime.Seconds)
                ConsoleWrite("Creation date: " & $FormatDate  & @CRLF)
            EndIf
I like that, very nice! :(

The format currently presented is actually the default from _Now(). Then there are the various _Dat_* UDFs that return a DLL struct! If you get a string with _Date_Time_DOSDateTimeToStr() you still get MM/DD/YYYY. That appears to be more "standard" with AutoIt than _DateDiff() format.

I would welcome more input on what the most useful date/time format would be...

:)


Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law

Share this post


Link to post
Share on other sites
weaponx

I wish there was a standard datetime format that every function followed. Its frustrating to have to account for more than one.

Here is a good reference.

http://dev.mysql.com/doc/refman/5.0/en/datetime.html

Another cool trick with date/time format.

$oDateTime.Value = $oProc.CreationDate

With $oDateTime
    $FormatDate = StringFormat("%s/%s/%s %s:%s:%s",.Year, .Month, .Day, .Hours, .Minutes, .Seconds)
EndWith

You're supposed to be able to output $oDateTime.GetVarDate which should be local format but its always WMI format.

Edited by weaponx

Share this post


Link to post
Share on other sites
jennico

thank you very much for this sripty.

I would welcome more input on what the most useful date/time format would be...

from the practical point of wiev, the american date standard is the worst.

better is DD.MM.YYYY moreover because the time format also uses 24 hour format and not am/pm.

but the best way is to sort the format items by size: Year>Month>Day>Hour>Min>Sec. this is the correct scientific use and can be chronologically sorted (if used without separators) without converting it. moreover you can directly calculate the format, e.g. use the difference when converted to integer.

so i vote clearly for: YYYY-MM-DD HH:MM:SS or (why not?) YYYYMMDDHHMMSS

so now is: 20080604231804

cheers

j.

Edited by jennico

Spoiler

I actively support Wikileaks | Freedom for Julian Assange ! | Defend freedom of speech ! | Fight censorship ! | I will not silence.OixB7.jpgDon't forget this IP: 213.251.145.96

 

Share this post


Link to post
Share on other sites
amokoura

How about putting as many characters as the number indicates. Meaning that 20080604231804 would be

YYYYYYYYYYYYY... (2008 times)...YYYYYYYYYYYYYYYMMMMMMDDDDHHHHHHHHHHHHHHHHHHHHMMMMMMMMMMMMMMMMMMSSSS

It's boring that people think numbers are COOLER than characters. We need a revolution on this one.

Share this post


Link to post
Share on other sites
weaponx

How about putting as many characters as the number indicates. Meaning that 20080604231804 would be

YYYYYYYYYYYYY... (2008 times)...YYYYYYYYYYYYYYYMMMMMMDDDDHHHHHHHHHHHHHHHHHHHHMMMMMMMMMMMMMMMMMMSSSS

It's boring that people think numbers are COOLER than characters. We need a revolution on this one.

Why did you waste a post for that?

Share this post


Link to post
Share on other sites
jennico

How about putting as many characters as the number indicates. Meaning that 20080604231804 would be

YYYYYYYYYYYYY... (2008 times)...YYYYYYYYYYYYYYYMMMMMMDDDDHHHHHHHHHHHHHHHHHHHHMMMMMMMMMMMMMMMMMMSSSS

It's boring that people think numbers are COOLER than characters. We need a revolution on this one.

SPAM + IGNORANCE :)

Edited by jennico

Spoiler

I actively support Wikileaks | Freedom for Julian Assange ! | Defend freedom of speech ! | Fight censorship ! | I will not silence.OixB7.jpgDon't forget this IP: 213.251.145.96

 

Share this post


Link to post
Share on other sites
PsaltyDS

How about putting as many characters as the number indicates. Meaning that 20080604231804 would be

YYYYYYYYYYYYY... (2008 times)...YYYYYYYYYYYYYYYMMMMMMDDDDHHHHHHHHHHHHHHHHHHHHMMMMMMMMMMMMMMMMMMSSSS

It's boring that people think numbers are COOLER than characters. We need a revolution on this one.

That's brilliant. I hear The SCO Group is looking for developers, you should send them your resume.

:)


Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law

Share this post


Link to post
Share on other sites
NELyon

How about putting as many characters as the number indicates. Meaning that 20080604231804 would be

YYYYYYYYYYYYY... (2008 times)...YYYYYYYYYYYYYYYMMMMMMDDDDHHHHHHHHHHHHHHHHHHHHMMMMMMMMMMMMMMMMMMSSSS

It's boring that people think numbers are COOLER than characters. We need a revolution on this one.

Numbers are characters... just numerical characters... Totally irrelevant.

Share this post


Link to post
Share on other sites
duckling78

I have a memory leak in one of my programs and seemed to have narrowed it down to using this function. My program is a program to help testing programs we make that tracks memory, cpu and thread usage over time intervals. It uses _ProcessListProperties to check CPU usage for a specified executable.

Is there a memory leak? Here is an example script showing memory usage blowing up from calling this function.

(Paste the _ProcessListProperties function at the end of the script)

#include <array.au3>

While 1
    $array = _ProcessListProperties("AutoIt3.exe")  ;Replace "AutoIt3.exe" with any running process
    If IsArray($array) Then
        For $i = 0 To UBound($array, 2) - 1
            Switch $i
                Case 0
                    $string = $array[1][0]
                Case Else
                    $string = $string & @TAB & $array[1][$i]
            EndSwitch
        Next
        ConsoleWrite("_ArrayToString($array): " & $string & @CRLF)
    Else
        ConsoleWrite("$array: " & $array & @CRLF)
    EndIf
WEnd

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×