Skip to content

Instantly share code, notes, and snippets.

@Lorenzo501
Last active June 25, 2025 17:04
Show Gist options
  • Save Lorenzo501/66c5004c4f3826353132b86f0c142bf6 to your computer and use it in GitHub Desktop.
Save Lorenzo501/66c5004c4f3826353132b86f0c142bf6 to your computer and use it in GitHub Desktop.
/*
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