|
#Requires AutoHotkey v2.0 |
|
#SingleInstance Force |
|
DetectHiddenWindows true |
|
|
|
global Debug := false |
|
global LockFile := A_Temp "\AutoPauseResume.lock" |
|
global IsProcessSuspended := false |
|
global IsProcessResumed := false |
|
|
|
OnExit(Cleanup) |
|
|
|
if FileExist(LockFile) |
|
FileDelete(LockFile) |
|
|
|
; --------------------------------------------------------------------------- |
|
; Excluded processes (lowercase) |
|
; --------------------------------------------------------------------------- |
|
ExcludedProcesses := Map( |
|
"explorer.exe", true, |
|
"csrss.exe", true, |
|
"winlogon.exe", true, |
|
"svchost.exe", true, |
|
"dwm.exe", true, |
|
"cmd.exe", true, |
|
"powershell.exe", true, |
|
"chrome.exe", true, |
|
"steam.exe", true, |
|
"sublime_text.exe", true, |
|
"sublime_merge.exe", true, |
|
"steamwebhelper.exe", true, |
|
"openconsole.exe", true, |
|
"windowsterminal.exe", true, |
|
"playnite.desktopapp.exe", true, |
|
"playnite.fullscreenapp.exe", true |
|
) |
|
|
|
if !WinExist("A") |
|
{ |
|
if Debug |
|
MsgBox "No active window detected. Please focus a window to suspend." |
|
ExitApp |
|
} |
|
|
|
pid := WinGetPID("A") |
|
if !pid |
|
{ |
|
if Debug |
|
MsgBox "Failed to get the active window's PID." |
|
ExitApp |
|
} |
|
|
|
processName := WinGetProcessName("ahk_pid " pid) |
|
if !processName |
|
{ |
|
if Debug |
|
MsgBox "Failed to retrieve the active window's process name." |
|
ExitApp |
|
} |
|
|
|
if ExcludedProcesses.Has(StrLower(processName)) |
|
ExitApp |
|
|
|
; --------------------------------------------------------------------------- |
|
; Suspend the process and write PID to lock file for resume.ahk |
|
; --------------------------------------------------------------------------- |
|
if !CallNtProcessFunc(pid, "NtSuspendProcess") |
|
{ |
|
if Debug |
|
MsgBox "Failed to suspend process '" processName "' (PID: " pid ")." |
|
ExitApp |
|
} |
|
|
|
IsProcessSuspended := true |
|
FileAppend(pid, LockFile) ; PID in lock file so resume.ahk can act independently |
|
|
|
; Wait for resume.ahk to delete the lock file |
|
SetTimer(CheckResumed, 500) |
|
return |
|
|
|
; --------------------------------------------------------------------------- |
|
CheckResumed(*) |
|
{ |
|
global IsProcessResumed |
|
if !FileExist(LockFile) |
|
{ |
|
IsProcessResumed := true |
|
ExitApp |
|
} |
|
} |
|
|
|
; --------------------------------------------------------------------------- |
|
; Safety net: resume the process if this script exits unexpectedly. |
|
; --------------------------------------------------------------------------- |
|
Cleanup(exitReason, exitCode) |
|
{ |
|
global pid, IsProcessSuspended, IsProcessResumed |
|
if IsProcessSuspended && !IsProcessResumed |
|
{ |
|
CallNtProcessFunc(pid, "NtResumeProcess") |
|
if FileExist(LockFile) |
|
FileDelete(LockFile) |
|
} |
|
} |
|
|
|
; --------------------------------------------------------------------------- |
|
; Calls NtSuspendProcess or NtResumeProcess on a PID. |
|
; Uses PROCESS_SUSPEND_RESUME (0x0800) - the minimum required access right. |
|
; Returns true on success, false on failure. |
|
; --------------------------------------------------------------------------- |
|
CallNtProcessFunc(pid, funcName) |
|
{ |
|
hProc := DllCall("OpenProcess", "UInt", 0x0800, "Int", 0, "UInt", pid, "Ptr") |
|
if !hProc |
|
return false |
|
|
|
hNtdll := DllCall("GetModuleHandle", "Str", "ntdll.dll", "Ptr") |
|
if !hNtdll |
|
{ |
|
DllCall("CloseHandle", "Ptr", hProc) |
|
return false |
|
} |
|
|
|
pFunc := DllCall("GetProcAddress", "Ptr", hNtdll, "AStr", funcName, "Ptr") |
|
if !pFunc |
|
{ |
|
DllCall("CloseHandle", "Ptr", hProc) |
|
return false |
|
} |
|
|
|
DllCall(pFunc, "Ptr", hProc) |
|
DllCall("CloseHandle", "Ptr", hProc) |
|
return true |
|
} |