├── .gitattributes ├── .gitignore ├── ComUtils.h ├── DesktopData.cpp ├── DesktopData.h ├── DesktopsMenu.cpp ├── DesktopsMenu.def ├── DesktopsMenu.rc ├── DesktopsMenu.sln ├── DesktopsMenu.vcxproj ├── DesktopsMenu.vcxproj.filters ├── Hooks.cpp ├── Hooks.h ├── README.md ├── Win10Desktops.h ├── dllmain.cpp ├── docs └── screenshot.png ├── pch.cpp ├── pch.h ├── res └── main.ico └── resource.h /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Int 2 | 3 | ## Ignore Visual Studio temporary files, build results, and 4 | ## files generated by popular Visual Studio add-ons. 5 | ## 6 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 7 | 8 | # User-specific files 9 | *.rsuser 10 | *.suo 11 | *.user 12 | *.userosscache 13 | *.sln.docstates 14 | 15 | # User-specific files (MonoDevelop/Xamarin Studio) 16 | *.userprefs 17 | 18 | # Mono auto generated files 19 | mono_crash.* 20 | 21 | # Build results 22 | [Dd]ebug/ 23 | [Dd]ebugPublic/ 24 | [Rr]elease/ 25 | [Rr]eleases/ 26 | x64/ 27 | x86/ 28 | [Ww][Ii][Nn]32/ 29 | [Aa][Rr][Mm]/ 30 | [Aa][Rr][Mm]64/ 31 | bld/ 32 | [Bb]in/ 33 | [Oo]bj/ 34 | [Oo]ut/ 35 | [Ll]og/ 36 | [Ll]ogs/ 37 | 38 | # Visual Studio 2015/2017 cache/options directory 39 | .vs/ 40 | # Uncomment if you have tasks that create the project's static files in wwwroot 41 | #wwwroot/ 42 | 43 | # Visual Studio 2017 auto generated files 44 | Generated\ Files/ 45 | 46 | # MSTest test Results 47 | [Tt]est[Rr]esult*/ 48 | [Bb]uild[Ll]og.* 49 | 50 | # NUnit 51 | *.VisualState.xml 52 | TestResult.xml 53 | nunit-*.xml 54 | 55 | # Build Results of an ATL Project 56 | [Dd]ebugPS/ 57 | [Rr]eleasePS/ 58 | dlldata.c 59 | 60 | # Benchmark Results 61 | BenchmarkDotNet.Artifacts/ 62 | 63 | # .NET Core 64 | project.lock.json 65 | project.fragment.lock.json 66 | artifacts/ 67 | 68 | # ASP.NET Scaffolding 69 | ScaffoldingReadMe.txt 70 | 71 | # StyleCop 72 | StyleCopReport.xml 73 | 74 | # Files built by Visual Studio 75 | *_i.c 76 | *_p.c 77 | *_h.h 78 | *.ilk 79 | *.meta 80 | *.obj 81 | *.iobj 82 | *.pch 83 | *.pdb 84 | *.ipdb 85 | *.pgc 86 | *.pgd 87 | *.rsp 88 | *.sbr 89 | *.tlb 90 | *.tli 91 | *.tlh 92 | *.tmp 93 | *.tmp_proj 94 | *_wpftmp.csproj 95 | *.log 96 | *.vspscc 97 | *.vssscc 98 | .builds 99 | *.pidb 100 | *.svclog 101 | *.scc 102 | 103 | # Chutzpah Test files 104 | _Chutzpah* 105 | 106 | # Visual C++ cache files 107 | ipch/ 108 | *.aps 109 | *.ncb 110 | *.opendb 111 | *.opensdf 112 | *.sdf 113 | *.cachefile 114 | *.VC.db 115 | *.VC.VC.opendb 116 | 117 | # Visual Studio profiler 118 | *.psess 119 | *.vsp 120 | *.vspx 121 | *.sap 122 | 123 | # Visual Studio Trace Files 124 | *.e2e 125 | 126 | # TFS 2012 Local Workspace 127 | $tf/ 128 | 129 | # Guidance Automation Toolkit 130 | *.gpState 131 | 132 | # ReSharper is a .NET coding add-in 133 | _ReSharper*/ 134 | *.[Rr]e[Ss]harper 135 | *.DotSettings.user 136 | 137 | # TeamCity is a build add-in 138 | _TeamCity* 139 | 140 | # DotCover is a Code Coverage Tool 141 | *.dotCover 142 | 143 | # AxoCover is a Code Coverage Tool 144 | .axoCover/* 145 | !.axoCover/settings.json 146 | 147 | # Coverlet is a free, cross platform Code Coverage Tool 148 | coverage*.json 149 | coverage*.xml 150 | coverage*.info 151 | 152 | # Visual Studio code coverage results 153 | *.coverage 154 | *.coveragexml 155 | 156 | # NCrunch 157 | _NCrunch_* 158 | .*crunch*.local.xml 159 | nCrunchTemp_* 160 | 161 | # MightyMoose 162 | *.mm.* 163 | AutoTest.Net/ 164 | 165 | # Web workbench (sass) 166 | .sass-cache/ 167 | 168 | # Installshield output folder 169 | [Ee]xpress/ 170 | 171 | # DocProject is a documentation generator add-in 172 | DocProject/buildhelp/ 173 | DocProject/Help/*.HxT 174 | DocProject/Help/*.HxC 175 | DocProject/Help/*.hhc 176 | DocProject/Help/*.hhk 177 | DocProject/Help/*.hhp 178 | DocProject/Help/Html2 179 | DocProject/Help/html 180 | 181 | # Click-Once directory 182 | publish/ 183 | 184 | # Publish Web Output 185 | *.[Pp]ublish.xml 186 | *.azurePubxml 187 | # Note: Comment the next line if you want to checkin your web deploy settings, 188 | # but database connection strings (with potential passwords) will be unencrypted 189 | *.pubxml 190 | *.publishproj 191 | 192 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 193 | # checkin your Azure Web App publish settings, but sensitive information contained 194 | # in these scripts will be unencrypted 195 | PublishScripts/ 196 | 197 | # NuGet Packages 198 | *.nupkg 199 | # NuGet Symbol Packages 200 | *.snupkg 201 | # The packages folder can be ignored because of Package Restore 202 | **/[Pp]ackages/* 203 | # except build/, which is used as an MSBuild target. 204 | !**/[Pp]ackages/build/ 205 | # Uncomment if necessary however generally it will be regenerated when needed 206 | #!**/[Pp]ackages/repositories.config 207 | # NuGet v3's project.json files produces more ignorable files 208 | *.nuget.props 209 | *.nuget.targets 210 | 211 | # Microsoft Azure Build Output 212 | csx/ 213 | *.build.csdef 214 | 215 | # Microsoft Azure Emulator 216 | ecf/ 217 | rcf/ 218 | 219 | # Windows Store app package directories and files 220 | AppPackages/ 221 | BundleArtifacts/ 222 | Package.StoreAssociation.xml 223 | _pkginfo.txt 224 | *.appx 225 | *.appxbundle 226 | *.appxupload 227 | 228 | # Visual Studio cache files 229 | # files ending in .cache can be ignored 230 | *.[Cc]ache 231 | # but keep track of directories ending in .cache 232 | !?*.[Cc]ache/ 233 | 234 | # Others 235 | ClientBin/ 236 | ~$* 237 | *~ 238 | *.dbmdl 239 | *.dbproj.schemaview 240 | *.jfm 241 | *.pfx 242 | *.publishsettings 243 | orleans.codegen.cs 244 | 245 | # Including strong name files can present a security risk 246 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 247 | #*.snk 248 | 249 | # Since there are multiple workflows, uncomment next line to ignore bower_components 250 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 251 | #bower_components/ 252 | 253 | # RIA/Silverlight projects 254 | Generated_Code/ 255 | 256 | # Backup & report files from converting an old project file 257 | # to a newer Visual Studio version. Backup files are not needed, 258 | # because we have git ;-) 259 | _UpgradeReport_Files/ 260 | Backup*/ 261 | UpgradeLog*.XML 262 | UpgradeLog*.htm 263 | ServiceFabricBackup/ 264 | *.rptproj.bak 265 | 266 | # SQL Server files 267 | *.mdf 268 | *.ldf 269 | *.ndf 270 | 271 | # Business Intelligence projects 272 | *.rdl.data 273 | *.bim.layout 274 | *.bim_*.settings 275 | *.rptproj.rsuser 276 | *- [Bb]ackup.rdl 277 | *- [Bb]ackup ([0-9]).rdl 278 | *- [Bb]ackup ([0-9][0-9]).rdl 279 | 280 | # Microsoft Fakes 281 | FakesAssemblies/ 282 | 283 | # GhostDoc plugin setting file 284 | *.GhostDoc.xml 285 | 286 | # Node.js Tools for Visual Studio 287 | .ntvs_analysis.dat 288 | node_modules/ 289 | 290 | # Visual Studio 6 build log 291 | *.plg 292 | 293 | # Visual Studio 6 workspace options file 294 | *.opt 295 | 296 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 297 | *.vbw 298 | 299 | # Visual Studio LightSwitch build output 300 | **/*.HTMLClient/GeneratedArtifacts 301 | **/*.DesktopClient/GeneratedArtifacts 302 | **/*.DesktopClient/ModelManifest.xml 303 | **/*.Server/GeneratedArtifacts 304 | **/*.Server/ModelManifest.xml 305 | _Pvt_Extensions 306 | 307 | # Paket dependency manager 308 | .paket/paket.exe 309 | paket-files/ 310 | 311 | # FAKE - F# Make 312 | .fake/ 313 | 314 | # CodeRush personal settings 315 | .cr/personal 316 | 317 | # Python Tools for Visual Studio (PTVS) 318 | __pycache__/ 319 | *.pyc 320 | 321 | # Cake - Uncomment if you are using it 322 | # tools/** 323 | # !tools/packages.config 324 | 325 | # Tabs Studio 326 | *.tss 327 | 328 | # Telerik's JustMock configuration file 329 | *.jmconfig 330 | 331 | # BizTalk build output 332 | *.btp.cs 333 | *.btm.cs 334 | *.odx.cs 335 | *.xsd.cs 336 | 337 | # OpenCover UI analysis results 338 | OpenCover/ 339 | 340 | # Azure Stream Analytics local run output 341 | ASALocalRun/ 342 | 343 | # MSBuild Binary and Structured Log 344 | *.binlog 345 | 346 | # NVidia Nsight GPU debugger configuration file 347 | *.nvuser 348 | 349 | # MFractors (Xamarin productivity tool) working folder 350 | .mfractor/ 351 | 352 | # Local History for Visual Studio 353 | .localhistory/ 354 | 355 | # BeatPulse healthcheck temp database 356 | healthchecksdb 357 | 358 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 359 | MigrationBackup/ 360 | 361 | # Ionide (cross platform F# VS Code tools) working folder 362 | .ionide/ 363 | 364 | # Fody - auto-generated XML schema 365 | FodyWeavers.xsd 366 | -------------------------------------------------------------------------------- /ComUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ObjectArray.h" 4 | 5 | template 6 | struct ObjectArrayIndex 7 | { 8 | UINT i; 9 | IObjectArray* pObjectArray; 10 | 11 | ObjectArrayIndex& operator++() 12 | { 13 | ++i; 14 | return *this; 15 | } 16 | 17 | CComPtr operator*() 18 | { 19 | CComPtr p; 20 | if (FAILED(pObjectArray->GetAt(i, IID_PPV_ARGS(&p)))) 21 | return nullptr; 22 | return p; 23 | } 24 | }; 25 | 26 | template 27 | bool operator!=(const ObjectArrayIndex& l, const ObjectArrayIndex& r) 28 | { 29 | //assert(l.pObjectArray == r.pObjectArray); 30 | return l.i != r.i; 31 | } 32 | 33 | template 34 | class ObjectArrayRange 35 | { 36 | private: 37 | IObjectArray* pObjectArray; 38 | 39 | public: 40 | ObjectArrayRange(IObjectArray* pObjectArray) 41 | : pObjectArray(pObjectArray) 42 | { 43 | } 44 | 45 | ObjectArrayIndex begin() const 46 | { 47 | return { 0, pObjectArray }; 48 | } 49 | 50 | ObjectArrayIndex end() const 51 | { 52 | UINT count; 53 | if (FAILED(pObjectArray->GetCount(&count))) 54 | count = 0; 55 | return { count, pObjectArray }; 56 | } 57 | }; 58 | 59 | template 60 | struct ObjectArrayIndexRev 61 | { 62 | UINT i; 63 | IObjectArray* pObjectArray; 64 | 65 | ObjectArrayIndexRev& operator++() 66 | { 67 | --i; 68 | return *this; 69 | } 70 | 71 | CComPtr operator*() 72 | { 73 | CComPtr p; 74 | if (FAILED(pObjectArray->GetAt(i - 1, IID_PPV_ARGS(&p)))) 75 | return nullptr; 76 | return p; 77 | } 78 | }; 79 | 80 | template 81 | bool operator!=(const ObjectArrayIndexRev& l, const ObjectArrayIndexRev& r) 82 | { 83 | assert(l.pObjectArray == r.pObjectArray); 84 | return l.i != r.i; 85 | } 86 | 87 | template 88 | class ObjectArrayRangeRev 89 | { 90 | private: 91 | IObjectArray* pObjectArray; 92 | 93 | public: 94 | ObjectArrayRangeRev(IObjectArray* pObjectArray) 95 | : pObjectArray(pObjectArray) 96 | { 97 | } 98 | 99 | ObjectArrayIndexRev begin() const 100 | { 101 | UINT count; 102 | if (FAILED(pObjectArray->GetCount(&count))) 103 | count = 0; 104 | return { count, pObjectArray }; 105 | } 106 | 107 | ObjectArrayIndexRev end() const 108 | { 109 | return { 0, pObjectArray }; 110 | } 111 | }; 112 | -------------------------------------------------------------------------------- /DesktopData.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "DesktopData.h" 3 | #include "ComUtils.h" 4 | #include 5 | 6 | void Init(DesktopData& ws) 7 | { 8 | CComPtr pServiceProvider; 9 | CHECK_HR(pServiceProvider.CoCreateInstance(CLSID_ImmersiveShell, NULL, CLSCTX_LOCAL_SERVER)); 10 | 11 | CHECK_HR(pServiceProvider->QueryService(CLSID_VirtualDesktopManagerInternal, &ws.pVirtualDesktopManagerInternal)); 12 | CHECK_HR(pServiceProvider->QueryService(CLSID_VirtualDesktopPinnedApps, &ws.pVirtualDesktopPinnedApps)); 13 | CHECK_HR(pServiceProvider->QueryService(__uuidof(IApplicationViewCollection), &ws.pApplicationViewCollection)); 14 | } 15 | 16 | CComPtr GetDesktop(const DesktopData* ws, AdjacentDesktop uDirection) 17 | { 18 | CComPtr pDesktop; 19 | 20 | CComPtr pCurrentDesktop; 21 | CHECK_HR_RET(ws->pVirtualDesktopManagerInternal->GetCurrentDesktop(&pCurrentDesktop), pDesktop); 22 | 23 | if (FAILED(ws->pVirtualDesktopManagerInternal->GetAdjacentDesktop(pCurrentDesktop, uDirection, &pDesktop))) 24 | return pDesktop; 25 | 26 | return pDesktop; 27 | } 28 | 29 | CComPtr GetDesktop(const DesktopData* ws, int dn) 30 | { 31 | CComPtr pDesktop; 32 | 33 | CComPtr pDesktopArray; 34 | if (ws->pVirtualDesktopManagerInternal && SUCCEEDED(ws->pVirtualDesktopManagerInternal->GetDesktops(&pDesktopArray))) 35 | { 36 | if (FAILED(pDesktopArray->GetAt(dn, IID_PPV_ARGS(&pDesktop)))) 37 | return pDesktop; 38 | } 39 | 40 | return pDesktop; 41 | } 42 | 43 | LRESULT OnDesktopPin(const DesktopData* ws, const HWND hWndSrc, const Message msg) 44 | { 45 | const LRESULT ret = msg == Message::Query ? MF_DISABLED : FALSE; 46 | try 47 | { 48 | CHECK_HR_RET(ws->pApplicationViewCollection->RefreshCollection(), ret); 49 | 50 | CComPtr pView; 51 | CHECK_HR_RET(ws->pApplicationViewCollection->GetViewForHwnd(hWndSrc, &pView), ret); 52 | 53 | switch (msg) 54 | { 55 | case Message::Query: 56 | { 57 | BOOL bPinned = FALSE; 58 | CHECK_HR_RET(ws->pVirtualDesktopPinnedApps->IsViewPinned(pView, &bPinned), ret); 59 | 60 | return MF_ENABLED | (bPinned ? MF_CHECKED : MF_UNCHECKED); 61 | } 62 | case Message::Select: 63 | { 64 | BOOL bPinned = FALSE; 65 | CHECK_HR_RET(ws->pVirtualDesktopPinnedApps->IsViewPinned(pView, &bPinned), ret); 66 | 67 | if (!bPinned) 68 | { 69 | CHECK_HR_RET(ws->pVirtualDesktopPinnedApps->PinView(pView), ret); 70 | } 71 | else 72 | { 73 | CHECK_HR_RET(ws->pVirtualDesktopPinnedApps->UnpinView(pView), ret); 74 | } 75 | 76 | return TRUE; 77 | } 78 | default: 79 | return ret; 80 | } 81 | } 82 | catch (...) 83 | { 84 | ReportError(_T("FAILED Exception")); 85 | return ret; 86 | } 87 | } 88 | 89 | LRESULT OnDesktopMove(const DesktopData* ws, const HWND hWndSrc, const Message msg, CComPtr pDesktop) 90 | { 91 | const LRESULT ret = msg == Message::Query ? MF_DISABLED : FALSE; 92 | try 93 | { 94 | CHECK_HR_RET(ws->pApplicationViewCollection->RefreshCollection(), ret); 95 | 96 | CComPtr pView; 97 | CHECK_HR_RET(ws->pApplicationViewCollection->GetViewForHwnd(hWndSrc, &pView), ret); 98 | 99 | switch (msg) 100 | { 101 | case Message::Query: 102 | { 103 | BOOL bCanMove = FALSE; 104 | CHECK_HR_RET(ws->pVirtualDesktopManagerInternal->CanViewMoveDesktops(pView, &bCanMove), ret); 105 | 106 | if (bCanMove) 107 | { 108 | if (!pDesktop) 109 | return MF_DISABLED; 110 | 111 | CComPtr pCurrentDesktop; 112 | CHECK_HR_RET(ws->pVirtualDesktopManagerInternal->GetCurrentDesktop(&pCurrentDesktop), ret); 113 | 114 | if (pCurrentDesktop.IsEqualObject(pDesktop)) 115 | return MF_ENABLED | MF_CHECKED; 116 | else 117 | return MF_ENABLED; 118 | } 119 | else 120 | return MF_DISABLED; 121 | } 122 | case Message::Select: 123 | { 124 | if (!pDesktop) 125 | return FALSE; 126 | 127 | CHECK_HR_RET(ws->pVirtualDesktopManagerInternal->MoveViewToDesktop(pView, pDesktop), ret); 128 | 129 | return TRUE; 130 | } 131 | default: 132 | return ret; 133 | } 134 | } 135 | catch (...) 136 | { 137 | ReportError(_T("FAILED Exception")); 138 | return ret; 139 | } 140 | } 141 | 142 | BOOL OnDesktopSwitch(const DesktopData* ws, CComPtr pDesktop) 143 | { 144 | const BOOL ret = FALSE; 145 | try 146 | { 147 | if (!pDesktop) 148 | return FALSE; 149 | 150 | CHECK_HR_RET(ws->pVirtualDesktopManagerInternal->SwitchDesktop(pDesktop), ret); 151 | 152 | return TRUE; 153 | } 154 | catch (...) 155 | { 156 | ReportError(_T("FAILED Exception")); 157 | return ret; 158 | } 159 | } 160 | 161 | int GetDesktopNames(const DesktopData* ws, LPTSTR text, const int size) 162 | { 163 | text[0] = _T('\0'); 164 | 165 | CComPtr pDesktopArray; 166 | if (ws->pVirtualDesktopManagerInternal && SUCCEEDED(ws->pVirtualDesktopManagerInternal->GetDesktops(&pDesktopArray))) 167 | { 168 | int dn = 0; 169 | for (CComPtr pDesktop : ObjectArrayRange(pDesktopArray)) 170 | { 171 | ++dn; 172 | 173 | HSTRING s = NULL; 174 | CHECK_HR_RET(pDesktop->GetName(&s), 0); 175 | 176 | if (text[0] != _T('\0')) 177 | wcscat_s(text, size, _T("|")); 178 | if (s == nullptr) 179 | { 180 | size_t len = wcslen(text); 181 | swprintf(text + len, size - len, _T("Desktop %d"), dn); 182 | } 183 | else 184 | { 185 | wcscat_s(text, size, WindowsGetStringRawBuffer(s, nullptr)); 186 | 187 | WindowsDeleteString(s); 188 | } 189 | } 190 | } 191 | return (int) wcslen(text); 192 | } 193 | 194 | std::vector GetDesktopNames(const DesktopData* ws, int* current) 195 | { 196 | std::vector names; 197 | CComPtr pCurrentDesktop; 198 | ws->pVirtualDesktopManagerInternal->GetCurrentDesktop(&pCurrentDesktop); 199 | CComPtr pDesktopArray; 200 | if (ws->pVirtualDesktopManagerInternal && SUCCEEDED(ws->pVirtualDesktopManagerInternal->GetDesktops(&pDesktopArray))) 201 | { 202 | int dn = 0; 203 | for (CComPtr pDesktop : ObjectArrayRange(pDesktopArray)) 204 | { 205 | ++dn; 206 | 207 | if (pCurrentDesktop.IsEqualObject(pDesktop)) 208 | *current = dn; 209 | 210 | HSTRING s = NULL; 211 | CHECK_HR_RET(pDesktop->GetName(&s), std::vector()); 212 | 213 | if (s == nullptr) 214 | { 215 | wchar_t n[100]; 216 | swprintf(n, ARRAYSIZE(n), _T("Desktop %d"), dn); 217 | names.push_back(n); 218 | } 219 | else 220 | { 221 | names.push_back(WindowsGetStringRawBuffer(s, nullptr)); 222 | 223 | WindowsDeleteString(s); 224 | } 225 | } 226 | } 227 | return names; 228 | } 229 | -------------------------------------------------------------------------------- /DesktopData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Win10Desktops.h" 4 | 5 | #include 6 | #include 7 | 8 | struct DesktopData 9 | { 10 | CComPtr pVirtualDesktopManagerInternal; 11 | CComPtr pVirtualDesktopPinnedApps; 12 | CComPtr pApplicationViewCollection; 13 | }; 14 | 15 | void Init(DesktopData& ws); 16 | CComPtr GetDesktop(const DesktopData* ws, AdjacentDesktop uDirection); 17 | CComPtr GetDesktop(const DesktopData* ws, int dn); 18 | LRESULT OnDesktopPin(const DesktopData* ws, const HWND hWndSrc, const Message msg); 19 | LRESULT OnDesktopMove(const DesktopData* ws, const HWND hWndSrc, const Message msg, CComPtr pDesktop); 20 | BOOL OnDesktopSwitch(const DesktopData* ws, CComPtr pDesktop); 21 | int GetDesktopNames(const DesktopData* ws, LPTSTR text, const int size); 22 | std::vector GetDesktopNames(const DesktopData* ws, int* current); 23 | -------------------------------------------------------------------------------- /DesktopsMenu.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "Hooks.h" 3 | #include "resource.h" 4 | #include "DesktopData.h" 5 | 6 | #ifdef _WIN64 7 | const LPCTSTR g_lpstrTitle = _T("Desktops Menu (x64)"); 8 | const LPCTSTR g_lpstrClass = _T("DESKTOPSMENU64"); 9 | #else 10 | const LPCTSTR g_lpstrTitle = _T("Desktops Menu (x86)"); 11 | const LPCTSTR g_lpstrClass = _T("DESKTOPSMENU32"); 12 | #endif 13 | #define WM_TRAY (WM_USER + 0x0200) 14 | 15 | HMENU LoadPopupMenu(HINSTANCE hInstance, DWORD id) 16 | { 17 | const HMENU hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(id)); 18 | const HMENU hPopupMenu = GetSubMenu(hMenu, 0); 19 | VALIDATEE(RemoveMenu(hMenu, 0, MF_BYPOSITION)); 20 | VALIDATEE(DestroyMenu(hMenu)); 21 | return hPopupMenu; 22 | } 23 | 24 | UINT MenuFindByCommand(HMENU hMenu, UINT id) 25 | { 26 | for (int nPos = 0; nPos < GetMenuItemCount(hMenu); ++nPos) 27 | { 28 | if (GetMenuItemID(hMenu, nPos) == id) 29 | return nPos; 30 | } 31 | return -1; 32 | } 33 | 34 | void InsertDesktopMenu(const HMENU hMenuSwitch, const UINT id, const std::vector& names) 35 | { 36 | const UINT pos = MenuFindByCommand(hMenuSwitch, id); 37 | VALIDATE(pos >= 0, _T("MenuFindByCommand not found")); 38 | DeleteMenu(hMenuSwitch, pos, MF_BYPOSITION); 39 | 40 | UINT dn = 0; 41 | for (const std::wstring& n : names) 42 | { 43 | TCHAR buf[1024]; 44 | wsprintf(buf, L"&%d. %s", (dn + 1), n.c_str()); 45 | InsertMenu(hMenuSwitch, pos + dn, MF_STRING, id + ((UINT_PTR) dn << 4), buf); 46 | ++dn; 47 | } 48 | } 49 | 50 | enum 51 | { 52 | HK_PIN, 53 | HK_SWITCH, 54 | }; 55 | 56 | struct HookDeleter 57 | { 58 | typedef HHOOK pointer; 59 | void operator()(HHOOK handle) 60 | { 61 | if (handle != NULL) 62 | UnhookWindowsHookEx(handle); 63 | } 64 | }; 65 | typedef std::unique_ptr unique_hook_t; 66 | 67 | INT_PTR CALLBACK AboutDlg(const HWND hDlg, const UINT uMsg, const WPARAM wParam, const LPARAM lParam) 68 | { 69 | switch (uMsg) 70 | { 71 | case WM_INITDIALOG: 72 | { 73 | SendMessage(GetDlgItem(hDlg, IDC_ABOUT_APPICON), STM_SETICON, (WPARAM) LoadIcon(g_hDllInstance, MAKEINTRESOURCE(IDI_MAIN)), 0); 74 | 75 | TCHAR FileName[1024]; 76 | GetModuleFileName(g_hDllInstance, FileName, 1024); 77 | 78 | DWORD Dummy; 79 | DWORD Size = GetFileVersionInfoSize(FileName, &Dummy); 80 | 81 | if (Size > 0) 82 | { 83 | void* Info = malloc(Size); 84 | if (Info != nullptr) 85 | { 86 | // VS_VERSION_INFO VS_VERSIONINFO VS_FIXEDFILEINFO 87 | 88 | //Dummy = 0; 89 | GetFileVersionInfo(FileName, Dummy, Size, Info); 90 | 91 | TCHAR* String; 92 | UINT Length; 93 | VerQueryValue(Info, TEXT("\\StringFileInfo\\0c0904b0\\FileVersion"), (LPVOID*) &String, &Length); 94 | SetWindowText(GetDlgItem(hDlg, IDC_ABOUT_VERSION), String); 95 | VerQueryValue(Info, TEXT("\\StringFileInfo\\0c0904b0\\ProductName"), (LPVOID*) &String, &Length); 96 | SetWindowText(GetDlgItem(hDlg, IDC_ABOUT_PRODUCT), String); 97 | 98 | free(Info); 99 | } 100 | } 101 | else 102 | { 103 | SetWindowText(GetDlgItem(hDlg, IDC_ABOUT_VERSION), TEXT("Unknown")); 104 | } 105 | 106 | return TRUE; 107 | } 108 | case WM_COMMAND: 109 | { 110 | if (HIWORD(wParam) == BN_CLICKED) 111 | { 112 | switch (LOWORD(wParam)) 113 | { 114 | case IDC_ABOUT_WEBSITE: 115 | case IDC_ABOUT_MAIL: 116 | { 117 | TCHAR Url[1024]; 118 | GetWindowText((HWND) lParam, Url, 1024); 119 | ShellExecute(hDlg, TEXT("open"), Url, NULL, NULL, SW_SHOW); 120 | return TRUE; 121 | } 122 | break; 123 | case IDCANCEL: 124 | case IDOK: 125 | { 126 | EndDialog(hDlg, TRUE); 127 | return TRUE; 128 | } 129 | break; 130 | } 131 | } 132 | } 133 | } 134 | return FALSE; 135 | } 136 | 137 | LRESULT CALLBACK WndProc(const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam) 138 | { 139 | static UINT s_uTaskbarRestart = 0; 140 | if (s_uTaskbarRestart != 0 && uMsg == s_uTaskbarRestart) 141 | { 142 | NOTIFYICONDATA nid = {}; 143 | nid.cbSize = sizeof(nid); 144 | nid.hWnd = hWnd; 145 | nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE; 146 | nid.uCallbackMessage = WM_TRAY; 147 | wcscpy_s(nid.szTip, g_lpstrTitle); 148 | //LoadIconMetric(g_hDllInstance, MAKEINTRESOURCE(IDI_MAIN), LIM_SMALL, &(nid.hIcon)); 149 | nid.hIcon = (HICON) GetClassLongPtr(hWnd, GCLP_HICON); 150 | VALIDATEE(Shell_NotifyIcon(NIM_ADD, &nid)); 151 | 152 | DesktopData* ws = (DesktopData*) GetWindowLongPtr(hWnd, GWLP_USERDATA); 153 | Init(*ws); 154 | 155 | return TRUE; 156 | } 157 | switch (uMsg) 158 | { 159 | case WM_CREATE: 160 | { 161 | LPCREATESTRUCT cs = (LPCREATESTRUCT) lParam; 162 | SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) cs->lpCreateParams); 163 | 164 | VALIDATEE(RegisterHotKey(hWnd, HK_PIN, MOD_CONTROL | MOD_ALT | MOD_NOREPEAT, _T('P'))); 165 | VALIDATEE(RegisterHotKey(hWnd, HK_SWITCH, MOD_CONTROL | MOD_SHIFT | MOD_NOREPEAT, _T('D'))); 166 | 167 | NOTIFYICONDATA nid = {}; 168 | nid.cbSize = sizeof(nid); 169 | nid.hWnd = hWnd; 170 | nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE; 171 | nid.uCallbackMessage = WM_TRAY; 172 | wcscpy_s(nid.szTip, g_lpstrTitle); 173 | //LoadIconMetric(g_hDllInstance, MAKEINTRESOURCE(IDI_MAIN), LIM_SMALL, &(nid.hIcon)); 174 | nid.hIcon = (HICON) GetClassLongPtr(hWnd, GCLP_HICON); 175 | VALIDATEE(Shell_NotifyIcon(NIM_ADD, &nid)); 176 | 177 | s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); 178 | 179 | return DefWindowProc(hWnd, uMsg, wParam, lParam); 180 | } 181 | 182 | case WM_DESTROY: 183 | { 184 | NOTIFYICONDATA nid = {}; 185 | nid.cbSize = sizeof(nid); 186 | nid.hWnd = hWnd; 187 | nid.uFlags = NIF_MESSAGE; 188 | nid.uCallbackMessage = WM_TRAY; 189 | VALIDATEE(Shell_NotifyIcon(NIM_DELETE, &nid)); 190 | 191 | VALIDATEE(UnregisterHotKey(hWnd, HK_PIN)); 192 | VALIDATEE(UnregisterHotKey(hWnd, HK_SWITCH)); 193 | 194 | return DefWindowProc(hWnd, uMsg, wParam, lParam); 195 | } 196 | 197 | case WM_CLOSE: 198 | PostQuitMessage(0); 199 | return DefWindowProc(hWnd, uMsg, wParam, lParam); 200 | 201 | case WM_COPYDATA: 202 | { 203 | const HWND hWndSrc = (HWND) wParam; 204 | const COPYDATASTRUCT* cds = (COPYDATASTRUCT*) lParam; 205 | if (cds->dwData == CD_DESKTOPS && cds->cbData == sizeof(MessageStruct)) 206 | { 207 | const DesktopData* ws = (DesktopData*) GetWindowLongPtr(hWnd, GWLP_USERDATA); 208 | const MessageStruct* ms = (MessageStruct*) cds->lpData; 209 | switch (ms->type) 210 | { 211 | case SC_PIN: return OnDesktopPin(ws, hWndSrc, ms->msg); 212 | case SC_MOVE_PREV: return OnDesktopMove(ws, hWndSrc, ms->msg, GetDesktop(ws, LeftDirection)); 213 | case SC_MOVE_NEXT: return OnDesktopMove(ws, hWndSrc, ms->msg, GetDesktop(ws, RightDirection)); 214 | default: 215 | if (ms->type >= SC_MOVE_DESKTOP && ms->type < (SC_MOVE_DESKTOP + 100)) 216 | return OnDesktopMove(ws, hWndSrc, ms->msg, GetDesktop(ws, (ms->type - SC_MOVE_DESKTOP) >> 4)); 217 | else 218 | return TRUE; 219 | } 220 | } 221 | else 222 | return FALSE; 223 | } 224 | 225 | case WM_GETTEXT: 226 | { 227 | int nSize = static_cast(wParam); 228 | const DesktopData* ws = (DesktopData*) GetWindowLongPtr(hWnd, GWLP_USERDATA); 229 | if (ws != nullptr && nSize == 1024) // Size 1024 means it comes from HookProc 230 | return GetDesktopNames(ws, (TCHAR*) lParam, nSize); 231 | else 232 | return DefWindowProc(hWnd, uMsg, wParam, lParam); 233 | } 234 | 235 | case WM_COMMAND: 236 | switch (LOWORD(wParam)) 237 | { 238 | case ID_MAIN_ABOUT: 239 | DialogBox(g_hDllInstance, MAKEINTRESOURCE(IDD_ABOUT), hWnd, AboutDlg); 240 | return FALSE; 241 | 242 | case ID_MAIN_EXIT: 243 | PostMessage(hWnd, WM_CLOSE, 0, 0); 244 | return FALSE; 245 | 246 | case ID_SWITCH_PREVIOUS: 247 | { 248 | const DesktopData* ws = (DesktopData*) GetWindowLongPtr(hWnd, GWLP_USERDATA); 249 | VALIDATEE(OnDesktopSwitch(ws, GetDesktop(ws, LeftDirection))); 250 | return FALSE; 251 | } 252 | 253 | case ID_SWITCH_NEXT: 254 | { 255 | const DesktopData* ws = (DesktopData*) GetWindowLongPtr(hWnd, GWLP_USERDATA); 256 | VALIDATEE(OnDesktopSwitch(ws, GetDesktop(ws, RightDirection))); 257 | return FALSE; 258 | } 259 | 260 | default: 261 | if (LOWORD(wParam) >= ID_SWITCH_DESKTOPS && LOWORD(wParam) < (ID_SWITCH_DESKTOPS + 100)) 262 | { 263 | const DesktopData* ws = (DesktopData*) GetWindowLongPtr(hWnd, GWLP_USERDATA); 264 | VALIDATEE(OnDesktopSwitch(ws, GetDesktop(ws, (LOWORD(wParam) - ID_SWITCH_DESKTOPS) >> 4))); 265 | return FALSE; 266 | } 267 | else 268 | return TRUE; 269 | } 270 | 271 | case WM_HOTKEY: 272 | switch (wParam) 273 | { 274 | case HK_PIN: 275 | { 276 | HWND hWndFG = GetForegroundWindow(); 277 | const DesktopData* ws = (DesktopData*) GetWindowLongPtr(hWnd, GWLP_USERDATA); 278 | OnDesktopPin(ws, hWndFG, Message::Select); 279 | return TRUE; 280 | } 281 | case HK_SWITCH: 282 | { 283 | const DesktopData* ws = (DesktopData*) GetWindowLongPtr(hWnd, GWLP_USERDATA); 284 | 285 | const HMENU hMenuSwitch = LoadPopupMenu(g_hDllInstance, IDR_SWITCH); 286 | int radio = -1; 287 | InsertDesktopMenu(hMenuSwitch, ID_SWITCH_DESKTOPS, GetDesktopNames(ws, &radio)); 288 | if (radio != (UINT) -1) 289 | CheckMenuRadioItem(hMenuSwitch, ID_SWITCH_DESKTOPS, ID_SWITCH_DESKTOPS + ((UINT_PTR) 100 << 4), ID_SWITCH_DESKTOPS + ((UINT_PTR) (radio - 1) << 4), MF_BYCOMMAND); 290 | 291 | const HWND hWndFG = GetForegroundWindow(); 292 | HMONITOR hMonitor = MonitorFromWindow(hWndFG, MONITOR_DEFAULTTONEAREST); 293 | MONITORINFO mi = { sizeof(MONITORINFO) }; 294 | VALIDATEE(GetMonitorInfo(hMonitor, &mi)); 295 | 296 | VALIDATEE(SetForegroundWindow(hWnd)); 297 | POINT pt = { (mi.rcWork.left + mi.rcWork.right) / 2, (mi.rcWork.top + mi.rcWork.bottom) / 2 }; 298 | VALIDATEE(TrackPopupMenu(hMenuSwitch, TPM_CENTERALIGN | TPM_VCENTERALIGN, pt.x, pt.y, 0, hWnd, nullptr)); 299 | 300 | DestroyMenu(hMenuSwitch); 301 | 302 | return TRUE; 303 | } 304 | default: 305 | return FALSE; 306 | } 307 | 308 | case WM_TRAY: 309 | if (lParam == WM_RBUTTONUP) 310 | { 311 | VALIDATEE(SetForegroundWindow(hWnd)); 312 | POINT pt; 313 | VALIDATEE(GetCursorPos(&pt)); 314 | const HMENU hMenu = LoadPopupMenu(g_hDllInstance, IDR_MAIN); 315 | VALIDATEE(TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN, pt.x, pt.y, 0, hWnd, nullptr)); 316 | DestroyMenu(hMenu); 317 | } 318 | return TRUE; 319 | 320 | default: 321 | return DefWindowProc(hWnd, uMsg, wParam, lParam); 322 | } 323 | } 324 | 325 | struct CoInit 326 | { 327 | HRESULT Init(DWORD dwCoInit) 328 | { 329 | return CoInitializeEx(nullptr, dwCoInit); 330 | } 331 | 332 | ~CoInit() 333 | { 334 | CoUninitialize(); 335 | } 336 | }; 337 | 338 | extern "C" 339 | { 340 | 341 | #ifdef _UNICODE 342 | #define DesktopsMenu DesktopsMenuW 343 | 344 | void __declspec(dllexport) CALLBACK DesktopsMenuA(const HWND hParentWnd, const HINSTANCE hInstance, const LPCSTR lpszCmdLine, const int nCmdShow) 345 | { 346 | MessageBox(hParentWnd, _T("TODO translate"), g_lpstrTitle, MB_ICONWARNING | MB_OK); 347 | } 348 | 349 | #else 350 | #define DesktopsMenu DesktopsMenuA 351 | 352 | void __declspec(dllexport) CALLBACK DesktopsMenuW(const HWND hParentWnd, const HINSTANCE hInstance, const LPCWSTR lpszCmdLine, const int nCmdShow) 353 | { 354 | MessageBox(hParentWnd, _T("TODO translate"), lpstrTitle, MB_ICONWARNING | MB_OK); 355 | } 356 | #endif 357 | 358 | void __declspec(dllexport) CALLBACK DesktopsMenu(const HWND hParentWnd, const HINSTANCE hInstance, const LPCTSTR lpszCmdLine, const int nCmdShow) 359 | { 360 | g_hMsgWnd = hParentWnd; 361 | 362 | CoInit coinit; 363 | CHECK_HR(coinit.Init(COINIT_MULTITHREADED)); 364 | 365 | DesktopData ws; 366 | Init(ws); 367 | 368 | const unique_hook_t hHookCall(SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, g_hDllInstance, 0)); 369 | CHECK_MSG(hHookCall != NULL, _T("Error SetWindowsHookEx WH_CALLWNDPROC")); 370 | const unique_hook_t hHookGet(SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hDllInstance, 0)); 371 | CHECK_MSG(hHookGet != NULL, _T("Error SetWindowsHookEx WH_GETMESSAGE")); 372 | 373 | WNDCLASS wc = {}; 374 | wc.lpfnWndProc = WndProc; 375 | wc.hInstance = hInstance; 376 | wc.hIcon = LoadIcon(g_hDllInstance, MAKEINTRESOURCE(IDI_MAIN)); 377 | wc.hCursor = LoadCursor(NULL, IDC_ARROW); 378 | wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); 379 | wc.lpszClassName = g_lpstrClass; 380 | RegisterClass(&wc); 381 | 382 | CREATESTRUCT cs = {}; 383 | cs.lpszClass = wc.lpszClassName; 384 | cs.lpszName = g_lpstrTitle; 385 | cs.style = WS_OVERLAPPEDWINDOW; 386 | cs.dwExStyle = WS_EX_TOPMOST; 387 | cs.x = CW_USEDEFAULT; 388 | cs.x = CW_USEDEFAULT; 389 | cs.cx = CW_USEDEFAULT; 390 | cs.cy = CW_USEDEFAULT; 391 | cs.hwndParent = hParentWnd; 392 | cs.lpCreateParams = &ws; 393 | HWND hDMWnd = CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams); 394 | CHECK_MSG(hDMWnd != NULL, _T("Error CreateWindowEx")); 395 | 396 | g_hMsgWnd = hDMWnd; 397 | //ShowWindow(hDMWnd, nCmdShow); 398 | 399 | MSG msg; 400 | while (GetMessage(&msg, nullptr, 0, 0)) 401 | { 402 | TranslateMessage(&msg); 403 | DispatchMessage(&msg); 404 | } 405 | } 406 | 407 | } 408 | -------------------------------------------------------------------------------- /DesktopsMenu.def: -------------------------------------------------------------------------------- 1 | LIBRARY DesktopsMenuWin32 2 | EXPORTS 3 | DesktopsMenuA @1 4 | DesktopsMenuW @2 5 | -------------------------------------------------------------------------------- /DesktopsMenu.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United Kingdom) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK 20 | #pragma code_page(1252) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_MAIN ICON "res\\main.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Menu 61 | // 62 | 63 | IDR_MAIN MENU 64 | BEGIN 65 | POPUP "Main" 66 | BEGIN 67 | MENUITEM "&About...", ID_MAIN_ABOUT 68 | MENUITEM "E&xit", ID_MAIN_EXIT 69 | END 70 | END 71 | 72 | IDR_SWITCH MENU 73 | BEGIN 74 | POPUP "Switch" 75 | BEGIN 76 | MENUITEM "&Previous", ID_SWITCH_PREVIOUS 77 | MENUITEM "&Next", ID_SWITCH_NEXT 78 | MENUITEM SEPARATOR 79 | MENUITEM "Desktops", ID_SWITCH_DESKTOPS 80 | END 81 | END 82 | 83 | IDR_SYSTEM MENU 84 | BEGIN 85 | POPUP "System" 86 | BEGIN 87 | MENUITEM "&Pin to All Desktops", SC_PIN 88 | POPUP "Move to &Desktop" 89 | BEGIN 90 | MENUITEM "&Previous", SC_MOVE_PREV 91 | MENUITEM "&Next", SC_MOVE_NEXT 92 | MENUITEM SEPARATOR 93 | MENUITEM "Desktops", SC_MOVE_DESKTOP 94 | END 95 | END 96 | END 97 | 98 | 99 | ///////////////////////////////////////////////////////////////////////////// 100 | // 101 | // Version 102 | // 103 | 104 | VS_VERSION_INFO VERSIONINFO 105 | FILEVERSION 1,3,0,0 106 | PRODUCTVERSION 1,3,0,0 107 | FILEFLAGSMASK 0x3fL 108 | #ifdef _DEBUG 109 | FILEFLAGS 0x1L 110 | #else 111 | FILEFLAGS 0x0L 112 | #endif 113 | FILEOS 0x40000L 114 | FILETYPE 0x2L 115 | FILESUBTYPE 0x0L 116 | BEGIN 117 | BLOCK "StringFileInfo" 118 | BEGIN 119 | BLOCK "0c0904b0" 120 | BEGIN 121 | VALUE "CompanyName", "RadSoft" 122 | VALUE "FileDescription", "Add menu items to system menus for virtual desktop" 123 | VALUE "FileVersion", "1.3.0.0" 124 | VALUE "InternalName", "DesktopsMenu" 125 | VALUE "LegalCopyright", "Copyright (C) 2021" 126 | VALUE "OriginalFilename", "DesktopsMenu.dll" 127 | VALUE "ProductName", "Desktops Menu" 128 | VALUE "ProductVersion", "1.3.0.0" 129 | END 130 | END 131 | BLOCK "VarFileInfo" 132 | BEGIN 133 | VALUE "Translation", 0xc09, 1200 134 | END 135 | END 136 | 137 | 138 | ///////////////////////////////////////////////////////////////////////////// 139 | // 140 | // Dialog 141 | // 142 | 143 | IDD_ABOUT DIALOGEX 0, 0, 186, 72 144 | STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 145 | CAPTION "About " 146 | FONT 8, "MS Sans Serif", 0, 0, 0x0 147 | BEGIN 148 | PUSHBUTTON "Close",IDCANCEL,129,7,50,14 149 | ICON "",IDC_ABOUT_APPICON,7,7,20,20 150 | LTEXT "Static",IDC_ABOUT_PRODUCT,35,7,85,8 151 | LTEXT "Version:",IDC_STATIC,35,20,28,8 152 | LTEXT "Static",IDC_ABOUT_VERSION,67,20,57,8 153 | PUSHBUTTON "https://github.com/RadAd/DesktopsMenu",IDC_ABOUT_WEBSITE,7,33,172,14,BS_FLAT 154 | PUSHBUTTON "mailto:adamgates84+software@gmail.com",IDC_ABOUT_MAIL,7,50,172,14,BS_FLAT 155 | END 156 | 157 | 158 | ///////////////////////////////////////////////////////////////////////////// 159 | // 160 | // DESIGNINFO 161 | // 162 | 163 | #ifdef APSTUDIO_INVOKED 164 | GUIDELINES DESIGNINFO 165 | BEGIN 166 | IDD_ABOUT, DIALOG 167 | BEGIN 168 | VERTGUIDE, 7 169 | VERTGUIDE, 179 170 | HORZGUIDE, 7 171 | HORZGUIDE, 64 172 | END 173 | END 174 | #endif // APSTUDIO_INVOKED 175 | 176 | 177 | ///////////////////////////////////////////////////////////////////////////// 178 | // 179 | // AFX_DIALOG_LAYOUT 180 | // 181 | 182 | IDD_DIALOG1 AFX_DIALOG_LAYOUT 183 | BEGIN 184 | 0 185 | END 186 | 187 | IDD_ABOUT AFX_DIALOG_LAYOUT 188 | BEGIN 189 | 0 190 | END 191 | 192 | #endif // English (United Kingdom) resources 193 | ///////////////////////////////////////////////////////////////////////////// 194 | 195 | 196 | 197 | #ifndef APSTUDIO_INVOKED 198 | ///////////////////////////////////////////////////////////////////////////// 199 | // 200 | // Generated from the TEXTINCLUDE 3 resource. 201 | // 202 | 203 | 204 | ///////////////////////////////////////////////////////////////////////////// 205 | #endif // not APSTUDIO_INVOKED 206 | 207 | -------------------------------------------------------------------------------- /DesktopsMenu.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32210.238 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DesktopsMenu", "DesktopsMenu.vcxproj", "{4930CE8C-C73C-44E0-9F70-B38690768FCC}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {4930CE8C-C73C-44E0-9F70-B38690768FCC}.Debug|x64.ActiveCfg = Debug|x64 17 | {4930CE8C-C73C-44E0-9F70-B38690768FCC}.Debug|x64.Build.0 = Debug|x64 18 | {4930CE8C-C73C-44E0-9F70-B38690768FCC}.Debug|x86.ActiveCfg = Debug|Win32 19 | {4930CE8C-C73C-44E0-9F70-B38690768FCC}.Debug|x86.Build.0 = Debug|Win32 20 | {4930CE8C-C73C-44E0-9F70-B38690768FCC}.Release|x64.ActiveCfg = Release|x64 21 | {4930CE8C-C73C-44E0-9F70-B38690768FCC}.Release|x64.Build.0 = Release|x64 22 | {4930CE8C-C73C-44E0-9F70-B38690768FCC}.Release|x86.ActiveCfg = Release|Win32 23 | {4930CE8C-C73C-44E0-9F70-B38690768FCC}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {27D3C9F6-B228-4FC1-A1BC-53B2139353E6} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /DesktopsMenu.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 | 16.0 23 | Win32Proj 24 | {4930ce8c-c73c-44e0-9f70-b38690768fcc} 25 | DesktopsMenu 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | DesktopsMenu$(Platform) 76 | $(SolutionDir)Bin\$(Configuration)\ 77 | Int\$(Platform)$(Configuration)\ 78 | 79 | 80 | false 81 | DesktopsMenu$(Platform) 82 | $(SolutionDir)Bin\$(Configuration)\ 83 | Int\$(Platform)$(Configuration)\ 84 | 85 | 86 | true 87 | DesktopsMenu$(Platform) 88 | $(SolutionDir)Bin\$(Configuration)\ 89 | Int\$(Platform)$(Configuration)\ 90 | 91 | 92 | false 93 | DesktopsMenu$(Platform) 94 | $(SolutionDir)Bin\$(Configuration)\ 95 | Int\$(Platform)$(Configuration)\ 96 | 97 | 98 | 99 | Level3 100 | true 101 | WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 102 | true 103 | Use 104 | pch.h 105 | 106 | 107 | Windows 108 | true 109 | false 110 | DesktopsMenu.def 111 | WindowsApp.lib;version.lib;%(AdditionalDependencies) 112 | 113 | 114 | 115 | 116 | Level3 117 | true 118 | true 119 | true 120 | WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 121 | true 122 | Use 123 | pch.h 124 | 125 | 126 | Windows 127 | true 128 | true 129 | true 130 | false 131 | DesktopsMenu.def 132 | WindowsApp.lib;version.lib;%(AdditionalDependencies) 133 | 134 | 135 | 136 | 137 | Level3 138 | true 139 | _DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 140 | true 141 | Use 142 | pch.h 143 | 144 | 145 | Windows 146 | true 147 | false 148 | 149 | 150 | WindowsApp.lib;version.lib;%(AdditionalDependencies) 151 | 152 | 153 | 154 | 155 | Level3 156 | true 157 | true 158 | true 159 | NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 160 | true 161 | Use 162 | pch.h 163 | 164 | 165 | Windows 166 | true 167 | true 168 | true 169 | false 170 | 171 | 172 | WindowsApp.lib;version.lib;%(AdditionalDependencies) 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | Create 193 | Create 194 | Create 195 | Create 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | -------------------------------------------------------------------------------- /DesktopsMenu.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;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 | Source Files 20 | 21 | 22 | 23 | 24 | Header Files 25 | 26 | 27 | Header Files 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | 43 | 44 | Source Files 45 | 46 | 47 | Source Files 48 | 49 | 50 | Source Files 51 | 52 | 53 | Source Files 54 | 55 | 56 | Source Files 57 | 58 | 59 | 60 | 61 | Resource Files 62 | 63 | 64 | 65 | 66 | Resource Files 67 | 68 | 69 | Resource Files 70 | 71 | 72 | -------------------------------------------------------------------------------- /Hooks.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "Hooks.h" 3 | #include "resource.h" 4 | #include 5 | #include 6 | #include 7 | 8 | HMENU LoadPopupMenu(HINSTANCE hInstance, DWORD id); 9 | UINT MenuFindByCommand(HMENU hMenu, UINT id); 10 | void InsertDesktopMenu(const HMENU hMenuSwitch, const UINT id, const std::vector& names); 11 | 12 | std::vector GetDesktopNames(const HWND hDesktopsWnd) 13 | { 14 | std::vector dtnames; 15 | 16 | TCHAR names[1024] = _T(""); 17 | //GetWindowText(hDesktopsWnd, names, ARRAYSIZE(names)); 18 | SendMessage(hDesktopsWnd, WM_GETTEXT, ARRAYSIZE(names), (LPARAM) names); 19 | TCHAR* n = names; 20 | while (TCHAR* e = wcschr(n, _T('|'))) 21 | { 22 | *e = _T('\0'); 23 | dtnames.emplace_back(n); 24 | n = e + 1; 25 | } 26 | dtnames.emplace_back(n); 27 | 28 | return dtnames; 29 | } 30 | 31 | LRESULT SendDesktopMessage(const HWND hDesktopsWnd, const HWND hWnd, const UINT type, const Message msg) 32 | { 33 | const MessageStruct ms = { type, msg }; 34 | COPYDATASTRUCT cds = { CD_DESKTOPS, sizeof(ms), (PVOID) &ms }; 35 | return SendMessage(hDesktopsWnd, WM_COPYDATA, (WPARAM) hWnd, (LPARAM) &cds); 36 | } 37 | 38 | void HookProc(const HWND hWnd, const UINT message, const WPARAM wParam, const LPARAM lParam) 39 | { 40 | switch (message) 41 | { 42 | case WM_INITMENUPOPUP: 43 | { 44 | const HMENU hMenu = (HMENU) wParam; 45 | const HMENU hMenuSystem = GetSystemMenu(hWnd, FALSE); 46 | if (hMenu == hMenuSystem) 47 | { 48 | const HWND hDesktopsWnd = FindWindow(g_lpstrClass, nullptr); 49 | 50 | if (MenuFindByCommand(hMenuSystem, SC_PIN) == -1) 51 | { 52 | const HMENU hMenuDesktop = LoadPopupMenu(g_hDllInstance, IDR_SYSTEM); 53 | InsertDesktopMenu(GetSubMenu(hMenuDesktop, 1), SC_MOVE_DESKTOP, GetDesktopNames(hDesktopsWnd)); 54 | 55 | Shell_MergeMenus(hMenuSystem, hMenuDesktop, MenuFindByCommand(hMenuSystem, SC_CLOSE), 0, 0xFFFF, MM_ADDSEPARATOR); 56 | 57 | DestroyMenu(hMenuDesktop); 58 | } 59 | 60 | for (UINT type : { SC_PIN, SC_MOVE_PREV, SC_MOVE_NEXT }) 61 | { 62 | const LRESULT result = SendDesktopMessage(hDesktopsWnd, hWnd, type, Message::Query); 63 | EnableMenuItem(hMenuSystem, type, MF_BYCOMMAND | static_cast(result & MF_DISABLED)); 64 | CheckMenuItem(hMenuSystem, type, MF_BYCOMMAND | static_cast(result & MF_CHECKED)); 65 | } 66 | UINT radio = -1; 67 | UINT max = SC_MOVE_DESKTOP + 0x90; 68 | for (UINT type = SC_MOVE_DESKTOP + 0; type <= max; type += 0x10) 69 | { 70 | const LRESULT result = SendDesktopMessage(hDesktopsWnd, hWnd, type, Message::Query); 71 | if (EnableMenuItem(hMenuSystem, type, MF_BYCOMMAND | static_cast(result & MF_DISABLED)) < 0) 72 | { 73 | max = type - 1; 74 | break; 75 | } 76 | if (result & MF_CHECKED) 77 | radio = type; 78 | } 79 | if (radio != (UINT) -1) 80 | CheckMenuRadioItem(hMenuSystem, SC_MOVE_DESKTOP, max, radio, MF_BYCOMMAND); 81 | } 82 | break; 83 | } 84 | 85 | case WM_SYSCOMMAND: 86 | switch (((UINT) wParam & 0xFFF0)) 87 | { 88 | case SC_PIN: 89 | case SC_MOVE_PREV: 90 | case SC_MOVE_NEXT: 91 | case SC_MOVE_DESKTOP + 0x00: 92 | case SC_MOVE_DESKTOP + 0x10: 93 | case SC_MOVE_DESKTOP + 0x20: 94 | case SC_MOVE_DESKTOP + 0x30: 95 | case SC_MOVE_DESKTOP + 0x40: 96 | case SC_MOVE_DESKTOP + 0x50: 97 | case SC_MOVE_DESKTOP + 0x60: 98 | case SC_MOVE_DESKTOP + 0x70: 99 | case SC_MOVE_DESKTOP + 0x80: 100 | case SC_MOVE_DESKTOP + 0x90: 101 | { 102 | const HWND hDesktopsWnd = FindWindow(g_lpstrClass, nullptr); 103 | SendDesktopMessage(hDesktopsWnd, hWnd, (UINT) wParam, Message::Select); 104 | break; 105 | } 106 | } 107 | break; 108 | } 109 | } 110 | 111 | LRESULT CALLBACK CallWndProc(_In_ const int nCode, _In_ const WPARAM wParam, _In_ const LPARAM lParam) 112 | { 113 | if (nCode == HC_ACTION) 114 | { 115 | const CWPSTRUCT* sMsg = (CWPSTRUCT*) lParam; 116 | HookProc(sMsg->hwnd, sMsg->message, sMsg->wParam, sMsg->lParam); 117 | } 118 | 119 | return CallNextHookEx(NULL, nCode, wParam, lParam); 120 | } 121 | 122 | LRESULT CALLBACK GetMsgProc(_In_ const int nCode, _In_ const WPARAM wParam, _In_ const LPARAM lParam) 123 | { 124 | if (nCode == HC_ACTION) 125 | { 126 | if (wParam == PM_REMOVE) 127 | { 128 | const MSG* pMsg = (MSG*) lParam; 129 | HookProc(pMsg->hwnd, pMsg->message, pMsg->wParam, pMsg->lParam); 130 | } 131 | } 132 | 133 | return CallNextHookEx(NULL, nCode, wParam, lParam); 134 | } 135 | -------------------------------------------------------------------------------- /Hooks.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | LRESULT CALLBACK CallWndProc(_In_ const int nCode, _In_ const WPARAM wParam, _In_ const LPARAM lParam); 4 | LRESULT CALLBACK GetMsgProc(_In_ const int nCode, _In_ const WPARAM wParam, _In_ const LPARAM lParam); 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Desktops Menu 3 | ========== 4 | 5 | Adds items to the windows system menu for virtual desktops 6 | 7 | ![Windows](https://img.shields.io/badge/platform-Windows-blue.svg) 8 | [![Releases](https://img.shields.io/github/release/RadAd/DesktopsMenu.svg)](https://github.com/RadAd/DesktopsMenu/releases/latest) 9 | [![commits-since](https://img.shields.io/github/commits-since/RadAd/DesktopsMenu/latest.svg)](commits/master) 10 | [![Build](https://img.shields.io/appveyor/ci/RadAd/DesktopsMenu.svg)](https://ci.appveyor.com/project/RadAd/DesktopsMenu) 11 | 12 | ![Screenshot](docs/screenshot.png) 13 | 14 | Build 15 | ======= 16 | ```bat 17 | msbuild DesktopsMenu.vcxproj -p:Configuration=Release -p:Platform=x86 18 | msbuild DesktopsMenu.vcxproj -p:Configuration=Release -p:Platform=x64 19 | ``` 20 | Run 21 | ======= 22 | ```bat 23 | rundll32.exe Bin\Release\DesktopsMenuWin32.dll,DesktopsMenu 24 | rundll32.exe Bin\Release\DesktopsMenux64.dll,DesktopsMenu 25 | ``` 26 | -------------------------------------------------------------------------------- /Win10Desktops.h: -------------------------------------------------------------------------------- 1 | // https://github.com/Ciantic/VirtualDesktopAccessor 2 | 3 | // See for more up-to-date 4 | // https://github.com/skottmckay/VirtualDesktopAccessor 5 | 6 | #pragma once 7 | 8 | //#include "framework.h" 9 | #include 10 | #include 11 | #include 12 | 13 | #define FC_USER_MARSHAL void 14 | 15 | class DECLSPEC_UUID("C2F03A33-21F5-47FA-B4BB-156362A2F239") ImmersiveShell; 16 | const CLSID CLSID_ImmersiveShell = __uuidof(ImmersiveShell); 17 | 18 | class DECLSPEC_UUID("C5E0CDCA-7B6E-41B2-9FC4-D93975CC467B") VirtualDesktopManagerInternal; 19 | const CLSID CLSID_VirtualDesktopManagerInternal = __uuidof(VirtualDesktopManagerInternal); 20 | 21 | class DECLSPEC_UUID("A501FDEC-4A09-464C-AE4E-1B9C21B84918") VirtualNotificationService; 22 | const CLSID CLSID_VirtualNotificationService = __uuidof(VirtualNotificationService); 23 | 24 | class DECLSPEC_UUID("B5A399E7-1C87-46B8-88E9-FC5747B171BD") VirtualDesktopPinnedApps; 25 | const CLSID CLSID_VirtualDesktopPinnedApps = __uuidof(VirtualDesktopPinnedApps); 26 | 27 | // Ignore following API's: 28 | interface IAsyncCallback; 29 | interface IImmersiveMonitor; 30 | interface APPLICATION_VIEW_COMPATIBILITY_POLICY; 31 | interface IShellPositionerPriority; 32 | interface IApplicationViewOperation; 33 | interface APPLICATION_VIEW_CLOAK_TYPE; 34 | interface IApplicationViewPosition; 35 | interface IImmersiveApplication; 36 | interface IApplicationViewChangeListener; 37 | 38 | 39 | DECLARE_INTERFACE_IID_(IApplicationView, IInspectable, "372E1D3B-38D3-42E4-A15B-8AB2B178F513") 40 | { 41 | STDMETHOD(SetFocus)(THIS) PURE; // Proc6 42 | STDMETHOD(SwitchTo)(THIS) PURE; // Proc7 43 | STDMETHOD(TryInvokeBack)(THIS_ IAsyncCallback*) PURE; // Proc8 44 | STDMETHOD(GetThumbnailWindow)(THIS_ HWND*) PURE; // Proc9 45 | STDMETHOD(GetMonitor)(THIS_ IImmersiveMonitor**) PURE; // Proc10 46 | STDMETHOD(GetVisibility)(THIS_ int*) PURE; // Proc11 47 | STDMETHOD(SetCloak)(THIS_ APPLICATION_VIEW_CLOAK_TYPE, int) PURE; // Proc12 48 | STDMETHOD(GetPosition)(THIS_ REFIID, void**) PURE; // Proc13 49 | STDMETHOD(SetPosition)(THIS_ IApplicationViewPosition*) PURE; // Proc14 50 | STDMETHOD(InsertAfterWindow)(THIS_ HWND) PURE; // Proc15 51 | STDMETHOD(GetExtendedFramePosition)(THIS_ RECT*) PURE; // Proc16 52 | STDMETHOD(GetAppUserModelId)(THIS_ PWSTR*) PURE; // Proc17 53 | STDMETHOD(SetAppUserModelId)(THIS_ PCWSTR) PURE; // Proc18 54 | STDMETHOD(IsEqualByAppUserModelId)(THIS_ PCWSTR, int*) PURE; // Proc19 55 | STDMETHOD(GetViewState)(THIS_ UINT*) PURE; // Proc20 56 | STDMETHOD(SetViewState)(THIS_ UINT) PURE; // Proc21 57 | STDMETHOD(GetNeediness)(THIS_ int*) PURE; // Proc22 58 | STDMETHOD(GetLastActivationTimestamp)(THIS_ ULONGLONG*) PURE; // Proc23 59 | STDMETHOD(SetLastActivationTimestamp)(THIS_ ULONGLONG) PURE; // Proc24 60 | STDMETHOD(GetVirtualDesktopId)(THIS_ GUID*) PURE; // Proc25 61 | STDMETHOD(SetVirtualDesktopId)(THIS_ REFGUID) PURE; // Proc26 62 | STDMETHOD(GetShowInSwitchers)(THIS_ int*) PURE; // Proc27 63 | STDMETHOD(SetShowInSwitchers)(THIS_ int) PURE; // Proc28 64 | STDMETHOD(GetScaleFactor)(THIS_ int*) PURE; // Proc29 65 | STDMETHOD(CanReceiveInput)(THIS_ BOOL*) PURE; // Proc30 66 | STDMETHOD(GetCompatibilityPolicyType)(THIS_ APPLICATION_VIEW_COMPATIBILITY_POLICY*) PURE; // Proc31 67 | STDMETHOD(SetCompatibilityPolicyType)(THIS_ APPLICATION_VIEW_COMPATIBILITY_POLICY) PURE; // Proc32 68 | //STDMETHOD(GetPositionPriority)(THIS_ IShellPositionerPriority**) PURE; // removed in 1803 // Proc33 69 | //STDMETHOD(SetPositionPriority)(THIS_ IShellPositionerPriority*) PURE; // removed in 1803 // Proc34 70 | STDMETHOD(GetSizeConstraints)(THIS_ IImmersiveMonitor*, SIZE*, SIZE*) PURE; // Proc35 71 | STDMETHOD(GetSizeConstraintsForDpi)(THIS_ UINT, SIZE*, SIZE*) PURE; // Proc36 72 | STDMETHOD(SetSizeConstraintsForDpi)(THIS_ const UINT*, const SIZE*, const SIZE*) PURE; // Proc37 73 | //STDMETHOD(QuerySizeConstraintsFromApp)(THIS) PURE; // removed in 1803 // Proc38 74 | STDMETHOD(OnMinSizePreferencesUpdated)(THIS_ HWND) PURE; // Proc39 75 | STDMETHOD(ApplyOperation)(THIS_ IApplicationViewOperation*) PURE; // Proc40 76 | STDMETHOD(IsTray)(THIS_ BOOL*) PURE; // Proc41 77 | STDMETHOD(IsInHighZOrderBand)(THIS_ BOOL*) PURE; // Proc42 78 | STDMETHOD(IsSplashScreenPresented)(THIS_ BOOL*) PURE; // Proc43 79 | STDMETHOD(Flash)(THIS) PURE; // Proc44 80 | STDMETHOD(GetRootSwitchableOwner)(THIS_ IApplicationView**) PURE; // proc45 81 | STDMETHOD(EnumerateOwnershipTree)(THIS_ IObjectArray**) PURE; // proc46 82 | STDMETHOD(GetEnterpriseId)(THIS_ PWSTR*) PURE; // proc47 83 | STDMETHOD(IsMirrored)(THIS_ BOOL*) PURE; // Proc48 84 | STDMETHOD(Proc49)(THIS_ int*) PURE; 85 | STDMETHOD(Proc50)(THIS_ /* ENUM32 */ int32_t*) PURE; 86 | STDMETHOD(Proc51)(THIS_ int*) PURE; 87 | STDMETHOD(Proc52)(THIS_ int) PURE; 88 | STDMETHOD(Proc53)(THIS_ int*) PURE; 89 | STDMETHOD(Proc54)(THIS_ int) PURE; 90 | STDMETHOD(Proc55)(THIS) PURE; 91 | STDMETHOD(Proc56)(THIS_ int*) PURE; 92 | STDMETHOD(Proc57)(THIS_ int) PURE; 93 | STDMETHOD(Proc58)(THIS_ /* ENUM32 */ uint32_t, /* ENUM32 */ uint32_t) PURE; 94 | STDMETHOD(Proc59)(THIS_ int) PURE; 95 | STDMETHOD(Proc60)(THIS_ SIZE*) PURE; 96 | STDMETHOD(Proc61)(THIS_ PWSTR*) PURE; 97 | }; 98 | 99 | const __declspec(selectany) IID& IID_IApplicationView = __uuidof(IApplicationView); 100 | 101 | DECLARE_INTERFACE_IID_(IApplicationViewCollection, IUnknown, "1841C6D7-4F9D-42C0-AF41-8747538F10E5") 102 | { 103 | STDMETHOD(GetViews)(THIS_ IObjectArray**) PURE; 104 | STDMETHOD(GetViewsByZOrder)(THIS_ IObjectArray**) PURE; 105 | STDMETHOD(GetViewsByAppUserModelId)(THIS_ PCWSTR, IObjectArray**) PURE; 106 | STDMETHOD(GetViewForHwnd)(THIS_ HWND, IApplicationView**) PURE; 107 | STDMETHOD(GetViewForApplication)(THIS_ IImmersiveApplication*, IApplicationView**) PURE; 108 | STDMETHOD(GetViewForAppUserModelId)(THIS_ PCWSTR, IApplicationView**) PURE; 109 | STDMETHOD(GetViewInFocus)(THIS_ IApplicationView**) PURE; 110 | STDMETHOD(TryGetLastActiveVisibleView)(THIS_ IApplicationView**) PURE; 111 | STDMETHOD(RefreshCollection)(THIS) PURE; 112 | STDMETHOD(RegisterForApplicationViewChanges)(THIS_ IApplicationViewChangeListener*, DWORD*) PURE; 113 | STDMETHOD(UnregisterForApplicationViewChanges)(THIS_ DWORD) PURE; 114 | }; 115 | 116 | const __declspec(selectany) IID& IID_IApplicationViewCollection = __uuidof(IApplicationViewCollection); 117 | 118 | DECLARE_INTERFACE_IID_(IVirtualDesktopPinnedApps, IUnknown, "4CE81583-1E4C-4632-A621-07A53543148F") 119 | { 120 | STDMETHOD(IsAppIdPinned)(THIS_ PCWSTR appId, BOOL*) PURE; 121 | STDMETHOD(PinAppID)(THIS_ PCWSTR appId) PURE; 122 | STDMETHOD(UnpinAppID)(THIS_ PCWSTR appId) PURE; 123 | STDMETHOD(IsViewPinned)(THIS_ IApplicationView*, BOOL*) PURE; 124 | STDMETHOD(PinView)(THIS_ IApplicationView*) PURE; 125 | STDMETHOD(UnpinView)(THIS_ IApplicationView*) PURE; 126 | }; 127 | 128 | namespace Win10 { 129 | 130 | MIDL_INTERFACE("FF72FFDD-BE7E-43FC-9C03-AD81681E88E4") 131 | IVirtualDesktop : public IUnknown 132 | { 133 | public: 134 | virtual HRESULT STDMETHODCALLTYPE IsViewVisible( 135 | _In_ IApplicationView* pView, 136 | _Out_ BOOL* pfVisible) = 0; 137 | 138 | virtual HRESULT STDMETHODCALLTYPE GetID( 139 | _Out_ GUID* pGuid) = 0; 140 | }; 141 | 142 | MIDL_INTERFACE("31EBDE3F-6EC3-4CBD-B9FB-0EF6D09B41F4") 143 | IVirtualDesktop2: public IVirtualDesktop 144 | { 145 | public: 146 | virtual HRESULT STDMETHODCALLTYPE GetName( 147 | _Out_ HSTRING* pStr) = 0; 148 | }; 149 | 150 | } 151 | 152 | namespace Win11 { 153 | 154 | MIDL_INTERFACE("536D3495-B208-4CC9-AE26-DE8111275BF8") 155 | IVirtualDesktop : public IUnknown 156 | { 157 | public: 158 | virtual HRESULT STDMETHODCALLTYPE IsViewVisible( 159 | _In_ IApplicationView* pView, 160 | _Out_ BOOL* pfVisible) = 0; 161 | 162 | virtual HRESULT STDMETHODCALLTYPE GetID( 163 | _Out_ GUID* pGuid) = 0; 164 | 165 | virtual HRESULT STDMETHODCALLTYPE GetMonitor( 166 | _Out_ HMONITOR* pMonitor) = 0; 167 | 168 | virtual HRESULT STDMETHODCALLTYPE GetName( 169 | _Out_ HSTRING* p0) = 0; 170 | 171 | virtual HRESULT STDMETHODCALLTYPE GetWallpaperPath( 172 | _Out_ FC_USER_MARSHAL** p0) = 0; 173 | }; 174 | 175 | } 176 | 177 | enum AdjacentDesktop 178 | { 179 | LeftDirection = 3, 180 | RightDirection = 4 181 | }; 182 | 183 | namespace Win10 { 184 | 185 | MIDL_INTERFACE("F31574D6-B682-4CDC-BD56-1827860ABEC6") 186 | IVirtualDesktopManagerInternal : public IUnknown 187 | { 188 | public: 189 | virtual HRESULT STDMETHODCALLTYPE GetCount( 190 | _Out_ UINT* pCount) = 0; 191 | 192 | virtual HRESULT STDMETHODCALLTYPE MoveViewToDesktop( 193 | _In_ IApplicationView* pView, 194 | _In_ IVirtualDesktop* pDesktop) = 0; 195 | 196 | // Since build 10240 197 | virtual HRESULT STDMETHODCALLTYPE CanViewMoveDesktops( 198 | _In_ IApplicationView* pView, 199 | _Out_ BOOL* pfCanViewMoveDesktops) = 0; 200 | 201 | virtual HRESULT STDMETHODCALLTYPE GetCurrentDesktop( 202 | _Out_ IVirtualDesktop** desktop) = 0; 203 | 204 | virtual HRESULT STDMETHODCALLTYPE GetDesktops( 205 | _Out_ IObjectArray** ppDesktops) = 0; 206 | 207 | virtual HRESULT STDMETHODCALLTYPE GetAdjacentDesktop( 208 | _In_ IVirtualDesktop* pDesktopReference, 209 | _In_ AdjacentDesktop uDirection, 210 | _Out_ IVirtualDesktop** ppAdjacentDesktop) = 0; 211 | 212 | virtual HRESULT STDMETHODCALLTYPE SwitchDesktop( 213 | _In_ IVirtualDesktop* pDesktop) = 0; 214 | 215 | virtual HRESULT STDMETHODCALLTYPE CreateDesktopW( 216 | _Out_ IVirtualDesktop** ppNewDesktop) = 0; 217 | 218 | virtual HRESULT STDMETHODCALLTYPE RemoveDesktop( 219 | _In_ IVirtualDesktop* pRemove, 220 | _In_ IVirtualDesktop* pFallbackDesktop) = 0; 221 | 222 | // Since build 10240 223 | virtual HRESULT STDMETHODCALLTYPE FindDesktop( 224 | _In_ GUID* desktopId, 225 | _Out_ IVirtualDesktop** ppDesktop) = 0; 226 | 227 | // Since build ?? 228 | virtual HRESULT STDMETHODCALLTYPE Proc13( 229 | _In_ IVirtualDesktop* pDesktop, 230 | _Out_ IObjectArray** ppDesktops1, 231 | _Out_ IObjectArray** ppDesktops2) = 0; 232 | }; 233 | 234 | MIDL_INTERFACE("0F3A72B0-4566-487E-9A33-4ED302F6D6CE") 235 | IVirtualDesktopManagerInternal2 : public IVirtualDesktopManagerInternal 236 | { 237 | public: 238 | virtual HRESULT STDMETHODCALLTYPE SetName( 239 | _In_ IVirtualDesktop* p0, 240 | _In_ HSTRING name) = 0; 241 | }; 242 | 243 | MIDL_INTERFACE("FE538FF5-D53B-4F5A-9DAD-8E72873CB360") 244 | IVirtualDesktopManagerInternal3 : public IVirtualDesktopManagerInternal2 245 | { 246 | public: 247 | virtual HRESULT STDMETHODCALLTYPE Proc15( 248 | _In_ IApplicationView* p0, 249 | _In_ IApplicationView* p1) = 0; 250 | }; 251 | 252 | } 253 | 254 | namespace Win11 { 255 | 256 | MIDL_INTERFACE("B2F925B9-5A0F-4D2E-9F4D-2B1507593C10") 257 | IVirtualDesktopManagerInternal : public IUnknown 258 | { 259 | public: 260 | virtual HRESULT STDMETHODCALLTYPE GetCount( 261 | _In_opt_ HMONITOR monitor, 262 | _Out_ UINT* pCount) = 0; 263 | 264 | virtual HRESULT STDMETHODCALLTYPE MoveViewToDesktop( 265 | _In_ IApplicationView* pView, 266 | _In_ IVirtualDesktop* pDesktop) = 0; 267 | 268 | virtual HRESULT STDMETHODCALLTYPE CanViewMoveDesktops( 269 | _In_ IApplicationView* pView, 270 | _Out_ BOOL* pfCanViewMoveDesktops) = 0; 271 | 272 | virtual HRESULT STDMETHODCALLTYPE GetCurrentDesktop( 273 | _In_opt_ HMONITOR monitor, 274 | _Out_ IVirtualDesktop** desktop) = 0; 275 | 276 | virtual HRESULT STDMETHODCALLTYPE GetAllCurrentDesktops( 277 | _Out_ IObjectArray** ppDesktops) = 0; 278 | 279 | virtual HRESULT STDMETHODCALLTYPE GetDesktops( 280 | _In_opt_ HMONITOR monitor, 281 | _Out_ IObjectArray** ppDesktops) = 0; 282 | 283 | virtual HRESULT STDMETHODCALLTYPE GetAdjacentDesktop( 284 | _In_ IVirtualDesktop* pDesktopReference, 285 | _In_ AdjacentDesktop uDirection, 286 | _Out_ IVirtualDesktop** ppAdjacentDesktop) = 0; 287 | 288 | virtual HRESULT STDMETHODCALLTYPE SwitchDesktop( 289 | _In_opt_ HMONITOR monitor, 290 | _In_ IVirtualDesktop* pDesktop) = 0; 291 | 292 | virtual HRESULT STDMETHODCALLTYPE CreateDesktopW( 293 | _In_opt_ HMONITOR monitor, 294 | _Out_ IVirtualDesktop** ppNewDesktop) = 0; 295 | 296 | virtual HRESULT STDMETHODCALLTYPE MoveDesktop( // New for Win11 297 | _In_ IVirtualDesktop* desktop, 298 | _In_opt_ HMONITOR monitor, 299 | _In_ INT32 index) = 0; 300 | 301 | virtual HRESULT STDMETHODCALLTYPE RemoveDesktop( 302 | _In_ IVirtualDesktop* pRemove, 303 | _In_ IVirtualDesktop* pFallbackDesktop) = 0; 304 | 305 | virtual HRESULT STDMETHODCALLTYPE FindDesktop( 306 | _In_ GUID* desktopId, 307 | _Out_ IVirtualDesktop** ppDesktop) = 0; 308 | 309 | virtual HRESULT STDMETHODCALLTYPE GetDesktopSwitchIncludeExcludeViews( 310 | _In_ IVirtualDesktop* pDesktop, 311 | _Out_ IObjectArray** ppDesktops1, 312 | _Out_ IObjectArray** ppDesktops2) = 0; 313 | 314 | virtual HRESULT STDMETHODCALLTYPE SetName( 315 | _In_ IVirtualDesktop* p0, 316 | _In_ HSTRING name) = 0; 317 | 318 | virtual HRESULT STDMETHODCALLTYPE SetWallpaper( 319 | _In_ IVirtualDesktop* p0, 320 | _In_ HSTRING name) = 0; 321 | 322 | virtual HRESULT STDMETHODCALLTYPE UpdateWallpaperPathForAllDesktops( 323 | _In_ HSTRING name) = 0; 324 | 325 | virtual HRESULT STDMETHODCALLTYPE CopyDesktopState( 326 | _In_ IApplicationView* p0, 327 | _In_ IApplicationView* p1) = 0; 328 | 329 | virtual HRESULT STDMETHODCALLTYPE GetDesktopIsPerMonitor( 330 | _Out_ BOOL* p0) = 0; 331 | 332 | virtual HRESULT STDMETHODCALLTYPE SetDesktopIsPerMonitor( 333 | _In_ BOOL p0) = 0; 334 | }; 335 | 336 | } 337 | 338 | namespace Win10 { 339 | 340 | MIDL_INTERFACE("C179334C-4295-40D3-BEA1-C654D965605A") 341 | IVirtualDesktopNotification : public IUnknown 342 | { 343 | public: 344 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopCreated( 345 | _In_ IVirtualDesktop* pDesktop) = 0; 346 | 347 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopDestroyBegin( 348 | _In_ IVirtualDesktop* pDesktopDestroyed, 349 | _In_ IVirtualDesktop* pDesktopFallback) = 0; 350 | 351 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopDestroyFailed( 352 | _In_ IVirtualDesktop* pDesktopDestroyed, 353 | _In_ IVirtualDesktop* pDesktopFallback) = 0; 354 | 355 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopDestroyed( 356 | _In_ IVirtualDesktop* pDesktopDestroyed, 357 | _In_ IVirtualDesktop* pDesktopFallback) = 0; 358 | 359 | virtual HRESULT STDMETHODCALLTYPE ViewVirtualDesktopChanged( 360 | _In_ IApplicationView* pView) = 0; 361 | 362 | virtual HRESULT STDMETHODCALLTYPE CurrentVirtualDesktopChanged( 363 | _In_ IVirtualDesktop* pDesktopOld, 364 | _In_ IVirtualDesktop* pDesktopNew) = 0; 365 | }; 366 | 367 | const __declspec(selectany) IID& IID_IVirtualDesktopNotification = __uuidof(IVirtualDesktopNotification); 368 | 369 | MIDL_INTERFACE("1BA7CF30-3591-43FA-ABFA-4AAF7ABEEDB7") 370 | IVirtualDesktopNotification2 : public IVirtualDesktopNotification 371 | { 372 | public: 373 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopNameChanged( 374 | _In_ IVirtualDesktop* pDesktop, 375 | _In_ HSTRING p1) = 0; 376 | }; 377 | 378 | MIDL_INTERFACE("0CD45E71-D927-4F15-8B0A-8FEF525337BF") 379 | IVirtualDesktopNotificationService : public IUnknown 380 | { 381 | public: 382 | virtual HRESULT STDMETHODCALLTYPE Register( 383 | _In_ IVirtualDesktopNotification * pNotification, 384 | _Out_ DWORD * pdwCookie) = 0; 385 | 386 | virtual HRESULT STDMETHODCALLTYPE Unregister( 387 | _In_ DWORD dwCookie) = 0; 388 | }; 389 | 390 | } 391 | 392 | namespace Win11 { 393 | 394 | MIDL_INTERFACE("CD403E52-DEED-4C13-B437-B98380F2B1E8") 395 | IVirtualDesktopNotification : public IUnknown 396 | { 397 | public: 398 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopCreated( 399 | _In_ IObjectArray* monitors, 400 | _In_ IVirtualDesktop* pDesktop) = 0; 401 | 402 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopDestroyBegin( 403 | _In_ IObjectArray* monitors, 404 | _In_ IVirtualDesktop* pDesktopDestroyed, 405 | _In_ IVirtualDesktop* pDesktopFallback) = 0; 406 | 407 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopDestroyFailed( 408 | _In_ IObjectArray* monitors, 409 | _In_ IVirtualDesktop* pDesktopDestroyed, 410 | _In_ IVirtualDesktop* pDesktopFallback) = 0; 411 | 412 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopDestroyed( 413 | _In_ IObjectArray* monitors, 414 | _In_ IVirtualDesktop* pDesktopDestroyed, 415 | _In_ IVirtualDesktop* pDesktopFallback) = 0; 416 | 417 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopIsPerMonitorChanged( 418 | _In_ BOOL isPerMonitor) = 0; 419 | 420 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopMoved( 421 | _In_ IObjectArray* monitors, 422 | _In_ IVirtualDesktop* pDesktop, 423 | _In_ int64_t oldIndex, 424 | _In_ int64_t newIndex) = 0; 425 | 426 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopNameChanged11( 427 | _In_ IVirtualDesktop* pDesktop, 428 | _In_ HSTRING name) = 0; 429 | 430 | virtual HRESULT STDMETHODCALLTYPE ViewVirtualDesktopChanged11( 431 | _In_ IApplicationView* pView) = 0; 432 | 433 | virtual HRESULT STDMETHODCALLTYPE CurrentVirtualDesktopChanged( 434 | _In_ IObjectArray* monitors, 435 | _In_ IVirtualDesktop* pDesktopOld, 436 | _In_ IVirtualDesktop* pDesktopNew) = 0; 437 | 438 | virtual HRESULT STDMETHODCALLTYPE VirtualDesktopWallpaperChanged( 439 | _In_ IVirtualDesktop* pDesktop, 440 | _In_ HSTRING name) = 0; 441 | }; 442 | 443 | const __declspec(selectany) IID& IID_IVirtualDesktopNotification = __uuidof(IVirtualDesktopNotification); 444 | 445 | MIDL_INTERFACE("0CD45E71-D927-4F15-8B0A-8FEF525337BF") 446 | IVirtualDesktopNotificationService : public IUnknown 447 | { 448 | public: 449 | virtual HRESULT STDMETHODCALLTYPE Register( 450 | _In_ IVirtualDesktopNotification * pNotification, 451 | _Out_ DWORD * pdwCookie) = 0; 452 | 453 | virtual HRESULT STDMETHODCALLTYPE Unregister( 454 | _In_ DWORD dwCookie) = 0; 455 | }; 456 | 457 | } 458 | 459 | template 460 | inline HRESULT GetCurrentDesktop(VDMI* pDesktopManagerInternal, VD** desktop) 461 | { 462 | return pDesktopManagerInternal->GetCurrentDesktop(desktop); 463 | } 464 | 465 | template <> 466 | inline HRESULT GetCurrentDesktop(Win11::IVirtualDesktopManagerInternal* pDesktopManagerInternal, Win11::IVirtualDesktop** desktop) 467 | { 468 | return pDesktopManagerInternal->GetCurrentDesktop(NULL, desktop); 469 | } 470 | 471 | template 472 | inline HRESULT GetDesktops(VDMI* pDesktopManagerInternal, IObjectArray** ppDesktops) 473 | { 474 | return pDesktopManagerInternal->GetDesktops(ppDesktops); 475 | } 476 | 477 | template <> 478 | inline HRESULT GetDesktops(Win11::IVirtualDesktopManagerInternal* pDesktopManagerInternal, IObjectArray** ppDesktops) 479 | { 480 | return pDesktopManagerInternal->GetDesktops(NULL, ppDesktops); 481 | } 482 | 483 | template 484 | inline HRESULT SwitchDesktop(VDMI* pDesktopManagerInternal, VD* desktop) 485 | { 486 | return pDesktopManagerInternal->SwitchDesktop(desktop); 487 | } 488 | 489 | template <> 490 | inline HRESULT SwitchDesktop(Win11::IVirtualDesktopManagerInternal* pDesktopManagerInternal, Win11::IVirtualDesktop* desktop) 491 | { 492 | return pDesktopManagerInternal->SwitchDesktop(NULL, desktop); 493 | } 494 | 495 | template 496 | inline HRESULT CreateDesktop(VDMI* pDesktopManagerInternal, VD** desktop) 497 | { 498 | return pDesktopManagerInternal->CreateDesktop(desktop); 499 | } 500 | 501 | template <> 502 | inline HRESULT CreateDesktop(Win11::IVirtualDesktopManagerInternal* pDesktopManagerInternal, Win11::IVirtualDesktop** desktop) 503 | { 504 | return pDesktopManagerInternal->CreateDesktop(NULL, desktop); 505 | } 506 | -------------------------------------------------------------------------------- /dllmain.cpp: -------------------------------------------------------------------------------- 1 | // dllmain.cpp : Defines the entry point for the DLL application. 2 | #include "pch.h" 3 | 4 | HINSTANCE g_hDllInstance = NULL; 5 | 6 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 7 | { 8 | switch (ul_reason_for_call) 9 | { 10 | case DLL_PROCESS_ATTACH: 11 | g_hDllInstance = hModule; 12 | //CoInitializeEx(nullptr, COINIT_MULTITHREADED); 13 | break; 14 | case DLL_PROCESS_DETACH: 15 | //CoUninitialize(); 16 | break; 17 | case DLL_THREAD_ATTACH: 18 | case DLL_THREAD_DETACH: 19 | break; 20 | } 21 | return TRUE; 22 | } 23 | 24 | HWND g_hMsgWnd = NULL; 25 | 26 | void ReportError(LPCTSTR msg) 27 | { 28 | #if 0 29 | MessageBox(g_hMsgWnd, msg, g_lpstrTitle, MB_ICONERROR | MB_OK); 30 | #else 31 | TCHAR buf[1024]; 32 | swprintf_s(buf, _T("%s\n"), msg); 33 | OutputDebugString(buf); 34 | #endif 35 | } 36 | -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RadAd/DesktopsMenu/6ee68acef3a2f177392f4cc08dbfea1a6806b2c2/docs/screenshot.png -------------------------------------------------------------------------------- /pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: This is a precompiled header file. 2 | // Files listed below are compiled only once, improving build performance for future builds. 3 | // This also affects IntelliSense performance, including code completion and many code browsing features. 4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds. 5 | // Do not add files here that you will be updating frequently as this negates the performance advantage. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | //#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | extern HINSTANCE g_hDllInstance; 17 | extern const LPCTSTR g_lpstrTitle; 18 | extern const LPCTSTR g_lpstrClass; 19 | 20 | #define CD_DESKTOPS 0x0100 21 | 22 | enum class Message 23 | { 24 | Query, 25 | Select, 26 | }; 27 | 28 | struct MessageStruct 29 | { 30 | UINT type; 31 | Message msg; 32 | }; 33 | 34 | extern HWND g_hMsgWnd; 35 | 36 | void ReportError(LPCTSTR msg); 37 | 38 | #define CHECK_HR_MSG(x, msg) if (FAILED(x)) { ReportError(_T("Check failed: ") msg); return; } 39 | #define CHECK_HR(x) CHECK_HR_MSG(x, _CRT_WIDE(#x)) 40 | #define CHECK_HR_MSG_RET(x, msg, r) if (FAILED(x)) { ReportError(_T("Check failed: ") msg); return r; } 41 | #define CHECK_HR_RET(x, r) CHECK_HR_MSG_RET(x, _CRT_WIDE(#x), r) 42 | #define CHECK_MSG(x, msg) if (!(x)) { ReportError(msg); return; } 43 | #define CHECK(x) CHECK_MSG(x, _CRT_WIDE(#x)) 44 | // No return 45 | #define VALIDATE(x, msg) if (!(x)) { ReportError(msg); } 46 | #define VALIDATEE(x) VALIDATE(x, _T("Error: ") _CRT_WIDE(#x)) 47 | 48 | #endif //PCH_H 49 | -------------------------------------------------------------------------------- /res/main.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RadAd/DesktopsMenu/6ee68acef3a2f177392f4cc08dbfea1a6806b2c2/res/main.ico -------------------------------------------------------------------------------- /resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by DesktopsMenu.rc 4 | // 5 | #define IDI_MAIN 5 6 | #define IDR_MAIN 102 7 | #define IDD_ABOUT 103 8 | #define IDR_SWITCH 106 9 | #define IDR_SYSTEM 108 10 | #define IDC_ABOUT_APPICON 1001 11 | #define IDC_ABOUT_PRODUCT 1002 12 | #define IDC_ABOUT_VERSION 1003 13 | #define IDC_ABOUT_WEBSITE 1004 14 | #define IDC_ABOUT_MAIL 1005 15 | #define ID_MAIN_EXIT 40001 16 | #define ID_MAIN_ABOUT 40002 17 | #define ID_SWITCH_PREVIOUS 40003 18 | #define ID_SWITCH_NEXT 40004 19 | #define ID_SWITCH_DESKTOPS 40805 20 | #define SC_PIN 0xF200 21 | #define SC_MOVE_PREV 0xF210 22 | #define SC_MOVE_NEXT 0xF220 23 | #define SC_MOVE_DESKTOP 0xF300 24 | 25 | // Next default values for new objects 26 | // 27 | #ifdef APSTUDIO_INVOKED 28 | #ifndef APSTUDIO_READONLY_SYMBOLS 29 | #define _APS_NEXT_RESOURCE_VALUE 109 30 | #define _APS_NEXT_COMMAND_VALUE 40011 31 | #define _APS_NEXT_CONTROL_VALUE 1006 32 | #define _APS_NEXT_SYMED_VALUE 101 33 | #endif 34 | #endif 35 | --------------------------------------------------------------------------------