VD.ahk gives AutoHotkey the virtual desktop API Windows never exposed to scripts
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
At a glance
- What is it?
- VD.ahk is a single AutoHotkey library that wraps the undocumented Windows virtual desktop interface so scripts can switch desktops, move windows between them, pin windows across all of them and wait for a desktop switch to finish. It suits an AutoHotkey user arranging a heavy multi-application workflow, and it is Windows-only and tied to the alpha channel of AutoHotkey 2.1.
- Who is it for?
- VD.ahk fits an AutoHotkey user who already thinks in hotkeys and wants the Windows virtual desktop to be scriptable, since the library gives you switching, moving, pinning and change notification as named functions rather than as undocumented COM calls. It does not fit a non-Windows user, and it does not fit someone on AutoHotkey 1.1, since the README asks for the 2.1 alpha channel and keeps separate v1 and v2_port branches.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 12 days ago.
- What is it written in?
- Mainly AutoHotkey, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One file, one namespace, and the API Windows kept private
VD.ahk exists because Windows exposes virtual desktop control to programs through an interface that is not part of the documented API surface. The project description makes the aim plain: switch desktop, move a window by window title to the current desktop, createDesktop, PinWindow, getCount, getDesktopNumOfWindow.
The implementation is a single AutoHotkey file, VD.ahk, alongside an examples file called `VD examples.ahk`, a notes/ directory, an other examples/ directory and the README. The whole namespace is prefixed VD, so a script includes the file and then calls VD.something.
The requirement is narrow and specific: AutoHotkey alpha, with the download page linked and the latest listed as AutoHotkey_2.1-alpha.18. There are separate branches for other AutoHotkey generations, a v1 branch at the class_VD branch and a v2 branch at the v2_port branch, and the default branch here is alpha. The note in the README is that the alpha branch is a rewrite, and that if functionality is missing the author will add it back.
That arrangement, one branch per AutoHotkey generation, is worth understanding before you start. The library is not a package you can install with a version constraint; it is a file you copy, and the copy has to match the AutoHotkey version you run.
The project is MIT licensed with the LICENSE file at the repository root, is not archived, and the last push was on 2026-10-11. There are no GitHub releases, so you track the branch rather than a version number.
Switching desktops is three lines of hotkey
The examples are the documentation, as the README says: just run the examples, everything explained inside.
Switching by absolute number is the simplest case.
$numpad1::VD.goToDesktopNum(1)
$numpad2::VD.goToDesktopNum(2)
$numpad3::VD.goToDesktopNum(3)The `$` prefix has a specific meaning here and the README calls it out: you should use `$` hotkeys if you want to spam them and avoid flashing icons. In AutoHotkey, `The examples are the documentation, as the README says: just run the examples, everything explained inside.
Switching by absolute number is the simplest case.
$numpad1::VD.goToDesktopNum(1)
$numpad2::VD.goToDesktopNum(2)
$numpad3::VD.goToDesktopNum(3)The `$` prefix has a specific meaning here and the README calls it out: you should use `$` hotkeys if you want to spam them and avoid flashing icons. forces the keyboard hook so the hotkey fires on the physical key, which the README recommends for hotkeys you spam, to avoid flashing icons.
Moving the active window works the same way, with a window title of `A` meaning the active window.
$numpad4::VD.MoveWindowToDesktopNum("A",1,true)
$numpad5::VD.MoveWindowToDesktopNum("A",2,true)
$numpad6::VD.MoveWindowToDesktopNum("A",3,true)The third argument is the follow flag. The README contrasts it with the plain version, which just moves the window and leaves you where you are.
$numpad7::VD.MoveWindowToDesktopNum("A",1)
$numpad8::VD.MoveWindowToDesktopNum("A",2)
$numpad9::VD.MoveWindowToDesktopNum("A",3)So the choice is between moving a window somewhere and moving it with you, which is the behaviour most people actually want, since the point of the hotkey is usually to work with that window on the new desktop.
Relative movement, and the wrapping behaviour at the end of the list
Moving between desktops by relative offset is what the arrow key bindings use, and the arrows come in two flavours because moving a window and moving yourself are different operations.
The plain arrow keys move you, and the comment in the example explains the edge case: wrapping, or cycling back to the first desktop when at the last.
; wrapping / cycle back to first desktop when at the last
$^#left::VD.goToRelativeDesktopNum(-1)
$^#right::VD.goToRelativeDesktopNum(+1)
; move window to left and follow it
$#!left::VD.MoveWindowToRelativeDesktopNum("A", -1, true)
; move window to right and follow it
$#!right::VD.MoveWindowToRelativeDesktopNum("A", 1, true)The modifiers differ between the two pairs. Ctrl plus Win plus arrow moves you, and Ctrl plus Alt plus arrow moves the window and follows it. The choice of Alt rather than Win for the second pair is what keeps the two bindings from colliding, since Win and arrow is already bound in Windows itself.
The relative functions take an offset rather than a number, so -1 is the previous desktop and +1 is the next. The wrapping behaviour is in the library rather than something you write, and it is the difference between a binding that stops at the ends and one that cycles.
Waiting for the desktop switch, which is the part you will get wrong
The single most useful thing in this library is a function the README mentions only in passing: when writing a script, you can use `VD.WaitDesktopSwitched()`.
The example is a paste into Microsoft Teams, and it shows the pattern. The script copies, records the current desktop number, waits for the switch to the desktop that holds the Teams window, then pastes, then waits for the switch back.
#include %A_LineFile%\..\VD.ahk
; paste image to ms-teams
$f1::{
SendInput "^c"
old_desktopNum := VD.getCurrentDesktopNum()
VD.WaitDesktopSwitched(
VD.goToDesktopOfWindow("ahk_exe ms-teams.exe")
)
SendInput "^r" ; focus the message composer
Sleep 100
SendInput "^v"
Sleep 100 ; Sleep after ^v but Before Switch
VD.WaitDesktopSwitched(
VD.goToDesktopNum(old_desktopNum)
)
}The window is addressed by an ahk_exe match on ms-teams.exe rather than by title, which is the more reliable identifier when a program has several windows or a changing title.
The two Sleep calls are doing something specific rather than being filler. The first waits after focusing the message composer so the focus change has landed before pasting, and the second comment says it explicitly: sleep after the paste but before the switch. Without the wait, the paste can be dispatched to a window that is in the middle of moving between desktops, and the keystrokes go to the wrong place.
The return trip uses the saved `old_desktopNum`, so the script returns to where it started rather than to desktop 1.
Listening for desktop changes and returning to where you were
The library also lets you detect that the current desktop changed, and the README shows how to build a hotkey that returns you to the previous desktop from that event.
#Include %A_LineFile%\..\VD.ahk
VD.previous_desktopNum := VD.getCurrentDesktopNum()
VD.ListenersCurrentVirtualDesktopChanged[(desktopNum_old, currentDesktopNum) {
VD.previous_desktopNum := desktopNum_old
ToolTip "changed from " desktopNum_old " to " currentDesktopNum
}] := 1
$Numpad0::VD.goToDesktopNum(VD.previous_desktopNum)The listener registration is a map assignment with a function as the value, keyed by the event name, and the callback receives the old and new desktop numbers. The map is called ListenersCurrentVirtualDesktopChanged and the assignment is set to 1, which is what registers it.
The pattern is worth copying. A hotkey that jumps to the previous desktop is hard to implement without an event, because the script has to know where you were before it moved you, and here the answer comes from the change event itself. The ToolTip line is a debugging aid that shows the transition.
The library also exposes `getCurrentDesktopNum()` for reading the current desktop as a number, which is what makes the round trip in the Teams example possible.
The rest of the virtual desktop API, and the parts still to come
Beyond switching and moving, the README lists the rest of what is covered.
There is `createDesktop()`, and there is renaming a desktop with `VD.setNameToDesktopNum("custom Desktop Name",desktopNum)`. The Windows 11 feature called Show this window on all desktops corresponds to `VD.PinWindow(wintitle)`, and Show windows from this app on all desktops corresponds to `VD.PinExe(exe_path)`. There is `getCount()`, which reports how many virtual desktops you now have, and `getDesktopNumOfWindow` for finding which desktop holds a window. The README summarises the coverage as pretty much everything virtual desktop, or so the author thinks.
There is also a clear boundary. The README states that Windows 11 has more functionality, and points at the VirtualDesktop helper by MScholtes as the source for the extra calls, listing MoveDesktop to move the current desktop to the desktop at an index from 0 to Count-1, SetDesktopName, SetDesktopWallpaper and GetDesktopIsPerMonitor, and anything else not on that list. Those are requests, with an invitation to open an issue saying which functions are needed and what you would use them for.
The author asks for the same thing about missing functionality in VD.ahk itself: open an issue with what you are using it for. That is a reasonable pattern for a library this small, and it means the function list will grow along with what people actually build.
Where VD.ahk is the wrong choice
Four constraints limit who should reach for this.
The first is the platform. It is Windows only, and specifically a Windows implementation of the virtual desktop interface, so a multi-platform script that needs this behaviour has no path through this library. The same applies to the AutoHotkey 1.1 user, since the alpha branch targets 2.1 alpha and the older generations live in separate branches you would have to find yourself.
The second is the fragility of the underlying interface. The project exists because the virtual desktop API is not a documented Windows API, and features such as the per-application pinning and the desktop wallpaper calls come from community reverse engineering. Windows updates can change that interface, and a library built on it carries that risk. The README's own note, that the alpha branch is a rewrite, is a reminder that this code has been reshuffled before.
The third is that this is a helper library, not a program. It gives you named functions, but the ergonomics of the hotkeys, the window matching, the workflow of pasting across desktops and the error handling are yours to write. The Teams example is a dozen lines of careful sequencing with sleeps, and that is a fair sample of the work involved.
The alternative is to use the Windows keyboard shortcuts directly. Win plus Ctrl plus the arrow keys switch desktops, Win plus Ctrl plus D creates one, and Win plus Ctrl plus the arrow keys with a window held by the same combination moves that window. That is free, needs no script, and handles the common cases. VD.ahk earns its place when you need the parts the shortcuts do not cover, which are switching to the desktop that holds a specific window, waiting for a switch to complete, pinning a window to all desktops, and reacting to desktop changes in a script.
Editorial conclusion
VD.ahk fits an AutoHotkey user who already thinks in hotkeys and wants the Windows virtual desktop to be scriptable, since the library gives you switching, moving, pinning and change notification as named functions rather than as undocumented COM calls. It does not fit a non-Windows user, and it does not fit someone on AutoHotkey 1.1, since the README asks for the 2.1 alpha channel and keeps separate v1 and v2_port branches. Copy the hotkey patterns from `VD examples.ahk`, and use `VD.WaitDesktopSwitched()` around any `VD.goToDesktopOfWindow` call, because without it the following keystrokes land on the wrong desktop.
Frequently asked questions
What does VD.ahk do?
It is an AutoHotkey library that wraps the Windows virtual desktop interface, letting a script switch desktops, move windows between desktops, pin a window or an application across all desktops, count desktops, rename them and wait for a desktop switch to complete.
Which version of AutoHotkey does VD.ahk need?
The README asks for AutoHotkey alpha, with the latest listed as AutoHotkey_2.1-alpha.18, and the default branch here is alpha, which the author describes as a rewrite. Separate branches exist for AutoHotkey v1 at class_VD and a v2 branch at v2_port.
How do I move a window to another virtual desktop in AutoHotkey?
Use VD.MoveWindowToDesktopNum with the window title and the desktop number, for example VD.MoveWindowToDesktopNum("A",1) where A means the active window. Passing true as a third argument follows the window to the new desktop, and there is a relative variant, VD.MoveWindowToRelativeDesktopNum("A", -1, true), which takes an offset instead.
Why do I need VD.WaitDesktopSwitched() in my script?
Because a desktop switch is not instantaneous, and keystrokes sent immediately after the switch can land on the wrong desktop. The README example wraps the switch call in VD.WaitDesktopSwitched and adds a Sleep after the paste but before the switch back, so the paste lands in the intended window.
How do I detect that the current virtual desktop changed?
Register a callback in VD.ListenersCurrentVirtualDesktopChanged, which receives the old and new desktop numbers. The README example stores the previous number in VD.previous_desktopNum so a hotkey can return you to the desktop you came from.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/fupeijiang-vd-ahk)Was this useful?