Skip to content

Instantly share code, notes, and snippets.

@shoover
Last active January 12, 2018 19:18
Show Gist options
  • Save shoover/1fab3e21017854923b8d52fd1056b7e4 to your computer and use it in GitHub Desktop.
Save shoover/1fab3e21017854923b8d52fd1056b7e4 to your computer and use it in GitHub Desktop.
AutoHotKey NextWindow function using window class and IVirtualDesktopManager
; Activate the next window in the same window class and virtual desktop.
; This works by searching windows by ahk_cls.
NextWindow()
{
; Get the active window class
WinGetClass cls, A
; Sometimes the active window class is blank when a Chrome tab is loading.
if !RegExMatch(cls, "[^\s]")
return
; Get the active virtual desktop
deskMgr := new VirtualDesktopManager()
activeDesktop := deskMgr.GetWindowDesktopId(WinExist("A"))
; Loop over windows in the class and activate the last one.
WinGet, id, list, ahk_class %cls%
Loop, %id%
{
i := id - A_Index + 1
desktopId := deskMgr.GetWindowDesktopId(id%i%)
if (desktopId = activeDesktop) {
WinActivate % "ahk_id" id%i%
return
}
}
}
; Wrapper for COM interface IVirtualDesktopManager.
; c:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/um/ShObjIdl.h
; Usage:
; deskMgr := new VirtualDesktopManager()
; desktopId := deskMgr.GetWindowDesktopId(WinExist("A"))
; MsgBox % _guidToStr(desktopId)
; if (desktopId = deskMgr.GetWindowDesktopId(otherHwnd))
; ...
class VirtualDesktopManager
{
static CLSID := "{aa509086-5ca9-4c25-8f95-589d3c07b48a}" ; VirtualDesktopManager clsid
static IID := "{a5cd92ff-29be-454c-8d04-d82879fb3f1b}" ; IID_IVirtualDesktopManager
__New()
{
this.ptr := ComObjCreate(this.CLSID, this.IID)
}
__Delete()
{
ObjRelease(this.ptr)
}
; Returns the virtual desktop ID of the window as a binary GUID.
GetWindowDesktopId(hwnd)
{
; Initialize a GUID. By some quirk of VarSetCapacity, initializing to 0 yields buggy results.
VarSetCapacity(desktopId, 16, 0xEE)
res := DllCall(vtable(this.ptr, 4), "Ptr", this.ptr, "Ptr", hwnd, "Ptr", &desktopId, "Int")
; A_LastError is also of interest, but it doesn't seem to clear between calls
if res or ErrorLevel
throw Exception("Virtual call failure", -1
,Format("HWND: {1:d}, Result: {:d}, ErrorLevel: {:d}, A_LastError: {:d}, GUID: {:s}"
,hwnd, res, ErrorLevel, A_LastError, _guidToStr(desktopId)))
return desktopId
}
}
; Get the address of function #n in an interface pointer's vtable.
vtable(ptr, n)
{
return NumGet(NumGet(ptr+0), n*A_PtrSize)
}
; https://github.com/cocobelgica/AutoHotkey-Util/blob/master/Guid.ahk#L36
_guidToStr(ByRef VarOrAddress)
{
pGuid := IsByRef(VarOrAddress) ? &VarOrAddress : VarOrAddress
VarSetCapacity(sGuid, 78) ; (38 + 1) * 2
if !DllCall("ole32\StringFromGUID2", "Ptr", pGuid, "Ptr", &sGuid, "Int", 39)
throw Exception("Invalid GUID", -1, Format("<at {1:p}>", pGuid))
return StrGet(&sGuid, "UTF-16")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment