Last active
June 25, 2025 17:04
-
-
Save Lorenzo501/66c5004c4f3826353132b86f0c142bf6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Windows API information. | |
********** ACCESSIBILITY ********** | |
Event Constants = https://learn.microsoft.com/en-us/windows/win32/winauto/event-constants | |
SetWinEventHook function = https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwineventhook | |
WinEventProc callback function = https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-wineventproc | |
UnhookWinEvent function = https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-unhookwinevent | |
********** PERFORMANCE COUNTER ********** | |
QueryPerformanceFrequency function = https://learn.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancefrequency | |
QueryPerformanceCounter function = https://learn.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter | |
*/ | |
#Requires AutoHotkey 2.1-alpha.9 ; For guiControl.SetCue | |
#SingleInstance Off ; Each process can have its own filter but the embedded INI stores only the latest filter changed by any process, overwriting both previously retained filter conditions | |
; To make the script work when an admin window is active | |
if (!InStr(A_AhkPath, "_UIA.exe")) | |
Run("*UIAccess " A_ScriptFullPath), ExitApp() | |
#NoTrayIcon | |
A_WinDelay := -1 ; For Dbg.__New | |
A_ControlDelay := -1 ; For ControlSetEnabled | |
F1::(ProcessWatcher.Instance._Hook) ? Dbg.Instance.Update() : Exit() ; Adds a point marker/blank line | |
; Hook/unhook with kbd led indicator (the led turns/stays on when hooking via this hotkey and turns/stays off when unhooking via either this hotkey or by clicking the GUI btn) | |
ScrollLock::(ProcessWatcher.Instance._Hook) ? (ProcessWatcher.Instance.Unhook(), SetScrollLockState("Off")) : (ProcessWatcher.Instance.Hook(), SetScrollLockState("On")) | |
; Deletes selected row in the listview of the filter dialog | |
~Delete:: | |
{ | |
if (WinActive(Dbg.Instance.FilterGui) && Dbg.Instance.FilterGui.FocusedCtrl = "" && ListViewGetContent("Count Selected", Dbg.Instance.FilterListView)) | |
{ | |
ProcessWatcher.Instance.WindowCriteriaArrays.RemoveAt(ListViewGetContent("Count Focused", Dbg.Instance.FilterListView)) | |
Dbg.Instance.IniWriteWindowCondition() | |
Dbg.Instance.IniWriteEventCondition() ; It has to store this too, so that the embedded INI retains both associated conditions of one process while multiple processes exist | |
Dbg.Instance.PopulateFilterListView() | |
} | |
} | |
#HotIf WinActive("WinEvent Monitor ahk_class AutoHotkeyGUI") | |
^c::A_Clipboard := Dbg.Instance.GetList("Selected") ; Ctrl+C to copy selected line(s) | |
class ProcessWatcher | |
{ | |
static Instance := ProcessWatcher() | |
__New() | |
{ | |
this.Events := Map(0x0001, "EVENT_SYSTEM_SOUND", 0x0002, "EVENT_SYSTEM_ALERT", 0x0003, "EVENT_SYSTEM_FOREGROUND", 0x0004, "EVENT_SYSTEM_MENUSTART", 0x0005, "EVENT_SYSTEM_MENUEND", 0x0006, "EVENT_SYSTEM_MENUPOPUPSTART", 0x0007, "EVENT_SYSTEM_MENUPOPUPEND", 0x0008, "EVENT_SYSTEM_CAPTURESTART", 0x0009, "EVENT_SYSTEM_CAPTUREEND", 0x000A, "EVENT_SYSTEM_MOVESIZESTART", 0x000B, "EVENT_SYSTEM_MOVESIZEEND", 0x000C, "EVENT_SYSTEM_CONTEXTHELPSTART", 0x000D, "EVENT_SYSTEM_CONTEXTHELPEND", 0x000E, "EVENT_SYSTEM_DRAGDROPSTART", 0x000F, "EVENT_SYSTEM_DRAGDROPEND", 0x0010, "EVENT_SYSTEM_DIALOGSTART", 0x0011, "EVENT_SYSTEM_DIALOGEND", 0x0012, "EVENT_SYSTEM_SCROLLINGSTART", 0x0013, "EVENT_SYSTEM_SCROLLINGEND", 0x0014, "EVENT_SYSTEM_SWITCHSTART", 0x0015, "EVENT_SYSTEM_SWITCHEND", 0x0016, "EVENT_SYSTEM_MINIMIZESTART", 0x0017, "EVENT_SYSTEM_MINIMIZEEND", 0x0020, "EVENT_SYSTEM_DESKTOPSWITCH", 0x00FF, "EVENT_SYSTEM_END", 0x0101, "EVENT_OEM_DEFINED_START", 0x01FF, "EVENT_OEM_DEFINED_END", 0x4E00, "EVENT_UIA_EVENTID_START", 0x4EFF, "EVENT_UIA_EVENTID_END", 0x7500, "EVENT_UIA_PROPID_START", 0x75FF, "EVENT_UIA_PROPID_END", 0x8000, "EVENT_OBJECT_CREATE", 0x8001, "EVENT_OBJECT_DESTROY", 0x8002, "EVENT_OBJECT_SHOW", 0x8003, "EVENT_OBJECT_HIDE", 0x8004, "EVENT_OBJECT_REORDER", 0x8005, "EVENT_OBJECT_FOCUS", 0x8006, "EVENT_OBJECT_SELECTION", 0x8007, "EVENT_OBJECT_SELECTIONADD", 0x8008, "EVENT_OBJECT_SELECTIONREMOVE", 0x8009, "EVENT_OBJECT_SELECTIONWITHIN", 0x800A, "EVENT_OBJECT_STATECHANGE", 0x800B, "EVENT_OBJECT_LOCATIONCHANGE", 0x800C, "EVENT_OBJECT_NAMECHANGE", 0x800D, "EVENT_OBJECT_DESCRIPTIONCHANGE", 0x800E, "EVENT_OBJECT_VALUECHANGE", 0x800F, "EVENT_OBJECT_PARENTCHANGE", 0x8010, "EVENT_OBJECT_HELPCHANGE", 0x8011, "EVENT_OBJECT_DEFACTIONCHANGE", 0x8012, "EVENT_OBJECT_ACCELERATORCHANGE", 0x8013, "EVENT_OBJECT_INVOKED", 0x8014, "EVENT_OBJECT_TEXTSELECTIONCHANGED", 0x8015, "EVENT_OBJECT_CONTENTSCROLLED", 0x8016, "EVENT_SYSTEM_ARRANGMENTPREVIEW", 0x8017, "EVENT_OBJECT_CLOAKED", 0x8018, "EVENT_OBJECT_UNCLOAKED", 0x8019, "EVENT_OBJECT_LIVEREGIONCHANGED", 0x8020, "EVENT_OBJECT_HOSTEDOBJECTSINVALIDATED", 0x8021, "EVENT_OBJECT_DRAGSTART", 0x8022, "EVENT_OBJECT_DRAGCANCEL", 0x8023, "EVENT_OBJECT_DRAGCOMPLETE", 0x8024, "EVENT_OBJECT_DRAGENTER", 0x8025, "EVENT_OBJECT_DRAGLEAVE", 0x8026, "EVENT_OBJECT_DRAGDROPPED", 0x8027, "EVENT_OBJECT_IME_SHOW", 0x8028, "EVENT_OBJECT_IME_HIDE", 0x8029, "EVENT_OBJECT_IME_CHANGE", 0x8030, "EVENT_OBJECT_TEXTEDIT_CONVERSIONTARGETCHANGED", 0x80FF, "EVENT_OBJECT_END", 0xA000, "EVENT_AIA_START", 0xAFFF, "EVENT_AIA_END") | |
this.EVENT_MIN := 0x00000001 | |
this.EVENT_MAX := 0x7FFFFFFF | |
this._Hook := 0 | |
this.Cb := CallbackCreate((params*) => (this.HandleWinEvent)(this, params*),, this.HandleWinEvent.MaxParams - 1) | |
; Parses the INI condition string | |
{ | |
this.WindowCriteriaArrays := [] | |
this.EventCriteriaMap := Map() | |
windowConditionLinesSplit := StrSplit(IniRead(A_ScriptFullPath, "WindowCondition"), "`n") | |
eventConditionLinesSplit := (iniEventCondition := IniRead(A_ScriptFullPath, "EventCondition")) = "*/" ? [] : StrSplit(StrSplit(iniEventCondition, "`n*/")[1], "`n") | |
for (iteratedLine in windowConditionLinesSplit) | |
{ | |
isWindowCriteriaUsable := !InStr(iteratedLine, "X",, 1, -1) | |
if (hwndPos := InStr(iteratedLine, "hWnd")) | |
{ | |
comparisonOperatorPos := hwndPos + StrLen("hWnd ") | |
shouldCaptureHwnd := SubStr(iteratedLine, comparisonOperatorPos, 1) = "=" | |
hwndPos := comparisonOperatorPos + StrLen(shouldCaptureHwnd ? "= " : "!= ") | |
hwndTrailingSpacePos := InStr(iteratedLine, " ",, hwndPos) | |
hWnd := SubStr(iteratedLine, hwndPos, hwndTrailingSpacePos - hwndPos) | |
} | |
if (windowTitlePos := InStr(iteratedLine, "windowTitle")) | |
{ | |
comparisonOperatorPos := windowTitlePos + StrLen("windowTitle ") | |
shouldCaptureWindowTitle := SubStr(iteratedLine, comparisonOperatorPos, 1) = "=" | |
windowTitlePos := comparisonOperatorPos + StrLen(shouldCaptureWindowTitle ? "= `"" : "!= `"") | |
windowTitleTrailingDoubleQuotePos := InStr(iteratedLine, "`"",, windowTitlePos) | |
windowTitle := SubStr(iteratedLine, windowTitlePos, windowTitleTrailingDoubleQuotePos - windowTitlePos) | |
} | |
if (windowClassPos := InStr(iteratedLine, "windowClass")) | |
{ | |
comparisonOperatorPos := windowClassPos + StrLen("windowClass ") | |
shouldCaptureWindowClass := SubStr(iteratedLine, comparisonOperatorPos, 1) = "=" | |
windowClassPos := comparisonOperatorPos + StrLen(shouldCaptureWindowClass ? "= `"" : "!= `"") | |
windowClassTrailingDoubleQuotePos := InStr(iteratedLine, "`"",, windowClassPos) | |
windowClass := SubStr(iteratedLine, windowClassPos, windowClassTrailingDoubleQuotePos - windowClassPos) | |
} | |
if (processPos := InStr(iteratedLine, "process")) | |
{ | |
comparisonOperatorPos := processPos + StrLen("process ") | |
shouldCaptureProcess := SubStr(iteratedLine, comparisonOperatorPos, 1) = "=" | |
processPos := comparisonOperatorPos + StrLen(shouldCaptureProcess ? "= `"" : "!= `"") | |
processTrailingDoubleQuotePos := InStr(iteratedLine, "`"",, processPos) | |
process := SubStr(iteratedLine, processPos, processTrailingDoubleQuotePos - processPos) | |
} | |
this.WindowCriteriaArrays.Push([isWindowCriteriaUsable, shouldCaptureHwnd?, hWnd?, shouldCaptureWindowTitle?, windowTitle?, shouldCaptureWindowClass?, windowClass? | |
, shouldCaptureProcess?, process?]) | |
shouldCaptureHwnd := hWnd := shouldCaptureWindowTitle := windowTitle := shouldCaptureWindowClass := windowClass := shouldCaptureProcess := process := unset | |
} | |
for (iteratedLine in eventConditionLinesSplit) | |
{ | |
eventPos := InStr(iteratedLine, "event") | |
comparisonOperatorPos := eventPos + StrLen("event ") | |
shouldCaptureSingleEvent := SubStr(iteratedLine, comparisonOperatorPos, 1) = "=" | |
eventPos := comparisonOperatorPos + StrLen(shouldCaptureSingleEvent ? "= " : ">= ") | |
eventTrailingSpacePos := InStr(iteratedLine, " ",, eventPos) | |
eventSingleOrRangeStartHex := SubStr(iteratedLine, eventPos, (eventTrailingSpacePos ? eventTrailingSpacePos - eventPos : unset)) | |
eventRangeEndHex := (eventTrailingSpacePos ? SubStr(iteratedLine, InStr(iteratedLine, " ",, eventPos, 4) + 1) : "") | |
this.EventCriteriaMap[eventSingleOrRangeStartHex] := (eventRangeEndHex?) | |
} | |
} | |
} | |
Hook() | |
{ | |
this.Index := 0 | |
Dbg.Instance.Reset(1) | |
this._Hook := DllCall("SetWinEventHook", "UInt", this.EVENT_MIN, "UInt", this.EVENT_MAX, "Ptr", 0, "Ptr", this.Cb, "UInt", 0, "UInt", 0, "UInt", 0, "Ptr") | |
DllCall("QueryPerformanceFrequency", "Int64*", &Qpf := 0), this.Qpf := Qpf | |
DllCall("QueryPerformanceCounter", "Int64*", &QpcPrevious := 0), this.QpcPrevious := QpcPrevious | |
} | |
Unhook() | |
{ | |
DllCall("UnhookWinEvent", "Ptr", this._Hook) | |
Dbg.Instance.Reset(2) | |
this._Hook := 0 | |
} | |
HandleWinEvent(hWinEventHook, event, hWnd, *) | |
{ | |
try | |
if | |
( | |
WinGetPID(A_ScriptHwnd) != WinGetPID(hWnd) && | |
DetermineIsEventAllowedByFilter(&windowTitle, &windowClass, &process) | |
) | |
{ | |
eventName := this.GetEventName(event, &eventHex) | |
DllCall("QueryPerformanceCounter", "Int64*", &qpc := 0) | |
Dbg.Instance.Update(++this.Index, Round((qpc - this.QpcPrevious) / this.Qpf * 1000), eventHex, eventName, hWnd, windowTitle?, windowClass?, process?) | |
this.QpcPrevious := qpc | |
} | |
DetermineIsEventAllowedByFilter(&windowTitle, &windowClass, &process) | |
{ | |
windowTitle := WinGetTitle(hWnd) | |
windowClass := WinGetClass(hWnd) | |
process := WinGetProcessName(hWnd) | |
usableWindowCriteriaArrayAmount := 0 | |
isEventAllowedByWindowCondition := false | |
isEventAllowedByEventCondition := false | |
if (ProcessWatcher.Instance.WindowCriteriaArrays.Length = 0 && ProcessWatcher.Instance.EventCriteriaMap.Count = 0) | |
return true | |
; Evaluate dynamically constructed window condition | |
for (iteratedWindowCriteriaArray in ProcessWatcher.Instance.WindowCriteriaArrays) | |
{ | |
if (iteratedWindowCriteriaArray[1]) | |
++usableWindowCriteriaArrayAmount | |
else | |
continue | |
if (iteratedWindowCriteriaArray.Has(2)) | |
isEventAllowedByWindowCondition := iteratedWindowCriteriaArray[2] ? hWnd = iteratedWindowCriteriaArray[3] : hWnd != iteratedWindowCriteriaArray[3] | |
if (iteratedWindowCriteriaArray.Has(4)) | |
{ | |
if (iteratedWindowCriteriaArray.Has(2)) | |
isEventAllowedByWindowCondition := isEventAllowedByWindowCondition && | |
(iteratedWindowCriteriaArray[4] ? windowTitle = iteratedWindowCriteriaArray[5] : windowTitle != iteratedWindowCriteriaArray[5]) | |
else | |
isEventAllowedByWindowCondition := iteratedWindowCriteriaArray[4] ? windowTitle = iteratedWindowCriteriaArray[5] : windowTitle != iteratedWindowCriteriaArray[5] | |
} | |
if (iteratedWindowCriteriaArray.Has(6)) | |
{ | |
if (iteratedWindowCriteriaArray.Has(2) || iteratedWindowCriteriaArray.Has(4)) | |
isEventAllowedByWindowCondition := isEventAllowedByWindowCondition && | |
(iteratedWindowCriteriaArray[6] ? windowClass = iteratedWindowCriteriaArray[7] : windowClass != iteratedWindowCriteriaArray[7]) | |
else | |
isEventAllowedByWindowCondition := iteratedWindowCriteriaArray[6] ? windowClass = iteratedWindowCriteriaArray[7] : windowClass != iteratedWindowCriteriaArray[7] | |
} | |
if (iteratedWindowCriteriaArray.Has(8)) | |
{ | |
if (iteratedWindowCriteriaArray.Has(2) || iteratedWindowCriteriaArray.Has(4) || iteratedWindowCriteriaArray.Has(6)) | |
isEventAllowedByWindowCondition := isEventAllowedByWindowCondition && | |
(iteratedWindowCriteriaArray[8] ? process = iteratedWindowCriteriaArray[9] : process != iteratedWindowCriteriaArray[9]) | |
else | |
isEventAllowedByWindowCondition := iteratedWindowCriteriaArray[8] ? process = iteratedWindowCriteriaArray[9] : process != iteratedWindowCriteriaArray[9] | |
} | |
if (isEventAllowedByWindowCondition) | |
break | |
} | |
; Evaluate dynamically constructed event condition | |
for (iteratedKey, iteratedValue in ProcessWatcher.Instance.EventCriteriaMap) | |
{ | |
if (usableWindowCriteriaArrayAmount = 0 || isEventAllowedByWindowCondition) | |
{ | |
if (iteratedValue = "") | |
{ | |
if (event = iteratedKey) | |
return true | |
} | |
else if (event >= iteratedKey && event <= iteratedValue) | |
return true | |
} | |
else | |
return false | |
} | |
return isEventAllowedByWindowCondition && isEventAllowedByEventCondition | |
} | |
} | |
GetEventName(event, &eventHex?) | |
{ | |
eventHex := Format("0x{1:04X}", event) | |
if (this.Events.Has(event)) | |
return this.Events[event] | |
if (event >= 0x0001 && event <= 0x00FF) | |
return "EVENT_SYSTEM_UNKNOWN" | |
if (event >= 0x0101 && event <= 0x01FF) | |
return "EVENT_OEM_UNKNOWN" | |
if (event >= 0x4E00 && event <= 0x4EFF) | |
return "EVENT_UIA_EVENTID_UNKNOWN" | |
if (event >= 0x7500 && event <= 0x75FF) | |
return "EVENT_UIA_PROPID_UNKNOWN" | |
if (event >= 0x8000 && event <= 0x80FF) | |
return "EVENT_OBJECT_UNKNOWN" | |
if (event >= 0xA000 && event <= 0xAFFF) | |
return "EVENT_AIA_UNKNOWN" | |
return "EVENT_UNKNOWN" | |
} | |
} | |
class Dbg | |
{ | |
static Instance := Dbg() | |
__New() | |
{ | |
header := " # | Interval (ms) | Event | Event Name | hWnd (dec) | Window Title | Window Class | Process" | |
this._Gui := Gui("-Theme", "WinEvent Monitor") | |
this._Gui.OnEvent("Close", (*) => ExitApp()) | |
this._Gui.SetFont(, "Consolas") | |
defaultButton := this._Gui.AddButton("Default", "Hook/Unhook <SᴄʀLᴋ>") | |
defaultButton.OnEvent("Click", (*) => ((ProcessWatcher.Instance._Hook ? ProcessWatcher.Instance.Unhook() : ProcessWatcher.Instance.Hook()) | |
, !ProcessWatcher.Instance._Hook ? SetScrollLockState("Off") : Exit())) | |
this._Gui.AddButton("x+6", "Filter").OnEvent("Click", (*) => (FocusDefaultButton(), Sleep(10), ControlFocus(this.FilterListView), this.FilterGui.Show())) | |
this._Gui.AddButton("x+6", "Always On Top").OnEvent("Click", (*) => (ToggleAlwaysOnTop(), FocusDefaultButton())) | |
pointMarkerButton := this._Gui.AddButton("x+6 w131", "Point Marker <F1>") | |
pointMarkerButton.OnEvent("Click", (*) => (ProcessWatcher.Instance._Hook ? (Dbg.Instance.Update(), FocusDefaultButton()) : FocusDefaultButton())) | |
pointMarkerIcon := LoadPicture("C:\Windows\system32\ActionCenterCPL.dll", "w16 h16 Icon" 1, &imgType) | |
SendMessage(0xf7, 1, pointMarkerIcon,, pointMarkerButton.Hwnd) | |
this._Gui.AddButton("x+6", "Export To TXT").OnEvent("Click", (*) => (ExportToTextFile(), FocusDefaultButton())) | |
this.ListView := this._Gui.AddListView("x10 w1115 h1080 LV0x10000 Count1000", StrSplit(header, " | ")) | |
this.ListView.OnEvent("Click", (*) => FocusDefaultButton()) | |
this.ListView.OnEvent("ColClick", (_, sortedColNumber) => (FocusDefaultButton(), SaveColSortState(sortedColNumber))) | |
for (iteratedColNumber in [1, 2, 3, 5]) | |
this.ListView.ModifyCol(iteratedColNumber, "Integer") | |
for (iteratedColNumber in [1, 2, 5]) | |
this.ListView.ModifyCol(iteratedColNumber, "Right") | |
for (iteratedColNumber in [6, 7, 8]) | |
this.ListView.ModifyCol(iteratedColNumber, 216) | |
CreateFilterDialog() | |
this._Gui.Show() | |
; Hooks in a new thread when ScrollLock dbg mode is on (because hooking uses the Dbg singleton indirectly which this constructor cannot use) | |
if (GetKeyState("ScrollLock", "T")) | |
SetTimer(() => ProcessWatcher.Instance.Hook(), -1) | |
ToggleAlwaysOnTop() => WinSetAlwaysOnTop(-1, this._Gui) | |
FocusDefaultButton() => PostMessage(0x0028, defaultButton.Hwnd, true) ; 0x0028 = WM_NEXTDLGCTL | |
ExportToTextFile() | |
{ | |
filePath := A_Desktop "\logger.txt" | |
try FileDelete(filePath) | |
FileAppend(this.GetList(), filePath) | |
Msgbox("Exported to " filePath,, 0x40000) | |
} | |
SaveColSortState(sortedColNumber) | |
{ | |
this.ColSortOrder := this.SortedColNumber ?? false && (this.SortedColNumber = sortedColNumber ? (this.ColSortOrder = "" ? "Desc" : "") : "") | |
this.SortedColNumber := SortedColNumber | |
} | |
CreateFilterDialog() | |
{ | |
this.FilterGui := Gui("-Theme +Owner" this._Gui.Hwnd, "WinEvent Filter") | |
this.FilterGui.SetFont("s9", "Consolas") | |
hwndEdit := this.FilterGui.AddEdit("w216 Number") | |
hwndEdit.SetCue("hWnd (dec)", true) | |
winTitleEdit := this.FilterGui.AddEdit("x+6 w216") | |
winTitleEdit.SetCue("Window Title", true) | |
winClassEdit := this.FilterGui.AddEdit("x+6 w216") | |
winClassEdit.SetCue("Window Class", true) | |
processEdit := this.FilterGui.AddEdit("x+6 w216") | |
processEdit.SetCue("Process", true) | |
this.FilterGui.AddButton("x+6 h22", "Submit").OnEvent("Click", ProcessUserInput) | |
captureHwndRadio := this.FilterGui.AddRadio("x12 y+5 Checked", "Capture") | |
discardHwndRadio := this.FilterGui.AddRadio("x+7", "Discard") | |
captureTitleRadio := this.FilterGui.AddRadio("x+69 Checked Group", "Capture") | |
discardTitleRadio := this.FilterGui.AddRadio("x+7", "Discard") | |
captureClassRadio := this.FilterGui.AddRadio("x+69 Checked Group", "Capture") | |
discardClassRadio := this.FilterGui.AddRadio("x+7", "Discard") | |
captureProcessRadio := this.FilterGui.AddRadio("x+69 Checked Group", "Capture") | |
discardProcessRadio := this.FilterGui.AddRadio("x+7", "Discard") | |
this.FilterGui.AddGroupBox("x+136 y8 w725 h910 Section", "Filtered (All By Default)") | |
minMaxEventRangeCheckbox := this.FilterGui.AddCheckbox("xs+9 ys+26 v0x00000001-0x7FFFFFFF", "EVENT_MIN / EVENT_MAX") | |
minMaxEventRangeCheckbox.OnEvent("Click", ChangeFilteredWinEvents) | |
; Adds the rest of the checkboxes | |
for (iteratedKey, iteratedValue in ProcessWatcher.Instance.Events) | |
{ | |
iteratedKey := Format("0x{1:04X}", iteratedKey) | |
if (InStr(iteratedValue, "_START")) | |
eventRangeStartKey := iteratedKey, eventRangeStartValue := iteratedValue, --A_Index | |
else if (!Mod(A_Index, 2)) | |
{ | |
if (IsSet(eventRangeStartKey)) | |
{ | |
iteratedCheckbox := this.FilterGui.AddCheckbox("xs+9 yp+26 v" eventRangeStartKey "-" iteratedKey, eventRangeStartValue " / " iteratedValue) | |
iteratedCheckbox.OnEvent("Click", ChangeFilteredWinEvents) | |
if (ProcessWatcher.Instance.EventCriteriaMap.Has(eventRangeStartKey)) | |
iteratedCheckbox.Value := true | |
eventRangeStartKey := unset, eventRangeStartValue := unset | |
} | |
else | |
{ | |
iteratedCheckbox := this.FilterGui.AddCheckbox("xs+9 yp+26 v" iteratedKey, iteratedValue) | |
iteratedCheckbox.OnEvent("Click", ChangeFilteredWinEvents) | |
if (ProcessWatcher.Instance.EventCriteriaMap.Has(iteratedKey)) | |
iteratedCheckbox.Value := true | |
} | |
} | |
else if (IsSet(eventRangeStartKey)) | |
{ | |
iteratedCheckbox := this.FilterGui.AddCheckbox("xs+369 yp v" eventRangeStartKey "-" iteratedKey, eventRangeStartValue " / " iteratedValue) | |
iteratedCheckbox.OnEvent("Click", ChangeFilteredWinEvents) | |
if (ProcessWatcher.Instance.EventCriteriaMap.Has(eventRangeStartKey)) | |
iteratedCheckbox.Value := true | |
eventRangeStartKey := unset, eventRangeStartValue := unset | |
} | |
else | |
{ | |
iteratedCheckbox := this.FilterGui.AddCheckbox("xs+369 yp v" iteratedKey, iteratedValue) | |
iteratedCheckbox.OnEvent("Click", ChangeFilteredWinEvents) | |
if (ProcessWatcher.Instance.EventCriteriaMap.Has(iteratedKey)) | |
iteratedCheckbox.Value := true | |
} | |
} | |
if (ProcessWatcher.Instance.EventCriteriaMap.Has("0x00000001")) | |
minMaxEventRangeCheckbox.Value := true, ToggleMinMaxEventRangeCheckbox() | |
this.FilterListView := this.FilterGui.AddListView("x10 y60 w948 h858 -Hdr Checked LV0x10000 Count50 LV0x40 LV0x800 -Multi", [""]) | |
this.FilterListView.OnEvent("Focus", (*) => DllCall("SetFocus", "Ptr", 0)) | |
this.FilterListView.OnEvent("ItemCheck", ChangeWindowCriteriaUsableStatus) | |
this.FilterListView.OnEvent("ItemSelect", InsertSelectedFilterWindowCriteria) | |
this.PopulateFilterListView() | |
this.FilterGui.Show("w1702 Hide") | |
ProcessUserInput(*) | |
{ | |
hWnd := ControlGetText(hwndEdit) | |
winTitle := ControlGetText(winTitleEdit) | |
winClass := ControlGetText(winClassEdit) | |
process := ControlGetText(processEdit) | |
if (hWnd || winTitle || winClass || process) | |
{ | |
; This will either edit or create window criteria, depending on whether a row was selected in the listview of the filter dialog | |
if (ListViewGetContent("Count Selected", this.FilterListView)) | |
{ | |
selectedFilterListViewRow := ListViewGetContent("Count Focused", this.FilterListView) | |
ProcessWatcher.Instance.WindowCriteriaArrays[selectedFilterListViewRow][2] := hWnd ? ControlGetChecked(captureHwndRadio) : unset | |
ProcessWatcher.Instance.WindowCriteriaArrays[selectedFilterListViewRow][3] := hWnd ? hWnd : unset | |
ProcessWatcher.Instance.WindowCriteriaArrays[selectedFilterListViewRow][4] := winTitle ? ControlGetChecked(captureTitleRadio) : unset | |
ProcessWatcher.Instance.WindowCriteriaArrays[selectedFilterListViewRow][5] := winTitle ? winTitle : unset | |
ProcessWatcher.Instance.WindowCriteriaArrays[selectedFilterListViewRow][6] := winClass ? ControlGetChecked(captureClassRadio) : unset | |
ProcessWatcher.Instance.WindowCriteriaArrays[selectedFilterListViewRow][7] := winClass ? winClass : unset | |
ProcessWatcher.Instance.WindowCriteriaArrays[selectedFilterListViewRow][8] := process ? ControlGetChecked(captureProcessRadio) : unset | |
ProcessWatcher.Instance.WindowCriteriaArrays[selectedFilterListViewRow][9] := process ? process : unset | |
} | |
else | |
ProcessWatcher.Instance.WindowCriteriaArrays.Push([true | |
, (hWnd ? ControlGetChecked(captureHwndRadio) : unset) | |
, (hWnd ? hWnd : unset) | |
, (winTitle ? ControlGetChecked(captureTitleRadio) : unset) | |
, (winTitle ? winTitle : unset) | |
, (winClass ? ControlGetChecked(captureClassRadio) : unset) | |
, (winClass ? winClass : unset) | |
, (process ? ControlGetChecked(captureProcessRadio) : unset) | |
, (process ? process : unset)]) | |
this.IniWriteWindowCondition() | |
this.IniWriteEventCondition() ; It has to store this too, so that the embedded INI retains both associated conditions of one process while multiple processes exist | |
this.PopulateFilterListView() | |
} | |
} | |
ChangeFilteredWinEvents(checkbox, *) | |
{ | |
; This will either enable or disable all other checkboxes when the min-max event range checkbox is clicked | |
if (checkbox.Text = "EVENT_MIN / EVENT_MAX") | |
ToggleMinMaxEventRangeCheckbox() | |
eventSingleOrRange := StrSplit(checkbox.Name, "-") | |
; This will either add or remove the event criteria that's associated with the clicked checkbox | |
if (checkbox.Value) | |
ProcessWatcher.Instance.EventCriteriaMap[eventSingleOrRange[1]] := eventSingleOrRange.Has(2) ? eventSingleOrRange[2] : "" | |
else | |
ProcessWatcher.Instance.EventCriteriaMap.Delete(eventSingleOrRange[1]) | |
this.IniWriteWindowCondition() ; It has to store this too, so that the embedded INI retains both associated conditions of one process while multiple processes exist | |
this.IniWriteEventCondition() | |
} | |
ToggleMinMaxEventRangeCheckbox() | |
{ | |
for (, iteratedValue in ProcessWatcher.Instance.Events) | |
{ | |
if (InStr(iteratedValue, "_START")) | |
eventRangeStartValue := iteratedValue | |
else if (IsSet(eventRangeStartValue)) | |
{ | |
ControlSetEnabled(!minMaxEventRangeCheckbox.Value, eventRangeStartValue " / " iteratedValue, this.FilterGui) | |
eventRangeStartValue := unset | |
} | |
else | |
ControlSetEnabled(!minMaxEventRangeCheckbox.Value, iteratedValue, this.FilterGui) | |
} | |
} | |
ChangeWindowCriteriaUsableStatus(_, affectedRow, isChecked) | |
{ | |
ProcessWatcher.Instance.WindowCriteriaArrays[affectedRow][1] := isChecked | |
this.IniWriteWindowCondition() | |
this.IniWriteEventCondition() ; It has to store this too, so that the embedded INI retains both associated conditions of one process while multiple processes exist | |
} | |
InsertSelectedFilterWindowCriteria(_, selectedRow, hasSelection) | |
{ | |
if (!hasSelection) | |
return | |
ProcessWatcher.Instance.WindowCriteriaArrays[selectedRow][2] ?? true ? captureHwndRadio.Value := 1 : discardHwndRadio.Value := 1 | |
ControlSetText(ProcessWatcher.Instance.WindowCriteriaArrays[selectedRow][3] ?? "", hwndEdit) | |
ProcessWatcher.Instance.WindowCriteriaArrays[selectedRow][4] ?? true ? captureTitleRadio.Value := 1 : discardTitleRadio.Value := 1 | |
ControlSetText(ProcessWatcher.Instance.WindowCriteriaArrays[selectedRow][5] ?? "", winTitleEdit) | |
ProcessWatcher.Instance.WindowCriteriaArrays[selectedRow][6] ?? true ? captureClassRadio.Value := 1 : discardClassRadio.Value := 1 | |
ControlSetText(ProcessWatcher.Instance.WindowCriteriaArrays[selectedRow][7] ?? "", winClassEdit) | |
ProcessWatcher.Instance.WindowCriteriaArrays[selectedRow][8] ?? true ? captureProcessRadio.Value := 1 : discardProcessRadio.Value := 1 | |
ControlSetText(ProcessWatcher.Instance.WindowCriteriaArrays[selectedRow][9] ?? "", processEdit) | |
} | |
} | |
} | |
Update(params_text*) | |
{ | |
if (params_text.Length > 0) | |
params_text[1] := params_text[1] " " | |
else | |
params_text.Push(++ProcessWatcher.Instance.Index " ") | |
if (GetKeyState("LButton", "P") && WinActive(this._Gui.Hwnd)) | |
this.ListView.Add(, params_text*) | |
else | |
this.ListView.Modify(this.ListView.Add(, params_text*), "+Vis") | |
loop (5) | |
this.ListView.ModifyCol(A_Index, "AutoHdr") | |
try this.ListView.ModifyCol(this.SortedColNumber, "Sort" this.ColSortOrder) | |
} | |
Reset(mode) | |
{ | |
if (mode = 1) | |
{ | |
this.ListView.Delete() | |
this._Gui.Title := "WinEvent Monitor (hooked)" | |
} | |
else if (mode = 2) | |
this._Gui.Title := "WinEvent Monitor (unhooked)" | |
} | |
GetList(mode?) | |
{ | |
if (IsSet(mode) && mode != "Selected") | |
throw ValueError("Parameter invalid, leave unset to return all rows or use `"Selected`" to return those", -1, mode) | |
nextSelectedRowNumber := 0 | |
colOne := [], colTwo := [], colThree := [], colFour := [], colFive := [], colSix := [], colSeven := [], colEight := [] | |
; Initial column lengths determined by header column analysation | |
colOneLength := StrLen(LTrim(this.ListView.GetText(0))) | |
colTwoLength := StrLen(this.ListView.GetText(0, 2)) | |
colThreeLength := StrLen(RTrim(this.ListView.GetText(0, 3))) | |
colFourLength := StrLen(RTrim(this.ListView.GetText(0, 4))) | |
colFiveLength := StrLen(this.ListView.GetText(0, 5)) | |
colSixLength := StrLen(this.ListView.GetText(0, 6)) | |
colSevenLength := StrLen(this.ListView.GetText(0, 7)) | |
colEightLength := StrLen(this.ListView.GetText(0, 8)) | |
; Filling column arrays with either all rows or all selected rows | |
loop (this.ListView.GetCount(mode?)) | |
{ | |
nextSelectedRowNumber := this.ListView.GetNext(nextSelectedRowNumber) | |
invisibleCharRegEx := "[\x{0000}-\x{001F}\x{007F}-\x{009F}\x{200B}-\x{200F}\x{202A}-\x{202E}\x{2060}-\x{206F}\x{FEFF}]" | |
rowNumber := IsSet(mode) ? nextSelectedRowNumber : A_Index | |
colOne.Push(this.ListView.GetText(rowNumber)) | |
colTwo.Push(this.ListView.GetText(rowNumber, 2)) | |
colThree.Push(this.ListView.GetText(rowNumber, 3)) | |
colFour.Push(this.ListView.GetText(rowNumber, 4)) | |
colFive.Push(this.ListView.GetText(rowNumber, 5)) | |
colSix.Push(RegExReplace(this.ListView.GetText(rowNumber, 6), invisibleCharRegEx)) | |
colSeven.Push(RegExReplace(this.ListView.GetText(rowNumber, 7), invisibleCharRegEx)) | |
colEight.Push(RegExReplace(this.ListView.GetText(rowNumber, 8), invisibleCharRegEx)) | |
} | |
; Final column lengths determined by row column analysation | |
loop (this.ListView.GetCount(mode?)) | |
{ | |
if ((iteratedColLength := StrLen(RTrim(colOne[A_Index]))) > colOneLength) | |
colOneLength := iteratedColLength | |
if ((iteratedColLength := StrLen(colTwo[A_Index])) > colTwoLength) | |
colTwoLength := iteratedColLength | |
if ((iteratedColLength := StrLen(colThree[A_Index])) > colThreeLength) | |
colThreeLength := iteratedColLength | |
if ((iteratedColLength := StrLen(colFour[A_Index])) > colFourLength) | |
colFourLength := iteratedColLength | |
if ((iteratedColLength := StrLen(colFive[A_Index])) > colFiveLength) | |
colFiveLength := iteratedColLength | |
if ((iteratedColLength := StrLen(colSix[A_Index])) > colSixLength) | |
colSixLength := iteratedColLength | |
if ((iteratedColLength := StrLen(colSeven[A_Index])) > colSevenLength) | |
colSevenLength := iteratedColLength | |
if ((iteratedColLength := StrLen(colEight[A_Index])) > colEightLength) | |
colEightLength := iteratedColLength | |
} | |
; Creating formatted header | |
listToReturn := Format(" {1:" colOneLength "} | {2:" colTwoLength "} | {3:-" colThreeLength "} | {4:-" colFourLength "} | {5:" colFiveLength "} | {6:-" colSixLength "} | " | |
"{7:-" colSevenLength "} | {8:-" colEightLength "} `n", "#", "Interval (ms)", "Event", "Event Name", "hWnd (dec)", "Window Title", "Window Class", "Process") | |
; Creating formatted rows | |
loop (this.ListView.GetCount(mode?)) | |
listToReturn .= Format(" {1:" colOneLength "} | {2:" colTwoLength "} | {3:-" colThreeLength "} | {4:-" colFourLength "} | {5:" colFiveLength "} | {6:-" colSixLength "} | " | |
"{7:-" colSevenLength "} | {8}`n", RTrim(colOne[A_Index]), colTwo[A_Index], colThree[A_Index], colFour[A_Index], colFive[A_Index], colSix[A_Index], colSeven[A_Index] | |
, colEight[A_Index]) | |
return listToReturn | |
} | |
IniWriteWindowCondition() | |
{ | |
iniWindowCondition := ProcessWatcher.Instance.WindowCriteriaArrays.Length >= 1 && !ProcessWatcher.Instance.WindowCriteriaArrays[1][1] ? "X=" : "=" | |
; Writes the INI window condition string | |
for (iteratedWindowCriteriaArray in ProcessWatcher.Instance.WindowCriteriaArrays) | |
{ | |
if (iniWindowCondition != "X=" && iniWindowCondition != "=") | |
iniWindowCondition .= !iteratedWindowCriteriaArray[1] ? "`nX=OR " : "`n=OR " | |
if (iteratedWindowCriteriaArray.Has(2)) | |
iniWindowCondition .= "hWnd " (iteratedWindowCriteriaArray[2] ? "= " : "!= ") iteratedWindowCriteriaArray[3] | |
if (iteratedWindowCriteriaArray.Has(4)) | |
iniWindowCondition .= (iteratedWindowCriteriaArray.Has(2) ? " AND " : "") "windowTitle " (iteratedWindowCriteriaArray[4] ? "=" : "!=") " `"" | |
. iteratedWindowCriteriaArray[5] "`"" | |
if (iteratedWindowCriteriaArray.Has(6)) | |
iniWindowCondition .= (iteratedWindowCriteriaArray.Has(2) || iteratedWindowCriteriaArray.Has(4) ? " AND " : "") "windowClass " | |
. (iteratedWindowCriteriaArray[6] ? "=" : "!=") " `"" iteratedWindowCriteriaArray[7] "`"" | |
if (iteratedWindowCriteriaArray.Has(8)) | |
iniWindowCondition .= (iteratedWindowCriteriaArray.Has(2) || iteratedWindowCriteriaArray.Has(4) || iteratedWindowCriteriaArray.Has(6) ? " AND " : "") "process " | |
. (iteratedWindowCriteriaArray[8] ? "=" : "!=") " `"" iteratedWindowCriteriaArray[9] "`"" | |
} | |
if (iniWindowCondition = "=") | |
IniWrite("", A_ScriptFullPath, "WindowCondition") | |
else | |
IniWrite(iniWindowCondition, A_ScriptFullPath, "WindowCondition") | |
} | |
IniWriteEventCondition() | |
{ | |
iniEventCondition := "=" | |
; Writes the INI event condition string | |
for (iteratedKey, iteratedValue in ProcessWatcher.Instance.EventCriteriaMap) | |
{ | |
if (iniEventCondition != "=") | |
iniEventCondition .= "`n=OR " | |
if (iteratedValue = "") | |
iniEventCondition .= "event = " iteratedKey | |
else | |
iniEventCondition .= "event >= " iteratedKey " AND event <= " iteratedValue | |
} | |
IniWrite(iniEventCondition, A_ScriptFullPath, "EventCondition") | |
} | |
PopulateFilterListView() | |
{ | |
windowConditionLinesSplit := StrSplit(IniRead(A_ScriptFullPath, "WindowCondition"), "`n", "=") | |
this.FilterListView.Delete() | |
for (iteratedLine in windowConditionLinesSplit) | |
{ | |
if (InStr(iteratedLine, "X",, 1, -1)) | |
{ | |
iteratedLine := SubStr(iteratedLine, 3) ; Removes the window-condition-not-in-use marker | |
filterListViewRowOption := "" | |
} | |
else | |
filterListViewRowOption := "Check" | |
this.FilterListView.Add(filterListViewRowOption, iteratedLine) | |
} | |
} | |
} | |
/* | |
[WindowCondition] | |
X=windowClass != "SysListView32" | |
=OR windowClass != "SysListView32" AND process = "explorer.exe" | |
X=OR process = "chrome.exe" | |
X=OR process = "vlc.exe" | |
X=OR process = "LogonUI.exe" | |
X=OR process = "CalculatorApp.exe" | |
X=OR process = "AutoHotkey64.exe" | |
X=OR process = "SoftwareIdeasModeler.exe" | |
X=OR windowTitle = "Software Ideas Modeler`s" | |
X=OR process = "olk.exe" | |
[EventCondition] | |
=event >= 0x00000001 AND event <= 0x7FFFFFFF | |
=OR event = 0x0003 | |
=OR event = 0x8000 | |
=OR event = 0x8001 | |
=OR event = 0x8002 | |
=OR event = 0x8003 | |
=OR event = 0x8005 | |
=OR event = 0x800A | |
=OR event = 0x800B | |
=OR event = 0x800C | |
=OR event = 0x8017 | |
=OR event = 0x8018 | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment