And yes i know i could just use a sysinternals utility to do SOME of this, by those utilities don't provide a clearly defined hierarchy of new/destroyed processes,which I require.
Default: Tooltip On, Logging Off
Just run it,then start & kill a process to see what it does... if still unclear...
Code: Select all
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases. ; #Warn ; Enable warnings to assist with detecting common errors. SendMode Input ; Recommended for new scripts due to its superior speed and reliability. SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory. #Persistent ;set to 1 to log to file specified log = 0 logFile = %A_Desktop%\process.log.log ;double 'logs'are a matter of personal prefrence, nothing more. toolTipToggle = 1 ;ToolTip on/off @ coords 0,0 SetTimer, OnNewProc, 50 OnNewProc: GetAllProcesses(procList, processCount) If (processCount != lastCount){ thisProcessAttrib := "" processList := "" ;check for created Processes for process in ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process"){ ; thisProcessAttrib := process.ProcessID "|" process.ParentProcessID "|" Round(process.WorkingSetSize/1000000) . " MB" "|" process.Name "|" process.CommandLine "`n" thisProcessAttrib := process.ProcessID "|" process.ParentProcessID "|" process.Name "|" process.CommandLine "`n" | process.SessionId "`n" processList .= thisProcessAttrib If (lastProcessList AND !InStr(lastProcessList, thisProcessAttrib )){ If !thisTimerCreatedProcesses thisTimerCreatedProcesses .= "====================================== Created Processes ====================================== `n" thisTimerCreatedProcesses .= GetAllParents(thisProcessAttrib) Gosub, processesCreatedDestroyed SetTimer, destroyNotif, 5000 } } ;check for destroyed Processes Loop, parse, lastProcessList, `n If (lastProcessList AND !InStr(processList, A_LoopField )){ If !thisTimerDestroyedProcesses thisTimerDestroyedProcesses .= "====================================== Destroyed Processes ====================================== `n" thisTimerDestroyedProcesses .= GetAllParents(A_LoopField) Gosub, processesCreatedDestroyed SetTimer, destroyNotif, 5000 } lastProcessList := processList lastCount := processCount } Return processesCreatedDestroyed: If toolTipToggle{ CoordMode, ToolTip, screen ToolTip, % thisTimerCreatedProcesses "`n`n" thisTimerDestroyedProcesses, 0, 0 } Return ;distroy all notifications 5seconds after last notification was appended to notifications destroyNotif: ToolTip If (log AND thisTimerCreatedProcesses OR log AND thisTimerDestroyedProcesses ){ ;log last record before resetting on timeout FileAppend, `n============================================================================= `n %A_Now% `n =============================================================================`n , %logFile% FileAppend, % thisTimerCreatedProcesses "`n`n" thisTimerDestroyedProcesses, %logFile% FileAppend, `n=========================================================================================================================================================================`n , %logFile% } thisTimerCreatedProcesses := "" thisTimerDestroyedProcesses := "" Return ^!Esc::ExitApp GetParentProcess(thisProcessAttrib,procList){ PID := ParsePID(thisProcessAttrib) ParentPID := ParseParentPID(thisProcessAttrib) Loop, parse, procList, `n { thisPID := ParsePID(A_LoopField) thisParentPID := ParseParentPID(A_LoopField) IfEqual, thisPID, %ParentPID% IfNotEqual, thisPID, %PID% Return A_LoopField } } ParsePID(attribs){ Loop, parse, attribs, `| IfEqual, A_Index, 1 Return A_LoopField } ParseParentPID(attribs){ Loop, parse, attribs, `| IfEqual, A_Index, 2 Return A_LoopField } ;takes current process field and then parses all parents of the process using parentPid in the field recursively until apex parent process is parsed,then formats the result using tabs. GetAllParents(thisProcessAttrib){ Global parentProcesses := "" IfInString, thisProcessAttrib, `n parentProcesses .= thisProcessAttrib Else ;active process is parsed from a list when checking if it's Destroyed so it lacks a line break present in new process checks so add a line break for suitable formatting. parentProcesses .= thisProcessAttrib . "`n" Loop{ ;poplulate 'parent tree' of current process ;get first parent IfEqual, A_Index, 1 ;on first loop initialie with current process attribs thisLoop_ParentProcess := GetParentProcess(thisProcessAttrib,lastProcessList) IfGreater, A_Index, 1 thisLoop_ParentProcess := GetParentProcess(thisLast_ParentProcess,lastProcessList) If !thisLoop_ParentProcess Break IfGreater, A_Index, 10 ;infinite loop safeguard Break ;save current parent for reference in upcoming Loop thisLast_ParentProcess := thisLoop_ParentProcess ;all parents IfNotInString, parentProcesses, %thisLoop_ParentProcess% ;safeguard against against duplication of processes in tree,occur otherwise due to method of obtaining list. parentProcesses .= thisLoop_ParentProcess "`n" } ; MsgBox, % parentProcesses ;Reorder & tab parent tree accordingly reorderedParents := "" Loop, parse, parentProcesses, `n ;prepend current to previous list reorderedParents := A_LoopField . "`n" . reorderedParents ; MsgBox, % reorderedParents formattedParents := "" Loop, parse, reorderedParents, `n ;apply tab formatting { thisLoopField = %A_LoopField% IfGreater, A_Index, 1 Loop, % A_Index - 2 thisLoopField := A_Tab . thisLoopField formattedParents .= thisLoopField "`n" } ; MsgBox, % formattedParents Return, formattedParents } /* Win32_Process class The Win32_Process WMI class represents a process on an operating system. The following syntax is simplified from Managed Object Format (MOF) code and includes all of the inherited properties. Syntax [Dynamic, Provider("CIMWin32"), SupportsCreate, CreateBy("Create"), SupportsDelete, DeleteBy("DeleteInstance"), UUID("{8502C4DC-5FBB-11D2-AAC1-006008C78BC7}"), DisplayName("Processes"), AMENDMENT] class Win32_Process : CIM_Process { string CreationClassName; string Caption; string CommandLine; datetime CreationDate; string CSCreationClassName; string CSName; string Description; string ExecutablePath; uint16 ExecutionState; string Handle; uint32 HandleCount; datetime InstallDate; uint64 KernelModeTime; uint32 MaximumWorkingSetSize; uint32 MinimumWorkingSetSize; string Name; string OSCreationClassName; string OSName; uint64 OtherOperationCount; uint64 OtherTransferCount; uint32 PageFaults; uint32 PageFileUsage; uint32 ParentProcessId; uint32 PeakPageFileUsage; uint64 PeakVirtualSize; uint32 PeakWorkingSetSize; uint32 Priority = NULL; uint64 PrivatePageCount; uint32 ProcessId; uint32 QuotaNonPagedPoolUsage; uint32 QuotaPagedPoolUsage; uint32 QuotaPeakNonPagedPoolUsage; uint32 QuotaPeakPagedPoolUsage; uint64 ReadOperationCount; uint64 ReadTransferCount; uint32 SessionId; string Status; datetime TerminationDate; uint32 ThreadCount; uint64 UserModeTime; uint64 VirtualSize; string WindowsVersion; uint64 WorkingSetSize; uint64 WriteOperationCount; uint64 WriteTransferCount; }; */ ;MsgBox, 0, %processCount% Processes, %processList% GetAllProcesses(ByRef l, ByRef c){ ;l = processList, c = processCount d := " | " ; string separator s := 4096 ; size of buffers and arrays (4 KB) l:="", c:="" ;to prevent memory leak Process, Exist ; sets ErrorLevel to the PID of this running script ; Get the handle of this script with PROCESS_QUERY_INFORMATION (0x0400) h := DllCall("OpenProcess", "UInt", 0x0400, "Int", false, "UInt", ErrorLevel, "Ptr") ; Open an adjustable access token with this process (TOKEN_ADJUST_PRIVILEGES = 32) DllCall("Advapi32.dll\OpenProcessToken", "Ptr", h, "UInt", 32, "PtrP", t) VarSetCapacity(ti, 16, 0) ; structure of privileges NumPut(1, ti, 0, "UInt") ; one entry in the privileges array... ; Retrieves the locally unique identifier of the debug privilege: DllCall("Advapi32.dll\LookupPrivilegeValue", "Ptr", 0, "Str", "SeDebugPrivilege", "Int64P", luid) NumPut(luid, ti, 4, "Int64") NumPut(2, ti, 12, "UInt") ; enable this privilege: SE_PRIVILEGE_ENABLED = 2 ; Update the privileges of this process with the new access token: r := DllCall("Advapi32.dll\AdjustTokenPrivileges", "Ptr", t, "Int", false, "Ptr", &ti, "UInt", 0, "Ptr", 0, "Ptr", 0) DllCall("CloseHandle", "Ptr", t) ; close this access token handle to save memory DllCall("CloseHandle", "Ptr", h) ; close this process handle to save memory hModule := DllCall("LoadLibrary", "Str", "Psapi.dll") ; increase performance by preloading the library s := VarSetCapacity(a, s) ; an array that receives the list of process identifiers: c := 0 ; counter for process idendifiers DllCall("Psapi.dll\EnumProcesses", "Ptr", &a, "UInt", s, "UIntP", r) Loop, % r // 4 ; parse array for identifiers as DWORDs (32 bits): { id := NumGet(a, A_Index * 4, "UInt") ; Open process with: PROCESS_VM_READ (0x0010) | PROCESS_QUERY_INFORMATION (0x0400) h := DllCall("OpenProcess", "UInt", 0x0010 | 0x0400, "Int", false, "UInt", id, "Ptr") if !h continue VarSetCapacity(n, s, 0) ; a buffer that receives the base name of the module: e := DllCall("Psapi.dll\GetModuleBaseName", "Ptr", h, "Ptr", 0, "Str", n, "UInt", A_IsUnicode ? s//2 : s) if !e ; fall-back method for 64-bit processes when in 32-bit mode: if e := DllCall("Psapi.dll\GetProcessImageFileName", "Ptr", h, "Str", n, "UInt", A_IsUnicode ? s//2 : s) SplitPath n, n DllCall("CloseHandle", "Ptr", h) ; close process handle to save memory if (n && e) ; if image is not null add to list: l .= n . d, c++ } DllCall("FreeLibrary", "Ptr", hModule) ; unload the library to free memory ;Sort, l, C ; uncomment this line to sort the list alphabetically }