Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
TWF1212 opened this issue Jun 1, 2022 · 12 comments

Comments

@TWF1212
Copy link

TWF1212 commented Jun 1, 2022

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
Copy link
Contributor

me too.

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

@FuPeiJiang
Copy link
Owner

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
Copy link
Owner

FuPeiJiang commented Jun 29, 2022

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
Copy link
Owner

28 days ago, hmmm
I somehow missed this notification

@FuPeiJiang
Copy link
Owner

FuPeiJiang commented Jun 29, 2022

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 FuPeiJiang changed the title Is it possible to have a function registered to run whenever the virutal desktop changes? Is it possible to have a function registered to run whenever the virtual desktop changes? Jun 30, 2022
@FuPeiJiang
Copy link
Owner

it is done, other examples\CurrentVirtualDesktopChanged RegisterDesktopNotifications.ahk, 73e52f9

#Include %A_LineFile%\..\..\VD.ahk
VD.RegisterDesktopNotifications()
VD.CurrentVirtualDesktopChanged:=Func("CurrentVirtualDesktopChanged")
CurrentVirtualDesktopChanged(desktopNum_Old, desktopNum_New) {
ToolTip % desktopNum_Old ", " desktopNum_New
}

@TWF1212
Copy link
Author

TWF1212 commented Jun 30, 2022

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
Copy link
Owner

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}"

MScholtes/VirtualDesktop#29 (comment)
MScholtes/VirtualDesktop#25

@FuPeiJiang
Copy link
Owner

@FuPeiJiang
Copy link
Owner

@FuPeiJiang
Copy link
Owner

@TWF1212 notification, Win11 done

@TWF1212
Copy link
Author

TWF1212 commented Jul 5, 2022

@TWF1212 notification, Win11 done

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

@TWF1212 TWF1212 closed this as completed Jul 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants