Helgef wrote: ↑07 Jul 2019, 03:08
@jeeswg and @iPhilip thanks for sharing
.
Here is an example, combining some of your ideas,
Code: Select all
WinCloseOnExit(params*) {
static hWnds := []
local
if params.length() == 1 { ; adding a hwnd
if !hWnds.length()
OnExit(a_thisfunc)
return hWnds.Push(params[1])
}
; exiting...
for each, hWnd in hWnds
WinClose, % "ahk_id " hWnd
}
@Helgef thank you for this function. I like how you combined things into a single function. I modified it slightly in the version below to make it more evident how to use
Params* to conditionally close the windows.
Code: Select all
WinCloseOnExit(Params*) {
static hWnds := []
if (Params.Length() = 1) { ; Params[1] = hWnd
if !hWnds.Length()
OnExit(A_ThisFunc)
hWnds.Push(Params[1])
} else if (Params[1] = "Exit") ; Params[1] = ExitReason, Params[2] = ExitCode
for each, hWnd in hWnds
WinClose, % "ahk_id " hWnd
}
On the issue of hWnds and PIDs reusability ...
Helgef wrote: ↑07 Jul 2019, 03:08
As far as I know both hwnds and pids can be reused by other windows / programs after they have been killed. So there is no actual guarantee that the hwnd / pid refers to the same window / program when the exit function is called. A more secure approach could use a hook to remove hwnds / pids from the list when they are closed.
jeeswg wrote: ↑07 Jul 2019, 08:07
- @iPhilip/Helgef: hWnds and PIDs can be reused. It seemed relatively safe to check that the hWnd still had the same PID, but I might like to add in further checks. Even if you used a hook to check for a window being closed, checking the PID might still be a good safety measure.
- Perhaps the best approach would be to store and check the PID's creation time. See ProcessGetCreationTime:
jeeswg's dates tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=65544
Upon further reading, I agree with both of you that there is no guarantee that the
hwnd/
pid values will refer to the same window but I would argue that the
hwnd values will be unique for every existing window. After all, window handles are pointers to a piece of allocated memory. Thus, following up on
@Helgef suggestion, I added a hook to the
WinCloseOnExitClass class to remove the
hwnd values from the class instance when they are closed. I even added a method to manually remove a given
hwnd value. See below:
Code: Select all
#NoEnv
Loop, 2 {
Run, Notepad, , , PID
WinWaitActive, ahk_pid %PID%
WinCloseOnExit(WinExist())
}
Return
F3::WinCloseOnExit(WinExist("A"), "Remove")
Esc::ExitApp
;===================================================
WinCloseOnExit(hWnd, Action := "Add")
{
static oWinCloseOnExit := new WinCloseOnExitClass
if (Instr(Action, "A") = 1)
oWinCloseOnExit.Add(hWnd)
else
oWinCloseOnExit.Remove(hWnd)
}
;===================================================
class WinCloseOnExitClass
{
static _ := WinCloseOnExitClass.Init()
Init()
{
this.hWnds := []
DllCall("RegisterShellHookWindow", "Ptr", A_ScriptHwnd)
this.MsgNum := DllCall("RegisterWindowMessage", "Str", "SHELLHOOK")
OnMessage(this.MsgNum, ObjBindMethod(WinCloseOnExitClass, "ShellMessage"))
}
ShellMessage(wParam, lParam)
{
if (wParam = 2 && (Index := this.HasValue(lParam)))
Return this.hWnds.RemoveAt(Index)
}
HasValue(hWin)
{
for Each, hWnd in this.hWnds
if (hWnd = hWin)
Return Each
}
Add(hWnd)
{
Return this.hWnds.Push(hWnd)
}
Remove(hWnd)
{
Return this.ShellMessage(2, hWnd)
}
__Delete()
{
OnMessage(this.MsgNum, "")
for Each, hWnd in this.hWnds
WinClose, % "ahk_id " hWnd
}
}
On the v2 version:
Helgef wrote: ↑07 Jul 2019, 03:08
Edit, for v2 I'd use this, if I didn't care to implement a hook that is,
Code: Select all
WinCloseOnExit(p*){
onexit((*) => winclose(p*))
}
I love how simple and elegant that looks! I tested it with v2 but it didn't work
. The version below does (just as elegant
):
Code: Select all
WinCloseOnExit(p*){
OnExit((*) => WinClose("ahk_id " p[1]))
}
Finally,
Helgef wrote: ↑07 Jul 2019, 03:08
Edit 2: @iPhilip , note that your second approach relies on undocumented behaviour, that is, when the static var
_ is released, there is no guarantee that the static var
hWnds hasn't been released. You can bind the
hWnds array to the __delete callback, eg,
Code: Select all
WinCloseOnExit(hWnd) {
static hWnds := [], _ := {base:{__Delete:Func("WinCloseOnExit").Bind(hWnds)}}
if !isobject(hWnd)
hWnds.Push(hWnd)
else
for each, handle in hWnd
WinClose, % "ahk_id " handle
}
Thank you. I was unsure about my approach (though I did test it). I appreciate your suggestion as I don't like to rely on undocumented behavior.
Edit: Corrected
WinCloseOnExitClass class.