; ================================================ ; ClipboardFusion AppExceptions Registry Monitor ; AHK v2 - Final corrected version ; ================================================ #Requires AutoHotkey v2.0 #SingleInstance Force Persistent ; ========== CONFIGURATION ========== RegPath := "HKEY_CURRENT_USER\Software\Binary Fortress Software\ClipboardFusion\AppExceptions" PollInterval := 10000 LogFile := A_ScriptDir "\ClipboardFusion_AppExceptions_Monitor.log" ; ========== GLOBALS ========== knownSubkeys := Map() isPaused := false ; ========== TRAY MENU ========== Tray := A_TrayMenu Tray.Delete() Tray.Add("Force Check Now", ForceCheck) Tray.Add("View Log File", ViewLog) Tray.Add("Backup Settings Now", BackupSettings) Tray.Add() Tray.Add("Pause Monitoring", TogglePause) Tray.Add("Exit", (*) => ExitApp()) TraySetIcon("shell32.dll", 23) ; ========== STARTUP ========== LogEvent("STARTUP", "Monitor started. Watching: " RegPath) TrayTip("Monitoring AppExceptions folder...`nRight-click the tray icon for options.", "ClipboardFusion Monitor", "Iconi") CheckRegistry(true) SetTimer(CheckRegistry, PollInterval) ; ========== MAIN FUNCTION ========== CheckRegistry(initial := false) { global knownSubkeys, RegPath, LogFile, isPaused if (isPaused) return current := GetSubkeyList(RegPath) if (current = "") { if (!initial) { LogEvent("DELETION", "The entire AppExceptions key is missing or inaccessible!") TrayTip("AppExceptions key has been DELETED!", "ClipboardFusion Monitor", "Icon!") } knownSubkeys := Map() return } if (initial) { knownSubkeys := current.Clone() LogEvent("INFO", "Baseline set. Currently tracking " knownSubkeys.Count " app exception subkeys.") if (knownSubkeys.Count > 0) TrayTip("Monitoring started. Tracking " knownSubkeys.Count " exception keys.", "ClipboardFusion Monitor", "Iconi") return } deleted := [] for name, _ in knownSubkeys { if (!current.Has(name)) deleted.Push(name) } if (deleted.Length > 0) { details := deleted.Join("`n• ") LogEvent("DELETION", "Deleted subkey(s):`n" details) TrayTip("App exception key(s) DELETED!`n`n" details, "ClipboardFusion Monitor", "Icon!") } knownSubkeys := current.Clone() } GetSubkeyList(path) { list := Map() try { Loop Reg, path, "K" { list[A_LoopRegName] := true } } catch { return "" } return list } LogEvent(eventType, message) { global LogFile timestamp := FormatTime(A_Now, "yyyy-MM-dd HH:mm:ss") line := timestamp " | " eventType " | " message "`n" try FileAppend(line, LogFile) } ForceCheck(*) { CheckRegistry(false) TrayTip("Manual check completed.", "ClipboardFusion Monitor", "Iconi") } ViewLog(*) { global LogFile if FileExist(LogFile) Run(LogFile) else MsgBox("Log file does not exist yet.", "Log File") } BackupSettings(*) { global RegPath backupName := "AppExceptions_Backup_" FormatTime(A_Now, "yyyyMMdd_HHmmss") ".reg" backupPath := A_ScriptDir "\" backupName cmd := 'reg export "' RegPath '" "' backupPath '" /y' try { RunWait(cmd, , "Hide") LogEvent("BACKUP", "Full backup created: " backupName) TrayTip("Registry backup created:`n" backupName, "Backup Complete", "Iconi") } catch as e { MsgBox("Backup failed.`n`nError: " e.Message, "Backup Error", "Icon!") } } TogglePause(*) { global isPaused isPaused := !isPaused if (isPaused) { SetTimer(CheckRegistry, 0) Tray.Check("Pause Monitoring") TraySetIcon("shell32.dll", 28) TrayTip("Monitoring is now PAUSED", "ClipboardFusion Monitor", "Icon!") } else { SetTimer(CheckRegistry, PollInterval) Tray.Uncheck("Pause Monitoring") TraySetIcon("shell32.dll", 23) CheckRegistry(false) TrayTip("Monitoring resumed.", "ClipboardFusion Monitor", "Iconi") } }