├── .gitignore ├── LICENSE.txt ├── README.markdown ├── TestVirtualDesktopAccessorWin32 ├── TestVirtualDesktopAccessorWin32.cpp ├── TestVirtualDesktopAccessorWin32.h ├── TestVirtualDesktopAccessorWin32.vcxproj ├── TestVirtualDesktopAccessorWin32.vcxproj.filters ├── stdafx.cpp ├── stdafx.h └── targetver.h ├── VirtualDesktopAccessor.sln ├── VirtualDesktopAccessor ├── VirtualDesktopAccessor.vcxproj ├── VirtualDesktopAccessor.vcxproj.filters ├── Win10Desktops.h ├── dllmain.cpp ├── dllmain.def ├── dllmain.h ├── stdafx.cpp ├── stdafx.h └── targetver.h └── x64 └── Release └── VirtualDesktopAccessor.dll /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | *VC.* 25 | 26 | # Visual Studio 2015 cache/options directory 27 | .vs/ 28 | # Uncomment if you have tasks that create the project's static files in wwwroot 29 | #wwwroot/ 30 | 31 | # MSTest test Results 32 | [Tt]est[Rr]esult*/ 33 | [Bb]uild[Ll]og.* 34 | 35 | # NUNIT 36 | *.VisualState.xml 37 | TestResult.xml 38 | 39 | # Build Results of an ATL Project 40 | [Dd]ebugPS/ 41 | [Rr]eleasePS/ 42 | dlldata.c 43 | 44 | # DNX 45 | project.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | 84 | # Visual Studio profiler 85 | *.psess 86 | *.vsp 87 | *.vspx 88 | *.sap 89 | 90 | # TFS 2012 Local Workspace 91 | $tf/ 92 | 93 | # Guidance Automation Toolkit 94 | *.gpState 95 | 96 | # ReSharper is a .NET coding add-in 97 | _ReSharper*/ 98 | *.[Rr]e[Ss]harper 99 | *.DotSettings.user 100 | 101 | # JustCode is a .NET coding add-in 102 | .JustCode 103 | 104 | # TeamCity is a build add-in 105 | _TeamCity* 106 | 107 | # DotCover is a Code Coverage Tool 108 | *.dotCover 109 | 110 | # NCrunch 111 | _NCrunch_* 112 | .*crunch*.local.xml 113 | nCrunchTemp_* 114 | 115 | # MightyMoose 116 | *.mm.* 117 | AutoTest.Net/ 118 | 119 | # Web workbench (sass) 120 | .sass-cache/ 121 | 122 | # Installshield output folder 123 | [Ee]xpress/ 124 | 125 | # DocProject is a documentation generator add-in 126 | DocProject/buildhelp/ 127 | DocProject/Help/*.HxT 128 | DocProject/Help/*.HxC 129 | DocProject/Help/*.hhc 130 | DocProject/Help/*.hhk 131 | DocProject/Help/*.hhp 132 | DocProject/Help/Html2 133 | DocProject/Help/html 134 | 135 | # Click-Once directory 136 | publish/ 137 | 138 | # Publish Web Output 139 | *.[Pp]ublish.xml 140 | *.azurePubxml 141 | # TODO: Comment the next line if you want to checkin your web deploy settings 142 | # but database connection strings (with potential passwords) will be unencrypted 143 | *.pubxml 144 | *.publishproj 145 | 146 | # NuGet Packages 147 | *.nupkg 148 | # The packages folder can be ignored because of Package Restore 149 | **/packages/* 150 | # except build/, which is used as an MSBuild target. 151 | !**/packages/build/ 152 | # Uncomment if necessary however generally it will be regenerated when needed 153 | #!**/packages/repositories.config 154 | 155 | # Windows Azure Build Output 156 | csx/ 157 | *.build.csdef 158 | 159 | # Windows Azure Emulator 160 | efc/ 161 | rfc/ 162 | 163 | # Windows Store app package directory 164 | AppPackages/ 165 | 166 | # Visual Studio cache files 167 | # files ending in .cache can be ignored 168 | *.[Cc]ache 169 | # but keep track of directories ending in .cache 170 | !*.[Cc]ache/ 171 | 172 | # Others 173 | ClientBin/ 174 | [Ss]tyle[Cc]op.* 175 | ~$* 176 | *~ 177 | *.dbmdl 178 | *.dbproj.schemaview 179 | *.pfx 180 | *.publishsettings 181 | node_modules/ 182 | orleans.codegen.cs 183 | 184 | # RIA/Silverlight projects 185 | Generated_Code/ 186 | 187 | # Backup & report files from converting an old project file 188 | # to a newer Visual Studio version. Backup files are not needed, 189 | # because we have git ;-) 190 | _UpgradeReport_Files/ 191 | Backup*/ 192 | UpgradeLog*.XML 193 | UpgradeLog*.htm 194 | 195 | # SQL Server files 196 | *.mdf 197 | *.ldf 198 | 199 | # Business Intelligence projects 200 | *.rdl.data 201 | *.bim.layout 202 | *.bim_*.settings 203 | 204 | # Microsoft Fakes 205 | FakesAssemblies/ 206 | 207 | # GhostDoc plugin setting file 208 | *.GhostDoc.xml 209 | 210 | # Node.js Tools for Visual Studio 211 | .ntvs_analysis.dat 212 | 213 | # Visual Studio 6 build log 214 | *.plg 215 | 216 | # Visual Studio 6 workspace options file 217 | *.opt 218 | 219 | # Visual Studio LightSwitch build output 220 | **/*.HTMLClient/GeneratedArtifacts 221 | **/*.DesktopClient/GeneratedArtifacts 222 | **/*.DesktopClient/ModelManifest.xml 223 | **/*.Server/GeneratedArtifacts 224 | **/*.Server/ModelManifest.xml 225 | _Pvt_Extensions 226 | 227 | # Paket dependency manager 228 | .paket/paket.exe 229 | 230 | # FAKE - F# Make 231 | .fake/ 232 | 233 | !x64/Release/VirtualDesktopAccessor.dll -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, 2016 Jari Pennanen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # VirtualDesktopAccessor.dll 2 | 3 | DLL for accessing Windows 10 (tested with build 17134.48) Virtual Desktop features from e.g. AutoHotkey. MIT Licensed, see LICENSE.txt (c) Jari Pennanen, 2015-2018 4 | 5 | Download the VirtualDesktopAccessor.dll from directory x64\Release\VirtualDesktopAccessor.dll in the repository. This DLL works only on 64 bit Windows 10. 6 | 7 | You probably first need the [VS 2017 runtimes vc_redist.x64.exe and/or vc_redist.x86.exe](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads), if they are not installed already. I've built the DLL using VS 2017, and Microsoft is not providing those runtimes (who knows why) with Windows 10 yet. 8 | 9 | ## AutoHotkey script as example: 10 | 11 | ```AutoHotkey 12 | DetectHiddenWindows, On 13 | hwnd:=WinExist("ahk_pid " . DllCall("GetCurrentProcessId","Uint")) 14 | hwnd+=0x1000<<32 15 | 16 | hVirtualDesktopAccessor := DllCall("LoadLibrary", Str, "C:\Source\CandCPP\VirtualDesktopAccessor\x64\Release\VirtualDesktopAccessor.dll", "Ptr") 17 | GoToDesktopNumberProc := DllCall("GetProcAddress", Ptr, hVirtualDesktopAccessor, AStr, "GoToDesktopNumber", "Ptr") 18 | GetCurrentDesktopNumberProc := DllCall("GetProcAddress", Ptr, hVirtualDesktopAccessor, AStr, "GetCurrentDesktopNumber", "Ptr") 19 | IsWindowOnCurrentVirtualDesktopProc := DllCall("GetProcAddress", Ptr, hVirtualDesktopAccessor, AStr, "IsWindowOnCurrentVirtualDesktop", "Ptr") 20 | MoveWindowToDesktopNumberProc := DllCall("GetProcAddress", Ptr, hVirtualDesktopAccessor, AStr, "MoveWindowToDesktopNumber", "Ptr") 21 | RegisterPostMessageHookProc := DllCall("GetProcAddress", Ptr, hVirtualDesktopAccessor, AStr, "RegisterPostMessageHook", "Ptr") 22 | UnregisterPostMessageHookProc := DllCall("GetProcAddress", Ptr, hVirtualDesktopAccessor, AStr, "UnregisterPostMessageHook", "Ptr") 23 | IsPinnedWindowProc := DllCall("GetProcAddress", Ptr, hVirtualDesktopAccessor, AStr, "IsPinnedWindow", "Ptr") 24 | RestartVirtualDesktopAccessorProc := DllCall("GetProcAddress", Ptr, hVirtualDesktopAccessor, AStr, "RestartVirtualDesktopAccessor", "Ptr") 25 | ; GetWindowDesktopNumberProc := DllCall("GetProcAddress", Ptr, hVirtualDesktopAccessor, AStr, "GetWindowDesktopNumber", "Ptr") 26 | activeWindowByDesktop := {} 27 | 28 | ; Restart the virtual desktop accessor when Explorer.exe crashes, or restarts (e.g. when coming from fullscreen game) 29 | explorerRestartMsg := DllCall("user32\RegisterWindowMessage", "Str", "TaskbarCreated") 30 | OnMessage(explorerRestartMsg, "OnExplorerRestart") 31 | OnExplorerRestart(wParam, lParam, msg, hwnd) { 32 | global RestartVirtualDesktopAccessorProc 33 | DllCall(RestartVirtualDesktopAccessorProc, UInt, result) 34 | } 35 | 36 | MoveCurrentWindowToDesktop(number) { 37 | global MoveWindowToDesktopNumberProc, GoToDesktopNumberProc, activeWindowByDesktop 38 | WinGet, activeHwnd, ID, A 39 | activeWindowByDesktop[number] := 0 ; Do not activate 40 | DllCall(MoveWindowToDesktopNumberProc, UInt, activeHwnd, UInt, number) 41 | DllCall(GoToDesktopNumberProc, UInt, number) 42 | } 43 | 44 | GoToPrevDesktop() { 45 | global GetCurrentDesktopNumberProc, GoToDesktopNumberProc 46 | current := DllCall(GetCurrentDesktopNumberProc, UInt) 47 | if (current = 0) { 48 | GoToDesktopNumber(7) 49 | } else { 50 | GoToDesktopNumber(current - 1) 51 | } 52 | return 53 | } 54 | 55 | GoToNextDesktop() { 56 | global GetCurrentDesktopNumberProc, GoToDesktopNumberProc 57 | current := DllCall(GetCurrentDesktopNumberProc, UInt) 58 | if (current = 7) { 59 | GoToDesktopNumber(0) 60 | } else { 61 | GoToDesktopNumber(current + 1) 62 | } 63 | return 64 | } 65 | 66 | GoToDesktopNumber(num) { 67 | global GetCurrentDesktopNumberProc, GoToDesktopNumberProc, IsPinnedWindowProc, activeWindowByDesktop 68 | 69 | ; Store the active window of old desktop, if it is not pinned 70 | WinGet, activeHwnd, ID, A 71 | current := DllCall(GetCurrentDesktopNumberProc, UInt) 72 | isPinned := DllCall(IsPinnedWindowProc, UInt, activeHwnd) 73 | if (isPinned == 0) { 74 | activeWindowByDesktop[current] := activeHwnd 75 | } 76 | 77 | ; Try to avoid flashing task bar buttons, deactivate the current window if it is not pinned 78 | if (isPinned != 1) { 79 | WinActivate, ahk_class Shell_TrayWnd 80 | } 81 | 82 | ; Change desktop 83 | DllCall(GoToDesktopNumberProc, Int, num) 84 | return 85 | } 86 | 87 | ; Windows 10 desktop changes listener 88 | DllCall(RegisterPostMessageHookProc, Int, hwnd, Int, 0x1400 + 30) 89 | OnMessage(0x1400 + 30, "VWMess") 90 | VWMess(wParam, lParam, msg, hwnd) { 91 | global IsWindowOnCurrentVirtualDesktopProc, IsPinnedWindowProc, activeWindowByDesktop 92 | 93 | desktopNumber := lParam + 1 94 | 95 | ; Try to restore active window from memory (if it's still on the desktop and is not pinned) 96 | WinGet, activeHwnd, ID, A 97 | isPinned := DllCall(IsPinnedWindowProc, UInt, activeHwnd) 98 | oldHwnd := activeWindowByDesktop[lParam] 99 | isOnDesktop := DllCall(IsWindowOnCurrentVirtualDesktopProc, UInt, oldHwnd, UInt) 100 | if (isOnDesktop == 1 && isPinned != 1) { 101 | WinActivate, ahk_id %oldHwnd% 102 | } 103 | 104 | ; Menu, Tray, Icon, Icons/icon%desktopNumber%.ico 105 | 106 | ; When switching to desktop 1, set background pluto.jpg 107 | ; if (lParam == 0) { 108 | ; DllCall("SystemParametersInfo", UInt, 0x14, UInt, 0, Str, "C:\Users\Jarppa\Pictures\Backgrounds\saturn.jpg", UInt, 1) 109 | ; When switching to desktop 2, set background DeskGmail.png 110 | ; } else if (lParam == 1) { 111 | ; DllCall("SystemParametersInfo", UInt, 0x14, UInt, 0, Str, "C:\Users\Jarppa\Pictures\Backgrounds\DeskGmail.png", UInt, 1) 112 | ; When switching to desktop 7 or 8, set background DeskMisc.png 113 | ; } else if (lParam == 2 || lParam == 3) { 114 | ; DllCall("SystemParametersInfo", UInt, 0x14, UInt, 0, Str, "C:\Users\Jarppa\Pictures\Backgrounds\DeskMisc.png", UInt, 1) 115 | ; Other desktops, set background to DeskWork.png 116 | ; } else { 117 | ; DllCall("SystemParametersInfo", UInt, 0x14, UInt, 0, Str, "C:\Users\Jarppa\Pictures\Backgrounds\DeskWork.png", UInt, 1) 118 | ; } 119 | } 120 | 121 | ; Switching desktops: 122 | ; Win + Ctrl + 1 = Switch to desktop 1 123 | *#1::GoToDesktopNumber(0) 124 | 125 | ; Win + Ctrl + 2 = Switch to desktop 2 126 | *#2::GoToDesktopNumber(1) 127 | 128 | ; Moving windowes: 129 | ; Win + Shift + 1 = Move current window to desktop 1, and go there 130 | +#2::MoveCurrentWindowToDesktop(1) 131 | ``` 132 | 133 | ## All functions exported by DLL: 134 | 135 | * int GetCurrentDesktopNumber() 136 | * int GetDesktopCount() 137 | * GUID GetDesktopIdByNumber(int number) // Returns zeroed GUID with invalid number found 138 | * int GetDesktopNumber(IVirtualDesktop *pDesktop) 139 | * int GetDesktopNumberById(GUID desktopId) 140 | * GUID GetWindowDesktopId(HWND window) 141 | * int GetWindowDesktopNumber(HWND window) 142 | * int IsWindowOnCurrentVirtualDesktop(HWND window) 143 | * BOOL MoveWindowToDesktopNumber(HWND window, int number) 144 | * void GoToDesktopNumber(int number) 145 | * void RegisterPostMessageHook(HWND listener, int messageOffset) 146 | * void UnregisterPostMessageHook(HWND hwnd) 147 | * int IsPinnedWindow(HWND hwnd) // Returns 1 if pinned, 0 if not pinned, -1 if not valid 148 | * void PinWindow(HWND hwnd) 149 | * void UnPinWindow(HWND hwnd) 150 | * int IsPinnedApp(HWND hwnd) // Returns 1 if pinned, 0 if not pinned, -1 if not valid 151 | * void PinApp(HWND hwnd) 152 | * void UnPinApp(HWND hwnd) 153 | * int IsWindowOnDesktopNumber(HWND window, int number) / 154 | * void RestartVirtualDesktopAccessor() // Call this during taskbar created message 155 | 156 | * void EnableKeepMinimized() // Deprecated, does nothing 157 | * void RestoreMinimized() // Deprecated, does nothing 158 | -------------------------------------------------------------------------------- /TestVirtualDesktopAccessorWin32/TestVirtualDesktopAccessorWin32.cpp: -------------------------------------------------------------------------------- 1 | // TestVirtualDesktopAccessorWin32.cpp : Defines the entry point for the application. 2 | // 3 | 4 | #include "stdafx.h" 5 | #include "TestVirtualDesktopAccessorWin32.h" 6 | #include "dllmain.h" 7 | 8 | WCHAR szClassName[] = L"TestVirtualDesktopAccesorWin32"; 9 | 10 | #define MESSAGE_OFFSET WM_USER + 60 11 | 12 | LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 13 | { 14 | 15 | switch (message) { 16 | case WM_DESTROY: 17 | PostQuitMessage(0); 18 | break; 19 | case MESSAGE_OFFSET + VDA_CurrentVirtualDesktopChanged: 20 | std::wcout << L"CurrentVirtualDesktopChanged old: " << wParam << " new:" << lParam << "\r\n"; 21 | break; 22 | case MESSAGE_OFFSET + VDA_ViewVirtualDesktopChanged: 23 | std::wcout << L"CurrentVirtualDesktopChanged old: " << wParam << " new:" << lParam << "\r\n"; 24 | break; 25 | case MESSAGE_OFFSET + VDA_VirtualDesktopCreated: 26 | std::wcout << L"CurrentVirtualDesktopChanged old: " << wParam << " new:" << lParam << "\r\n"; 27 | break; 28 | case MESSAGE_OFFSET + VDA_VirtualDesktopDestroyBegin: 29 | std::wcout << L"CurrentVirtualDesktopChanged old: " << wParam << " new:" << lParam << "\r\n"; 30 | break; 31 | case MESSAGE_OFFSET + VDA_VirtualDesktopDestroyed: 32 | std::wcout << L"CurrentVirtualDesktopChanged old: " << wParam << " new:" << lParam << "\r\n"; 33 | break; 34 | case MESSAGE_OFFSET + VDA_VirtualDesktopDestroyFailed: 35 | std::wcout << L"CurrentVirtualDesktopChanged old: " << wParam << " new:" << lParam << "\r\n"; 36 | break; 37 | default: 38 | return DefWindowProc(hWnd, message, wParam, lParam); 39 | } 40 | return 0; 41 | } 42 | 43 | int APIENTRY wWinMain(_In_ HINSTANCE hInstance, 44 | _In_opt_ HINSTANCE hPrevInstance, 45 | _In_ LPWSTR lpCmdLine, 46 | _In_ int nCmdShow) 47 | { 48 | HWND hwnd; 49 | MSG messages; 50 | WNDCLASSEX wincl; 51 | 52 | wincl.hInstance = hInstance; 53 | wincl.lpszClassName = szClassName; 54 | wincl.lpfnWndProc = WndProc; 55 | wincl.style = CS_DBLCLKS; 56 | wincl.cbSize = sizeof(WNDCLASSEX); 57 | wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION); 58 | wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION); 59 | wincl.hCursor = LoadCursor(NULL, IDC_ARROW); 60 | wincl.lpszMenuName = NULL; 61 | wincl.cbClsExtra = 0; 62 | wincl.cbWndExtra = 0; 63 | wincl.hbrBackground = (HBRUSH)COLOR_BACKGROUND; 64 | 65 | if (!RegisterClassEx(&wincl)) 66 | return 0; 67 | 68 | hwnd = CreateWindowEx(0, 69 | szClassName, 70 | L"TestVirtualDesktopAccesorWin32", 71 | WS_OVERLAPPEDWINDOW, 72 | CW_USEDEFAULT, 73 | CW_USEDEFAULT, 74 | 544, 75 | 375, 76 | HWND_DESKTOP, 77 | NULL, 78 | hInstance, 79 | NULL 80 | ); 81 | 82 | ShowWindow(hwnd, SW_HIDE); 83 | _OpenDllWindow(hInstance); 84 | 85 | RegisterPostMessageHook(hwnd, MESSAGE_OFFSET); 86 | std::wcout << "Desktops: " << GetDesktopCount() << "\r\n"; 87 | std::wcout << "Console Window's Desktop Number: " << GetWindowDesktopNumber(GetConsoleWindow()) << std::endl; 88 | std::wcout << "Current Desktop Number: " << GetCurrentDesktopNumber() << "\r\n"; 89 | 90 | HWND notepad = FindWindow(_T("Notepad"), NULL); 91 | if (notepad != 0) { 92 | int number = GetCurrentDesktopNumber(); 93 | 94 | std::wcout << "Is notepad on this desktop: " << IsWindowOnDesktopNumber(notepad, number) << std::endl; 95 | 96 | // Test pinning it 97 | std::wcout << "Try pinning the notepad (the window)." << std::endl; 98 | PinWindow(notepad); 99 | std::wcout << "Is notepad pinned?" << IsPinnedWindow(notepad) << std::endl; 100 | GoToDesktopNumber(number + 1); 101 | Sleep(2000); 102 | GoToDesktopNumber(number); 103 | UnPinWindow(notepad); 104 | std::wcout << "Is notepad pinned?" << IsPinnedWindow(notepad) << std::endl; 105 | 106 | Sleep(2000); 107 | 108 | // Test pinning the whole app! 109 | std::wcout << "Try pinning the notepad (the app executable)." << std::endl; 110 | PinApp(notepad); 111 | std::wcout << "Is notepad pinned app?" << IsPinnedApp(notepad) << std::endl; 112 | GoToDesktopNumber(number + 1); 113 | Sleep(2000); 114 | GoToDesktopNumber(number); 115 | UnPinApp(notepad); 116 | std::wcout << "Is notepad pinned app?" << IsPinnedApp(notepad) << std::endl; 117 | 118 | // Test moving it 119 | if (!IsWindowOnCurrentVirtualDesktop(notepad)) { 120 | MoveWindowToDesktopNumber(notepad, number); 121 | } 122 | std::wcout << "Notepad is on on the current desktop. " << std::endl; 123 | Sleep(1000); 124 | MoveWindowToDesktopNumber(notepad, number + 1); 125 | std::wcout << "Notepad should now have been moved to the next desktop." << std::endl; 126 | Sleep(1000); 127 | MoveWindowToDesktopNumber(notepad, number); 128 | } 129 | else { 130 | std::wcout << "Start notepad to try moving a window, or pinning it" << std::endl; 131 | } 132 | 133 | 134 | GUID g = GetDesktopIdByNumber(GetCurrentDesktopNumber()); 135 | WCHAR text[255]; 136 | StringFromGUID2(g, &text[0], 255); 137 | std::wcout << "Current Desktop GUID: " << text << std::endl; 138 | 139 | GUID g2 = GetWindowDesktopId(GetConsoleWindow()); 140 | WCHAR text2[255]; 141 | StringFromGUID2(g2, &text2[0], 255); 142 | std::wcout << "Console Window's Desktop GUID: " << text2 << std::endl; 143 | while (GetMessage(&messages, NULL, 0, 0)) 144 | { 145 | TranslateMessage(&messages); 146 | DispatchMessage(&messages); 147 | } 148 | 149 | UnregisterPostMessageHook(hwnd); 150 | 151 | return messages.wParam; 152 | } 153 | 154 | 155 | int main() { 156 | return wWinMain(GetModuleHandle(NULL), NULL, GetCommandLine(), SW_SHOW); 157 | } 158 | -------------------------------------------------------------------------------- /TestVirtualDesktopAccessorWin32/TestVirtualDesktopAccessorWin32.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | -------------------------------------------------------------------------------- /TestVirtualDesktopAccessorWin32/TestVirtualDesktopAccessorWin32.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {FA401C23-A170-45D0-BB2C-9F21A8E07B2C} 23 | Win32Proj 24 | TestVirtualDesktopAccessorWin32 25 | 10.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v143 32 | Unicode 33 | 34 | 35 | Application 36 | false 37 | v143 38 | true 39 | Unicode 40 | 41 | 42 | Application 43 | true 44 | v143 45 | Unicode 46 | 47 | 48 | Application 49 | false 50 | v143 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | 75 | 76 | true 77 | 78 | 79 | false 80 | 81 | 82 | false 83 | $(SolutionDir)$(Platform)\$(Configuration)\ 84 | 85 | 86 | 87 | Use 88 | Level3 89 | Disabled 90 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 91 | true 92 | ..\VirtualDesktopAccessor;%(AdditionalIncludeDirectories) 93 | 94 | 95 | Console 96 | true 97 | 98 | 99 | 100 | 101 | Use 102 | Level3 103 | Disabled 104 | _DEBUG;_WINDOWS;%(PreprocessorDefinitions) 105 | true 106 | ..\VirtualDesktopAccessor;%(AdditionalIncludeDirectories) 107 | 108 | 109 | Console 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | Use 117 | MaxSpeed 118 | true 119 | true 120 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 121 | true 122 | ..\VirtualDesktopAccessor;%(AdditionalIncludeDirectories) 123 | 124 | 125 | Console 126 | true 127 | true 128 | true 129 | 130 | 131 | 132 | 133 | Level3 134 | Use 135 | MaxSpeed 136 | true 137 | true 138 | NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 139 | true 140 | ..\VirtualDesktopAccessor;%(AdditionalIncludeDirectories) 141 | 142 | 143 | Console 144 | true 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | Create 157 | Create 158 | Create 159 | Create 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /TestVirtualDesktopAccessorWin32/TestVirtualDesktopAccessorWin32.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | 29 | 30 | Source Files 31 | 32 | 33 | Source Files 34 | 35 | 36 | -------------------------------------------------------------------------------- /TestVirtualDesktopAccessorWin32/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // TestVirtualDesktopAccessorWin32.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /TestVirtualDesktopAccessorWin32/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 11 | // Windows Header Files: 12 | #include 13 | 14 | // C RunTime Header Files 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | // TODO: reference additional headers your program requires here 22 | -------------------------------------------------------------------------------- /TestVirtualDesktopAccessorWin32/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /VirtualDesktopAccessor.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 16 4 | VisualStudioVersion = 15.0.27703.2018 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VirtualDesktopAccessor", "VirtualDesktopAccessor\VirtualDesktopAccessor.vcxproj", "{75640BB7-FA6D-4DED-A074-E4758CCEF7D1}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestVirtualDesktopAccessorWin32", "TestVirtualDesktopAccessorWin32\TestVirtualDesktopAccessorWin32.vcxproj", "{FA401C23-A170-45D0-BB2C-9F21A8E07B2C}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{88DF21E3-B4B2-4CE5-A9B2-C4A60BF898B5}" 11 | ProjectSection(SolutionItems) = preProject 12 | README.markdown = README.markdown 13 | EndProjectSection 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|x64 = Debug|x64 18 | Debug|x86 = Debug|x86 19 | Release|x64 = Release|x64 20 | Release|x86 = Release|x86 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {75640BB7-FA6D-4DED-A074-E4758CCEF7D1}.Debug|x64.ActiveCfg = Debug|x64 24 | {75640BB7-FA6D-4DED-A074-E4758CCEF7D1}.Debug|x64.Build.0 = Debug|x64 25 | {75640BB7-FA6D-4DED-A074-E4758CCEF7D1}.Debug|x86.ActiveCfg = Debug|Win32 26 | {75640BB7-FA6D-4DED-A074-E4758CCEF7D1}.Debug|x86.Build.0 = Debug|Win32 27 | {75640BB7-FA6D-4DED-A074-E4758CCEF7D1}.Release|x64.ActiveCfg = Release|x64 28 | {75640BB7-FA6D-4DED-A074-E4758CCEF7D1}.Release|x64.Build.0 = Release|x64 29 | {75640BB7-FA6D-4DED-A074-E4758CCEF7D1}.Release|x86.ActiveCfg = Release|Win32 30 | {75640BB7-FA6D-4DED-A074-E4758CCEF7D1}.Release|x86.Build.0 = Release|Win32 31 | {FA401C23-A170-45D0-BB2C-9F21A8E07B2C}.Debug|x64.ActiveCfg = Debug|x64 32 | {FA401C23-A170-45D0-BB2C-9F21A8E07B2C}.Debug|x64.Build.0 = Debug|x64 33 | {FA401C23-A170-45D0-BB2C-9F21A8E07B2C}.Debug|x86.ActiveCfg = Debug|Win32 34 | {FA401C23-A170-45D0-BB2C-9F21A8E07B2C}.Debug|x86.Build.0 = Debug|Win32 35 | {FA401C23-A170-45D0-BB2C-9F21A8E07B2C}.Release|x64.ActiveCfg = Release|x64 36 | {FA401C23-A170-45D0-BB2C-9F21A8E07B2C}.Release|x64.Build.0 = Release|x64 37 | {FA401C23-A170-45D0-BB2C-9F21A8E07B2C}.Release|x86.ActiveCfg = Release|Win32 38 | {FA401C23-A170-45D0-BB2C-9F21A8E07B2C}.Release|x86.Build.0 = Release|Win32 39 | EndGlobalSection 40 | GlobalSection(SolutionProperties) = preSolution 41 | HideSolutionNode = FALSE 42 | EndGlobalSection 43 | GlobalSection(ExtensibilityGlobals) = postSolution 44 | SolutionGuid = {762C7D7B-DF75-46B0-9409-DE72C96B2A3F} 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /VirtualDesktopAccessor/VirtualDesktopAccessor.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {75640BB7-FA6D-4DED-A074-E4758CCEF7D1} 23 | Win32Proj 24 | VirtualDesktopAccessor 25 | 10.0 26 | 27 | 28 | 29 | DynamicLibrary 30 | true 31 | v143 32 | Unicode 33 | 34 | 35 | DynamicLibrary 36 | false 37 | v143 38 | true 39 | Unicode 40 | 41 | 42 | DynamicLibrary 43 | true 44 | v143 45 | Unicode 46 | 47 | 48 | DynamicLibrary 49 | false 50 | v143 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | 75 | 76 | true 77 | 78 | 79 | false 80 | 81 | 82 | false 83 | $(SolutionDir)$(Platform)\$(Configuration)\ 84 | $(Platform)\$(Configuration)\ 85 | 86 | 87 | 88 | Use 89 | Level3 90 | Disabled 91 | WIN32;_DEBUG;_WINDOWS;_USRDLL;VIRTUALDESKTOPACCESSOR_EXPORTS;%(PreprocessorDefinitions) 92 | true 93 | 94 | 95 | Windows 96 | true 97 | dllmain.def 98 | 99 | 100 | 101 | 102 | Use 103 | Level3 104 | Disabled 105 | _DEBUG;_WINDOWS;_USRDLL;VIRTUALDESKTOPACCESSOR_EXPORTS;%(PreprocessorDefinitions) 106 | true 107 | 108 | 109 | Windows 110 | true 111 | dllmain.def 112 | 113 | 114 | 115 | 116 | Level3 117 | Use 118 | MaxSpeed 119 | true 120 | true 121 | WIN32;NDEBUG;_WINDOWS;_USRDLL;VIRTUALDESKTOPACCESSOR_EXPORTS;%(PreprocessorDefinitions) 122 | true 123 | 124 | 125 | Windows 126 | true 127 | true 128 | true 129 | dllmain.def 130 | 131 | 132 | 133 | 134 | Level3 135 | Use 136 | MaxSpeed 137 | true 138 | true 139 | NDEBUG;_WINDOWS;_USRDLL;VIRTUALDESKTOPACCESSOR_EXPORTS;%(PreprocessorDefinitions) 140 | true 141 | 142 | 143 | Windows 144 | true 145 | true 146 | true 147 | dllmain.def 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | false 159 | 160 | 161 | false 162 | 163 | 164 | false 165 | 166 | 167 | false 168 | 169 | 170 | 171 | 172 | Create 173 | Create 174 | Create 175 | Create 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /VirtualDesktopAccessor/VirtualDesktopAccessor.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | 32 | 33 | Source Files 34 | 35 | 36 | Source Files 37 | 38 | 39 | 40 | 41 | Source Files 42 | 43 | 44 | -------------------------------------------------------------------------------- /VirtualDesktopAccessor/Win10Desktops.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdafx.h" 4 | 5 | // Status 6 | // Up to date with ae16abb6 7 | 8 | const IID IID_IServiceProvider = { 9 | 0x6D5140C1, 0x7436, 0x11CE, 0x80, 0x34, 0x00, 0xAA, 0x00, 0x60, 0x09, 0xFA }; 10 | 11 | const CLSID CLSID_ImmersiveShell = { 12 | 0xC2F03A33, 0x21F5, 0x47FA, 0xB4, 0xBB, 0x15, 0x63, 0x62, 0xA2, 0xF2, 0x39 }; 13 | 14 | // SID_VirtualDesktopManager 15 | const CLSID CLSID_VirtualDesktopManagerInternal = { 16 | 0xC5E0CDCA, 0x7B6E, 0x41B2, 0x9F, 0xC4, 0xD9, 0x39, 0x75, 0xCC, 0x46, 0x7B }; 17 | 18 | // SID_VirtualDesktopNotificationService 19 | const CLSID CLSID_IVirtualNotificationService = { 20 | 0xA501FDEC, 0x4A09, 0x464C, 0xAE, 0x4E, 0x1B, 0x9C, 0x21, 0xB8, 0x49, 0x18 21 | }; 22 | 23 | const CLSID CLSID_IVirtualDesktopManager = { 24 | 0xAA509086, 0x5CA9, 0x4C25, { 0x8f, 0x95, 0x58, 0x9d, 0x3c, 0x07, 0xb4, 0x8a } 25 | }; 26 | 27 | const CLSID CLSID_VirtualDesktopPinnedApps = { 28 | 0xb5a399e7, 0x1c87, 0x46b8, 0x88, 0xe9, 0xfc, 0x57, 0x47, 0xb1, 0x71, 0xbd 29 | }; 30 | 31 | // IID same as in MIDL IVirtualDesktopNotification 32 | const IID IID_IVirtualDesktopNotification = { 33 | 0xB287FA1C, 0x7771, 0x471A, { 0xA2, 0xDF, 0x9B, 0x6B, 0x21, 0xF0, 0xD6, 0x75 } 34 | }; 35 | 36 | // Ignore following API's: 37 | #define IAsyncCallback UINT 38 | #define IImmersiveMonitor UINT 39 | #define APPLICATION_VIEW_COMPATIBILITY_POLICY UINT 40 | #define IShellPositionerPriority UINT 41 | #define IApplicationViewOperation UINT 42 | #define APPLICATION_VIEW_CLOAK_TYPE UINT 43 | #define IApplicationViewPosition UINT 44 | 45 | DECLARE_INTERFACE_IID_(IApplicationView, IInspectable, "372E1D3B-38D3-42E4-A15B-8AB2B178F513") 46 | { 47 | /*** IUnknown methods ***/ 48 | STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR * ppvObject) PURE; 49 | STDMETHOD_(ULONG, AddRef)(THIS) PURE; 50 | STDMETHOD_(ULONG, Release)(THIS) PURE; 51 | 52 | /*** IInspectable methods ***/ 53 | STDMETHOD(GetIids)(__RPC__out ULONG * iidCount, __RPC__deref_out_ecount_full_opt(*iidCount) IID * *iids) PURE; 54 | STDMETHOD(GetRuntimeClassName)(__RPC__deref_out_opt HSTRING * className) PURE; 55 | STDMETHOD(GetTrustLevel)(__RPC__out TrustLevel * trustLevel) PURE; 56 | 57 | /*** IApplicationView methods ***/ 58 | STDMETHOD(SetFocus)(THIS) PURE; 59 | STDMETHOD(SwitchTo)(THIS) PURE; 60 | STDMETHOD(TryInvokeBack)(THIS_ IAsyncCallback*) PURE; 61 | STDMETHOD(GetThumbnailWindow)(THIS_ HWND*) PURE; 62 | STDMETHOD(GetMonitor)(THIS_ IImmersiveMonitor**) PURE; 63 | STDMETHOD(GetVisibility)(THIS_ int*) PURE; 64 | STDMETHOD(SetCloak)(THIS_ APPLICATION_VIEW_CLOAK_TYPE, int) PURE; 65 | STDMETHOD(GetPosition)(THIS_ REFIID, void**) PURE; 66 | STDMETHOD(SetPosition)(THIS_ IApplicationViewPosition*) PURE; 67 | STDMETHOD(InsertAfterWindow)(THIS_ HWND) PURE; 68 | STDMETHOD(GetExtendedFramePosition)(THIS_ RECT*) PURE; 69 | STDMETHOD(GetAppUserModelId)(THIS_ PWSTR*) PURE; 70 | STDMETHOD(SetAppUserModelId)(THIS_ PCWSTR) PURE; 71 | STDMETHOD(IsEqualByAppUserModelId)(THIS_ PCWSTR, int*) PURE; 72 | STDMETHOD(GetViewState)(THIS_ UINT*) PURE; 73 | STDMETHOD(SetViewState)(THIS_ UINT) PURE; 74 | STDMETHOD(GetNeediness)(THIS_ int*) PURE; 75 | STDMETHOD(GetLastActivationTimestamp)(THIS_ ULONGLONG*) PURE; 76 | STDMETHOD(SetLastActivationTimestamp)(THIS_ ULONGLONG) PURE; 77 | STDMETHOD(GetVirtualDesktopId)(THIS_ GUID*) PURE; 78 | STDMETHOD(SetVirtualDesktopId)(THIS_ REFGUID) PURE; 79 | STDMETHOD(GetShowInSwitchers)(THIS_ int*) PURE; 80 | STDMETHOD(SetShowInSwitchers)(THIS_ int) PURE; 81 | STDMETHOD(GetScaleFactor)(THIS_ int*) PURE; 82 | STDMETHOD(CanReceiveInput)(THIS_ BOOL*) PURE; 83 | STDMETHOD(GetCompatibilityPolicyType)(THIS_ APPLICATION_VIEW_COMPATIBILITY_POLICY*) PURE; 84 | STDMETHOD(SetCompatibilityPolicyType)(THIS_ APPLICATION_VIEW_COMPATIBILITY_POLICY) PURE; 85 | //STDMETHOD(GetPositionPriority)(THIS_ IShellPositionerPriority**) PURE; // removed in 1803 86 | //STDMETHOD(SetPositionPriority)(THIS_ IShellPositionerPriority*) PURE; // removed in 1803 87 | STDMETHOD(GetSizeConstraints)(THIS_ IImmersiveMonitor*, SIZE*, SIZE*) PURE; 88 | STDMETHOD(GetSizeConstraintsForDpi)(THIS_ UINT, SIZE*, SIZE*) PURE; 89 | STDMETHOD(SetSizeConstraintsForDpi)(THIS_ const UINT*, const SIZE*, const SIZE*) PURE; 90 | //STDMETHOD(QuerySizeConstraintsFromApp)(THIS) PURE; // removed in 1803 91 | STDMETHOD(OnMinSizePreferencesUpdated)(THIS_ HWND) PURE; 92 | STDMETHOD(ApplyOperation)(THIS_ IApplicationViewOperation*) PURE; 93 | STDMETHOD(IsTray)(THIS_ BOOL*) PURE; 94 | STDMETHOD(IsInHighZOrderBand)(THIS_ BOOL*) PURE; 95 | STDMETHOD(IsSplashScreenPresented)(THIS_ BOOL*) PURE; 96 | STDMETHOD(Flash)(THIS) PURE; 97 | STDMETHOD(GetRootSwitchableOwner)(THIS_ IApplicationView**) PURE; 98 | STDMETHOD(EnumerateOwnershipTree)(THIS_ IObjectArray**) PURE; 99 | 100 | STDMETHOD(GetEnterpriseId)(THIS_ PWSTR*) PURE; 101 | STDMETHOD(IsMirrored)(THIS_ BOOL*) PURE; 102 | 103 | // Unsure 104 | STDMETHOD(Unknown1)(THIS_ int*) PURE; 105 | STDMETHOD(Unknown2)(THIS_ int*) PURE; 106 | STDMETHOD(Unknown3)(THIS_ int*) PURE; 107 | STDMETHOD(Unknown4)(THIS_ int) PURE; 108 | 109 | // 17661? 110 | //STDMETHOD(Unknown5)(THIS) PURE; 111 | //STDMETHOD(Unknown6)(THIS_ int*) PURE; 112 | //STDMETHOD(Unknown7)(THIS_ int) PURE; 113 | //STDMETHOD(Unknown8)(THIS_ int, THIS_ int) PURE; 114 | 115 | }; 116 | 117 | const __declspec(selectany) IID& IID_IApplicationView = __uuidof(IApplicationView); 118 | 119 | DECLARE_INTERFACE_IID_(IVirtualDesktopPinnedApps, IUnknown, "4ce81583-1e4c-4632-a621-07a53543148f") 120 | { 121 | /*** IUnknown methods ***/ 122 | STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR * ppvObject) PURE; 123 | STDMETHOD_(ULONG, AddRef)(THIS) PURE; 124 | STDMETHOD_(ULONG, Release)(THIS) PURE; 125 | 126 | /*** IVirtualDesktopPinnedApps methods ***/ 127 | STDMETHOD(IsAppIdPinned)(THIS_ PCWSTR appId, BOOL*) PURE; 128 | STDMETHOD(PinAppID)(THIS_ PCWSTR appId) PURE; 129 | STDMETHOD(UnpinAppID)(THIS_ PCWSTR appId) PURE; 130 | STDMETHOD(IsViewPinned)(THIS_ IApplicationView*, BOOL*) PURE; 131 | STDMETHOD(PinView)(THIS_ IApplicationView*) PURE; 132 | STDMETHOD(UnpinView)(THIS_ IApplicationView*) PURE; 133 | 134 | }; 135 | 136 | // Ignore following API's: 137 | #define IImmersiveApplication UINT 138 | #define IApplicationViewChangeListener UINT 139 | 140 | DECLARE_INTERFACE_IID_(IApplicationViewCollection, IUnknown, "1841C6D7-4F9D-42C0-AF41-8747538F10E5") 141 | { 142 | /*** IUnknown methods ***/ 143 | STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR * ppvObject) PURE; 144 | STDMETHOD_(ULONG, AddRef)(THIS) PURE; 145 | STDMETHOD_(ULONG, Release)(THIS) PURE; 146 | 147 | /*** IApplicationViewCollection methods ***/ 148 | STDMETHOD(GetViews)(THIS_ IObjectArray**) PURE; 149 | STDMETHOD(GetViewsByZOrder)(THIS_ IObjectArray**) PURE; 150 | STDMETHOD(GetViewsByAppUserModelId)(THIS_ PCWSTR, IObjectArray**) PURE; 151 | STDMETHOD(GetViewForHwnd)(THIS_ HWND, IApplicationView**) PURE; 152 | STDMETHOD(GetViewForApplication)(THIS_ IImmersiveApplication*, IApplicationView**) PURE; 153 | STDMETHOD(GetViewForAppUserModelId)(THIS_ PCWSTR, IApplicationView**) PURE; 154 | STDMETHOD(GetViewInFocus)(THIS_ IApplicationView**) PURE; 155 | STDMETHOD(TryGetLastActiveVisibleView)(IApplicationView**) PURE; 156 | STDMETHOD(RefreshCollection)(THIS) PURE; 157 | STDMETHOD(RegisterForApplicationViewChanges)(THIS_ IApplicationViewChangeListener*, DWORD*) PURE; 158 | // STDMETHOD(RegisterForApplicationViewPositionChanges)(THIS_ IApplicationViewChangeListener*, DWORD*) PURE; 159 | STDMETHOD(UnregisterForApplicationViewChanges)(THIS_ DWORD) PURE; 160 | }; 161 | 162 | MIDL_INTERFACE("3F07F4BE-B107-441A-AF0F-39D82529072C") 163 | IVirtualDesktop : public IUnknown 164 | { 165 | public: 166 | virtual HRESULT STDMETHODCALLTYPE IsViewVisible(IApplicationView * pView, int* pfVisible) = 0; 167 | virtual HRESULT STDMETHODCALLTYPE GetID(GUID* pGuid) = 0; 168 | virtual HRESULT STDMETHODCALLTYPE GetName(HSTRING* name) = 0; 169 | virtual HRESULT STDMETHODCALLTYPE GetWallpaper(HSTRING* wallpaperPath) = 0; 170 | }; 171 | 172 | MIDL_INTERFACE("A871910E-6CC0-4E65-8B9B-458CE9115E30") 173 | IVirtualDesktop2 : public IUnknown 174 | { 175 | public: 176 | virtual HRESULT STDMETHODCALLTYPE IsRemote(BOOL * isRemote) = 0; 177 | }; 178 | 179 | enum AdjacentDesktop 180 | { 181 | LeftDirection = 3, 182 | RightDirection = 4 183 | }; 184 | 185 | MIDL_INTERFACE("4970BA3D-FD4E-4647-BEA3-D89076EF4B9C") 186 | IVirtualDesktopManagerInternal : public IUnknown 187 | { 188 | public: 189 | virtual HRESULT STDMETHODCALLTYPE GetCount(UINT * pCount) = 0; 190 | 191 | virtual HRESULT STDMETHODCALLTYPE MoveViewToDesktop( 192 | IApplicationView* pView, 193 | IVirtualDesktop* pDesktop) = 0; 194 | 195 | // Since build 10240 196 | virtual HRESULT STDMETHODCALLTYPE CanViewMoveDesktops( 197 | IApplicationView* pView, 198 | int* pfCanViewMoveDesktops) = 0; 199 | 200 | virtual HRESULT STDMETHODCALLTYPE GetCurrentDesktop( 201 | IVirtualDesktop** desktop) = 0; 202 | 203 | virtual HRESULT STDMETHODCALLTYPE GetDesktops( 204 | IObjectArray** ppDesktops) = 0; 205 | 206 | virtual HRESULT STDMETHODCALLTYPE GetAdjacentDesktop( 207 | IVirtualDesktop* pDesktopReference, 208 | AdjacentDesktop uDirection, 209 | IVirtualDesktop** ppAdjacentDesktop) = 0; 210 | 211 | virtual HRESULT STDMETHODCALLTYPE SwitchDesktop( 212 | IVirtualDesktop* pDesktop) = 0; 213 | 214 | virtual HRESULT STDMETHODCALLTYPE CreateDesktop( 215 | IVirtualDesktop** ppNewDesktop) = 0; 216 | 217 | virtual HRESULT STDMETHODCALLTYPE MoveDesktop( 218 | IVirtualDesktop* desktop, 219 | INT32 index); 220 | 221 | virtual HRESULT STDMETHODCALLTYPE RemoveDesktop( 222 | IVirtualDesktop* pRemove, 223 | IVirtualDesktop* pFallbackDesktop) = 0; 224 | 225 | // Since build 10240 226 | virtual HRESULT STDMETHODCALLTYPE FindDesktop( 227 | GUID* desktopId, 228 | IVirtualDesktop** ppDesktop) = 0; 229 | 230 | /* 231 | HRESULT GetDesktopSwitchIncludeExcludeViews([in] IVirtualDesktop *pTargetDesktop, [out] IObjectArray **ppIncludeViews, [out] IObjectArray **ppExcludeViews); 232 | HRESULT SetDesktopName([in] IVirtualDesktop* desktop, [in] HSTRING name); 233 | HRESULT SetDesktopWallpaper([in] IVirtualDesktop* desktop, [in] HSTRING wallpaperPath); 234 | HRESULT UpdateWallpaperPathForAllDesktops([in] HSTRING wallpaperPath); 235 | HRESULT CopyDesktopState([in] IApplicationView* target, [in] IApplicationView* source); 236 | */ 237 | }; 238 | 239 | MIDL_INTERFACE("A3175F2D-239C-4BD2-8AA0-EEBA8B0B138E") 240 | IVirtualDesktopManagerInternal2 : public IUnknown 241 | { 242 | public: 243 | virtual HRESULT STDMETHODCALLTYPE CreateRemoteDesktop(HSTRING name, IVirtualDesktop * *ppNewDesktop) = 0; 244 | virtual HRESULT STDMETHODCALLTYPE SwitchRemoteDesktop(IVirtualDesktop* pDesktop) = 0; 245 | virtual HRESULT STDMETHODCALLTYPE SwitchDesktopWithAnimation(IVirtualDesktop* pDesktop) = 0; 246 | virtual HRESULT STDMETHODCALLTYPE GetLastActiveDesktop(IVirtualDesktop** lastActiveVirtualDesktop) = 0; 247 | }; 248 | 249 | MIDL_INTERFACE("a5cd92ff-29be-454c-8d04-d82879fb3f1b") 250 | IVirtualDesktopManager : public IUnknown 251 | { 252 | public: 253 | virtual HRESULT STDMETHODCALLTYPE IsWindowOnCurrentVirtualDesktop( 254 | /* [in] */ __RPC__in HWND topLevelWindow, 255 | /* [out] */ __RPC__out BOOL * onCurrentDesktop) = 0; 256 | 257 | virtual HRESULT STDMETHODCALLTYPE GetWindowDesktopId( 258 | /* [in] */ __RPC__in HWND topLevelWindow, 259 | /* [out] */ __RPC__out GUID* desktopId) = 0; 260 | 261 | virtual HRESULT STDMETHODCALLTYPE MoveWindowToDesktop( 262 | /* [in] */ __RPC__in HWND topLevelWindow, 263 | /* [in] */ __RPC__in REFGUID desktopId) = 0; 264 | }; 265 | 266 | MIDL_INTERFACE("B287FA1C-7771-471A-A2DF-9B6B21F0D675") 267 | IVirtualDesktopNotification : public IUnknown 268 | { 269 | public: 270 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopCreated( 271 | IVirtualDesktop * pDesktop) = 0; 272 | 273 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopDestroyBegin( 274 | IVirtualDesktop* pDesktopDestroyed, 275 | IVirtualDesktop* pDesktopFallback) = 0; 276 | 277 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopDestroyFailed( 278 | IVirtualDesktop* pDesktopDestroyed, 279 | IVirtualDesktop* pDesktopFallback) = 0; 280 | 281 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopDestroyed( 282 | IVirtualDesktop* pDesktopDestroyed, 283 | IVirtualDesktop* pDesktopFallback) = 0; 284 | 285 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopMoved( 286 | IVirtualDesktop* pDesktop, 287 | UINT64 oldIndex, 288 | UINT64 newIndex) = 0; 289 | 290 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopNameChanged( 291 | IVirtualDesktop* pDesktop, 292 | HSTRING name) = 0; 293 | 294 | virtual HRESULT STDMETHODCALLTYPE ViewVirtualDesktopChanged( 295 | IApplicationView* pView) = 0; 296 | 297 | virtual HRESULT STDMETHODCALLTYPE CurrentVirtualDesktopChanged( 298 | IVirtualDesktop* pDesktopOld, 299 | IVirtualDesktop* pDesktopNew) = 0; 300 | 301 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopWallpaperChanged( 302 | IVirtualDesktop* pDesktop, 303 | HSTRING name) = 0; 304 | 305 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopSwitched( 306 | IVirtualDesktop* pDesktopNew) = 0; 307 | 308 | virtual HRESULT STDMETHODCALLTYPE RemoteVirtualDesktopConnected( 309 | IVirtualDesktop* remoteVirtualDesktop) = 0; 310 | }; 311 | 312 | MIDL_INTERFACE("0CD45E71-D927-4F15-8B0A-8FEF525337BF") 313 | IVirtualDesktopNotificationService : public IUnknown 314 | { 315 | public: 316 | virtual HRESULT STDMETHODCALLTYPE Register( 317 | IVirtualDesktopNotification * pNotification, 318 | DWORD * pdwCookie) = 0; 319 | 320 | virtual HRESULT STDMETHODCALLTYPE Unregister( 321 | DWORD dwCookie) = 0; 322 | }; 323 | -------------------------------------------------------------------------------- /VirtualDesktopAccessor/dllmain.cpp: -------------------------------------------------------------------------------- 1 | // dllmain.cpp : Defines the entry point for the DLL application. 2 | #include "stdafx.h" 3 | #include "dllmain.h" 4 | 5 | BOOL APIENTRY DllMain( HMODULE hModule, 6 | DWORD ul_reason_for_call, 7 | LPVOID lpReserved 8 | ) 9 | { 10 | switch (ul_reason_for_call) 11 | { 12 | case DLL_PROCESS_ATTACH: 13 | _OpenDllWindow(hModule); 14 | break; 15 | case DLL_THREAD_ATTACH: 16 | case DLL_THREAD_DETACH: 17 | case DLL_PROCESS_DETACH: 18 | break; 19 | } 20 | return TRUE; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /VirtualDesktopAccessor/dllmain.def: -------------------------------------------------------------------------------- 1 | LIBRARY DLLMAIN 2 | EXPORTS 3 | GetCurrentDesktopNumber @8 4 | GetDesktopCount @1 5 | GetDesktopIdByNumber @12 6 | GetDesktopNumber @7 7 | GetDesktopNumberById @2 8 | GetWindowDesktopId @3 9 | GetWindowDesktopNumber @4 10 | IsWindowOnCurrentVirtualDesktop @5 11 | MoveWindowToDesktopNumber @6 12 | GoToDesktopNumber @9 13 | RegisterPostMessageHook @10 14 | UnregisterPostMessageHook @11 15 | IsPinnedWindow @13 16 | PinWindow @14 17 | UnPinWindow @15 18 | IsPinnedApp @16 19 | PinApp @17 20 | UnPinApp @18 21 | 22 | EnableKeepMinimized @19 23 | RestoreMinimized @20 24 | IsWindowOnDesktopNumber @21 25 | RestartVirtualDesktopAccessor @22 -------------------------------------------------------------------------------- /VirtualDesktopAccessor/dllmain.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "stdafx.h" 3 | #include "Win10Desktops.h" 4 | 5 | #define DllExport __declspec( dllexport ) 6 | 7 | #define VDA_VirtualDesktopCreated 5 8 | #define VDA_VirtualDesktopDestroyBegin 4 9 | #define VDA_VirtualDesktopDestroyFailed 3 10 | #define VDA_VirtualDesktopDestroyed 2 11 | #define VDA_ViewVirtualDesktopChanged 1 12 | #define VDA_CurrentVirtualDesktopChanged 0 13 | 14 | #define VDA_IS_NORMAL 1 15 | #define VDA_IS_MINIMIZED 2 16 | #define VDA_IS_MAXIMIZED 3 17 | 18 | std::map listeners; 19 | IServiceProvider* pServiceProvider = nullptr; 20 | IVirtualDesktopManagerInternal* pDesktopManagerInternal = nullptr; 21 | IVirtualDesktopManager* pDesktopManager = nullptr; 22 | IApplicationViewCollection* viewCollection = nullptr; 23 | IVirtualDesktopPinnedApps* pinnedApps = nullptr; 24 | IVirtualDesktopNotificationService* pDesktopNotificationService = nullptr; 25 | BOOL registeredForNotifications = FALSE; 26 | 27 | DWORD idNotificationService = 0; 28 | 29 | struct ChangeDesktopAction { 30 | GUID newDesktopGuid; 31 | GUID oldDesktopGuid; 32 | }; 33 | 34 | void _PostMessageToListeners(int msgOffset, WPARAM wParam, LPARAM lParam) { 35 | for each (std::pair listener in listeners) { 36 | PostMessage(listener.first, listener.second + msgOffset, wParam, lParam); 37 | } 38 | } 39 | 40 | void _RegisterService(BOOL force = FALSE) { 41 | if (force) { 42 | pServiceProvider = nullptr; 43 | pDesktopManagerInternal = nullptr; 44 | pDesktopManager = nullptr; 45 | viewCollection = nullptr; 46 | pinnedApps = nullptr; 47 | pDesktopNotificationService = nullptr; 48 | registeredForNotifications = FALSE; 49 | } 50 | 51 | if (pServiceProvider != nullptr) { 52 | return; 53 | } 54 | ::CoInitialize(NULL); 55 | ::CoCreateInstance( 56 | CLSID_ImmersiveShell, NULL, CLSCTX_LOCAL_SERVER, 57 | __uuidof(IServiceProvider), (PVOID*)&pServiceProvider); 58 | 59 | if (pServiceProvider == nullptr) { 60 | std::wcout << L"FATAL ERROR: pServiceProvider is null"; 61 | return; 62 | } 63 | pServiceProvider->QueryService(__uuidof(IApplicationViewCollection), &viewCollection); 64 | 65 | pServiceProvider->QueryService(__uuidof(IVirtualDesktopManager), &pDesktopManager); 66 | 67 | pServiceProvider->QueryService( 68 | CLSID_VirtualDesktopPinnedApps, 69 | __uuidof(IVirtualDesktopPinnedApps), (PVOID*)&pinnedApps); 70 | 71 | pServiceProvider->QueryService( 72 | CLSID_VirtualDesktopManagerInternal, 73 | __uuidof(IVirtualDesktopManagerInternal), (PVOID*)&pDesktopManagerInternal); 74 | 75 | if (viewCollection == nullptr) { 76 | std::wcout << L"FATAL ERROR: viewCollection is null"; 77 | return; 78 | } 79 | 80 | if (pDesktopManagerInternal == nullptr) { 81 | std::wcout << L"FATAL ERROR: pDesktopManagerInternal is null"; 82 | return; 83 | } 84 | 85 | // Notification service 86 | HRESULT hrNotificationService = pServiceProvider->QueryService( 87 | CLSID_IVirtualNotificationService, 88 | __uuidof(IVirtualDesktopNotificationService), 89 | (PVOID*)&pDesktopNotificationService); 90 | } 91 | 92 | 93 | IApplicationView* _GetApplicationViewForHwnd(HWND hwnd) { 94 | if (hwnd == 0) 95 | return nullptr; 96 | IApplicationView* app = nullptr; 97 | viewCollection->GetViewForHwnd(hwnd, &app); 98 | return app; 99 | } 100 | 101 | void DllExport EnableKeepMinimized() { 102 | //_keepMinimized = true; 103 | } 104 | 105 | void DllExport RestoreMinimized() { 106 | } 107 | 108 | int DllExport GetDesktopCount() 109 | { 110 | _RegisterService(); 111 | 112 | IObjectArray* pObjectArray = nullptr; 113 | HRESULT hr = pDesktopManagerInternal->GetDesktops(&pObjectArray); 114 | 115 | if (SUCCEEDED(hr)) 116 | { 117 | UINT count; 118 | hr = pObjectArray->GetCount(&count); 119 | pObjectArray->Release(); 120 | return count; 121 | } 122 | 123 | return -1; 124 | } 125 | 126 | int DllExport GetDesktopNumberById(GUID desktopId) { 127 | _RegisterService(); 128 | 129 | IObjectArray* pObjectArray = nullptr; 130 | HRESULT hr = pDesktopManagerInternal->GetDesktops(&pObjectArray); 131 | int found = -1; 132 | 133 | if (SUCCEEDED(hr)) 134 | { 135 | UINT count; 136 | hr = pObjectArray->GetCount(&count); 137 | 138 | if (SUCCEEDED(hr)) 139 | { 140 | for (UINT i = 0; i < count; i++) 141 | { 142 | IVirtualDesktop* pDesktop = nullptr; 143 | 144 | if (FAILED(pObjectArray->GetAt(i, __uuidof(IVirtualDesktop), (void**)&pDesktop))) 145 | continue; 146 | 147 | GUID id = { 0 }; 148 | if (SUCCEEDED(pDesktop->GetID(&id)) && id == desktopId) 149 | { 150 | found = i; 151 | pDesktop->Release(); 152 | break; 153 | } 154 | 155 | pDesktop->Release(); 156 | } 157 | } 158 | 159 | pObjectArray->Release(); 160 | } 161 | 162 | return found; 163 | } 164 | 165 | IVirtualDesktop* _GetDesktopByNumber(int number) { 166 | _RegisterService(); 167 | 168 | IObjectArray* pObjectArray = nullptr; 169 | HRESULT hr = pDesktopManagerInternal->GetDesktops(&pObjectArray); 170 | IVirtualDesktop* found = nullptr; 171 | 172 | if (SUCCEEDED(hr)) 173 | { 174 | UINT count; 175 | hr = pObjectArray->GetCount(&count); 176 | pObjectArray->GetAt(number, __uuidof(IVirtualDesktop), (void**)&found); 177 | pObjectArray->Release(); 178 | } 179 | 180 | return found; 181 | } 182 | 183 | GUID DllExport GetWindowDesktopId(HWND window) { 184 | _RegisterService(); 185 | 186 | GUID pDesktopId = {}; 187 | pDesktopManager->GetWindowDesktopId(window, &pDesktopId); 188 | 189 | return pDesktopId; 190 | } 191 | 192 | int DllExport GetWindowDesktopNumber(HWND window) { 193 | _RegisterService(); 194 | 195 | GUID* pDesktopId = new GUID({ 0 }); 196 | if (SUCCEEDED(pDesktopManager->GetWindowDesktopId(window, pDesktopId))) { 197 | return GetDesktopNumberById(*pDesktopId); 198 | } 199 | 200 | return -1; 201 | } 202 | 203 | int DllExport IsWindowOnCurrentVirtualDesktop(HWND window) { 204 | _RegisterService(); 205 | 206 | BOOL b; 207 | if (SUCCEEDED(pDesktopManager->IsWindowOnCurrentVirtualDesktop(window, &b))) { 208 | return b; 209 | } 210 | 211 | return -1; 212 | } 213 | 214 | GUID DllExport GetDesktopIdByNumber(int number) { 215 | GUID id; 216 | IVirtualDesktop* pDesktop = _GetDesktopByNumber(number); 217 | if (pDesktop != nullptr) { 218 | pDesktop->GetID(&id); 219 | pDesktop->Release(); 220 | } 221 | return id; 222 | } 223 | 224 | 225 | int DllExport IsWindowOnDesktopNumber(HWND window, int number) { 226 | _RegisterService(); 227 | IApplicationView* app = nullptr; 228 | if (window == 0) { 229 | return -1; 230 | } 231 | viewCollection->GetViewForHwnd(window, &app); 232 | GUID desktopId = { 0 }; 233 | app->GetVirtualDesktopId(&desktopId); 234 | GUID desktopCheckId = GetDesktopIdByNumber(number); 235 | app->Release(); 236 | if (desktopCheckId == GUID_NULL || desktopId == GUID_NULL) { 237 | return -1; 238 | } 239 | 240 | if (GetDesktopIdByNumber(number) == desktopId) { 241 | return 1; 242 | } 243 | else { 244 | return 0; 245 | } 246 | 247 | return -1; 248 | } 249 | 250 | BOOL DllExport MoveWindowToDesktopNumber(HWND window, int number) { 251 | _RegisterService(); 252 | IVirtualDesktop* pDesktop = _GetDesktopByNumber(number); 253 | if (pDesktopManager == nullptr) { 254 | std::wcout << L"ARRGH?"; 255 | return false; 256 | } 257 | if (window == 0) { 258 | return false; 259 | } 260 | if (pDesktop != nullptr) { 261 | GUID id = { 0 }; 262 | if (SUCCEEDED(pDesktop->GetID(&id))) { 263 | IApplicationView* app = nullptr; 264 | viewCollection->GetViewForHwnd(window, &app); 265 | if (app != nullptr) { 266 | pDesktopManagerInternal->MoveViewToDesktop(app, pDesktop); 267 | return true; 268 | } 269 | } 270 | } 271 | return false; 272 | } 273 | 274 | int DllExport GetDesktopNumber(IVirtualDesktop* pDesktop) { 275 | _RegisterService(); 276 | 277 | if (pDesktop == nullptr) { 278 | return -1; 279 | } 280 | 281 | GUID guid; 282 | 283 | if (SUCCEEDED(pDesktop->GetID(&guid))) { 284 | return GetDesktopNumberById(guid); 285 | } 286 | 287 | return -1; 288 | } 289 | IVirtualDesktop* GetCurrentDesktop() { 290 | _RegisterService(); 291 | 292 | if (pDesktopManagerInternal == nullptr) { 293 | return nullptr; 294 | } 295 | IVirtualDesktop* found = nullptr; 296 | pDesktopManagerInternal->GetCurrentDesktop(&found); 297 | return found; 298 | } 299 | 300 | int DllExport GetCurrentDesktopNumber() { 301 | IVirtualDesktop* virtualDesktop = GetCurrentDesktop(); 302 | int number = GetDesktopNumber(virtualDesktop); 303 | virtualDesktop->Release(); 304 | return number; 305 | } 306 | 307 | void DllExport GoToDesktopNumber(int number) { 308 | _RegisterService(); 309 | 310 | if (pDesktopManagerInternal == nullptr) { 311 | return; 312 | } 313 | 314 | IVirtualDesktop* oldDesktop = GetCurrentDesktop(); 315 | GUID oldId = { 0 }; 316 | oldDesktop->GetID(&oldId); 317 | oldDesktop->Release(); 318 | 319 | IObjectArray* pObjectArray = nullptr; 320 | HRESULT hr = pDesktopManagerInternal->GetDesktops(&pObjectArray); 321 | int found = -1; 322 | 323 | if (SUCCEEDED(hr)) 324 | { 325 | UINT count; 326 | hr = pObjectArray->GetCount(&count); 327 | 328 | if (SUCCEEDED(hr)) 329 | { 330 | for (UINT i = 0; i < count; i++) 331 | { 332 | IVirtualDesktop* pDesktop = nullptr; 333 | 334 | if (FAILED(pObjectArray->GetAt(i, __uuidof(IVirtualDesktop), (void**)&pDesktop))) 335 | continue; 336 | 337 | GUID id = { 0 }; 338 | pDesktop->GetID(&id); 339 | if (i == number) { 340 | pDesktopManagerInternal->SwitchDesktop(pDesktop); 341 | } 342 | 343 | pDesktop->Release(); 344 | } 345 | } 346 | pObjectArray->Release(); 347 | } 348 | } 349 | 350 | struct ShowWindowOnDesktopAction { 351 | int desktopNumber; 352 | int cmdShow; 353 | }; 354 | 355 | LPWSTR _GetApplicationIdForHwnd(HWND hwnd) { 356 | if (hwnd == 0) 357 | return nullptr; 358 | IApplicationView* app = _GetApplicationViewForHwnd(hwnd); 359 | if (app != nullptr) { 360 | LPWSTR appId = new TCHAR[1024]; 361 | app->GetAppUserModelId(&appId); 362 | app->Release(); 363 | return appId; 364 | } 365 | return nullptr; 366 | } 367 | 368 | int DllExport IsPinnedWindow(HWND hwnd) { 369 | if (hwnd == 0) 370 | return -1; 371 | _RegisterService(); 372 | IApplicationView* pView = _GetApplicationViewForHwnd(hwnd); 373 | BOOL isPinned = false; 374 | if (pView != nullptr) { 375 | pinnedApps->IsViewPinned(pView, &isPinned); 376 | pView->Release(); 377 | if (isPinned) { 378 | return 1; 379 | } 380 | else { 381 | return 0; 382 | } 383 | } 384 | 385 | return -1; 386 | } 387 | 388 | void DllExport PinWindow(HWND hwnd) { 389 | if (hwnd == 0) 390 | return; 391 | _RegisterService(); 392 | IApplicationView* pView = _GetApplicationViewForHwnd(hwnd); 393 | if (pView != nullptr) { 394 | pinnedApps->PinView(pView); 395 | pView->Release(); 396 | } 397 | } 398 | 399 | void DllExport UnPinWindow(HWND hwnd) { 400 | if (hwnd == 0) 401 | return; 402 | _RegisterService(); 403 | IApplicationView* pView = _GetApplicationViewForHwnd(hwnd); 404 | if (pView != nullptr) { 405 | pinnedApps->UnpinView(pView); 406 | pView->Release(); 407 | } 408 | } 409 | 410 | int DllExport IsPinnedApp(HWND hwnd) { 411 | if (hwnd == 0) 412 | return -1; 413 | _RegisterService(); 414 | LPWSTR appId = _GetApplicationIdForHwnd(hwnd); 415 | if (appId != nullptr) { 416 | BOOL isPinned = false; 417 | pinnedApps->IsAppIdPinned(appId, &isPinned); 418 | if (isPinned) { 419 | return 1; 420 | } 421 | else { 422 | return 0; 423 | } 424 | } 425 | return -1; 426 | } 427 | 428 | void DllExport PinApp(HWND hwnd) { 429 | if (hwnd == 0) 430 | return; 431 | _RegisterService(); 432 | LPWSTR appId = _GetApplicationIdForHwnd(hwnd); 433 | if (appId != nullptr) { 434 | pinnedApps->PinAppID(appId); 435 | } 436 | } 437 | 438 | void DllExport UnPinApp(HWND hwnd) { 439 | if (hwnd == 0) 440 | return; 441 | _RegisterService(); 442 | LPWSTR appId = _GetApplicationIdForHwnd(hwnd); 443 | if (appId != nullptr) { 444 | pinnedApps->UnpinAppID(appId); 445 | } 446 | } 447 | 448 | class _Notifications : public IVirtualDesktopNotification { 449 | private: 450 | ULONG _referenceCount; 451 | public: 452 | // Inherited via IVirtualDesktopNotification 453 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override 454 | { 455 | // Always set out parameter to NULL, validating it first. 456 | if (!ppvObject) 457 | return E_INVALIDARG; 458 | *ppvObject = NULL; 459 | 460 | if (riid == IID_IUnknown || riid == IID_IVirtualDesktopNotification) 461 | { 462 | // Increment the reference count and return the pointer. 463 | *ppvObject = (LPVOID)this; 464 | AddRef(); 465 | return S_OK; 466 | } 467 | return E_NOINTERFACE; 468 | } 469 | 470 | virtual ULONG STDMETHODCALLTYPE AddRef() override 471 | { 472 | return InterlockedIncrement(&_referenceCount); 473 | } 474 | 475 | virtual ULONG STDMETHODCALLTYPE Release() override 476 | { 477 | ULONG result = InterlockedDecrement(&_referenceCount); 478 | if (result == 0) 479 | { 480 | delete this; 481 | } 482 | return 0; 483 | } 484 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopCreated(IVirtualDesktop* pDesktop) override 485 | { 486 | _PostMessageToListeners(VDA_VirtualDesktopCreated, GetDesktopNumber(pDesktop), 0); 487 | return S_OK; 488 | } 489 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopDestroyBegin(IVirtualDesktop* pDesktopDestroyed, 490 | IVirtualDesktop* pDesktopFallback) override 491 | { 492 | _PostMessageToListeners(VDA_VirtualDesktopDestroyBegin, 493 | GetDesktopNumber(pDesktopDestroyed), 494 | GetDesktopNumber(pDesktopFallback)); 495 | return S_OK; 496 | } 497 | 498 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopDestroyFailed( 499 | IVirtualDesktop* pDesktopDestroyed, IVirtualDesktop* pDesktopFallback) override 500 | { 501 | _PostMessageToListeners(VDA_VirtualDesktopDestroyFailed, 502 | GetDesktopNumber(pDesktopDestroyed), 503 | GetDesktopNumber(pDesktopFallback)); 504 | return S_OK; 505 | } 506 | 507 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopDestroyed( 508 | IVirtualDesktop* pDesktopDestroyed, IVirtualDesktop* pDesktopFallback) override 509 | { 510 | _PostMessageToListeners(VDA_VirtualDesktopDestroyed, 511 | GetDesktopNumber(pDesktopDestroyed), 512 | GetDesktopNumber(pDesktopFallback)); 513 | return S_OK; 514 | } 515 | 516 | virtual HRESULT STDMETHODCALLTYPE ViewVirtualDesktopChanged(IApplicationView* pView) override 517 | { 518 | _PostMessageToListeners(VDA_ViewVirtualDesktopChanged, 0, 0); 519 | return S_OK; 520 | } 521 | 522 | virtual HRESULT STDMETHODCALLTYPE CurrentVirtualDesktopChanged( 523 | IVirtualDesktop* pDesktopOld, 524 | IVirtualDesktop* pDesktopNew) override 525 | { 526 | // not sure how/why this happens but have crash dump where it did. 527 | if (pDesktopOld == nullptr && pDesktopNew == nullptr) { 528 | return S_OK; 529 | } 530 | 531 | viewCollection->RefreshCollection(); 532 | 533 | ChangeDesktopAction act; 534 | if (pDesktopOld != nullptr) { 535 | pDesktopOld->GetID(&act.oldDesktopGuid); 536 | } 537 | 538 | if (pDesktopNew != nullptr) { 539 | pDesktopNew->GetID(&act.newDesktopGuid); 540 | } 541 | 542 | _PostMessageToListeners(VDA_CurrentVirtualDesktopChanged, 543 | GetDesktopNumberById(act.oldDesktopGuid), 544 | GetDesktopNumberById(act.newDesktopGuid)); 545 | return S_OK; 546 | } 547 | 548 | virtual HRESULT __stdcall VirtualDesktopMoved( 549 | IVirtualDesktop* pDesktop, UINT64 oldIndex, UINT64 newIndex) override { 550 | return S_OK; 551 | } 552 | 553 | virtual HRESULT __stdcall VirtualDesktopNameChanged(IVirtualDesktop* pDesktop, HSTRING name) override { 554 | return S_OK; 555 | } 556 | 557 | virtual HRESULT __stdcall VirtualDesktopWallpaperChanged(IVirtualDesktop* pDesktop, HSTRING name) override { 558 | return S_OK; 559 | } 560 | 561 | virtual HRESULT __stdcall VirtualDesktopSwitched(IVirtualDesktop* pDesktopNew) override { 562 | return S_OK; 563 | } 564 | 565 | virtual HRESULT __stdcall RemoteVirtualDesktopConnected(IVirtualDesktop* remoteVirtualDesktop) override { 566 | return S_OK; 567 | } 568 | }; 569 | 570 | void _RegisterDesktopNotifications() { 571 | _RegisterService(); 572 | if (pDesktopNotificationService == nullptr) { 573 | return; 574 | } 575 | if (registeredForNotifications) { 576 | return; 577 | } 578 | _Notifications* nf = new _Notifications(); 579 | HRESULT res = pDesktopNotificationService->Register(nf, &idNotificationService); 580 | if (SUCCEEDED(res)) { 581 | registeredForNotifications = TRUE; 582 | } 583 | } 584 | 585 | void DllExport RestartVirtualDesktopAccessor() { 586 | _RegisterService(TRUE); 587 | _RegisterDesktopNotifications(); 588 | } 589 | 590 | void DllExport RegisterPostMessageHook(HWND listener, int messageOffset) { 591 | _RegisterService(); 592 | 593 | listeners.insert(std::pair(listener, messageOffset)); 594 | if (listeners.size() != 1) { 595 | return; 596 | } 597 | _RegisterDesktopNotifications(); 598 | } 599 | 600 | void DllExport UnregisterPostMessageHook(HWND hwnd) { 601 | _RegisterService(); 602 | 603 | listeners.erase(hwnd); 604 | if (listeners.size() != 0) { 605 | return; 606 | } 607 | 608 | if (pDesktopNotificationService == nullptr) { 609 | return; 610 | } 611 | 612 | if (idNotificationService > 0) { 613 | registeredForNotifications = TRUE; 614 | pDesktopNotificationService->Unregister(idNotificationService); 615 | } 616 | } 617 | 618 | VOID _OpenDllWindow(HINSTANCE injModule) { 619 | } 620 | -------------------------------------------------------------------------------- /VirtualDesktopAccessor/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // VirtualDesktopAccessor.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /VirtualDesktopAccessor/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 11 | // Windows Header Files: 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | // TODO: reference additional headers your program requires here 27 | -------------------------------------------------------------------------------- /VirtualDesktopAccessor/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /x64/Release/VirtualDesktopAccessor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skottmckay/VirtualDesktopAccessor/d7fb98abb5935931384dc747754c61af07f030ef/x64/Release/VirtualDesktopAccessor.dll --------------------------------------------------------------------------------