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

; 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()

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)

; enumerates all desktops
$iHresult = $oVirtualDesktopManagerInternal.GetDesktops($pArray)
$oArray = ObjCreateInterface($pArray, $IID_IObjectArray, $tagIObjectArray)
ConsoleWrite("Array = " & IsObj($oArray) & @CRLF)
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)


; 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


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.


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



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


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



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  😃



Edited by Hermano
Link to post
Share on other sites
  • 3 months later...

Up to now I am not really using Virtual Desktops, but this library made me think about it.  
I tried a wallpaper changer found in this forum to set individual desktops, but the new wallpaper is set for all VDT, just as if I do it via Windows desktop settings. 
So a static approach does not seem to work.

I wonder if a dynamic approach would work.
Could I use this library to react on a VDT switch (Ctrl+Win+Arrow) and set the wallpaper individually for the new VDT?

Edited by PowerJack
Link to post
Share on other sites
Posted (edited)

@PowerJack Sorry for the late reply, I was busy on another project.  I believe there is an interface that can actually set wallpaper individually.  It is called IActiveDesktop with the method SetWallpaper.  I will try to find some times to look it up more closely but it seems promising.

Edited by Nine
Link to post
Share on other sites

Well, well, my current OS (Win10 21H1) does not allow me to set individual wallpaper for each virtual desktops.  So I cannot test if this interface is working or not.  At present time, it changes the wallpaper to all VDT.  To test if you are allowed or not, press Win+Tab, right-click on the VDT you want to change, it should offer you the possibility (Choose background).  Like me, if it does not, it means your OS has not that feature.

Link to post
Share on other sites

Here is the code to change the wallpaper of individual virtual desktop.  If someone can test it, it would be greatly appreciated.  Just create a new VDT, navigate to it and run this script.  Since my OS does not allow it (see previous post), I do not know if it is actually working like expected.  

Opt("MustDeclareVars", True)

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

Global Const $AD_GETWP_IMAGE = 1
Global Const $AD_APPLY_ALL = 7

Global $CLSID_ActiveDesktop = "{75048700-ef1f-11d0-9888-006097deacf9}"
Global $IID_IActiveDesktop = "{f490eb00-1240-11d1-9888-006097deacf9}"

; implemented only first 3 methods
Global $tagIActiveDesktop = _
  "ApplyChanges hresult(dword);" & _
  "GetWallpaper hresult(struct*;uint;dword);" & _
  "SetWallpaper hresult(wstr;dword)"

; create object
Local $oActiveDesktop = ObjCreateInterface($CLSID_ActiveDesktop, $IID_IActiveDesktop, $tagIActiveDesktop)
ConsoleWrite(IsObj($oActiveDesktop) & @CRLF)

; get current wallpaper
Local $iHresult, $iLength = 100
Local $tName = DllStructCreate("wchar string[" & $iLength & "]")
$iHresult = $oActiveDesktop.GetWallpaper(DllStructGetPtr($tName), $iLength, $AD_GETWP_IMAGE)
ConsoleWrite($tName.string & "/" & $iHresult & @CRLF)

; set new wallpaper
$iHresult = $oActiveDesktop.SetWallpaper("C:\Windows\Web\Wallpaper\acer01.jpg", 0)  ; <<<<<<<<<<< set correct path to WP file
ConsoleWrite($iHresult & @CRLF)
$iHresult = $oActiveDesktop.ApplyChanges($AD_APPLY_ALL)
ConsoleWrite($iHresult & @CRLF)

Thank you all.

Link to post
Share on other sites

Thank you @Nine.

My Windows doesn’t support it either. My idea was to catch a VDT switch done by the user and then set a new wallpaper (for all DT). But the more I think about It the more it feels like overkill. 

I think your code example comes in handy as soon as Microsoft have done their homework on my PC or when I upgrade to Win11.

Link to post
Share on other sites
  • 4 months later...

Hi, I have a few questions:

1) will this work under Windows 11 as well? I ask because I notice that some virtual desktop tools I've been using before on win10 don't seem to work on win11

2) where can I find this 



function ?

Link to post
Share on other sites
Link to post
Share on other sites

I get 


: ==> Variable must be of type "Object".:
$iHresult = $oVirtualDesktopManagerInternal.GetCount($iCount)
$iHresult = $oVirtualDesktopManagerInternal^ ERROR

so I guess the answer is no, it doesn't work on windows 11 (

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...