FuPeiJiang / VD.ahk

Windows Virtual Desktop, AutoHotkey, Windows 11 support, Windows Server 2022, switch desktop, move window(wintitle) to current desktop; createDesktop, PinWindow, getCount, getDesktopNumOfWindow -> mute all windows in Virtual Desktop
MIT License
358 stars 48 forks source link

Is it possible to have a function registered to run whenever the virtual desktop changes? #14

Closed TWF1212 closed 2 years ago

TWF1212 commented 2 years ago

I've been using some form of my own custom Virtual Desktop manager using AHK for awhile, and it all broke with Windows 11.

I found your class, and was able to implement it and everything is working perfectly. Thank you!

One thing that would be nice, that seemed to work in windows 10, using an OnMessage Listener:

; Windows 10 desktop changes listener
DllCall(RegisterPostMessageHookProc, Int, hwnd, Int, 0x1400 + 30)
OnMessage(0x1400 + 30, "VDChanged")
VDChanged(wParam, lParam, msg, hwnd) {
    ; Will run when desktop changes, even if not changed using this program
}

I don't remember where I got that bit of code from, but it worked in Windows 10.

My use case is that I have an icon that I made myself that shows which desktop I am currently on located in the taskbar. I can manually make sure it is accurate when I use the program to change desktops, but on occasion Windows will pull me to another desktop when I click on a notification, or I'll sometimes use the built-in Ctrl+Win+Left/Right to change desktop instead. This then de-syncs my icon until I use the program to switch again.

I have a work around of using a SetTimer that will always get current number and update the icon accordingly, but I'd prefer to again be able to just have a function that runs whenever the desktop is changed.

I was thinking of maybe being able to register a function in your VD class that will then be used as this callback function. i.e.

VD.addCallback(FunctionName)

FunctionName() {
    ; Runs whenever desktop is changed from anywhere
}

Figured I'd ask, if this isn't possible not a problem as I am happy enough with my workaround.

Thank you.

huo-feng-ding commented 2 years ago

me too.

that code maybe from https://github.com/Ciantic/VirtualDesktopAccessor

FuPeiJiang commented 2 years ago

that code maybe from Ciantic/VirtualDesktopAccessor

this is cool (that it's possible and that I am taught how to do something this complicated(well something that I didn't know how to do before(no pointers))) https://github.com/Ciantic/VirtualDesktopAccessor/blob/5bc1bbaab247b5d72e70abc9432a15275fd2d229/VirtualDesktopAccessor/dllmain.h#L718-L794 https://github.com/Ciantic/VirtualDesktopAccessor/blob/5bc1bbaab247b5d72e70abc9432a15275fd2d229/VirtualDesktopAccessor/dllmain.h#L85-L89 I'll try to port this to ahk

thanks @MaxSherry (and Ciantic)

FuPeiJiang commented 2 years ago

instead of VD.addCallback("FunctionName"), I prefer assign method to method , wait, it's the same thing as a setter..

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
#SingleInstance force
ListLines Off
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
SetBatchLines -1
#KeyHistory 0

Address1 := RegisterCallback("VD._CurrentVirtualDesktopChanged")

CurrentVirtualDesktopChanged(desktopNum) {
    MsgBox % "desktopNum: " desktopNum
}
DllCall(Address1)
VD.CurrentVirtualDesktopChanged:=Func("CurrentVirtualDesktopChanged")
DllCall(Address1)
VD.CurrentVirtualDesktopChanged:=anotherClass.CurrentVirtualDesktopChanged.Bind(VD) ;automatically is a Func("anotherClass.CurrentVirtualDesktopChanged")
DllCall(Address1)
class anotherClass {
    CurrentVirtualDesktopChanged(desktopNum) {
        MsgBox % "desktopNum from anotherClass: " desktopNum
    }
}
class VD {
    _CurrentVirtualDesktopChanged() {
        VD.CurrentVirtualDesktopChanged.Call(1) ;Call removes `this` as 1st argument
    }
    CurrentVirtualDesktopChanged(desktopNum) {
    }
}

return

f3::Exitapp

I've just realized that there are many complications with this

FuPeiJiang commented 2 years ago

28 days ago, hmmm I somehow missed this notification

FuPeiJiang commented 2 years ago

you can have these 2 files for now: RegisterDesktopNotifications.ah2

#SingleInstance force
ListLines 0
KeyHistory 0
SendMode "Input" ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir A_ScriptDir ; Ensures a consistent starting directory.

VD.RegisterDesktopNotifications()

class VD {

    static _QueryInterface(riid, ppvObject) { ;https://www.autohotkey.com/boards/viewtopic.php?t=36025&start=20#p176225
        if (!ppvObject) {
            return 0x80070057 ;E_INVALIDARG
        }

        str_IID_IUnknown:="{00000000-0000-0000-C000-000000000046}"
        str_IID_IVirtualDesktopNotification:="{C179334C-4295-40D3-BEA1-C654D965605A}"

        someStr:=Buffer(80)
        DllCall("Ole32\StringFromGUID2", "Ptr", riid, "Ptr",someStr, "Ptr",40)
        str_riid:=StrGet(someStr)
        if (str_riid==str_IID_IUnknown || str_riid==str_IID_IVirtualDesktopNotification) {
            NumPut("Ptr", this, ppvObject, 0)
            VD._AddRef.Call(this)
            return 0 ;S_OK
        }
        ; *ppvObject = NULL;
        NumPut("Ptr", 0, ppvObject, 0)
        return 0x80004002 ;E_NOINTERFACE

        ; // Always set out parameter to NULL, validating it first.
        ; if (!ppvObject)
            ; return E_INVALIDARG;
        ; *ppvObject = NULL;
;
        ; if (riid == IID_IUnknown || riid == IID_IVirtualDesktopNotification)
        ; {
            ; // Increment the reference count and return the pointer.
            ; *ppvObject = (LPVOID)this;
            ; AddRef();
            ; return S_OK;
        ; }
        ; return E_NOINTERFACE;
    }

    static _AddRef() {
        refCount:=NumGet(this, A_PtrSize, "UInt")
        refCount++
        NumPut("UInt", refCount, this, A_PtrSize)

        ; return InterlockedIncrement(&_referenceCount);
        return refCount
    }

    static _Release() {
        refCount:=NumGet(this, A_PtrSize, "UInt")
        refCount--
        NumPut("UInt", refCount, this, A_PtrSize)
        ; ULONG result = InterlockedDecrement(&_referenceCount);
        ; if (result == 0)
        ; {
            ; delete this;
        ; }
        return refCount
    }
    static _VirtualDesktopCreated() {
        Tooltip 11111
        return 0 ;S_OK
    }
        static _VirtualDesktopDestroyBegin() {
        Tooltip 22222
        return 0 ;S_OK
    }
    static _VirtualDesktopDestroyFailed() {
        Tooltip 33333
        return 0 ;S_OK
    }
    static _VirtualDesktopDestroyed() {
        Tooltip 44444
        return 0 ;S_OK
    }
    static _ViewVirtualDesktopChanged() {
        Tooltip 55555
        return 0 ;S_OK
    }
    static _CurrentVirtualDesktopChanged() {
        Tooltip 66666
        return 0 ;S_OK
    }

    static RegisterDesktopNotifications() {
        methods:=Buffer(9*A_PtrSize)
        NumPut("Ptr", CallbackCreate(VD._QueryInterface, "F"), methods, 0*A_PtrSize)
        NumPut("Ptr", CallbackCreate(VD._AddRef, "F"), methods, 1*A_PtrSize)
        NumPut("Ptr", CallbackCreate(VD._Release, "F"), methods, 2*A_PtrSize)
        NumPut("Ptr", CallbackCreate(VD._VirtualDesktopCreated, "F"), methods, 3*A_PtrSize)
        NumPut("Ptr", CallbackCreate(VD._VirtualDesktopDestroyBegin, "F"), methods, 4*A_PtrSize)
        NumPut("Ptr", CallbackCreate(VD._VirtualDesktopDestroyFailed, "F"), methods, 5*A_PtrSize)
        NumPut("Ptr", CallbackCreate(VD._VirtualDesktopDestroyed, "F"), methods, 6*A_PtrSize)
        NumPut("Ptr", CallbackCreate(VD._ViewVirtualDesktopChanged, "F"), methods, 7*A_PtrSize)
        NumPut("Ptr", CallbackCreate(VD._CurrentVirtualDesktopChanged, "F"), methods, 8*A_PtrSize)

        obj:=Buffer(A_PtrSize + 4)
        NumPut("Ptr", methods.Ptr, obj, 0)
        NumPut("UInt", 0, obj, A_PtrSize) ;refCount

        this.methods:=methods ;PLEASE DON'T GARBAGE COLLECT IT, this took me hours to debug, I was lucky ahkv2 garbage collected slowly
        this.obj:=obj ;PLEASE DON'T GARBAGE COLLECT IT, this took me hours to debug, I was lucky ahkv2 garbage collected slowly

        ; "CoCreateInstance", "Ptr" rclsid, IntPtr pUnkOuter, UInt32 dwClsContext, IntPtr riid, IntPtr ppv);
        ; pDesktopNotificationService:=ComObject("{A501FDEC-4A09-464C-AE4E-1B9C21B84918}", "{0CD45E71-D927-4F15-8B0A-8FEF525337BF}")
        this.IServiceProvider := ComObject("{C2F03A33-21F5-47FA-B4BB-156362A2F239}", "{6D5140C1-7436-11CE-8034-00AA006009FA}")

        pDesktopNotificationService := ComObjQuery(this.IServiceProvider, "{A501FDEC-4A09-464C-AE4E-1B9C21B84918}", "{0CD45E71-D927-4F15-8B0A-8FEF525337BF}")
        HRESULT:=ComCall(3, pDesktopNotificationService, "Ptr",obj, "Uint*",&pdwCookie:=0)
        ok2:=A_LastError
        ok:=0

        ; HRESULT hrNotificationService = pServiceProvider->QueryService(
        ; CLSID_IVirtualNotificationService,
        ; __uuidof(IVirtualDesktopNotificationService),
        ; (PVOID*)&pDesktopNotificationService);
    }

    static _vtable(ppv, index) {
        Return NumGet(NumGet(ppv.Ptr, 0, "Ptr")+A_PtrSize*index, 0, "Ptr")

    }
}

return

f3::Exitapp

RegisterDesktopNotifications.ahk

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
#SingleInstance force
ListLines Off
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
SetBatchLines -1
#KeyHistory 0

VD.RegisterDesktopNotifications()

class VD {

    _QueryInterface(riid, ppvObject) { ;https://www.autohotkey.com/boards/viewtopic.php?t=36025&start=20#p176225
        if (!ppvObject) {
            return 0x80070057 ;E_INVALIDARG
        }

        str_IID_IUnknown:="{00000000-0000-0000-C000-000000000046}"
        str_IID_IVirtualDesktopNotification:="{C179334C-4295-40D3-BEA1-C654D965605A}"

        VarSetCapacity(someStr, 40)
        DllCall("Ole32\StringFromGUID2", "Ptr", riid, "Ptr",&someStr, "Ptr",40)
        str_riid:=StrGet(&someStr)

        if (str_riid==str_IID_IUnknown || str_riid==str_IID_IVirtualDesktopNotification) {
            NumPut(this, ppvObject+0, 0, "Ptr")
            VD._AddRef.Call(this)
            return 0 ;S_OK
        }
        ; *ppvObject = NULL;
        NumPut(0, ppvObject+0, 0, "Ptr")
        return 0x80004002 ;E_NOINTERFACE

        ; // Always set out parameter to NULL, validating it first.
        ; if (!ppvObject)
            ; return E_INVALIDARG;
        ; *ppvObject = NULL;
;
        ; if (riid == IID_IUnknown || riid == IID_IVirtualDesktopNotification)
        ; {
            ; // Increment the reference count and return the pointer.
            ; *ppvObject = (LPVOID)this;
            ; AddRef();
            ; return S_OK;
        ; }
        ; return E_NOINTERFACE;
    }

    _AddRef() {
        refCount:=NumGet(this+0, A_PtrSize, "UInt")
        refCount++
        NumPut(refCount, this+0, A_PtrSize, "UInt")

        ; return InterlockedIncrement(&_referenceCount);
        return refCount
    }

    _Release() {
        refCount:=NumGet(this+0, A_PtrSize, "UInt")
        refCount--
        NumPut(refCount, this+0, A_PtrSize, "UInt")
        ; ULONG result = InterlockedDecrement(&_referenceCount);
        ; if (result == 0)
        ; {
            ; delete this;
        ; }
        return refCount
    }
    _VirtualDesktopCreated() {
        Tooltip % 11111
        return 0 ;S_OK
    }
    _VirtualDesktopDestroyBegin() {
        Tooltip % 22222
        return 0 ;S_OK
    }
    _VirtualDesktopDestroyFailed() {
        Tooltip % 33333
        return 0 ;S_OK
    }
    _VirtualDesktopDestroyed() {
        Tooltip % 44444
        return 0 ;S_OK
    }
    _ViewVirtualDesktopChanged() {
        Tooltip % 55555
        return 0 ;S_OK
    }
    _CurrentVirtualDesktopChanged() {
        Tooltip % 66666
        return 0 ;S_OK
    }
    RegisterDesktopNotifications() {
        methods:=DllCall("GlobalAlloc", "Uint",0x40, "Uint",8*A_PtrSize) ;PLEASE DON'T GARBAGE COLLECT IT, this took me hours to debug, I was lucky ahkv2 garbage collected slowly
        NumPut(RegisterCallback("VD._QueryInterface", "F"), methods+0, 0*A_PtrSize, "Ptr")
        NumPut(RegisterCallback("VD._AddRef", "F"), methods+0, 1*A_PtrSize, "Ptr")
        NumPut(RegisterCallback("VD._Release", "F"), methods+0, 2*A_PtrSize, "Ptr")
        NumPut(RegisterCallback("VD._VirtualDesktopCreated", "F"), methods+0, 3*A_PtrSize, "Ptr")
        NumPut(RegisterCallback("VD._VirtualDesktopDestroyBegin", "F"), methods+0, 4*A_PtrSize, "Ptr")
        NumPut(RegisterCallback("VD._VirtualDesktopDestroyFailed", "F"), methods+0, 5*A_PtrSize, "Ptr")
        NumPut(RegisterCallback("VD._VirtualDesktopDestroyed", "F"), methods+0, 6*A_PtrSize, "Ptr")
        NumPut(RegisterCallback("VD._ViewVirtualDesktopChanged", "F"), methods+0, 7*A_PtrSize, "Ptr")
        NumPut(RegisterCallback("VD._CurrentVirtualDesktopChanged", "F"), methods+0, 8*A_PtrSize, "Ptr")

        obj:=DllCall("GlobalAlloc", "Uint",0x40, "Uint",A_PtrSize + 4) ;PLEASE DON'T GARBAGE COLLECT IT, this took me hours to debug, I was lucky ahkv2 garbage collected slowly
        NumPut(methods, obj+0, 0, "Ptr")
        NumPut(0, obj+0, A_PtrSize, "UInt") ;refCount

        ; "CoCreateInstance", "Ptr" rclsid, IntPtr pUnkOuter, UInt32 dwClsContext, IntPtr riid, IntPtr ppv);
        ; pDesktopNotificationService:=ComObjCreate("{A501FDEC-4A09-464C-AE4E-1B9C21B84918}", "{0CD45E71-D927-4F15-8B0A-8FEF525337BF}")
        this.IServiceProvider := ComObjCreate("{C2F03A33-21F5-47FA-B4BB-156362A2F239}", "{6D5140C1-7436-11CE-8034-00AA006009FA}")

        pDesktopNotificationService := ComObjQuery(this.IServiceProvider, "{A501FDEC-4A09-464C-AE4E-1B9C21B84918}", "{0CD45E71-D927-4F15-8B0A-8FEF525337BF}")
        Register:=this._vtable(pDesktopNotificationService, 3)
        HRESULT:=DllCall(Register,"UPtr",pDesktopNotificationService, "Ptr",obj, "Uint*",pdwCookie:=0)

        ok1:=ErrorLevel
        ok2:=A_LastError
        ok:=0

        ; HRESULT hrNotificationService = pServiceProvider->QueryService(
        ; CLSID_IVirtualNotificationService,
        ; __uuidof(IVirtualDesktopNotificationService),
        ; (PVOID*)&pDesktopNotificationService);
    }

    _vtable(ppv, index) {
        Return NumGet(NumGet(ppv+0, 0, "Ptr")+index*A_PtrSize, 0, "Ptr")
        ; Return NumGet(NumGet(0+ppv)+A_PtrSize*index)
    }
}

return

f3::Exitapp
FuPeiJiang commented 2 years ago

it is done, other examples\CurrentVirtualDesktopChanged RegisterDesktopNotifications.ahk, https://github.com/FuPeiJiang/VD.ahk/commit/73e52f9fd5e6c2b413061105ad91da3653fa56e1

https://github.com/FuPeiJiang/VD.ahk/blob/73e52f9fd5e6c2b413061105ad91da3653fa56e1/other%20examples/CurrentVirtualDesktopChanged%20RegisterDesktopNotifications.ahk#L9-L14

TWF1212 commented 2 years ago

it is done

Thank you so much for working on this request, and so quickly once you started!!

I've tested this out on my two computers and it works perfectly in Windows 10, but still doesn't work in Windows 11 for me.

I'm assuming the {GUID} is different in Windows 11, but I don't know enough about that to figure it out and test.

Thank you again for looking into it, and for adding the functionality.

FuPeiJiang commented 2 years ago
IID_IUnknown:="{00000000-0000-0000-C000-000000000046}"
IID_IVirtualDesktopNotification:="{C179334C-4295-40D3-BEA1-C654D965605A}"
CLSID_IVirtualNotificationService:="{A501FDEC-4A09-464C-AE4E-1B9C21B84918}"
IID_IVirtualDesktopNotificationService:="{0CD45E71-D927-4F15-8B0A-8FEF525337BF}"

https://github.com/MScholtes/VirtualDesktop/pull/29#issue-998833854 https://github.com/MScholtes/VirtualDesktop/pull/25

FuPeiJiang commented 2 years ago

https://github.com/Grabacr07/VirtualDesktop/pull/57#issuecomment-864395313 https://github.com/tyranid/oleviewdotnet

FuPeiJiang commented 2 years ago

Thanks to @NyaMisty https://github.com/mntone/VirtualDesktop/pull/1#issuecomment-922269079

FuPeiJiang commented 2 years ago

@TWF1212 notification, Win11 done

TWF1212 commented 2 years ago

@TWF1212 notification, Win11 done

Amazing, it is working perfectly! thank you so much.