-
-
Save ClassicOldSong/0a088d2a92a06f86e48fe328b67ec995 to your computer and use it in GitHub Desktop.
#Requires AutoHotkey v2.0 | |
#SingleInstance Force | |
DetectHiddenWindows true | |
; Globals to manage the suspend/resume state | |
global IsProcessSuspended := false | |
global IsProcessResumed := false | |
LockFile := A_Temp "\AutoPauseResume.lock" | |
; Ensure we clean up on script exit: | |
OnExit(Cleanup) | |
; Check if the lock file exists before deleting it | |
if FileExist(LockFile) | |
FileDelete(LockFile) | |
; -------------------------------------------------------------------------- | |
; 1) Define excluded processes as a map (key-value) | |
; - The key is the process name in lowercase, mapped to `true`. | |
; -------------------------------------------------------------------------- | |
excludedProcesses := Map() | |
excludedProcesses["explorer.exe"] := true | |
excludedProcesses["csrss.exe"] := true | |
excludedProcesses["winlogon.exe"] := true | |
excludedProcesses["svchost.exe"] := true | |
excludedProcesses["dwm.exe"] := true | |
excludedProcesses["cmd.exe"] := true | |
excludedProcesses["chrome.exe"] := true | |
excludedProcesses["powershell.exe"] := true | |
excludedProcesses["sublime_text.exe"] := true | |
excludedProcesses["sublime_merge.exe"] := true | |
excludedProcesses["steamwebhelper.exe"] := true | |
excludedProcesses["openconsole.exe"] := true | |
excludedProcesses["windowsterminal.exe"] := true | |
excludedProcesses["playnite.desktopapp.exe"] := true | |
excludedProcesses["playnite.fullscreenapp.exe"] := true | |
; -------------------------------------------------------------------------- | |
; 2) Get the active window's PID & Process Name, confirm it's not excluded | |
; -------------------------------------------------------------------------- | |
if !WinExist("A") | |
{ | |
MsgBox "No active window detected. Please focus a window to suspend." | |
ExitApp | |
} | |
pid := WinGetPID("A") | |
if !pid | |
{ | |
MsgBox "Failed to get the active window's PID." | |
ExitApp | |
} | |
processName := WinGetProcessName() | |
if !processName | |
{ | |
MsgBox "Failed to retrieve the active window's process name." | |
ExitApp | |
} | |
; Convert the process name to lowercase for case-insensitive matching | |
procLower := StrLower(processName) | |
; Check if `excludedProcesses` has this process in its keys, and if the value is true | |
if excludedProcesses.Has(procLower) && excludedProcesses[procLower] | |
{ | |
; Excluded process, so do nothing and exit. | |
ExitApp | |
} | |
; -------------------------------------------------------------------------- | |
; 3) Suspend the process & start checking for the lock file in a timer | |
; -------------------------------------------------------------------------- | |
SuspendProcess(pid) | |
IsProcessSuspended := true ; Mark that we have indeed suspended a process | |
; Optional: show a short tooltip or do a quick beep instead of a MsgBox | |
; MsgBox "Process '" processName "' (PID: " pid ") suspended. Waiting for lock file..." | |
SetTimer(CheckLockFile, 500) ; check the lock file every 500 ms | |
return | |
; -------------------------------------------------------------------------- | |
CheckLockFile(*) | |
{ | |
global LockFile, pid, processName, IsProcessResumed | |
if FileExist(LockFile) | |
{ | |
ResumeProcess(pid) | |
IsProcessResumed := true | |
; MsgBox "Process '" processName "' (PID: " pid ") resumed. Exiting." | |
FileDelete(LockFile) | |
ExitApp | |
} | |
} | |
; -------------------------------------------------------------------------- | |
SuspendProcess(pid) | |
{ | |
PROCESS_ALL_ACCESS := 0x1F0FFF | |
hProc := DllCall("OpenProcess", "UInt", PROCESS_ALL_ACCESS, "Int", 0, "UInt", pid, "Ptr") | |
if !hProc | |
{ | |
MsgBox "Failed to open process (PID: " pid ")." | |
ExitApp | |
} | |
hNtdll := DllCall("GetModuleHandle", "Str", "ntdll.dll", "Ptr") | |
if !hNtdll | |
{ | |
MsgBox "Failed to get module handle for ntdll.dll." | |
ExitApp | |
} | |
NtSuspendProcess := DllCall("GetProcAddress", "Ptr", hNtdll, "AStr", "NtSuspendProcess", "Ptr") | |
if !NtSuspendProcess | |
{ | |
MsgBox "Failed to locate NtSuspendProcess in ntdll.dll." | |
ExitApp | |
} | |
DllCall(NtSuspendProcess, "Ptr", hProc) | |
DllCall("CloseHandle", "Ptr", hProc) | |
} | |
; -------------------------------------------------------------------------- | |
ResumeProcess(pid) | |
{ | |
PROCESS_ALL_ACCESS := 0x1F0FFF | |
hProc := DllCall("OpenProcess", "UInt", PROCESS_ALL_ACCESS, "Int", 0, "UInt", pid, "Ptr") | |
if !hProc | |
{ | |
; Process may no longer exist, don't do anything. | |
; MsgBox "Failed to open process (PID: " pid ")." | |
ExitApp | |
} | |
hNtdll := DllCall("GetModuleHandle", "Str", "ntdll.dll", "Ptr") | |
if !hNtdll | |
{ | |
MsgBox "Failed to get module handle for ntdll.dll." | |
ExitApp | |
} | |
NtResumeProcess := DllCall("GetProcAddress", "Ptr", hNtdll, "AStr", "NtResumeProcess", "Ptr") | |
if !NtResumeProcess | |
{ | |
MsgBox "Failed to locate NtResumeProcess in ntdll.dll." | |
ExitApp | |
} | |
DllCall(NtResumeProcess, "Ptr", hProc) | |
DllCall("CloseHandle", "Ptr", hProc) | |
} | |
; -------------------------------------------------------------------------- | |
; Automatically resume the process if the user manually exits the script | |
; or the script ends unexpectedly. | |
; -------------------------------------------------------------------------- | |
Cleanup(exitReason, exitCode) | |
{ | |
global pid, IsProcessSuspended, IsProcessResumed | |
; If the process was suspended and hasn't been resumed via the lock file, | |
; auto-resume it here. | |
if IsProcessSuspended && !IsProcessResumed | |
{ | |
ResumeProcess(pid) | |
} | |
} |
#Requires AutoHotkey v2.0 | |
#SingleInstance Force | |
LockFile := A_Temp "\AutoPauseResume.lock" | |
; Simply create the lock file and exit | |
FileAppend("", LockFile) | |
ExitApp |
If this happens occasionally, go to the system tray you’ll see a running AHK icon, click on it and exit the script will unfreeze the app.
If this always happens, then you can add a delay to the execution of resume.
Tried with the delay before even writing my comment. AHK sleep in resume just delayed the stream from closing.
I just finished writing my own version of the scripts. I'm using environmental variables to store the game process when starting it, to avoid guessing the window to suspend. For some reason your suspend method stopped working for me so I moved to PSTools PSSuspend from sysinternals to suspend it. In the end they are shorter and easier to read for the price of some dependencies.
I'm also using a script to launch the game itself instead of using a detached command. WinWaitClose in ahk allows for tracking when game closes so the stream ends with it.
Appreciate the response and a very quick one!
Well usually you don’t put the game start command in detached command, but directly in command. So when the game quits, the stream will terminate automatically.
Envvars can be unreliable in some cases that’s why I didn’t use the method.
Where in global undo i should put the resume,ahk?
Is it for example the detach command for steam big picutre? I got steam big picture frozen when i exit stream.
In Configuration, first tab General, you'll find a section Command Preparations. Add a new one there and only fill out the Undo Command, put the path to the resume script there.
Or you can add steam.exe to the ignore list.
For some reason, when quitting the game global undo command gets executed before client disconnect command. So my global resume runs, doing nothing and then client disconnect pause runs, pausing the foreground app. Any way around this?