Skip to content

Instantly share code, notes, and snippets.

@Rovsau
Last active August 11, 2023 14:33
Show Gist options
  • Save Rovsau/89ed4cdacbf41cb28a3b163ba8d15626 to your computer and use it in GitHub Desktop.
Save Rovsau/89ed4cdacbf41cb28a3b163ba8d15626 to your computer and use it in GitHub Desktop.

Running AutoHotKey scripts can potentially trigger Anti-Cheat systems.
That means forgetting to stop your scripts before starting a video game could result in consequences.

This script checks if a newly Focused window is either Fullscreen or Blacklisted.
If any of those are true, and the window not Whitelisted, the script will close all other AHK instances, and then itself.

Disclaimer: No guarantees.

/*
    # AHKiller
    Conditionally Terminates All AutoHotKey scripts.
    by Rovsau @ Github

    Script Initialization
*/
#Persistent  ; Keep the script running
#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
#Warn  ; Enable warnings to assist with detecting common errors. (Use for Debugging).
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
SetTitleMatchMode 2  ; Title Contains (Not really used by this script).
StringCaseSense, On  ; String-comparisons are case-sensitive. 
global _allowedList := {}
global _allowedCount = 1
global _deniedList := {}
global _deniedCount = 1

/*
    # Debug Settings
*/
global debugToTextFile := true
global debugLogFilePath := "AHKiller.log"
/*
    # ReadMe

    This script will terminate all AHK instances (ungracefully) when conditions are met. 
    Focusing a Fullscreen application will trigger termination, unless the app has been added to the Allowed list by the user.
    Non-fullscreen applications can also trigger termination, if the app has been added to the Denied list by the user. 
    The initial purpose of the script is to avoid running AHK scripts while running applications which may be paranoid of such scripts. 

    Blacklist() and Whitelist() store a string of text in the Denied/Allowed lists. 
    If this string is CONTAINED within a Window Title which comes into focus, termination will either be triggered or avoided. 

    If an app is found to be Fullscreen or Denied, the script will also check if the app is found in the Allowed list. 
    This ensures that ambiguity can be solved by using both Blacklist() and Whitelist(). 
    

    # Example
    
    Blacklist("Notepad")
    Whitelist("Notepad++")

    # Limitations

    If an application is already focused before it is Fullscreen, 
    this script will not detect that, 
    until another app has been focused, and the fullscreen app is focused again. 

*/



/*
    # User Input
*/
Blacklist("Notepad")
Whitelist("Notepad++")

Allow("Google Chrome", ".mkv")



/*
    # Script Core
*/
Debug(message) {
    if (debugToTextFile) {
        FileAppend, %A_Now% - %message%`n, %debugLogFilePath%
    }
}

Whitelist(params*) {
    Allow(params*)
}

Blacklist(params*) {
    Deny(params*)
}

Allow(params*) {
    for index, appName in params {
        _allowedList[_allowedCount++] := appName
    }
}
Deny(params*) {
    for index, appName in params {
        _deniedList[_deniedCount++] := appName
    }
}

OnWindowActivate(windowTitle) {
    Debug("------------------------")
    Debug(windowTitle)

    if (IsDenied(windowTitle) and not IsAllowed(windowTitle)) {
        Debug(windowTitle " is Denied. AHK will terminate.")
        TerminateAHK()
    }

    if (IsFullscreen(windowTitle)) {
        if (!IsAllowed(windowTitle)) {
            Debug(windowTitle " is Fullscreen. AHK will terminate.")
            TerminateAHK()
        }
        else {
            Debug(windowTitle " is Fullscreen, and is Allowed.")
        }
    }
}

IsDenied(windowTitle) {
    for index, appName in _deniedList {
        if (InStr(windowTitle, appName)) {
            Debug("Denied App: <" appName "> in Window Title: <" windowTitle ">")
            return true
        }
    }
    return false
}

IsAllowed(windowTitle) {
    for index, appName in _allowedList {
        if (InStr(windowTitle, appName)) {
            Debug("Allowed App: <" appName "> in Window Title: <" windowTitle ">")
            return true
        }
    }
    return false
}

IsFullscreen(windowTitle) {
    winID := WinExist(windowTitle)
    if (!winID) {
        return false
    }
    WinGet style, Style, ahk_id %winID%
    WinGetPos ,,,winW,winH, %windowTitle%
    return ((style & 0x20800000) or winH < A_ScreenHeight or winW < A_ScreenWidth) ? false : true
}

TerminateAHK() {
    ; List of all AHK executables to be terminated
    ahkExecutables := ["AutoHotkey.exe", "AutoHotkeyA32.exe", "AutoHotkeyU32.exe", "AutoHotkeyU64.exe", "AutoHotkey32.exe", "AutoHotkey64.exe", "AutoHotkeyUX.exe", "AutoHotkey32_UIA.exe", "AutoHotkey64_UIA.exe"]
    
    ; Get the Process ID of this script to avoid terminating itself prematurely
    thisProcessId := DllCall("GetCurrentProcessId", "UInt")
    
    ; Loop through and terminate each AHK process excluding the current script
    for index, exeName in ahkExecutables {
        pid := ProcessExist(exeName) ; get the process ID
        While pid && (pid != thisProcessId) {
            Process, Close, %pid%
            pid := ProcessExist(exeName) ; check again if the process is still running
        }
    }
    
    ; Allow the log file to be appended (somehow..)
    ExitApp ; Close the script itself
}

ProcessExist(exeName) {
    Process, Exist, %exeName%
    return ErrorLevel ; Returns the process ID if the process exists, otherwise 0
}

; Keeps track of the current active window.
DllCall("RegisterShellHookWindow", UInt, A_ScriptHwnd)
MsgNum := DllCall("RegisterWindowMessage", Str, "SHELLHOOK")
OnMessage(MsgNum, "ShellMessage")

; Delegate for ShellHook. 
ShellMessage(wParam, lParam) {
    WinGetTitle, title, ahk_id %lParam%
    switch (wParam) {
        case 4, 0x8004:
        ; 4 = HSHELL_WINDOWACTIVATED, 0x8004 = HSHELL_RUDEAPPACTIVATED (HSHELL_WINDOWACTIVATED | HSHELL_HIGHBIT)
            OnWindowActivate(title)
            ; Debug("ShellMessage: <" title ">")
    }
}
; End of File.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment