Reopen Last closed window script (v1 -> v2) Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
emp00
Posts: 163
Joined: 15 Apr 2023, 12:02

Reopen Last closed window script (v1 -> v2)

Post by emp00 » 19 May 2024, 05:37

Dear Team, I am looking for a script to "simply" reopen the last closed explorer window via hotkey.
Found this v1 script dated Q3/2022 by @teadrinker : viewtopic.php?p=484659#p484659

Has anybody successfully ported this to v2 already? Thanks for sharing!

Rohwedder
Posts: 7768
Joined: 04 Jun 2014, 08:33
Location: Germany

Re: Reopen Last closed window script (v1 -> v2)

Post by Rohwedder » 20 May 2024, 02:37

Hallo,
only If your Explorer shows full path in Address Bar!
https://www.thewindowsclub.com/make-win ... 0then%20OK

Code: Select all

#Requires AutoHotkey v2.0
PreviousExplorerTitle := False
DllCall("RegisterShellHookWindow", "UInt", A_ScriptHwnd)
OnMessage(DllCall("RegisterWindowMessage", "Str", "SHELLHOOK"), WinActivated)
q:: {
	Global PreviousExplorerTitle
	IF !PreviousExplorerTitle
		MsgBox "No Previous Explorer"
	Else IF WinExist(PreviousExplorerTitle)
		WinActivate
	Else Run PreviousExplorerTitle
}
WinActivated(wParam, lParam, msg, hWnd)
{
    Global PreviousExplorerTitle
	; ToolTip Format("0x{:04X}",wParam)
    If (wParam = 0x4 OR wParam = 0x8004 OR wParam = 0x6)
	; WINDOWACTIVATED or HSHELL_REDRAW
	And WinActive("ahk_class CabinetWClass ahk_exe explorer.exe")
        PreviousExplorerTitle := WinGetTitle("A")
}
Edit: or HSHELL_REDRAW
Last edited by Rohwedder on 20 May 2024, 10:19, edited 1 time in total.

User avatar
Noitalommi_2
Posts: 321
Joined: 16 Aug 2023, 10:58

Re: Reopen Last closed window script (v1 -> v2)

Post by Noitalommi_2 » 20 May 2024, 02:58

Hi.

I went through teadrinker's v1 script and here is the v2 result.
I haven't tested it for to long but it opens the last closed explorer window as expected.
And I noticed that the Shell_NavigateComplete2 function is not used in the script, so i commented it out.
As it turns out, this is important.

Code: Select all

#Requires AutoHotkey 2.0
#SingleInstance

ExistingWindows := Map()
CollectExistingWindows()
Hook := ShellHook(ShellProc)


!e:: {

    if !ExistingWindows.Has("lastClosed")
        MsgBox "No Explorer Window closed since script has been run."
    else
        OpenShellWindow(ExistingWindows["lastClosed"])
}

CollectExistingWindows() {

    for window in ComObject("Shell.Application").Windows {

        ExistingWindows[window.hwnd] := {currentPath: window.Document.Folder.Self.Path, window: window}
        ComObjConnect(window, "Shell_")
    }
}

ShellProc(nCode, wParam, *) {

    static HSHELL_WINDOWCREATED := 1, HSHELL_WINDOWDESTROYED := 2

    if (nCode = HSHELL_WINDOWCREATED) {
        winClass := WinGetClass("ahk_id" wParam)
    if (winClass != "CabinetWClass")
        return

        for window in ComObject("Shell.Application").Windows {

            if (wParam = window.hwnd) {

                ExistingWindows[window.hwnd] := {currentPath: window.Document.Folder.Self.Path, window: window}
                ComObjConnect(window, "Shell_")
                break
            }
        }
    }
    if (nCode = HSHELL_WINDOWDESTROYED && ExistingWindows.Has(wParam)) {

        ComObjConnect(ExistingWindows[wParam].window)
        ExistingWindows["lastClosed"] := ExistingWindows.Delete(wParam).currentPath
    }
}

OpenShellWindow(folderPath) {

    for window in ComObject("Shell.Application").Windows {

        if (window.Document.Folder.Self.Path = folderPath && found := window.hwnd)
        break
    }
    if found ?? 0
        WinActivate "ahk_id" . found
    else
        Run folderPath
}

Shell_NavigateComplete2(Window, &URL, *) {
   ExistingWindows[Window.Hwnd].CurrentPath := URL
}

class ShellHook {

    __New(shellProc) {

        DllCall("RegisterShellHookWindow", "Ptr", A_ScriptHwnd)
        OnMessage(this.msg := DllCall("RegisterWindowMessage", "Str", "SHELLHOOK"), this.shellProc := shellProc)
    }

    __Delete() {
        if IsObject(this.shellProc)
            OnMessage(this.msg, this.shellProc, 0)
        else
            OnMessage(this.msg, "")
        DllCall("DeregisterShellHookWindow", "Ptr", A_ScriptHwnd)
    }
}
Last edited by Noitalommi_2 on 21 May 2024, 16:19, edited 2 times in total.

just me
Posts: 9574
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Reopen Last closed window script (v1 -> v2)  Topic is solved

Post by just me » 20 May 2024, 06:16

Shell_NavigateComplete2() is used to update the current path after navigating in an existing explorer window.
My attempt:

Code: Select all

#Requires AutoHotkey v2.0
Shell := ComObject("Shell.Application")

ExistingWindows := Map("LastClosed", "")
CollectExistingWindows()
Hook := ShellHook(ShellHookProc)

!e:: {
   If !ExistingWindows["LastClosed"]
      MsgBox("No Explorer Window closed since script has been run.")
   Else
      OpenShellWindow(ExistingWindows["LastClosed"])
}

OpenShellWindow(FolderPath) {
   MsgBox(FolderPath)
   Local Hwnd := 0
   For Window In Shell.Windows {
      If (Window.Document.Folder.Self.Path = FolderPath) && (Hwnd := Window.Hwnd)
         Break
   }
   If Hwnd
      WinActivate(Hwnd)
   Else
      Run((SubStr(FolderPath, 1, 3) = "::{" ? "Shell:" : "") . FolderPath)
}

CollectExistingWindows() {
   For Window in Shell.Windows {
      ExistingWindows[Window.Hwnd] := {CurrentPath: Window.Document.Folder.Self.Path, Window: Window}
      ComObjConnect(Window, "Shell_")
   }
}

ShellHookProc(nCode, wParam, *) {
   Static HSHELL_WINDOWCREATED := 1, HSHELL_WINDOWDESTROYED := 2
   Switch nCode {
      Case HSHELL_WINDOWCREATED:
         If (WinGetClass(wParam) != "CabinetWClass")
            Return
         For Window In Shell.Windows {
            If (wParam = Window.Hwnd) {
               ExistingWindows[Window.Hwnd] := {CurrentPath: Window.Document.Folder.Self.Path, Window: Window}
               ComObjConnect(Window, "Shell_")
               Break
            }
         }
      Case HSHELL_WINDOWDESTROYED:
         If ExistingWindows.Has(wParam) {
            ComObjConnect(ExistingWindows[wParam].Window)
            ExistingWindows["LastClosed"] := ExistingWindows.Delete(wParam).CurrentPath
         }
   }
}

Shell_NavigateComplete2(Window, &URL, *) {
   ExistingWindows[Window.Hwnd].CurrentPath := URL
}

Class ShellHook {
   Static Msg := DllCall("RegisterWindowMessage", "Str", "SHELLHOOK", "UInt")
   __New(ShellProc) {
      DllCall("RegisterShellHookWindow", "Ptr", A_ScriptHwnd)
      This.ShellProc := ShellProc
      OnMessage(ShellHook.Msg, This.ShellProc)
   }
   ; ---------------------------------------------------------------------------
   __Delete() {
      If IsObject(This.ShellProc)
         OnMessage(ShellHook.Msg, This.ShellProc, 0)
      DllCall("DeregisterShellHookWindow", "Ptr", A_ScriptHwnd)
   }
}

User avatar
Noitalommi_2
Posts: 321
Joined: 16 Aug 2023, 10:58

Re: Reopen Last closed window script (v1 -> v2)

Post by Noitalommi_2 » 20 May 2024, 07:38

just me wrote:
20 May 2024, 06:16
Shell_NavigateComplete2() is used to update the current path after navigating in an existing explorer window.
But where and when is Shell_NavigateComplete2() called in this script?

just me
Posts: 9574
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Reopen Last closed window script (v1 -> v2)

Post by just me » 20 May 2024, 12:18

Code: Select all

               ComObjConnect(Window, "Shell_")
It's called for NavigateComplete2 events for connected shell windows (https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa768285(v=vs.85)).

emp00
Posts: 163
Joined: 15 Apr 2023, 12:02

Re: Reopen Last closed window script (v1 -> v2)

Post by emp00 » 20 May 2024, 13:35

Thanks @just me - confirmed working! Brilliant. :-)

User avatar
Noitalommi_2
Posts: 321
Joined: 16 Aug 2023, 10:58

Re: Reopen Last closed window script (v1 -> v2)

Post by Noitalommi_2 » 20 May 2024, 20:01

just me wrote:
20 May 2024, 12:18

Code: Select all

               ComObjConnect(Window, "Shell_")
It's called for NavigateComplete2 events for connected shell windows (https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa768285(v=vs.85)).
Ah, ok. I thought this was a leftover without a caller, so i commented it out. But somehow the script throws no error and works regardless? Anyway, there would probably an error at some point.

teadrinker
Posts: 4411
Joined: 29 Mar 2015, 09:41
Contact:

Re: Reopen Last closed window script (v1 -> v2)

Post by teadrinker » 20 May 2024, 20:13

Noitalommi_2 wrote: But somehow the script throws no error and works regardless?
If you run your variant, open any folder with other folders inside it, and in the same window go to another folder, then close that window, then try to open the last closed one by hotkey - you will see what the problem is.
just me wrote: My attempt
I'd also wrap all of this into a class, since we have an object-oriented approach now:

Code: Select all

#Requires AutoHotkey v2

!e:: {
    if !LastClosedExplorerWindow.OpenLastClosed() {
        MsgBox 'No explorer window is closed after running the script'
    }
}

class LastClosedExplorerWindow
{
    static __New() {
        this.existingWindows := Map()
        this.EnumShellWindows(wnd => this.AddWindow(wnd))
        this.hook := ShellHook(ObjBindMethod(this, 'ShellProc'))
    }

    static OpenLastClosed() {
        if !this.existingWindows.Has('lastClosed') {
            return false
        }
        folderPath := this.existingWindows['lastClosed']
        if !this.EnumShellWindows(wnd => wnd.Document.Folder.Self.Path = folderPath && (WinActivate(wnd), true)) {
            Run(folderPath)
        }
        return true
    }

    static ShellProc(nCode, wParam, *) {
        static HSHELL_WINDOWCREATED := 1, HSHELL_WINDOWDESTROYED := 2
        if nCode = HSHELL_WINDOWCREATED && WinGetClass(wParam) = 'CabinetWClass' {
            this.EnumShellWindows(wnd => wParam = wnd.hwnd && (this.AddWindow(wnd), true))
        }
        if nCode = HSHELL_WINDOWDESTROYED && this.existingWindows.Has(wParam) {
            ComObjConnect(this.existingWindows[wParam]['window'])
            this.existingWindows['lastClosed'] := this.existingWindows.Delete(wParam)['currentPath']
        }
    }

    static AddWindow(window) {
        this.existingWindows[window.hwnd] := Map('currentPath', window.Document.Folder.Self.Path, 'window', window)
        ComObjConnect(window, this)
    }

    static EnumShellWindows(fn) {
        for window in ComObject('Shell.Application').Windows {
            res := fn(window)
        } until res
        return res ?? false
    }

    static NavigateComplete2(window, &url, *) => this.existingWindows[window.hwnd]['currentPath'] := url
}

class ShellHook
{
    __New(shellProc) {
        DllCall('RegisterShellHookWindow', 'Ptr', A_ScriptHwnd)
        this.msg := DllCall('RegisterWindowMessage', 'Str', 'SHELLHOOK')
        OnMessage(this.msg, this.shellProc := shellProc)
    }

    __Delete() {
        OnMessage(this.msg, this.shellProc, 0)
        DllCall('DeregisterShellHookWindow', 'Ptr', A_ScriptHwnd)
    }
}
Or like this:

Code: Select all

#Requires AutoHotkey v2

!e:: {
    if !LastClosedExplorerWindow.OpenLastClosed() {
        MsgBox 'No explorer window is closed after running the script'
    }
}

class LastClosedExplorerWindow
{
    static __New() {
        this.existingWindows := Map()
        this.EnumShellWindows(wnd => (this.AddWindow(wnd), false))
        this.hook := ShellHook(ObjBindMethod(this, 'ShellProc'))
    }

    static OpenLastClosed() {
        if !this.existingWindows.Has('lastClosed') {
            return false
        }
        folderPath := this.existingWindows['lastClosed']
        if !this.EnumShellWindows(wnd => wnd.Document.Folder.Self.Path = folderPath && (WinActivate(wnd), true)) {
            Run(folderPath)
        }
        return true
    }

    static EnumShellWindows(fn) {
        for window in ComObject('Shell.Application').Windows {
            res := fn(window)
        } until res
        return res ?? false
    }

    static AddWindow(window) => this.existingWindows[window.hwnd] := this.ShellWindow(this.existingWindows, window)

    static ShellProc(nCode, wParam, *) {
        static HSHELL_WINDOWCREATED := 1, HSHELL_WINDOWDESTROYED := 2
        if nCode = HSHELL_WINDOWCREATED && WinGetClass(wParam) = 'CabinetWClass' {
            this.EnumShellWindows(wnd => wParam = wnd.hwnd && this.AddWindow(wnd))
        }
        if nCode = HSHELL_WINDOWDESTROYED && this.existingWindows.Has(wParam) {
            this.existingWindows.Delete(wParam)
        }
    }

    class ShellWindow
    {
        __New(list, wnd) {
            this.map := list
            this.wnd := wnd
            this.path := wnd.Document.Folder.Self.Path
            ComObjConnect(wnd, this)
            ObjRelease(ObjPtr(this))
        }

        NavigateComplete2(window, &url, *) => this.path := url

        __Delete() {
            ObjAddRef(ObjPtr(this))
            ComObjConnect(this.wnd)
            this.map['lastClosed'] := this.path
        }
    }
}

class ShellHook
{
    __New(shellProc) {
        DllCall('RegisterShellHookWindow', 'Ptr', A_ScriptHwnd)
        this.msg := DllCall('RegisterWindowMessage', 'Str', 'SHELLHOOK')
        OnMessage(this.msg, this.shellProc := shellProc)
    }

    __Delete() {
        OnMessage(this.msg, this.shellProc, 0)
        DllCall('DeregisterShellHookWindow', 'Ptr', A_ScriptHwnd)
    }
}

User avatar
Noitalommi_2
Posts: 321
Joined: 16 Aug 2023, 10:58

Re: Reopen Last closed window script (v1 -> v2)

Post by Noitalommi_2 » 21 May 2024, 16:17

teadrinker wrote:
20 May 2024, 20:13
Noitalommi_2 wrote: But somehow the script throws no error and works regardless?
If you run your variant, open any folder with other folders inside it, and in the same window go to another folder, then close that window, then try to open the last closed one by hotkey - you will see what the problem is.
Ah ok, it wouldn't open the last closed window but the one before it.

Post Reply

Return to “Ask for Help (v2)”