Jump to content

Virtual Desktop Manager under Windows 10


Recommended Posts

Recently somebody asked how it could possible to manage the new feature of Virtual Desktop under Windows 10.  With the incentive of @KaFu I started to get something together that could be useful.  Since this code was never released under AutoIt, I felt it could serve as a good example.  Let me know what you think.

Version 2021-09-19

* Added support to IApplicationView and IVirtualDesktopPinnedApps interfaces (see example 2)

 

Example 1.  The documented interface of IVirtualDesktopManager offers a very limited list of methods.  Someone may ask WTF is it useful for ?  Well, I don't have an answer for that.

#include <Constants.au3>
#include <GUIConstants.au3>

If @OSVersion <> "WIN_10" Then Exit MsgBox($MB_SYSTEMMODAL, "", "This script only runs on Win 10")

; VirtualDesktopManager object
Local $CLSID_VirtualDesktopManager = "{aa509086-5ca9-4c25-8f95-589d3c07b48a}"
Local $IID_IVirtualDesktopManager = "{a5cd92ff-29be-454c-8d04-d82879fb3f1b}"
Local $tagIVirtualDesktopManager = _
    "IsWindowOnCurrentVirtualDesktop hresult(hwnd;bool*);" & _
    "GetWindowDesktopId hresult(hwnd;clsid*);" & _
    "MoveWindowToDesktop hresult(hwnd;clsid);"

; object creation
Local $oVDM = ObjCreateInterface($CLSID_VirtualDesktopManager, $IID_IVirtualDesktopManager, $tagIVirtualDesktopManager)
ConsoleWrite(IsObj($oVDM) & @CRLF)

; create process owned window
Local $hWnd = GUICreate("Test"), $bActive
GUISetState()

; check if window is on current desktop (not mandatory to own the window)
Local $iHresult = $oVDM.IsWindowOnCurrentVirtualDesktop($hWnd, $bActive)
ConsoleWrite($iHresult & "/" & $bActive & @CRLF)

; returns the CLSID of the desktop where the window resides
Local $sCLSID
$iHresult = $oVDM.GetWindowDesktopId($hWnd, $sCLSID)
ConsoleWrite($iHresult & "/" & $sCLSID & @CRLF)

Local $hNote = WinGetHandle("[CLASS:Notepad]")
$iHresult = $oVDM.GetWindowDesktopId($hNote, $sCLSID) ; (not mandatory to own the window)
ConsoleWrite($iHresult & "/" & $sCLSID & @CRLF)

; moves the window to a specific desktop (mandatory to own the window)
$iHresult = $oVDM.MoveWindowToDesktop($hWnd, $sCLSID)
ConsoleWrite($iHresult & @CRLF)

While True
  Switch GUIGetMsg()
    Case $GUI_EVENT_CLOSE
      Exit
  EndSwitch
WEnd

To perform this example you need to work a bit.  Create a second Desktop, start Notepad, go back to the first, and run this script: the GUI window will be transferred from the first desktop to the second.  Warning : you cannot use this method if you do not own the window (process-wise).

Example 2 : The "undocumented" interface of IVirtualDesktopManagerInternal has a lot more methods and resolves all the requests someone could have using Virtual Desktops.

#include <Constants.au3>

Opt("MustDeclareVars", True)

If @OSVersion <> "WIN_10" Then Exit MsgBox($MB_SYSTEMMODAL, "", "This script only runs on Win 10")

; Instanciation objects
Local $CLSID_ImmersiveShell = "{c2f03a33-21f5-47fa-b4bb-156362a2f239}"
Local $IID_IUnknown = "{00000000-0000-0000-c000-000000000046}"
Local $IID_IServiceProvider = "{6D5140C1-7436-11CE-8034-00AA006009FA}"
Local $tIID_IServiceProvider = __uuidof($IID_IServiceProvider)
Local $tagIServiceProvider  = _
    "QueryService hresult(struct*;struct*;ptr*);"

; VirtualDesktopManagerInternal object
Const Enum $eLeftDirection = 3, $eRightDirection
Local $CLSID_VirtualDesktopManagerInternal = "{C5E0CDCA-7B6E-41B2-9FC4-D93975CC467B}"
Local $tCLSID_VirtualDesktopManagerInternal = __uuidof($CLSID_VirtualDesktopManagerInternal)
Local $IID_IVirtualDesktopManagerInternal =  "{F31574D6-B682-4CDC-BD56-1827860ABEC6}"
Local $tIID_IVirtualDesktopManagerInternal = __uuidof($IID_IVirtualDesktopManagerInternal)
Local $tagIVirtualDesktopManagerInternal = _
    "GetCount hresult(int*);" & _
    "MoveViewToDesktop hresult(ptr;ptr);" & _
    "CanViewMoveDesktops hresult(ptr;bool*);" & _
    "GetCurrentDesktop hresult(ptr*);" & _
    "GetDesktops hresult(ptr*);" & _
    "GetAdjacentDesktop hresult(ptr;int;ptr*);" & _
    "SwitchDesktop hresult(ptr);" & _
    "CreateDesktopW hresult(int*);" & _
    "RemoveDesktop hresult(ptr;ptr);" & _
    "FindDesktop hresult(struct*;ptr*);"

; ApplicationViewCollection object
Local $CLSID_IApplicationViewCollection = "{1841C6D7-4F9D-42C0-AF41-8747538F10E5}"
Local $tCLSID_IApplicationViewCollection = __uuidof($CLSID_IApplicationViewCollection)
Local $IID_IApplicationViewCollection = "{1841C6D7-4F9D-42C0-AF41-8747538F10E5}"
Local $tIID_IApplicationViewCollection = __uuidof($IID_IApplicationViewCollection)
Local $tagIApplicationViewCollection = _
    "GetViews hresult(struct*);" & _
    "GetViewsByZOrder hresult(struct*);" & _
    "GetViewsByAppUserModelId hresult(wstr;struct*);" & _
    "GetViewForHwnd hresult(hwnd;ptr*);" & _
    "GetViewForApplication hresult(ptr;ptr*);" & _
    "GetViewForAppUserModelId hresult(wstr;int*);" & _
    "GetViewInFocus hresult(ptr*);"

; ApplicationView object
Local $IID_IApplicationView = "{372E1D3B-38D3-42E4-A15B-8AB2B178F513}"
Local $tagIApplicationView = _
    "GetIids hresult(ulong*;ptr*);" & _
    "GetRuntimeClassName hresult(str*);" & _
    "GetTrustLevel hresult(int*);" & _
    "SetFocus hresult();" & _
    "SwitchTo hresult();" & _
    "TryInvokeBack hresult(ptr);" & _
    "GetThumbnailWindow hresult(hwnd*);" & _
    "GetMonitor hresult(ptr*);" & _
    "GetVisibility hresult(int*);" & _
    "SetCloak hresult(int;int);" & _
    "GetPosition hresult(clsid;ptr*);" & _
    "SetPosition hresult(ptr);" & _
    "InsertAfterWindow hresult(hwnd);" & _
    "GetExtendedFramePosition hresult(struct*);" & _
    "GetAppUserModelId hresult(wstr*);" & _
    "SetAppUserModelId hresult(wstr);" & _
    "IsEqualByAppUserModelId hresult(wstr;int*);" & _
    "GetViewState hresult(uint*);" & _
    "SetViewState hresult(uint);" & _
    "GetNeediness hresult(int*);"

; VirtualDesktopPinnedApps object
Local $CLSID_VirtualDesktopPinnedApps = "{b5a399e7-1c87-46b8-88e9-fc5747b171bd}"
Local $tCLSID_VirtualDesktopPinnedApps = __uuidof($CLSID_VirtualDesktopPinnedApps)
Local $IID_IVirtualDesktopPinnedApps = "{4ce81583-1e4c-4632-a621-07a53543148f}"
Local $tIID_IVirtualDesktopPinnedApps = __uuidof($IID_IVirtualDesktopPinnedApps)
Local $tagIVirtualDesktopPinnedApps = _
    "IsAppIdPinned hresult(wstr;bool*);" & _
    "PinAppID hresult(wstr);" & _
    "UnpinAppID hresult(wstr);" & _
    "IsViewPinned hresult(ptr;bool*);" & _
    "PinView hresult(ptr);" & _
    "UnpinView hresult(ptr);"

; Miscellaneous objects
Local $IID_IObjectArray = "{92ca9dcd-5622-4bba-a805-5e9f541bd8c9}"
Local $tagIObjectArray = _
    "GetCount hresult(int*);" & _
    "GetAt hresult(int;ptr;ptr*);"
Local $IID_IVirtualDesktop = "{FF72FFDD-BE7E-43FC-9C03-AD81681E88E4}"
Local $tIID_IVirtualDesktop = __uuidof($IID_IVirtualDesktop)
Local $tagIVirtualDesktop = _
    "IsViewVisible hresult(ptr;bool*);" & _
    "GetId hresult(clsid*);"

; objects creation
Local $pService
Local $oImmersiveShell = ObjCreateInterface($CLSID_ImmersiveShell, $IID_IUnknown, "")
ConsoleWrite("Immersive shell = " & IsObj($oImmersiveShell) & @CRLF)
$oImmersiveShell.QueryInterface($tIID_IServiceProvider, $pService)
ConsoleWrite("Service pointer = " & $pService & @CRLF)
Local $oService = ObjCreateInterface($pService, $IID_IServiceProvider, $tagIServiceProvider)
ConsoleWrite("Service = " & IsObj($oService) & @CRLF)

Local $pApplicationViewCollection, $pVirtualDesktopManagerInternal, $pVirtualDesktopPinnedApps
$oService.QueryService($tCLSID_IApplicationViewCollection, $tIID_IApplicationViewCollection, $pApplicationViewCollection)
ConsoleWrite("View collection pointer = " & $pApplicationViewCollection & @CRLF)
Local $oApplicationViewCollection = ObjCreateInterface($pApplicationViewCollection, $IID_IApplicationViewCollection, $tagIApplicationViewCollection)
ConsoleWrite("View collection = " & IsObj($oApplicationViewCollection) & @CRLF)

$oService.QueryService($tCLSID_VirtualDesktopManagerInternal, $tIID_IVirtualDesktopManagerInternal, $pVirtualDesktopManagerInternal)
ConsoleWrite("Virtual Desktop pointer = " & $pVirtualDesktopManagerInternal & @CRLF)
Local $oVirtualDesktopManagerInternal = ObjCreateInterface($pVirtualDesktopManagerInternal, $IID_IVirtualDesktopManagerInternal, $tagIVirtualDesktopManagerInternal)
ConsoleWrite("Virtual Desktop = " & IsObj($oVirtualDesktopManagerInternal) & @CRLF)

$oService.QueryService($tCLSID_VirtualDesktopPinnedApps, $tIID_IVirtualDesktopPinnedApps, $pVirtualDesktopPinnedApps)
ConsoleWrite("Virtual Desktop Pinned Apps = " & $pVirtualDesktopPinnedApps & @CRLF)
Local $oVirtualDesktopPinnedApps = ObjCreateInterface($pVirtualDesktopPinnedApps, $IID_IVirtualDesktopPinnedApps, $tagIVirtualDesktopPinnedApps)
ConsoleWrite("Virtual Desktop Pinned Apps = " & IsObj($oVirtualDesktopPinnedApps) & @CRLF)

Local $iCount, $pCurrent, $pLeft, $pNew, $iHresult, $hWnd, $pView, $pArray, $pDesktop, $oArray, $oView, $sView, $bValue

; gives the number of virtual desktops
$iHresult = $oVirtualDesktopManagerInternal.GetCount($iCount)
ConsoleWrite("Number of Desktop = " & $iCount & "/" & $iHresult & @CRLF)
If $iCount > 1 Then Exit MsgBox($MB_SYSTEMMODAL, "", "Please close all additional Virtual Desktops")

; creates a new virtual desktop
$iHresult = $oVirtualDesktopManagerInternal.CreateDesktopW($pNew)
ConsoleWrite("Create = " & $pNew & "/" & $iHresult & @CRLF)
$iHresult = $oVirtualDesktopManagerInternal.SwitchDesktop($pNew)
ConsoleWrite("Switch = " & $iHresult & @CRLF)
Run("Notepad.exe")
WinWait("[CLASS:Notepad]")

; enumerates all desktops
$iHresult = $oVirtualDesktopManagerInternal.GetDesktops($pArray)
$oArray = ObjCreateInterface($pArray, $IID_IObjectArray, $tagIObjectArray)
ConsoleWrite("Array = " & IsObj($oArray) & @CRLF)
$oArray.GetCount($iCount)
ConsoleWrite("Count = " & $iCount & @CRLF)
$oArray.GetAt(0, DllStructGetPtr($tIID_IVirtualDesktop), $pDesktop)
ConsoleWrite("Desktop 0 = " & $pDesktop & @CRLF)
$oArray.GetAt(1, DllStructGetPtr($tIID_IVirtualDesktop), $pCurrent)
ConsoleWrite("Desktop 1 = " & $pCurrent & @CRLF)

; gives the current desktop id
$iHresult = $oVirtualDesktopManagerInternal.GetCurrentDesktop($pCurrent)
ConsoleWrite("Current = " & $pCurrent & "/" & $iHresult & @CRLF)

; returns the adjacent desktop id
$iHresult = $oVirtualDesktopManagerInternal.GetAdjacentDesktop($pCurrent, $eLeftDirection, $pLeft)
ConsoleWrite("Get Left = " & $pLeft & "/" & $iHresult & @CRLF)

; switches to a specific desktop
MsgBox ($MB_SYSTEMMODAL,"","Now it will return to previous desktop")
Sleep(500) ; gives time for the msg box to close
$iHresult = $oVirtualDesktopManagerInternal.SwitchDesktop($pLeft)
ConsoleWrite("Switch = " & $iHresult & @CRLF)

; get pointer to a view based on hwnd and create an application view
$hWnd = WinGetHandle("[CLASS:Notepad]")
$iHresult = $oApplicationViewCollection.GetViewForHwnd($hWnd, $pView)
ConsoleWrite("View from handle = " & $pView & "/" & $iHresult & @CRLF)
$oView = ObjCreateInterface($pView, $IID_IApplicationView, $tagIApplicationView)
ConsoleWrite("Application view = " & IsObj($oView) & @CRLF)
$iHresult = $oView.GetAppUserModelId($sView)
ConsoleWrite("Get App ID = " & $sView & "/" & $iHresult & @CRLF)

; verify if app is pinned with ptr and string
$iHresult = $oVirtualDesktopPinnedApps.IsViewPinned($pView, $bValue)
ConsoleWrite("Is View Pinned = " & $bValue & "/" & $iHresult & @CRLF)
$iHresult = $oVirtualDesktopPinnedApps.IsAppIdPinned($sView, $bValue)
ConsoleWrite("Is AppId Pinned = " & $bValue & "/" & $iHresult & @CRLF)

; move application to a specific desktop
$iHresult = $oVirtualDesktopManagerInternal.MoveViewToDesktop($pView, $pDesktop)
ConsoleWrite("Move = " & $iHresult & @CRLF)

Sleep(2000)

; deletes an existing desktop
$iHresult = $oVirtualDesktopManagerInternal.RemoveDesktop($pNew, $pDesktop)
ConsoleWrite("Delete = " & $iHresult & @CRLF)

Func __uuidof($sGUID)
    Local $tGUID = DllStructCreate("ulong Data1;ushort Data2;ushort Data3;byte Data4[8]")
    DllCall("ole32.dll", "long", "CLSIDFromString", "wstr", $sGUID, "struct*", $tGUID)
    If @error Then Return SetError(@error, @extended, 0)
    Return $tGUID
EndFunc   ;==>__uuidof

Enjoy.

Edited by Nine
Link to post
Share on other sites
  • 3 weeks later...

your job is stunning and a great start. Let's see if i can help with a little more CODE

Local $tagIVirtualDesktopPinnedApps = _
    "IsAppIdPinned hresult(wstr,bool*);" & _        ; this is the hwnd but might need to be passed as (string appId)
    "PinAppID hresult(wstr);" & _
    "UnpinAppID hresult(wstr);" & _
    "IsViewPinned hresult(ptr,bool*);" & _
    "PinView hresult(ptr);" & _
    "UnpinView hresult(ptr);"
       
   ; ------
        
$oService.QueryService($tCLSID_VirtualDesktopPinnedApps, $tIID_IVirtualDesktopPinnedApps, $pVirtualDesktopPinnedApps)
ConsoleWrite("Virtual Desktop Pinned Apps = " & $pVirtualDesktopPinnedApps & @CRLF)
Local $oVirtualDesktopPinnedApps = ObjCreateInterface($pVirtualDesktopPinnedApps, $IID_IVirtualDesktopPinnedApps, $tagIVirtualDesktopPinnedApps)
ConsoleWrite("Virtual Desktop Pinned Apps = " & IsObj($oVirtualDesktopPinnedApps) & @CRLF)

;-------------
$iHresult = $oVirtualDesktopPinnedApps.PinView($pView)
ConsoleWrite("Pinned View = " & $iHresult & @CRLF)
$iHresult = $oVirtualDesktopPinnedApps.UnpinView($pView)
ConsoleWrite("UnPinned View = " & $iHresult & @CRLF)

while everying works great if we pin the view i cannot get the PinAppID mode to work correctly. As a matter of fact it requires the appId to work and I do not know how to source that information properly.

Trust somebody can complete this, so we can finally create an amazing virtual desktop manager for win10/11 with few simple Autoit lines

Link to post
Share on other sites

Here I think how you could do. 

1- Get the ptr view of the hwnd with ApplicationViewCollection object (as already shown in my example)

2- Create an ApplicationView object with this ptr

3- Use GetAppUserModelId method to obtain the wstr of the AppID

4- Use that string within *AppId* method of VirtualDesktopPinnedApps object.

Untested.

If that works, please provide FULL snippet of the code you have created.  Thanks.

Link to post
Share on other sites
Local $IID_IApplicationView = "{372E1D3B-38D3-42E4-A15B-8AB2B178F513}"
; need to define tag of this here

...

$hWnd = WinGetHandle("[CLASS:Notepad]")
$iHresult = $oApplicationViewCollection.GetViewForHwnd($hWnd, $pView)
ConsoleWrite("View from handle = " & $pView & "/" & $iHresult & @CRLF)

Local $oView = ObjCreateInterface($pView, $IID_IApplicationView, "")  ; no need for tag to create object
ConsoleWrite("Application view = " & IsObj($oView) & @CRLF)

Works for me...So step 2 resolved :)

Link to post
Share on other sites
  •  ☺️
  •  may be you want to edit
  • since you did most of the work you might want to add the option to pin a view to all desktops, it is tested and works.
  • I still need to test the possibility to pin the appID which will allow all sub-windows opened by the app to stay pinned to all desktops
  • I am currently building the whole app to have a small virtual-desktop manager up and running in the Taskbar. With your permission I might upload the whole code when finished in my area

 

$eRightDirection=4

Link to post
Share on other sites
29 minutes ago, Hermano said:

$eRightDirection=4

No need to specify = 4 since the previous is at 3.

30 minutes ago, Hermano said:

With your permission I might upload the whole code when finished in my area

Glad you can run on my example.  No problem.

Link to post
Share on other sites

😓

VIRTUAL DESKTOP MANAGER LIBRARY FOR WINDOWS 10 (11 NOT TESTED)

it has been a lot of work and testing. I could find some minor imperfections in the previous snippets we wrote and finally here is the full library to properly manage win10 virtual desktop system. Every function has been tested but I am sure that our great community will find several improvements and adds-on we did not think of. Without all your contribution this would not have been possible.

The library has 4 examples embedded and commented (please do not hesitate to add more). The objects created offers lots of extra tags that I did not use

The core objects created where:

Global $oVirtualDesktopManagerInternal
Global $oApplicationViewCollection
Global $oVirtualDesktopPinnedApps
Local $oApplicationView

I list them here so that anyone interested in using the same objects for other functions can easily find a reference

Enjoy  😃

 

VritDsktLibrary.au3

Edited by Hermano
Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...