aveyo

Dota2_start_on_virtualdesktop

Feb 19th, 2024 (edited)
311
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Batch 105.95 KB | Gaming | 0 0
  1. @(echo off% <#%) & goto :start DOTA2 on dedicated virtual desktop
  2.  
  3.   prevent alt + tab issues by instead switching the virtual desktop with ctrl + win + left / ctrl + win + right
  4.   Desktop-friendly Fullscreen mode set for low input lag and fast alt + tab on native res [automatic]
  5.   Exclusive Fullscreen Legacy mode set for low input lag but slow alt + tab on scaled res [if using -w -h options]
  6.   script removes fullscreen [un]optimizations and verify integrity flags after crash to relaunch quicker
  7.  
  8.   because Valve moved all game configs to steam cloud folder, connection issues can cause missing / reset settings
  9.   script gathers them into cloud.cfg saved at the game cfg folder and reloads them with exec_async cloud
  10.   change video settings below - prefix with # any entry you do not need - preset : potato pixelated shadows
  11.  
  12. #>)["AveYo, 2024.02.20"]
  13.  
  14. #:: To force lower res than native in exclusive mode, edit the 0 below - or add launch options like: -w 1360 -h 768
  15. $force_w = 0
  16. $force_h = 0
  17.  
  18. $video = @{
  19.   "setting.defaultres" = "0"
  20.   "setting.defaultresheight" = "0"
  21.   "setting.refreshrate_numerator" = "0"
  22.   "setting.refreshrate_denominator" = "0"
  23.  
  24.   "setting.fullscreen" = "0"
  25.   "setting.coop_fullscreen" = "1"
  26.   "setting.nowindowborder" = "0"
  27.  
  28.   #"setting.mat_vsync" = "0"
  29.   "setting.fullscreen_min_on_focus_loss" = "0"
  30.   #"setting.high_dpi" = "0"
  31.   #"setting.cl_particle_fallback_base" = "2"
  32.   #"setting.cl_particle_fallback_multiplier" = "4.000000"
  33.   #"setting.dota_cheap_water" = "true"
  34.   #"setting.r_deferred_height_fog" = "false"
  35.   #"setting.r_deferred_simple_light" = "1"
  36.   #"setting.r_ssao" = "false"
  37.   #"setting.cl_globallight_shadow_mode" = "1"
  38.   #"setting.r_dota_fxaa" = "false"
  39.   #"setting.r_deferred_additive_pass" = "false"
  40.   #"setting.dota_portrait_animate" = "false"
  41.   #"setting.r_deferred_specular_bloom" = "false"
  42.   #"setting.r_deferred_specular" = "false"
  43.   #"setting.dota_ambient_creatures" = "false"
  44.   #"setting.dota_ambient_cloth" = "true"
  45.   #"setting.r_texture_stream_mip_bias" = "0"
  46.   #"setting.r_dota_normal_maps" = "true"
  47.   #"setting.r_dashboard_render_quality" = "false"
  48.   "setting.shaderquality" = "0"
  49.   #"setting.r_grass_quality" = "0"
  50.   #"setting.r_dota_allow_wind_on_trees" = "false"
  51.   #"setting.r_dota_allow_parallax_mapping" = "false"
  52.   #"setting.r_dota_bloom_compute_shader" = "1"
  53.   #"setting.r_depth_of_field" = "0"
  54.   #"setting.mat_viewportscale" = "0.810000"
  55.   #"setting.r_fullscreen_gamma" = "2.200000"
  56.   #"setting.r_dota_fsr_upsample" = "0"
  57.   "setting.r_low_latency" = "2"
  58.   "setting.useadvanced" = "1"
  59.   "setting.aspectratiomode" = "0"
  60. }
  61.  
  62. <# -------------------------------------------------------------------------------------------------------------------------------
  63. :start batch script section
  64. title %~n0 & color 0b
  65.  
  66. set "APPID=570"
  67. set "PROGR=dota 2 beta\game\bin\win64\dota2.exe"
  68. set "FILES=dota 2 beta\game\dota"
  69. set "UCVAR=user_convars_0_slot0.vcfg"
  70. set "MCVAR=machine_convars.vcfg"
  71. set "VCVAR=video.txt"
  72. :: detect STEAM path
  73. for /f "tokens=2*" %%R in ('reg query HKCU\SOFTWARE\Valve\Steam /v SteamPath 2^>nul') do set "steam_reg=%%S"
  74. for %%S in ("%steam_reg%") do set "STEAM=%%~fS"
  75. :: detect GAME path
  76. set STEAMAPPS=& for /f usebackq^ delims^=^"^ tokens^=4 %%s in (`findstr /c:":\\" "%STEAM%\steamapps\libraryfolders.vdf"`) do (
  77.   if exist "%%s\steamapps\appmanifest_%APPID%.acf" (if exist "%%s\steamapps\common\%PROGR%" set "lib=%%s"))
  78. if defined lib (set "STEAMAPPS=%lib:\\=\%\steamapps") else echo; ERROR! steamapps folder not found & pause & exit /b
  79. set "ACF=%STEAMAPPS%\appmanifest_%APPID%.acf" & set "FILES=%STEAMAPPS%\common\%FILES%" & set "PROGR=%STEAMAPPS%\common\%PROGR%"
  80. :: detect CLOUD path
  81. for /f "tokens=* delims=" %%v in ('dir /b /a:d /o:-n "%STEAM%\userdata"') do set "CLOUD=%STEAM%\userdata\%%v"
  82.  
  83. echo; steam = %STEAM%
  84. echo; progr = %PROGR%
  85. echo; files = %FILES%
  86. echo; cloud = %CLOUD%
  87. echo;
  88. echo; preparing video settings...
  89.  
  90. set 1=%*& set "0=%~f0" & powershell -nop -c iex ([io.file]::ReadAllText($env:0)) & exit /b
  91. :end batch script section
  92. ------------------------------------------------------------------------------------------------------------------------------- #>
  93.  
  94. $env:CD = split-path $env:0
  95.  
  96. #:: current res
  97. $vc = Get-WmiObject -class "Win32_VideoController"
  98. " native  = $($vc.CurrentHorizontalResolution) x $($vc.CurrentVerticalResolution) @$($vc.CurrentRefreshRate)Hz "
  99. $force = $false
  100.  
  101. #:: force res if set in the script or via game launch options
  102. if ($force_w -eq 0 -and $force_h -eq 0) {
  103.   $lo = (gc "$env:CLOUD\config\localconfig.vdf") -join "`n"
  104.   $lo = (($lo -split '\n\s{5}"' + $env:APPID + '"\n\s{5}{\n')[1] -split '\n\s{5}}\n')[0]
  105.   $lo = (($lo -split '\n\s{6}"LaunchOptions"\s+"')[1] -split '"\n')[0]
  106.   $lw = $lo -match '-w(idth)?\s+(\d+)';  if ($lw) { $_w = $matches[2] }
  107.   $lh = $lo -match '-h(eight)?\s+(\d+)'; if ($lh) { $_h = $matches[2] }
  108.   if ($_w -gt 0 -and $_h -gt 0) { $force_w = $_w; $force_h = $_h }
  109. }
  110. if ($force_w -gt 0 -and $force_h -gt 0) {
  111.   $video.defaultres = $force_w;       " width   = $($video.defaultres)"
  112.   $video.defaultresheight = $force_h; " height  = $($video.defaultresheight)"
  113.   $force = $true
  114. }
  115. " mode    = $(@('desktop-friendly','exclusive fullscreen')[$force])`n"
  116.  
  117. #:: prepare game launch options
  118. $coop = "+exec_async cloud -force_allow_coop_fullscreen -coop_fullscreen"
  119. $excl = "+exec_async cloud -force_allow_excl_fullscreen -fullscreen -width $force_w -height $force_h"
  120. $game_options = @($coop,$excl)[$force]
  121. " options = $game_options`n"
  122.  
  123. #:: prepare steam quick options
  124. $quick = '-quicklogin -skipinitialbootstrap -skipstreamingdrivers -vrdisable -nofriendsui -oldtraymenu -cef-disable-gpu -silent'
  125. $steam_options = "$QUICK -applaunch $env:APPID $game_options"
  126.  
  127. #:: update video settings txt file
  128. $VIDEO_TXT = "$env:CLOUD\$env:APPID\local\cfg\$env:VCVAR"
  129. (gc $VIDEO_TXT) |foreach { foreach ($k in $video.Keys) { if ($_ -like "*$k`"*") {
  130.   $_ = $_ -replace "(`"$k`"\s+)(`"[^`"]+`")","`$1`"$($video.$k)`"" } }; $_ } | sc $VIDEO_TXT -force -ea 0
  131.  
  132. #:: export steam cloud folder settings to cloud.cfg in game cfg folder - then can exec cloud to restore missing settings
  133. sc "$env:FILES\cfg\cloud.cfg" "// steam cloud settings from $env:MCVAR & $env:UCVAR`r`n" -force -ea 0
  134. gc "$env:CLOUD\$env:APPID\local\cfg\$env:MCVAR" |foreach { $l = $_ -split '"'; if ($l.count -eq 5) {
  135.   ac -Path "$env:FILES\cfg\cloud.cfg" -Value $($l[1].Split('$')[0] + ' "' + $l[3] + '"') } }
  136. gc "$env:CLOUD\$env:APPID\local\cfg\$env:UCVAR" |foreach { $l = $_ -split '"'; if ($l.count -eq 5) {
  137.   ac -Path "$env:FILES\cfg\cloud.cfg" -Value $($l[1] + ' "' + $l[3] + '"') } }
  138.  
  139. #:: clear verify integrity flags after crash for quicker relaunch
  140. $s = [io.file]::ReadAllText($env:ACF)
  141. if ($s -match '"FullValidateAfterNextUpdate"\s+"1"' -or $s -notmatch '"StateFlags"\s+"4"') {
  142.   " update or verify integrity flags detected, will clear them and restart Steam...`n"
  143.   'dota2','cs2','steamwebhelper','steam' |% {kill -name $_ -force -ea 0} ; sleep 3; del "$env:STEAM\.crash" -force -ea 0
  144.   $s = $s -replace '("FullValidateAfterNextUpdate"\s+)("\d+")',"`$1`"0`"" -replace '("StateFlags"\s+)("\d+")',"`$1`"4`""
  145.   [io.file]::WriteAllText($env:ACF, $s)
  146. }
  147.  
  148. #:: disable fullscreen optimizations for exe
  149. $AppCompatFlags = 'HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers'
  150. sp $AppCompatFlags $env:PROGR '~ DISABLEDXMAXIMIZEDWINDOWEDMODE HIGHDPIAWARE' -force -ea 0
  151.  
  152. #:: warn if steam not already running
  153. if ($null -eq (get-process 'steam' -ea 0)) { " Steam not already running, this will take a bit longer...`n" }
  154.  
  155. #:: VirtualDesktop.ps1 bundle start <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  156. # Author: Markus Scholtes, 2017/05/08
  157. # Version 2.18 - changes for Win 11 3085 and up, 2024/02/15
  158.  
  159. # prefer $PSVersionTable.BuildVersion to [Environment]::OSVersion.Version
  160. # since a wrong Windows version might be returned in RunSpaces
  161. if ($PSEdition -eq "Desktop")
  162. { # Windows Powershell
  163.     $OSVer = $PSVersionTable.BuildVersion.Major
  164.     $OSBuild = $PSVersionTable.BuildVersion.Build
  165. }
  166. else
  167. { # Powershell Core
  168.     $OSVer = [Environment]::OSVersion.Version.Major
  169.     $OSBuild = [Environment]::OSVersion.Version.Build
  170. }
  171.  
  172. if ($OSVer -lt 10)
  173. {
  174.     Write-Error "Windows 10 or above is required to run this script"
  175.     exit -1
  176. }
  177.  
  178. if ($OSBuild -lt 14393)
  179. {
  180.     Write-Error "Windows 10 1607 or above is required to run this script"
  181.     exit -1
  182. }
  183.  
  184. #:: AveYo: use csc to pre-compile the c# typefinition rather than powershell do it slowly every launch
  185. if (-not (test-path "$env:CD\VirtualDesktop.dll")) { $dll = @"
  186. using System;
  187. using System.Runtime.InteropServices;
  188. using System.Collections.Generic;
  189. using System.ComponentModel;
  190. using System.Text;
  191.  
  192. namespace VirtualDesktop
  193. {
  194.     #region Type definitions
  195.     // define HString on .Net 5 / overwrite HString on .Net 4 - https://github.com/dotnet/runtime/issues/39827
  196.     [StructLayout(LayoutKind.Sequential)]
  197.     public struct HString : IDisposable
  198.     {
  199.         private readonly IntPtr handle;
  200.         public static HString FromString(string s)
  201.         {
  202.             var h = Marshal.AllocHGlobal(IntPtr.Size);
  203.             Marshal.ThrowExceptionForHR(WindowsCreateString(s, s.Length, h));
  204.             return Marshal.PtrToStructure<HString>(h);
  205.         }
  206.  
  207.         public void Delete()
  208.         {
  209.             WindowsDeleteString(handle);
  210.         }
  211.  
  212.         [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)]
  213.         private static extern int WindowsCreateString([MarshalAs(UnmanagedType.LPWStr)] string sourceString, int length, [Out] IntPtr hstring);
  214.  
  215.         [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)]
  216.         private static extern int WindowsDeleteString(IntPtr hstring);
  217.  
  218.         [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall, ExactSpelling = true, CharSet = CharSet.Unicode)]
  219.         private static extern IntPtr WindowsGetStringRawBuffer(HString hString, IntPtr length);
  220.  
  221.         public void Dispose()
  222.         {
  223.             Delete();
  224.         }
  225.  
  226.         public static implicit operator string(HString hString)
  227.         {
  228.             var str = Marshal.PtrToStringUni(WindowsGetStringRawBuffer(hString, IntPtr.Zero));
  229.             hString.Delete();
  230.             if (null != str)
  231.                 return str;
  232.             else
  233.                 return string.Empty;
  234.         }
  235.     }
  236.     #endregion
  237.  
  238.     #region COM API
  239.     internal static class Guids
  240.     {
  241.         public static readonly Guid CLSID_ImmersiveShell = new Guid("C2F03A33-21F5-47FA-B4BB-156362A2F239");
  242.         public static readonly Guid CLSID_VirtualDesktopManagerInternal = new Guid("C5E0CDCA-7B6E-41B2-9FC4-D93975CC467B");
  243.         public static readonly Guid CLSID_VirtualDesktopManager = new Guid("AA509086-5CA9-4C25-8F95-589D3C07B48A");
  244.         public static readonly Guid CLSID_VirtualDesktopPinnedApps = new Guid("B5A399E7-1C87-46B8-88E9-FC5747B171BD");
  245.     }
  246.  
  247.     [StructLayout(LayoutKind.Sequential)]
  248.     internal struct Size
  249.     {
  250.         public int X;
  251.         public int Y;
  252.     }
  253.  
  254.     [StructLayout(LayoutKind.Sequential)]
  255.     internal struct Rect
  256.     {
  257.         public int Left;
  258.         public int Top;
  259.         public int Right;
  260.         public int Bottom;
  261.     }
  262.  
  263.     internal enum APPLICATION_VIEW_CLOAK_TYPE : int
  264.     {
  265.         AVCT_NONE = 0,
  266.         AVCT_DEFAULT = 1,
  267.         AVCT_VIRTUAL_DESKTOP = 2
  268.     }
  269.  
  270.     internal enum APPLICATION_VIEW_COMPATIBILITY_POLICY : int
  271.     {
  272.         AVCP_NONE = 0,
  273.         AVCP_SMALL_SCREEN = 1,
  274.         AVCP_TABLET_SMALL_SCREEN = 2,
  275.         AVCP_VERY_SMALL_SCREEN = 3,
  276.         AVCP_HIGH_SCALE_FACTOR = 4
  277.     }
  278.  
  279.     [ComImport]
  280. $(if (($PSEdition -eq "Core") -Or ($OSBuild -lt 17134)) {@"
  281. // Windows 10 1607 and Server 2016 or Powershell Core:
  282.     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  283. "@ } else {@"
  284. // Windows 10 1803, up and Windows Powershell:
  285.     [InterfaceType(ComInterfaceType.InterfaceIsIInspectable)]
  286. "@ })
  287. $(if ($OSBuild -lt 17134) {@"
  288. // Windows 10 1607 and Server 2016:
  289.     [Guid("9AC0B5C8-1484-4C5B-9533-4134A0F97CEA")]
  290. "@ })
  291. $(if (($OSBuild -ge 17134) -And ($OSBuild -lt 17661)) {@"
  292. // Windows 10 1803:
  293.     [Guid("871F602A-2B58-42B4-8C4B-6C43D642C06F")]
  294. "@ })
  295. $(if ($OSBuild -ge 17661) {@"
  296. // Windows 10 1809 or up and Windows 11:
  297.     [Guid("372E1D3B-38D3-42E4-A15B-8AB2B178F513")]
  298. "@ })
  299.     internal interface IApplicationView
  300.     {
  301.         int SetFocus();
  302.         int SwitchTo();
  303.         int TryInvokeBack(IntPtr /* IAsyncCallback* */ callback);
  304.         int GetThumbnailWindow(out IntPtr hwnd);
  305.         int GetMonitor(out IntPtr /* IImmersiveMonitor */ immersiveMonitor);
  306.         int GetVisibility(out int visibility);
  307.         int SetCloak(APPLICATION_VIEW_CLOAK_TYPE cloakType, int unknown);
  308.         int GetPosition(ref Guid guid /* GUID for IApplicationViewPosition */, out IntPtr /* IApplicationViewPosition** */ position);
  309.         int SetPosition(ref IntPtr /* IApplicationViewPosition* */ position);
  310.         int InsertAfterWindow(IntPtr hwnd);
  311.         int GetExtendedFramePosition(out Rect rect);
  312.         int GetAppUserModelId([MarshalAs(UnmanagedType.LPWStr)] out string id);
  313.         int SetAppUserModelId(string id);
  314.         int IsEqualByAppUserModelId(string id, out int result);
  315.         int GetViewState(out uint state);
  316.         int SetViewState(uint state);
  317.         int GetNeediness(out int neediness);
  318.         int GetLastActivationTimestamp(out ulong timestamp);
  319.         int SetLastActivationTimestamp(ulong timestamp);
  320.         int GetVirtualDesktopId(out Guid guid);
  321.         int SetVirtualDesktopId(ref Guid guid);
  322.         int GetShowInSwitchers(out int flag);
  323.         int SetShowInSwitchers(int flag);
  324.         int GetScaleFactor(out int factor);
  325.         int CanReceiveInput(out bool canReceiveInput);
  326.         int GetCompatibilityPolicyType(out APPLICATION_VIEW_COMPATIBILITY_POLICY flags);
  327.         int SetCompatibilityPolicyType(APPLICATION_VIEW_COMPATIBILITY_POLICY flags);
  328. $(if ($OSBuild -lt 17134) {@"
  329.         int GetPositionPriority(out IntPtr /* IShellPositionerPriority** */ priority);
  330.         int SetPositionPriority(IntPtr /* IShellPositionerPriority* */ priority);
  331. "@ })
  332.         int GetSizeConstraints(IntPtr /* IImmersiveMonitor* */ monitor, out Size size1, out Size size2);
  333.         int GetSizeConstraintsForDpi(uint uint1, out Size size1, out Size size2);
  334.         int SetSizeConstraintsForDpi(ref uint uint1, ref Size size1, ref Size size2);
  335. $(if ($OSBuild -lt 17134) {@"
  336.         int QuerySizeConstraintsFromApp();
  337. "@ })
  338.         int OnMinSizePreferencesUpdated(IntPtr hwnd);
  339.         int ApplyOperation(IntPtr /* IApplicationViewOperation* */ operation);
  340.         int IsTray(out bool isTray);
  341.         int IsInHighZOrderBand(out bool isInHighZOrderBand);
  342.         int IsSplashScreenPresented(out bool isSplashScreenPresented);
  343.         int Flash();
  344.         int GetRootSwitchableOwner(out IApplicationView rootSwitchableOwner);
  345.         int EnumerateOwnershipTree(out IObjectArray ownershipTree);
  346.         int GetEnterpriseId([MarshalAs(UnmanagedType.LPWStr)] out string enterpriseId);
  347.         int IsMirrored(out bool isMirrored);
  348. $(if (($OSBuild -ge 17134) -And ($OSBuild -lt 17661)) {@"
  349.         int Unknown1(out int unknown);
  350.         int Unknown2(out int unknown);
  351.         int Unknown3(out int unknown);
  352.         int Unknown4(out int unknown);
  353. "@ })
  354. $(if ($OSBuild -ge 17661) {@"
  355.         int Unknown1(out int unknown);
  356.         int Unknown2(out int unknown);
  357.         int Unknown3(out int unknown);
  358.         int Unknown4(out int unknown);
  359.         int Unknown5(out int unknown);
  360.         int Unknown6(int unknown);
  361.         int Unknown7();
  362.         int Unknown8(out int unknown);
  363.         int Unknown9(int unknown);
  364.         int Unknown10(int unknownX, int unknownY);
  365.         int Unknown11(int unknown);
  366.         int Unknown12(out Size size1);
  367. "@ })
  368.     }
  369.  
  370.     [ComImport]
  371.     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  372. $(if ($OSBuild -lt 17134) {@"
  373. // Windows 10 1607 and Server 2016:
  374.     [Guid("2C08ADF0-A386-4B35-9250-0FE183476FCC")]
  375. "@ })
  376. $(if (($OSBuild -ge 17134) -And ($OSBuild -lt 17661)) {@"
  377. // Windows 10 1803:
  378.     [Guid("2C08ADF0-A386-4B35-9250-0FE183476FCC")]
  379. "@ })
  380. $(if ($OSBuild -ge 17661) {@"
  381. // Windows 10 1809 or up and Windows 11:
  382.     [Guid("1841C6D7-4F9D-42C0-AF41-8747538F10E5")]
  383. "@ })
  384.     internal interface IApplicationViewCollection
  385.     {
  386.         int GetViews(out IObjectArray array);
  387.         int GetViewsByZOrder(out IObjectArray array);
  388.         int GetViewsByAppUserModelId(string id, out IObjectArray array);
  389.         int GetViewForHwnd(IntPtr hwnd, out IApplicationView view);
  390.         int GetViewForApplication(object application, out IApplicationView view);
  391.         int GetViewForAppUserModelId(string id, out IApplicationView view);
  392.         int GetViewInFocus(out IntPtr view);
  393. $(if ($OSBuild -ge 17134) {@"
  394. // Windows 10 1803 or up and Windows 11:
  395.         int Unknown1(out IntPtr view);
  396. "@ })
  397.         void RefreshCollection();
  398.         int RegisterForApplicationViewChanges(object listener, out int cookie);
  399. $(if ($OSBuild -lt 17661) {@"
  400. // Windows 10 1607 and Server 2016 and Windows 10 1803:
  401.         int RegisterForApplicationViewPositionChanges(object listener, out int cookie);
  402. "@ })
  403.         int UnregisterForApplicationViewChanges(int cookie);
  404.     }
  405.  
  406.     [ComImport]
  407.     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  408. $(if ($OSBuild -ge 22621) {@"
  409. // Windows 11 22H2 and up:
  410.     [Guid("3F07F4BE-B107-441A-AF0F-39D82529072C")]
  411. "@ })
  412. $(if (($OSBuild -ge 22000) -And ($OSBuild -lt 22621)) {@"
  413. // Windows 11 up to 21H2:
  414.     [Guid("536D3495-B208-4CC9-AE26-DE8111275BF8")]
  415. "@ })
  416. $(if ($OSBuild -eq 20348) {@"
  417. // Windows Server 2022:
  418.     [Guid("62fdf88b-11ca-4afb-8bd8-2296dfae49e2")]
  419. "@ })
  420. $(if ($OSBuild -lt 20348) {@"
  421. // Windows 10:
  422.     [Guid("FF72FFDD-BE7E-43FC-9C03-AD81681E88E4")]
  423. "@ })
  424.     internal interface IVirtualDesktop
  425.     {
  426.         bool IsViewVisible(IApplicationView view);
  427.         Guid GetId();
  428. $(if (($OSBuild -ge 20348) -And ($OSBuild -lt 22621)) {@"
  429. // Windows Server 2022 and Windows 11 up to 21H2
  430.         IntPtr Unknown1();
  431. "@ })
  432. $(if ($OSBuild -ge 20348) {@"
  433. // Windows Server 2022 and Windows 11
  434.         HString GetName();
  435. "@ })
  436. $(if ($OSBuild -ge 22000) {@"
  437. // Windows 11:
  438.         HString GetWallpaperPath();
  439. "@ })
  440. $(if ($OSBuild -ge 22621) {@"
  441. // Windows 11 22H2 and up:
  442.         bool IsRemote();
  443. "@ })
  444.     }
  445.  
  446.     [ComImport]
  447.     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  448. $(if ($OSBuild -ge 22621) {@"
  449. // Windows 11 22H2 and up:
  450.     [Guid("53F5CA0B-158F-4124-900C-057158060B27")]
  451.     internal interface IVirtualDesktopManagerInternal
  452.     {
  453.         int GetCount();
  454.         void MoveViewToDesktop(IApplicationView view, IVirtualDesktop desktop);
  455.         bool CanViewMoveDesktops(IApplicationView view);
  456.         IVirtualDesktop GetCurrentDesktop();
  457.         void GetDesktops(out IObjectArray desktops);
  458.         [PreserveSig]
  459.         int GetAdjacentDesktop(IVirtualDesktop from, int direction, out IVirtualDesktop desktop);
  460.         void SwitchDesktop(IVirtualDesktop desktop);
  461.         IVirtualDesktop CreateDesktop();
  462.         void MoveDesktop(IVirtualDesktop desktop, int nIndex);
  463.         void RemoveDesktop(IVirtualDesktop desktop, IVirtualDesktop fallback);
  464.         IVirtualDesktop FindDesktop(ref Guid desktopid);
  465.         void GetDesktopSwitchIncludeExcludeViews(IVirtualDesktop desktop, out IObjectArray unknown1, out IObjectArray unknown2);
  466.         void SetDesktopName(IVirtualDesktop desktop, HString name);
  467.         void SetDesktopWallpaper(IVirtualDesktop desktop, HString path);
  468.         void UpdateWallpaperPathForAllDesktops(HString path);
  469.         void CopyDesktopState(IApplicationView pView0, IApplicationView pView1);
  470.         void CreateRemoteDesktop(HString path, out IVirtualDesktop desktop);
  471.         void SwitchRemoteDesktop(IVirtualDesktop desktop, IntPtr switchtype);
  472.         void SwitchDesktopWithAnimation(IVirtualDesktop desktop);
  473.         void GetLastActiveDesktop(out IVirtualDesktop desktop);
  474.         void WaitForAnimationToComplete();
  475.     }
  476. "@ })
  477. $(if (($OSBuild -ge 22000) -And ($OSBuild -lt 22621)) {@"
  478. // Windows 11 up to 21H2:
  479.     [Guid("B2F925B9-5A0F-4D2E-9F4D-2B1507593C10")]
  480.     internal interface IVirtualDesktopManagerInternal
  481.     {
  482.         int GetCount(IntPtr hWndOrMon);
  483.         void MoveViewToDesktop(IApplicationView view, IVirtualDesktop desktop);
  484.         bool CanViewMoveDesktops(IApplicationView view);
  485.         IVirtualDesktop GetCurrentDesktop(IntPtr hWndOrMon);
  486. "@ })
  487. $(if (($OSBuild -ge 22449) -And ($OSBuild -lt 22621)) {@"
  488. // Windows 11 up to 21H2:
  489.         IObjectArray GetAllCurrentDesktops();
  490.         void GetDesktops(IntPtr hWndOrMon, out IObjectArray desktops);
  491.         [PreserveSig]
  492.         int GetAdjacentDesktop(IVirtualDesktop from, int direction, out IVirtualDesktop desktop);
  493.         void SwitchDesktop(IntPtr hWndOrMon, IVirtualDesktop desktop);
  494.         IVirtualDesktop CreateDesktop(IntPtr hWndOrMon);
  495.         void MoveDesktop(IVirtualDesktop desktop, IntPtr hWndOrMon, int nIndex);
  496.         void RemoveDesktop(IVirtualDesktop desktop, IVirtualDesktop fallback);
  497.         IVirtualDesktop FindDesktop(ref Guid desktopid);
  498.         void GetDesktopSwitchIncludeExcludeViews(IVirtualDesktop desktop, out IObjectArray unknown1, out IObjectArray unknown2);
  499.         void SetDesktopName(IVirtualDesktop desktop, HString name);
  500.         void SetDesktopWallpaper(IVirtualDesktop desktop, HString path);
  501.         void UpdateWallpaperPathForAllDesktops(HString path);
  502.         void CopyDesktopState(IApplicationView pView0, IApplicationView pView1);
  503.         int GetDesktopIsPerMonitor();
  504.         void SetDesktopIsPerMonitor(bool state);
  505.     }
  506. "@ })
  507. $(if ($OSBuild -eq 20348) {@"
  508. // Windows Server 2022:
  509.     [Guid("094afe11-44f2-4ba0-976f-29a97e263ee0")]
  510.     internal interface IVirtualDesktopManagerInternal
  511.     {
  512.         int GetCount(IntPtr hWndOrMon);
  513.         void MoveViewToDesktop(IApplicationView view, IVirtualDesktop desktop);
  514.         bool CanViewMoveDesktops(IApplicationView view);
  515.         IVirtualDesktop GetCurrentDesktop(IntPtr hWndOrMon);
  516.         void GetDesktops(IntPtr hWndOrMon, out IObjectArray desktops);
  517.         [PreserveSig]
  518.         int GetAdjacentDesktop(IVirtualDesktop from, int direction, out IVirtualDesktop desktop);
  519.         void SwitchDesktop(IntPtr hWndOrMon, IVirtualDesktop desktop);
  520.         IVirtualDesktop CreateDesktop(IntPtr hWndOrMon);
  521.         void RemoveDesktop(IVirtualDesktop desktop, IVirtualDesktop fallback);
  522.         IVirtualDesktop FindDesktop(ref Guid desktopid);
  523.         void GetDesktopSwitchIncludeExcludeViews(IVirtualDesktop desktop, out IObjectArray unknown1, out IObjectArray unknown2);
  524.         void SetDesktopName(IVirtualDesktop desktop, HString name);
  525.         void CopyDesktopState(IApplicationView pView0, IApplicationView pView1);
  526.         int GetDesktopIsPerMonitor();
  527.     }
  528. "@ })
  529. $(if ($OSBuild -lt 20348) {@"
  530. // Windows 10:
  531.     [Guid("F31574D6-B682-4CDC-BD56-1827860ABEC6")]
  532.     internal interface IVirtualDesktopManagerInternal
  533.     {
  534.         int GetCount();
  535.         void MoveViewToDesktop(IApplicationView view, IVirtualDesktop desktop);
  536.         bool CanViewMoveDesktops(IApplicationView view);
  537.         IVirtualDesktop GetCurrentDesktop();
  538.         void GetDesktops(out IObjectArray desktops);
  539.         [PreserveSig]
  540.         int GetAdjacentDesktop(IVirtualDesktop from, int direction, out IVirtualDesktop desktop);
  541.         void SwitchDesktop(IVirtualDesktop desktop);
  542.         IVirtualDesktop CreateDesktop();
  543.         void RemoveDesktop(IVirtualDesktop desktop, IVirtualDesktop fallback);
  544.         IVirtualDesktop FindDesktop(ref Guid desktopid);
  545.     }
  546.  
  547.     [ComImport]
  548.     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  549.     [Guid("0F3A72B0-4566-487E-9A33-4ED302F6D6CE")]
  550.     internal interface IVirtualDesktopManagerInternal2
  551.     {
  552.         int GetCount();
  553.         void MoveViewToDesktop(IApplicationView view, IVirtualDesktop desktop);
  554.         bool CanViewMoveDesktops(IApplicationView view);
  555.         IVirtualDesktop GetCurrentDesktop();
  556.         void GetDesktops(out IObjectArray desktops);
  557.         [PreserveSig]
  558.         int GetAdjacentDesktop(IVirtualDesktop from, int direction, out IVirtualDesktop desktop);
  559.         void SwitchDesktop(IVirtualDesktop desktop);
  560.         IVirtualDesktop CreateDesktop();
  561.         void RemoveDesktop(IVirtualDesktop desktop, IVirtualDesktop fallback);
  562.         IVirtualDesktop FindDesktop(ref Guid desktopid);
  563.         void Unknown1(IVirtualDesktop desktop, out IntPtr unknown1, out IntPtr unknown2);
  564.         void SetName(IVirtualDesktop desktop, HString name);
  565.     }
  566. "@ })
  567.  
  568.     [ComImport]
  569.     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  570.     [Guid("A5CD92FF-29BE-454C-8D04-D82879FB3F1B")]
  571.     internal interface IVirtualDesktopManager
  572.     {
  573.         bool IsWindowOnCurrentVirtualDesktop(IntPtr topLevelWindow);
  574.         Guid GetWindowDesktopId(IntPtr topLevelWindow);
  575.         void MoveWindowToDesktop(IntPtr topLevelWindow, ref Guid desktopId);
  576.     }
  577.  
  578.     [ComImport]
  579.     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  580.     [Guid("4CE81583-1E4C-4632-A621-07A53543148F")]
  581.     internal interface IVirtualDesktopPinnedApps
  582.     {
  583.         bool IsAppIdPinned(string appId);
  584.         void PinAppID(string appId);
  585.         void UnpinAppID(string appId);
  586.         bool IsViewPinned(IApplicationView applicationView);
  587.         void PinView(IApplicationView applicationView);
  588.         void UnpinView(IApplicationView applicationView);
  589.     }
  590.  
  591.     [ComImport]
  592.     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  593.     [Guid("92CA9DCD-5622-4BBA-A805-5E9F541BD8C9")]
  594.     internal interface IObjectArray
  595.     {
  596.         void GetCount(out int count);
  597.         void GetAt(int index, ref Guid iid, [MarshalAs(UnmanagedType.Interface)]out object obj);
  598.     }
  599.  
  600.     [ComImport]
  601.     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  602.     [Guid("6D5140C1-7436-11CE-8034-00AA006009FA")]
  603.     internal interface IServiceProvider10
  604.     {
  605.         [return: MarshalAs(UnmanagedType.IUnknown)]
  606.         object QueryService(ref Guid service, ref Guid riid);
  607.     }
  608.     #endregion
  609.  
  610.     #region COM wrapper
  611.     internal static class DesktopManager
  612.     {
  613.         static DesktopManager()
  614.         {
  615.             var shell = (IServiceProvider10)Activator.CreateInstance(Type.GetTypeFromCLSID(Guids.CLSID_ImmersiveShell));
  616.             VirtualDesktopManagerInternal = (IVirtualDesktopManagerInternal)shell.QueryService(Guids.CLSID_VirtualDesktopManagerInternal, typeof(IVirtualDesktopManagerInternal).GUID);
  617. $(if ($OSBuild -lt 20348) {@"
  618. // Windows 10:
  619.             try {
  620.                 VirtualDesktopManagerInternal2 = (IVirtualDesktopManagerInternal2)shell.QueryService(Guids.CLSID_VirtualDesktopManagerInternal, typeof(IVirtualDesktopManagerInternal2).GUID);
  621.             }
  622.             catch {
  623.                 VirtualDesktopManagerInternal2 = null;
  624.             }
  625. "@ })
  626.             VirtualDesktopManager = (IVirtualDesktopManager)Activator.CreateInstance(Type.GetTypeFromCLSID(Guids.CLSID_VirtualDesktopManager));
  627.             ApplicationViewCollection = (IApplicationViewCollection)shell.QueryService(typeof(IApplicationViewCollection).GUID, typeof(IApplicationViewCollection).GUID);
  628.             VirtualDesktopPinnedApps = (IVirtualDesktopPinnedApps)shell.QueryService(Guids.CLSID_VirtualDesktopPinnedApps, typeof(IVirtualDesktopPinnedApps).GUID);
  629.         }
  630.  
  631.         internal static IVirtualDesktopManagerInternal VirtualDesktopManagerInternal;
  632. $(if ($OSBuild -lt 20348) {@"
  633. // Windows 10:
  634.         internal static IVirtualDesktopManagerInternal2 VirtualDesktopManagerInternal2;
  635. "@ })
  636.         internal static IVirtualDesktopManager VirtualDesktopManager;
  637.         internal static IApplicationViewCollection ApplicationViewCollection;
  638.         internal static IVirtualDesktopPinnedApps VirtualDesktopPinnedApps;
  639.  
  640.         internal static IVirtualDesktop GetDesktop(int index)
  641.         {   // get desktop with index
  642. $(if (($OSBuild -lt 20348) -Or ($OSBuild -ge 22621)) {@"
  643.             int count = VirtualDesktopManagerInternal.GetCount();
  644. "@ } else {@"
  645.             int count = VirtualDesktopManagerInternal.GetCount(IntPtr.Zero);
  646. "@ })
  647.             if (index < 0 || index >= count) throw new ArgumentOutOfRangeException("index");
  648.             IObjectArray desktops;
  649. $(if (($OSBuild -lt 20348) -Or ($OSBuild -ge 22621)) {@"
  650.             VirtualDesktopManagerInternal.GetDesktops(out desktops);
  651. "@ } else {@"
  652.             VirtualDesktopManagerInternal.GetDesktops(IntPtr.Zero, out desktops);
  653. "@ })
  654.             object objdesktop;
  655.             desktops.GetAt(index, typeof(IVirtualDesktop).GUID, out objdesktop);
  656.             Marshal.ReleaseComObject(desktops);
  657.             return (IVirtualDesktop)objdesktop;
  658.         }
  659.  
  660.         internal static int GetDesktopIndex(IVirtualDesktop desktop)
  661.         { // get index of desktop
  662.             int index = -1;
  663.             Guid IdSearch = desktop.GetId();
  664.             IObjectArray desktops;
  665. $(if (($OSBuild -lt 20348) -Or ($OSBuild -ge 22621)) {@"
  666.             VirtualDesktopManagerInternal.GetDesktops(out desktops);
  667. "@ } else {@"
  668.             VirtualDesktopManagerInternal.GetDesktops(IntPtr.Zero, out desktops);
  669. "@ })
  670.             object objdesktop;
  671. $(if (($OSBuild -lt 20348) -Or ($OSBuild -ge 22621)) {@"
  672.             for (int i = 0; i < VirtualDesktopManagerInternal.GetCount(); i++)
  673. "@ } else {@"
  674.             for (int i = 0; i < VirtualDesktopManagerInternal.GetCount(IntPtr.Zero); i++)
  675. "@ })
  676.             {
  677.                 desktops.GetAt(i, typeof(IVirtualDesktop).GUID, out objdesktop);
  678.                 if (IdSearch.CompareTo(((IVirtualDesktop)objdesktop).GetId()) == 0)
  679.                 { index = i;
  680.                     break;
  681.                 }
  682.             }
  683.             Marshal.ReleaseComObject(desktops);
  684.             return index;
  685.         }
  686.  
  687.         internal static IApplicationView GetApplicationView(this IntPtr hWnd)
  688.         { // get application view to window handle
  689.             IApplicationView view;
  690.             ApplicationViewCollection.GetViewForHwnd(hWnd, out view);
  691.             return view;
  692.         }
  693.  
  694.         internal static string GetAppId(IntPtr hWnd)
  695.         { // get Application ID to window handle
  696.             string appId;
  697.             hWnd.GetApplicationView().GetAppUserModelId(out appId);
  698.             return appId;
  699.         }
  700.     }
  701.     #endregion
  702.  
  703.     #region public interface
  704.     public class WindowInformation
  705.     { // stores window informations
  706.         public string Title { get; set; }
  707.         public int Handle { get; set; }
  708.     }
  709.  
  710.     public class Desktop
  711.     {
  712.         // open registry key
  713.         [DllImport("advapi32.dll", CharSet=CharSet.Auto)]
  714.         private static extern int RegOpenKeyEx(UIntPtr hKey, string subKey, int ulOptions, int samDesired, out UIntPtr hkResult);
  715.  
  716.         // read registry value
  717.         [DllImport("advapi32.dll", SetLastError=true)]
  718.         private static extern uint RegQueryValueEx(UIntPtr hKey, string lpValueName, int lpReserved, ref int lpType, IntPtr lpData, ref int lpcbData);
  719.  
  720.         // close registry key
  721.         [DllImport("advapi32.dll", SetLastError=true)]
  722.         private static extern int RegCloseKey(UIntPtr hKey);
  723.  
  724.         // get window handle of current console window (even if powershell started in cmd)
  725.         [DllImport("Kernel32.dll")]
  726.         public static extern IntPtr GetConsoleWindow();
  727.  
  728.         // get process id of window handle
  729.         [DllImport("user32.dll")]
  730.         private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
  731.  
  732.         // get thread id of current process
  733.         [DllImport("kernel32.dll")]
  734.         static extern uint GetCurrentThreadId();
  735.  
  736.         // attach input to thread
  737.         [DllImport("user32.dll")]
  738.         static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
  739.  
  740.         // get handle of active window
  741.         [DllImport("user32.dll")]
  742.         public static extern IntPtr GetForegroundWindow();
  743.  
  744.         // try to set foreground window
  745.         [DllImport("user32.dll")]
  746.         [return: MarshalAs(UnmanagedType.Bool)]static extern bool SetForegroundWindow(IntPtr hWnd);
  747.  
  748.         // send message to window
  749.         [DllImport("user32.dll")]
  750.         static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
  751.         private const int SW_MINIMIZE = 6;
  752.  
  753.         private static UIntPtr HKEY_CURRENT_USER = new UIntPtr(0x80000001u);
  754.         private const int KEY_READ = 0x20019;
  755.  
  756.         private static string GetRegistryString(string registryPath, string valName)
  757.         { // reads string value out of user registry
  758.             UIntPtr hKey = UIntPtr.Zero;
  759.             IntPtr pResult = IntPtr.Zero;
  760.             string Result = null;
  761.  
  762.             try
  763.             {
  764.                 if (RegOpenKeyEx(HKEY_CURRENT_USER, registryPath, 0, KEY_READ, out hKey) == 0)
  765.                 {
  766.                     int size = 0;
  767.                     int type = 1; // REG_SZ
  768.  
  769.                     uint retVal = RegQueryValueEx(hKey, valName, 0, ref type, IntPtr.Zero, ref size);
  770.                     if (size != 0)
  771.                     {
  772.                         pResult = Marshal.AllocHGlobal(size);
  773.  
  774.                         retVal = RegQueryValueEx(hKey, valName, 0, ref type, pResult, ref size);
  775.                         if (retVal == 0) { Result = Marshal.PtrToStringAnsi(pResult); }
  776.                     }
  777.                 }
  778.             }
  779.             catch { }
  780.             finally
  781.             {
  782.                 if (hKey != UIntPtr.Zero) { RegCloseKey(hKey); }
  783.                 if (pResult != IntPtr.Zero) { Marshal.FreeHGlobal(pResult); }
  784.             }
  785.  
  786.             return Result;
  787.         }
  788.  
  789.         private static readonly Guid AppOnAllDesktops = new Guid("BB64D5B7-4DE3-4AB2-A87C-DB7601AEA7DC");
  790.         private static readonly Guid WindowOnAllDesktops = new Guid("C2DDEA68-66F2-4CF9-8264-1BFD00FBBBAC");
  791.  
  792.         private IVirtualDesktop ivd;
  793.         private Desktop(IVirtualDesktop desktop) { this.ivd = desktop; }
  794.  
  795.         public override int GetHashCode()
  796.         { // get hash
  797.             return ivd.GetHashCode();
  798.         }
  799.  
  800.         public override bool Equals(object obj)
  801.         { // compare with object
  802.             var desk = obj as Desktop;
  803.             return desk != null && object.ReferenceEquals(this.ivd, desk.ivd);
  804.         }
  805.  
  806.         public static int Count
  807.         { // return the number of desktops
  808. $(if (($OSBuild -lt 20348) -Or ($OSBuild -ge 22621)) {@"
  809.             get { return DesktopManager.VirtualDesktopManagerInternal.GetCount(); }
  810. "@ } else {@"
  811.             get { return DesktopManager.VirtualDesktopManagerInternal.GetCount(IntPtr.Zero); }
  812. "@ })
  813.         }
  814.  
  815.         public static Desktop Current
  816.         { // returns current desktop
  817. $(if (($OSBuild -lt 20348) -Or ($OSBuild -ge 22621)) {@"
  818.             get { return new Desktop(DesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop()); }
  819. "@ } else {@"
  820.             get { return new Desktop(DesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop(IntPtr.Zero)); }
  821. "@ })
  822.         }
  823.  
  824.         public static Desktop FromIndex(int index)
  825.         { // return desktop object from index (-> index = 0..Count-1)
  826.             return new Desktop(DesktopManager.GetDesktop(index));
  827.         }
  828.  
  829.         public static Desktop FromWindow(IntPtr hWnd)
  830.         { // return desktop object to desktop on which window <hWnd> is displayed
  831.             if (hWnd == IntPtr.Zero) throw new ArgumentNullException();
  832.             Guid id = DesktopManager.VirtualDesktopManager.GetWindowDesktopId(hWnd);
  833.             if ((id.CompareTo(AppOnAllDesktops) == 0) || (id.CompareTo(WindowOnAllDesktops) == 0))
  834. $(if (($OSBuild -lt 20348) -Or ($OSBuild -ge 22621)) {@"
  835.                 return new Desktop(DesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop());
  836. "@ } else {@"
  837.                 return new Desktop(DesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop(IntPtr.Zero));
  838. "@ })
  839.             else
  840.                 return new Desktop(DesktopManager.VirtualDesktopManagerInternal.FindDesktop(ref id));
  841.         }
  842.  
  843.         public static int FromDesktop(Desktop desktop)
  844.         { // return index of desktop object or -1 if not found
  845.             return DesktopManager.GetDesktopIndex(desktop.ivd);
  846.         }
  847.  
  848.         public static string DesktopNameFromDesktop(Desktop desktop)
  849.         { // return name of desktop or "Desktop n" if it has no name
  850.             Guid guid = desktop.ivd.GetId();
  851.  
  852.             // read desktop name in registry
  853.             string desktopName = null;
  854.             try {
  855.                 desktopName = GetRegistryString("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VirtualDesktops\\Desktops\\{" + guid.ToString() + "}", "Name");
  856.             }
  857.             catch { }
  858.  
  859.             // no name found, generate generic name
  860.             if (string.IsNullOrEmpty(desktopName))
  861.             { // create name "Desktop n" (n = number starting with 1)
  862.                 desktopName = "Desktop " + (DesktopManager.GetDesktopIndex(desktop.ivd) + 1).ToString();
  863.             }
  864.             return desktopName;
  865.         }
  866.  
  867.         public static string DesktopNameFromIndex(int index)
  868.         { // return name of desktop from index (-> index = 0..Count-1) or "Desktop n" if it has no name
  869.             Guid guid = DesktopManager.GetDesktop(index).GetId();
  870.  
  871.             // read desktop name in registry
  872.             string desktopName = null;
  873.             try {
  874.                 desktopName = GetRegistryString("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VirtualDesktops\\Desktops\\{" + guid.ToString() + "}", "Name");
  875.             }
  876.             catch { }
  877.  
  878.             // no name found, generate generic name
  879.             if (string.IsNullOrEmpty(desktopName))
  880.             { // create name "Desktop n" (n = number starting with 1)
  881.                 desktopName = "Desktop " + (index + 1).ToString();
  882.             }
  883.             return desktopName;
  884.         }
  885.  
  886.         public static bool HasDesktopNameFromIndex(int index)
  887.         { // return true is desktop is named or false if it has no name
  888.             Guid guid = DesktopManager.GetDesktop(index).GetId();
  889.  
  890.             // read desktop name in registry
  891.             string desktopName = null;
  892.             try {
  893.                 desktopName = GetRegistryString("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VirtualDesktops\\Desktops\\{" + guid.ToString() + "}", "Name");
  894.             }
  895.             catch { }
  896.  
  897.             // name found?
  898.             if (string.IsNullOrEmpty(desktopName))
  899.                 return false;
  900.             else
  901.                 return true;
  902.         }
  903.  
  904. $(if ($OSBuild -ge 22000) {@"
  905.         public static string DesktopWallpaperFromIndex(int index)
  906.         { // return name of desktop wallpaper from index (-> index = 0..Count-1)
  907.  
  908.             // get desktop name
  909.             string desktopwppath = "";
  910.             try {
  911.                 desktopwppath = DesktopManager.GetDesktop(index).GetWallpaperPath();
  912.             }
  913.             catch { }
  914.  
  915.             return desktopwppath;
  916.         }
  917. "@ })
  918.  
  919.         public static int SearchDesktop(string partialName)
  920.         { // get index of desktop with partial name, return -1 if no desktop found
  921.             int index = -1;
  922.  
  923. $(if (($OSBuild -lt 20348) -Or ($OSBuild -ge 22621)) {@"
  924.             for (int i = 0; i < DesktopManager.VirtualDesktopManagerInternal.GetCount(); i++)
  925. "@ } else {@"
  926.             for (int i = 0; i < DesktopManager.VirtualDesktopManagerInternal.GetCount(IntPtr.Zero); i++)
  927. "@ })
  928.             { // loop through all virtual desktops and compare partial name to desktop name
  929.                 if (DesktopNameFromIndex(i).ToUpper().IndexOf(partialName.ToUpper()) >= 0)
  930.                 { index = i;
  931.                     break;
  932.                 }
  933.             }
  934.  
  935.             return index;
  936.         }
  937.  
  938.         public static Desktop Create()
  939.         { // create a new desktop
  940. $(if (($OSBuild -lt 20348) -Or ($OSBuild -ge 22621)) {@"
  941.             return new Desktop(DesktopManager.VirtualDesktopManagerInternal.CreateDesktop());
  942. "@ } else {@"
  943.             return new Desktop(DesktopManager.VirtualDesktopManagerInternal.CreateDesktop(IntPtr.Zero));
  944. "@ })
  945.         }
  946.  
  947.         public void Remove(Desktop fallback = null)
  948.         { // destroy desktop and switch to <fallback>
  949.             IVirtualDesktop fallbackdesktop;
  950.             if (fallback == null)
  951.             { // if no fallback is given use desktop to the left except for desktop 0.
  952.                 Desktop dtToCheck = new Desktop(DesktopManager.GetDesktop(0));
  953.                 if (this.Equals(dtToCheck))
  954.                 { // desktop 0: set fallback to second desktop (= "right" desktop)
  955.                     DesktopManager.VirtualDesktopManagerInternal.GetAdjacentDesktop(ivd, 4, out fallbackdesktop); // 4 = RightDirection
  956.                 }
  957.                 else
  958.                 { // set fallback to "left" desktop
  959.                     DesktopManager.VirtualDesktopManagerInternal.GetAdjacentDesktop(ivd, 3, out fallbackdesktop); // 3 = LeftDirection
  960.                 }
  961.             }
  962.             else
  963.                 // set fallback desktop
  964.                 fallbackdesktop = fallback.ivd;
  965.  
  966.             DesktopManager.VirtualDesktopManagerInternal.RemoveDesktop(ivd, fallbackdesktop);
  967.         }
  968.  
  969. $(if ($OSBuild -lt 20348) {@"
  970. // Windows 10:
  971.         public static void RemoveAll()
  972.         { // remove all desktops but visible
  973.             int desktopcount = DesktopManager.VirtualDesktopManagerInternal.GetCount();
  974.             int desktopcurrent = DesktopManager.GetDesktopIndex(DesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop());
  975.  
  976.             if (desktopcurrent < desktopcount-1)
  977.             { // remove all desktops "right" from current
  978.                 for (int i = desktopcount-1; i > desktopcurrent; i--)
  979.                     DesktopManager.VirtualDesktopManagerInternal.RemoveDesktop(DesktopManager.GetDesktop(i), DesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop());
  980.             }
  981.             if (desktopcurrent > 0)
  982.             { // remove all desktops "left" from current
  983.                 for (int i = 0; i < desktopcurrent; i++)
  984.                     DesktopManager.VirtualDesktopManagerInternal.RemoveDesktop(DesktopManager.GetDesktop(0), DesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop());
  985.             }
  986.         }
  987. "@ })
  988.  
  989. $(if ($OSBuild -eq 20348) {@"
  990. // Windows Server 2022:
  991.         public static void RemoveAll()
  992.         { // remove all desktops but visible
  993.             int desktopcount = DesktopManager.VirtualDesktopManagerInternal.GetCount(IntPtr.Zero);
  994.             int desktopcurrent = DesktopManager.GetDesktopIndex(DesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop(IntPtr.Zero));
  995.  
  996.             if (desktopcurrent < desktopcount-1)
  997.             { // remove all desktops "right" from current
  998.                 for (int i = desktopcount-1; i > desktopcurrent; i--)
  999.                     DesktopManager.VirtualDesktopManagerInternal.RemoveDesktop(DesktopManager.GetDesktop(i), DesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop(IntPtr.Zero));
  1000.             }
  1001.             if (desktopcurrent > 0)
  1002.             { // remove all desktops "left" from current
  1003.                 for (int i = 0; i < desktopcurrent; i++)
  1004.                     DesktopManager.VirtualDesktopManagerInternal.RemoveDesktop(DesktopManager.GetDesktop(0), DesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop(IntPtr.Zero));
  1005.             }
  1006.         }
  1007. "@ })
  1008.  
  1009. $(if (($OSBuild -ge 22000) -And ($OSBuild -lt 22621)) {@"
  1010.         public static void RemoveAll()
  1011.         { // remove all desktops but visible
  1012.             DesktopManager.VirtualDesktopManagerInternal.SetDesktopIsPerMonitor(true);
  1013.         }
  1014.  
  1015.         public void Move(int index)
  1016.         { // move current desktop to desktop in index (-> index = 0..Count-1)
  1017.             DesktopManager.VirtualDesktopManagerInternal.MoveDesktop(ivd, IntPtr.Zero, index);
  1018.         }
  1019. "@ })
  1020.  
  1021. $(if ($OSBuild -ge 22621) {@"
  1022.         public static void RemoveAll()
  1023.         { // remove all desktops but visible
  1024.             int desktopcount = DesktopManager.VirtualDesktopManagerInternal.GetCount();
  1025.             int desktopcurrent = DesktopManager.GetDesktopIndex(DesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop());
  1026.  
  1027.             if (desktopcurrent < desktopcount-1)
  1028.             { // remove all desktops "right" from current
  1029.                 for (int i = desktopcount-1; i > desktopcurrent; i--)
  1030.                     DesktopManager.VirtualDesktopManagerInternal.RemoveDesktop(DesktopManager.GetDesktop(i), DesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop());
  1031.             }
  1032.             if (desktopcurrent > 0)
  1033.             { // remove all desktops "left" from current
  1034.                 for (int i = 0; i < desktopcurrent; i++)
  1035.                     DesktopManager.VirtualDesktopManagerInternal.RemoveDesktop(DesktopManager.GetDesktop(0), DesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop());
  1036.             }
  1037.         }
  1038.  
  1039.         public void Move(int index)
  1040.         { // move current desktop to desktop in index (-> index = 0..Count-1)
  1041.             DesktopManager.VirtualDesktopManagerInternal.MoveDesktop(ivd, index);
  1042.         }
  1043. "@ })
  1044.  
  1045. $(if ($OSBuild -lt 20348) {@"
  1046.         public void SetName(string Name)
  1047.         { // set name for desktop, empty string removes name
  1048.             if (DesktopManager.VirtualDesktopManagerInternal2 != null)
  1049.             { // only if interface to set name is present
  1050.                 HString hstring = HString.FromString(Name);
  1051.                 DesktopManager.VirtualDesktopManagerInternal2.SetName(this.ivd, hstring);
  1052.                 hstring.Delete();
  1053.             }
  1054.         }
  1055. "@ })
  1056. $(if ($OSBuild -ge 20348) {@"
  1057.         public void SetName(string Name)
  1058.         { // set name for desktop, empty string removes name
  1059.             HString hstring = HString.FromString(Name);
  1060.             DesktopManager.VirtualDesktopManagerInternal.SetDesktopName(this.ivd, hstring);
  1061.             hstring.Delete();
  1062.         }
  1063. "@ })
  1064.  
  1065. $(if ($OSBuild -ge 22000) {@"
  1066.         public void SetWallpaperPath(string Path)
  1067.         { // set path for wallpaper, empty string removes path
  1068.             if (string.IsNullOrEmpty(Path)) throw new ArgumentNullException();
  1069.             HString hstring = HString.FromString(Path);
  1070.             DesktopManager.VirtualDesktopManagerInternal.SetDesktopWallpaper(this.ivd, hstring);
  1071.             hstring.Delete();
  1072.         }
  1073.  
  1074.         public static void SetAllWallpaperPaths(string Path)
  1075.         { // set wallpaper path for all desktops
  1076.             if (string.IsNullOrEmpty(Path)) throw new ArgumentNullException();
  1077.             HString hstring = HString.FromString(Path);
  1078.             DesktopManager.VirtualDesktopManagerInternal.UpdateWallpaperPathForAllDesktops(hstring);
  1079.             hstring.Delete();
  1080.         }
  1081. "@ })
  1082.  
  1083.         public bool IsVisible
  1084.         { // return true if this desktop is the current displayed one
  1085. $(if (($OSBuild -lt 20348) -Or ($OSBuild -ge 22621)) {@"
  1086.             get { return object.ReferenceEquals(ivd, DesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop()); }
  1087. "@ } else {@"
  1088.             get { return object.ReferenceEquals(ivd, DesktopManager.VirtualDesktopManagerInternal.GetCurrentDesktop(IntPtr.Zero)); }
  1089. "@ })
  1090.         }
  1091.  
  1092.         public void MakeVisible()
  1093.         { // make this desktop visible
  1094.             WindowInformation wi = FindWindow("Program Manager");
  1095.  
  1096.             // activate desktop to prevent flashing icons in taskbar
  1097.             int dummy;
  1098.             uint DesktopThreadId = GetWindowThreadProcessId(new IntPtr(wi.Handle), out dummy);
  1099.             uint ForegroundThreadId = GetWindowThreadProcessId(GetForegroundWindow(), out dummy);
  1100.             uint CurrentThreadId = GetCurrentThreadId();
  1101.  
  1102.             if ((DesktopThreadId != 0) && (ForegroundThreadId != 0) && (ForegroundThreadId != CurrentThreadId))
  1103.             {
  1104.                 AttachThreadInput(DesktopThreadId, CurrentThreadId, true);
  1105.                 AttachThreadInput(ForegroundThreadId, CurrentThreadId, true);
  1106.                 SetForegroundWindow(new IntPtr(wi.Handle));
  1107.                 AttachThreadInput(ForegroundThreadId, CurrentThreadId, false);
  1108.                 AttachThreadInput(DesktopThreadId, CurrentThreadId, false);
  1109.             }
  1110.  
  1111. $(if (($OSBuild -lt 20348) -Or ($OSBuild -ge 22621)) {@"
  1112.             DesktopManager.VirtualDesktopManagerInternal.SwitchDesktop(ivd);
  1113. "@ } else {@"
  1114.             DesktopManager.VirtualDesktopManagerInternal.SwitchDesktop(IntPtr.Zero, ivd);
  1115. "@ })
  1116.  
  1117.             // direct desktop to give away focus
  1118.             ShowWindow(new IntPtr(wi.Handle), SW_MINIMIZE);
  1119.         }
  1120.  
  1121.         public Desktop Left
  1122.         { // return desktop at the left of this one, null if none
  1123.             get
  1124.             {
  1125.                 IVirtualDesktop desktop;
  1126.                 int hr = DesktopManager.VirtualDesktopManagerInternal.GetAdjacentDesktop(ivd, 3, out desktop); // 3 = LeftDirection
  1127.                 if (hr == 0)
  1128.                     return new Desktop(desktop);
  1129.                 else
  1130.                     return null;
  1131.             }
  1132.         }
  1133.  
  1134.         public Desktop Right
  1135.         { // return desktop at the right of this one, null if none
  1136.             get
  1137.             {
  1138.                 IVirtualDesktop desktop;
  1139.                 int hr = DesktopManager.VirtualDesktopManagerInternal.GetAdjacentDesktop(ivd, 4, out desktop); // 4 = RightDirection
  1140.                 if (hr == 0)
  1141.                     return new Desktop(desktop);
  1142.                 else
  1143.                     return null;
  1144.             }
  1145.         }
  1146.  
  1147.         public void MoveWindow(IntPtr hWnd)
  1148.         { // move window to this desktop
  1149.             int processId;
  1150.             if (hWnd == IntPtr.Zero) throw new ArgumentNullException();
  1151.             GetWindowThreadProcessId(hWnd, out processId);
  1152.  
  1153.             if (hWnd == GetConsoleWindow())
  1154.             { // own window
  1155.                 try // the easy way (powershell's own console)
  1156.                 {
  1157.                     DesktopManager.VirtualDesktopManager.MoveWindowToDesktop(hWnd, ivd.GetId());
  1158.                 }
  1159.                 catch // powershell in cmd console
  1160.                 {
  1161.                     IApplicationView view;
  1162.                     DesktopManager.ApplicationViewCollection.GetViewForHwnd(hWnd, out view);
  1163.                     DesktopManager.VirtualDesktopManagerInternal.MoveViewToDesktop(view, ivd);
  1164.                 }
  1165.             }
  1166.             else
  1167.             { // window of other process
  1168.                 IApplicationView view;
  1169.                 DesktopManager.ApplicationViewCollection.GetViewForHwnd(hWnd, out view);
  1170.                 try {
  1171.                     DesktopManager.VirtualDesktopManagerInternal.MoveViewToDesktop(view, ivd);
  1172.                 }
  1173.                 catch
  1174.                 { // could not move active window, try main window (or whatever windows thinks is the main window)
  1175.                     DesktopManager.ApplicationViewCollection.GetViewForHwnd(System.Diagnostics.Process.GetProcessById(processId).MainWindowHandle, out view);
  1176.                     DesktopManager.VirtualDesktopManagerInternal.MoveViewToDesktop(view, ivd);
  1177.                 }
  1178.             }
  1179.         }
  1180.  
  1181.         public void MoveActiveWindow()
  1182.         { // move active window to this desktop
  1183.             MoveWindow(GetForegroundWindow());
  1184.         }
  1185.  
  1186.         public bool HasWindow(IntPtr hWnd)
  1187.         { // return true if window is on this desktop
  1188.             if (hWnd == IntPtr.Zero) throw new ArgumentNullException();
  1189.             Guid id = DesktopManager.VirtualDesktopManager.GetWindowDesktopId(hWnd);
  1190.             if ((id.CompareTo(AppOnAllDesktops) == 0) || (id.CompareTo(WindowOnAllDesktops) == 0))
  1191.                 return true;
  1192.             else
  1193.                 return ivd.GetId() == id;
  1194.         }
  1195.  
  1196.         public static bool IsWindowPinned(IntPtr hWnd)
  1197.         { // return true if window is pinned to all desktops
  1198.             if (hWnd == IntPtr.Zero) throw new ArgumentNullException();
  1199.             return DesktopManager.VirtualDesktopPinnedApps.IsViewPinned(hWnd.GetApplicationView());
  1200.         }
  1201.  
  1202.         public static void PinWindow(IntPtr hWnd)
  1203.         { // pin window to all desktops
  1204.             if (hWnd == IntPtr.Zero) throw new ArgumentNullException();
  1205.             var view = hWnd.GetApplicationView();
  1206.             if (!DesktopManager.VirtualDesktopPinnedApps.IsViewPinned(view))
  1207.             { // pin only if not already pinned
  1208.                 DesktopManager.VirtualDesktopPinnedApps.PinView(view);
  1209.             }
  1210.         }
  1211.  
  1212.         public static void UnpinWindow(IntPtr hWnd)
  1213.         { // unpin window from all desktops
  1214.             if (hWnd == IntPtr.Zero) throw new ArgumentNullException();
  1215.             var view = hWnd.GetApplicationView();
  1216.             if (DesktopManager.VirtualDesktopPinnedApps.IsViewPinned(view))
  1217.             { // unpin only if not already unpinned
  1218.                 DesktopManager.VirtualDesktopPinnedApps.UnpinView(view);
  1219.             }
  1220.         }
  1221.  
  1222.         public static bool IsApplicationPinned(IntPtr hWnd)
  1223.         { // return true if application for window is pinned to all desktops
  1224.             if (hWnd == IntPtr.Zero) throw new ArgumentNullException();
  1225.             return DesktopManager.VirtualDesktopPinnedApps.IsAppIdPinned(DesktopManager.GetAppId(hWnd));
  1226.         }
  1227.  
  1228.         public static void PinApplication(IntPtr hWnd)
  1229.         { // pin application for window to all desktops
  1230.             if (hWnd == IntPtr.Zero) throw new ArgumentNullException();
  1231.             string appId = DesktopManager.GetAppId(hWnd);
  1232.             if (!DesktopManager.VirtualDesktopPinnedApps.IsAppIdPinned(appId))
  1233.             { // pin only if not already pinned
  1234.                 DesktopManager.VirtualDesktopPinnedApps.PinAppID(appId);
  1235.             }
  1236.         }
  1237.  
  1238.         public static void UnpinApplication(IntPtr hWnd)
  1239.         { // unpin application for window from all desktops
  1240.             if (hWnd == IntPtr.Zero) throw new ArgumentNullException();
  1241.             var view = hWnd.GetApplicationView();
  1242.             string appId = DesktopManager.GetAppId(hWnd);
  1243.             if (DesktopManager.VirtualDesktopPinnedApps.IsAppIdPinned(appId))
  1244.             { // unpin only if pinned
  1245.                 DesktopManager.VirtualDesktopPinnedApps.UnpinAppID(appId);
  1246.             }
  1247.         }
  1248.  
  1249.         // prepare callback function for window enumeration
  1250.         private delegate bool CallBackPtr(int hwnd, int lParam);
  1251.         private static CallBackPtr callBackPtr = Callback;
  1252.         // list of window informations
  1253.         private static List<WindowInformation> WindowInformationList = new List<WindowInformation>();
  1254.  
  1255.         // enumerate windows
  1256.         [DllImport("User32.dll", SetLastError = true)]
  1257.         [return: MarshalAs(UnmanagedType.Bool)]
  1258.         private static extern bool EnumWindows(CallBackPtr lpEnumFunc, IntPtr lParam);
  1259.  
  1260.         // get window title length
  1261.         [DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  1262.         private static extern int GetWindowTextLength(IntPtr hWnd);
  1263.  
  1264.         // get window title
  1265.         [DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  1266.         private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
  1267.  
  1268.         // callback function for window enumeration
  1269.         private static bool Callback(int hWnd, int lparam)
  1270.         {
  1271.             int length = GetWindowTextLength((IntPtr)hWnd);
  1272.             if (length > 0)
  1273.             {
  1274.                 StringBuilder sb = new StringBuilder(length + 1);
  1275.                 if (GetWindowText((IntPtr)hWnd, sb, sb.Capacity) > 0)
  1276.                 { WindowInformationList.Add(new WindowInformation {Handle = hWnd, Title = sb.ToString()}); }
  1277.             }
  1278.             return true;
  1279.         }
  1280.  
  1281.         // get list of all windows with title
  1282.         public static List<WindowInformation> GetWindows()
  1283.         {
  1284.             WindowInformationList = new List<WindowInformation>();
  1285.             EnumWindows(callBackPtr, IntPtr.Zero);
  1286.             return WindowInformationList;
  1287.         }
  1288.  
  1289.         // find first window with string in title
  1290.         public static WindowInformation FindWindow(string WindowTitle)
  1291.         {
  1292.             WindowInformationList = new List<WindowInformation>();
  1293.             EnumWindows(callBackPtr, IntPtr.Zero);
  1294.             WindowInformation result = WindowInformationList.Find(x => x.Title.IndexOf(WindowTitle, StringComparison.OrdinalIgnoreCase) >= 0);
  1295.             return result;
  1296.         }
  1297.     }
  1298.     #endregion
  1299. }
  1300. "@
  1301. #:: AveYo: [pre-compile for speed start]
  1302.   pushd $env:CD
  1303.   [io.file]::WriteAllText("$env:CD\VirtualDesktop.cs", $dll)
  1304.   $csc = join-path $([Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()) 'csc.exe'
  1305.   start $csc -args "/out:VirtualDesktop.dll /target:library /platform:anycpu /optimize /nologo VirtualDesktop.cs" -nonew -wait
  1306.   attrib +h "$env:CD\VirtualDesktop.dll"; del "$env:CD\VirtualDesktop.cs" -force -ea 0
  1307. }
  1308. Import-Module .\VirtualDesktop.dll
  1309. #:: AveYo: [pre-compile for speed end]
  1310.  
  1311. function Get-DesktopCount
  1312. {
  1313. <#
  1314. .SYNOPSIS
  1315. Get count of virtual desktops
  1316. .DESCRIPTION
  1317. Get count of virtual desktops
  1318. .INPUTS
  1319. None
  1320. .OUTPUTS
  1321. Int32
  1322. .EXAMPLE
  1323. Get-DesktopCount
  1324.  
  1325. Get count of virtual desktops
  1326. .LINK
  1327. https://github.com/MScholtes/PSVirtualDesktop
  1328. .LINK
  1329. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  1330. .LINK
  1331. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  1332. .NOTES
  1333. Author: Markus Scholtes
  1334. Created: 2017/05/08
  1335. Updated: 2020/06/27
  1336. #>
  1337.     [Cmdletbinding()]
  1338.     Param()
  1339.  
  1340.     Write-Verbose "Count of virtual desktops: $([VirtualDesktop.Desktop]::Count)"
  1341.     return [VirtualDesktop.Desktop]::Count
  1342. }
  1343.  
  1344.  
  1345. if ($OSBuild -ge 22000)
  1346. {
  1347.     function Get-DesktopList
  1348.     {
  1349.     <#
  1350.     .SYNOPSIS
  1351.     Get list of virtual desktops
  1352.     .DESCRIPTION
  1353.     Get list of virtual desktops
  1354.     .INPUTS
  1355.     None
  1356.     .OUTPUTS
  1357.     Object
  1358.     .EXAMPLE
  1359.     Get-DesktopList
  1360.  
  1361.     Get list of virtual desktops
  1362.     .LINK
  1363.     https://github.com/MScholtes/PSVirtualDesktop
  1364.     .LINK
  1365.     https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  1366.     .LINK
  1367.     https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  1368.     .NOTES
  1369.     Author: Markus Scholtes
  1370.     Created: 2020/06/27
  1371.     Updated: 2021/10/17
  1372.     #>
  1373.         $DesktopList = @()
  1374.         for ($I = 0; $I -lt [VirtualDesktop.Desktop]::Count; $I++)
  1375.         {
  1376.             $DesktopList += [PSCustomObject]@{
  1377.                 Number = $I
  1378.                 Name = [VirtualDesktop.Desktop]::DesktopNameFromIndex($I)
  1379.                 Wallpaper = [VirtualDesktop.Desktop]::DesktopWallpaperFromIndex($I)
  1380.                 Visible = if ([VirtualDesktop.Desktop]::FromDesktop([VirtualDesktop.Desktop]::Current) -eq $I) { $TRUE } else { $FALSE }
  1381.             }
  1382.         }
  1383.         return $DesktopList
  1384.     }
  1385. }
  1386. else
  1387. {
  1388.     function Get-DesktopList
  1389.     {
  1390.     <#
  1391.     .SYNOPSIS
  1392.     Get list of virtual desktops
  1393.     .DESCRIPTION
  1394.     Get list of virtual desktops
  1395.     .INPUTS
  1396.     None
  1397.     .OUTPUTS
  1398.     Object
  1399.     .EXAMPLE
  1400.     Get-DesktopList
  1401.  
  1402.     Get list of virtual desktops
  1403.     .LINK
  1404.     https://github.com/MScholtes/PSVirtualDesktop
  1405.     .LINK
  1406.     https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  1407.     .LINK
  1408.     https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  1409.     .NOTES
  1410.     Author: Markus Scholtes
  1411.     Created: 2020/06/27
  1412.     #>
  1413.         $DesktopList = @()
  1414.         for ($I = 0; $I -lt [VirtualDesktop.Desktop]::Count; $I++)
  1415.         {
  1416.             $DesktopList += [PSCustomObject]@{
  1417.                 Number = $I
  1418.                 Name = [VirtualDesktop.Desktop]::DesktopNameFromIndex($I)
  1419.                 Visible = if ([VirtualDesktop.Desktop]::FromDesktop([VirtualDesktop.Desktop]::Current) -eq $I) { $TRUE } else { $FALSE }
  1420.             }
  1421.         }
  1422.  
  1423.         return $DesktopList
  1424.     }
  1425. }
  1426.  
  1427. function New-Desktop
  1428. {
  1429. <#
  1430. .SYNOPSIS
  1431. Create virtual desktop
  1432. .DESCRIPTION
  1433. Create virtual desktop
  1434. .INPUTS
  1435. None
  1436. .OUTPUTS
  1437. Desktop object
  1438. .EXAMPLE
  1439. New-Desktop | Switch-Desktop
  1440.  
  1441. Create virtual desktop and switch to it
  1442. .LINK
  1443. https://github.com/MScholtes/PSVirtualDesktop
  1444. .LINK
  1445. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  1446. .LINK
  1447. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  1448. .NOTES
  1449. Author: Markus Scholtes
  1450. Created: 2017/05/08
  1451. Updated: 2020/06/27
  1452. #>
  1453.     [Cmdletbinding()]
  1454.     Param()
  1455.  
  1456.     $Desktop = [VirtualDesktop.Desktop]::Create()
  1457.     Write-Verbose "Created desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop))"
  1458.  
  1459.     return $Desktop
  1460. }
  1461.  
  1462.  
  1463. function Switch-Desktop
  1464. {
  1465. <#
  1466. .SYNOPSIS
  1467. Switch to virtual desktop
  1468. .DESCRIPTION
  1469. Switch to virtual desktop
  1470. .PARAMETER Desktop
  1471. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  1472. .INPUTS
  1473. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  1474. .OUTPUTS
  1475. None
  1476. .EXAMPLE
  1477. Switch-Desktop 0
  1478.  
  1479. Switch to first virtual desktop
  1480. .EXAMPLE
  1481. Switch-Desktop $Desktop
  1482.  
  1483. Switch to virtual desktop $Desktop
  1484. .EXAMPLE
  1485. "Desktop 1" | Switch-Desktop
  1486.  
  1487. Switch to second virtual desktop
  1488. .EXAMPLE
  1489. New-Desktop | Switch-Desktop
  1490.  
  1491. Create virtual desktop and switch to it
  1492. .LINK
  1493. https://github.com/MScholtes/PSVirtualDesktop
  1494. .LINK
  1495. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  1496. .LINK
  1497. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  1498. .NOTES
  1499. Author: Markus Scholtes
  1500. Created: 2017/05/08
  1501. Updated: 2020/06/27
  1502. #>
  1503.     [Cmdletbinding()]
  1504.     Param([Parameter(ValueFromPipeline = $TRUE)] $Desktop)
  1505.  
  1506.     if ($Desktop -is [VirtualDesktop.Desktop])
  1507.     {
  1508.         $Desktop.MakeVisible()
  1509.         Write-Verbose "Switched to desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')"
  1510.     }
  1511.     else
  1512.     {
  1513.         if ($Desktop -is [ValueType])
  1514.         {
  1515.             $TempDesktop = [VirtualDesktop.Desktop]::FromIndex($Desktop)
  1516.             if ($TempDesktop)
  1517.             {
  1518.                 $TempDesktop.MakeVisible()
  1519.                 Write-Verbose "Switched to desktop number $([VirtualDesktop.Desktop]::FromDesktop($TempDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($TempDesktop))')"
  1520.             }
  1521.         }
  1522.         else
  1523.         {
  1524.             if ($Desktop -is [STRING])
  1525.             {
  1526.                 $TempIndex = [VirtualDesktop.Desktop]::SearchDesktop($Desktop)
  1527.                 if ($TempIndex -ge 0)
  1528.                 {
  1529.                     ([VirtualDesktop.Desktop]::FromIndex($TempIndex)).MakeVisible()
  1530.                     Write-Verbose "Switched to desktop number $([VirtualDesktop.Desktop]::FromDesktop(([VirtualDesktop.Desktop]::FromIndex($TempIndex)))) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop([VirtualDesktop.Desktop]::FromIndex($TempIndex)))')"
  1531.                 }
  1532.                 else
  1533.                 {
  1534.                     Write-Error "No desktop with name part '$Desktop' found"
  1535.                 }
  1536.             }
  1537.             else
  1538.             {
  1539.                 Write-Error "Parameter -Desktop has to be a desktop object, an integer or a string"
  1540.             }
  1541.         }
  1542.     }
  1543. }
  1544.  
  1545.  
  1546. function Remove-Desktop
  1547. {
  1548. <#
  1549. .SYNOPSIS
  1550. Remove virtual desktop
  1551. .DESCRIPTION
  1552. Remove virtual desktop.
  1553. Windows on the desktop to be removed are moved to the virtual desktop to the left except for desktop 0 where the
  1554. second desktop is used instead. If the current desktop is removed, this fallback desktop is activated too.
  1555. If no desktop is supplied, the last desktop is removed.
  1556. .PARAMETER Desktop
  1557. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  1558. .INPUTS
  1559. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  1560. .OUTPUTS
  1561. None
  1562. .EXAMPLE
  1563. Remove-Desktop 0
  1564.  
  1565. Remove first virtual desktop
  1566. .EXAMPLE
  1567. Remove-Desktop $Desktop
  1568.  
  1569. Remove virtual desktop $Desktop
  1570. .EXAMPLE
  1571. "Desktop 1" | Remove-Desktop
  1572.  
  1573. Remove second virtual desktop
  1574. .EXAMPLE
  1575. New-Desktop | Remove-Desktop
  1576.  
  1577. Create virtual desktop and remove it immediately
  1578. .LINK
  1579. https://github.com/MScholtes/PSVirtualDesktop
  1580. .LINK
  1581. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  1582. .LINK
  1583. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  1584. .NOTES
  1585. Author: Markus Scholtes
  1586. Created: 2017/05/08
  1587. Updated: 2020/06/27
  1588. #>
  1589.     [Cmdletbinding()]
  1590.     Param([Parameter(ValueFromPipeline = $TRUE)] $Desktop)
  1591.  
  1592.     if ($NULL -eq $Desktop)
  1593.     {
  1594.         $Desktop = [VirtualDesktop.Desktop]::FromIndex(([VirtualDesktop.Desktop]::Count) -1)
  1595.     }
  1596.  
  1597.     if ($Desktop -is [VirtualDesktop.Desktop])
  1598.     {
  1599.         Write-Verbose "Removing desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')"
  1600.         $Desktop.Remove()
  1601.     }
  1602.     else
  1603.     {
  1604.         if ($Desktop -is [ValueType])
  1605.         {
  1606.             $TempDesktop = [VirtualDesktop.Desktop]::FromIndex($Desktop)
  1607.             if ($TempDesktop)
  1608.             {
  1609.                 Write-Verbose "Removing desktop number $([VirtualDesktop.Desktop]::FromDesktop($TempDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($TempDesktop))')"
  1610.                 $TempDesktop.Remove()
  1611.             }
  1612.         }
  1613.         else
  1614.         {
  1615.             if ($Desktop -is [STRING])
  1616.             {
  1617.                 $TempIndex = [VirtualDesktop.Desktop]::SearchDesktop($Desktop)
  1618.                 if ($TempIndex -ge 0)
  1619.                 {
  1620.                     Write-Verbose "Removing desktop number $([VirtualDesktop.Desktop]::FromDesktop(([VirtualDesktop.Desktop]::FromIndex($TempIndex)))) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop([VirtualDesktop.Desktop]::FromIndex($TempIndex)))')"
  1621.                     ([VirtualDesktop.Desktop]::FromIndex($TempIndex)).Remove()
  1622.                 }
  1623.                 else
  1624.                 {
  1625.                     Write-Error "No desktop with name part '$Desktop' found"
  1626.                 }
  1627.             }
  1628.             else
  1629.             {
  1630.                 Write-Error "Parameter -Desktop has to be a desktop object, an integer or a string"
  1631.             }
  1632.         }
  1633.     }
  1634. }
  1635.  
  1636.  
  1637. function Remove-AllDesktops
  1638. {
  1639. <#
  1640. .SYNOPSIS
  1641. Remove all virtual desktops but visible
  1642. .DESCRIPTION
  1643. Remove all virtual desktops but visible
  1644. .INPUTS
  1645. None
  1646. .OUTPUTS
  1647. None
  1648. .EXAMPLE
  1649. Remove-AllDesktops
  1650.  
  1651. Remove all virtual desktops but visible
  1652. .LINK
  1653. https://github.com/MScholtes/PSVirtualDesktop
  1654. .LINK
  1655. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  1656. .NOTES
  1657. Author: Markus Scholtes
  1658. Created: 2021/10/17
  1659. #>
  1660.     [VirtualDesktop.Desktop]::RemoveAll()
  1661. }
  1662.  
  1663.  
  1664. function Get-CurrentDesktop
  1665. {
  1666. <#
  1667. .SYNOPSIS
  1668. Get current virtual desktop
  1669. .DESCRIPTION
  1670. Get current virtual desktop as Desktop object
  1671. .INPUTS
  1672. None
  1673. .OUTPUTS
  1674. Desktop object
  1675. .EXAMPLE
  1676. Get-CurrentDesktop | Remove-Desktop
  1677.  
  1678. Remove current virtual desktop
  1679. .LINK
  1680. https://github.com/MScholtes/PSVirtualDesktop
  1681. .LINK
  1682. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  1683. .LINK
  1684. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  1685. .NOTES
  1686. Author: Markus Scholtes
  1687. Created: 2017/05/08
  1688. Updated: 2020/06/27
  1689. #>
  1690.     [Cmdletbinding()]
  1691.     Param()
  1692.  
  1693.     $Desktop = [VirtualDesktop.Desktop]::Current
  1694.     Write-Verbose "Current desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')"
  1695.     return $Desktop
  1696. }
  1697.  
  1698.  
  1699. function Get-Desktop
  1700. {
  1701. <#
  1702. .SYNOPSIS
  1703. Get virtual desktop with index number (0 to count-1) or string (part of desktop name)
  1704. .DESCRIPTION
  1705. Get virtual desktop with index number (0 to count-1) or string (part of desktop name)
  1706. Returns $NULL if index number is out of range.
  1707. Returns current desktop is index is omitted.
  1708. .PARAMETER Index
  1709. Number of desktop (starting with 0 to count-1) or string (part of desktop name)
  1710. .INPUTS
  1711. Int32 or STRING
  1712. .OUTPUTS
  1713. Desktop object
  1714. .EXAMPLE
  1715. Get-Desktop 1 | Switch-Desktop
  1716.  
  1717. Get object of second virtual desktop and switch to it
  1718. .EXAMPLE
  1719. "Desktop 1" | Get-Desktop | Switch-Desktop
  1720.  
  1721. Get object of second virtual desktop and switch to it
  1722. .LINK
  1723. https://github.com/MScholtes/PSVirtualDesktop
  1724. .LINK
  1725. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  1726. .LINK
  1727. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  1728. .NOTES
  1729. Author: Markus Scholtes
  1730. Created: 2017/05/08
  1731. Updated: 2020/06/27
  1732. #>
  1733.     [OutputType([VirtualDesktop.Desktop])]
  1734.     [Cmdletbinding()]
  1735.     Param([Parameter(ValueFromPipeline = $TRUE)] $Index)
  1736.  
  1737.     if ($NULL -eq $Index)
  1738.     {
  1739.         $Desktop = [VirtualDesktop.Desktop]::Current
  1740.         Write-Verbose "Current desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')"
  1741.         return $Desktop
  1742.     }
  1743.  
  1744.     if ($Index -is [ValueType])
  1745.     {
  1746.         $TempDesktop = [VirtualDesktop.Desktop]::FromIndex($Index)
  1747.         if ($NULL -ne $TempDesktop) { Write-Verbose "Desktop number $([VirtualDesktop.Desktop]::FromDesktop($TempDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($TempDesktop))')" }
  1748.         return $TempDesktop
  1749.     }
  1750.     else
  1751.     {
  1752.         if ($Index -is [STRING])
  1753.         {
  1754.             $TempIndex = [VirtualDesktop.Desktop]::SearchDesktop($Index)
  1755.             if ($TempIndex -ge 0)
  1756.             {
  1757.                 $TempDesktop = [VirtualDesktop.Desktop]::FromIndex($TempIndex)
  1758.                 Write-Verbose "Desktop number $([VirtualDesktop.Desktop]::FromDesktop($TempDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($TempDesktop))')"
  1759.                 return $TempDesktop
  1760.             }
  1761.             else
  1762.             {
  1763.                 Write-Error "No desktop with name part '$Index' found"
  1764.                 return $NULL
  1765.             }
  1766.         }
  1767.         else
  1768.         {
  1769.             Write-Error "Parameter -Index has to be an integer or string"
  1770.             return $NULL
  1771.         }
  1772.     }
  1773. }
  1774.  
  1775.  
  1776. function Get-DesktopIndex
  1777. {
  1778. <#
  1779. .SYNOPSIS
  1780. Get index number (0 to count-1) of virtual desktop
  1781. .DESCRIPTION
  1782. Get index number (0 to count-1) of virtual desktop
  1783. Returns -1 if desktop cannot be found.
  1784. Returns index of current desktop is parameter desktop is omitted.
  1785. .PARAMETER Desktop
  1786. Desktop object or string (part of desktop name)
  1787. .INPUTS
  1788. Desktop object or string (part of desktop name)
  1789. .OUTPUTS
  1790. Int32
  1791. .EXAMPLE
  1792. New-Desktop | Get-DesktopIndex
  1793.  
  1794. Get index number of new virtual desktop
  1795. .EXAMPLE
  1796. Get-DesktopIndex "desktop 1"
  1797.  
  1798. Get index number of desktop with name containing "desktop 1"
  1799. .LINK
  1800. https://github.com/MScholtes/PSVirtualDesktop
  1801. .LINK
  1802. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  1803. .LINK
  1804. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  1805. .NOTES
  1806. Author: Markus Scholtes
  1807. Created: 2017/05/08
  1808. Updated: 2021/02/28
  1809. #>
  1810.     [OutputType([INT32])]
  1811.     [Cmdletbinding()]
  1812.     Param([Parameter(ValueFromPipeline = $TRUE)] $Desktop)
  1813.  
  1814.     if ($NULL -eq $Desktop)
  1815.     {
  1816.         $Desktop = [VirtualDesktop.Desktop]::Current
  1817.     }
  1818.  
  1819.     if ($Desktop -is [VirtualDesktop.Desktop])
  1820.     {
  1821.         Write-Verbose "Desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')"
  1822.         return [VirtualDesktop.Desktop]::FromDesktop($Desktop)
  1823.     }
  1824.     else
  1825.     {
  1826.         if ($Desktop -is [STRING])
  1827.         {
  1828.             $TempIndex = [VirtualDesktop.Desktop]::SearchDesktop($Desktop)
  1829.             if ($TempIndex -ge 0)
  1830.             {
  1831.                 Write-Verbose "Desktop number $TempIndex ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop([VirtualDesktop.Desktop]::FromIndex($TempIndex)))')"
  1832.                 return $TempIndex
  1833.             }
  1834.             else
  1835.             {
  1836.                 Write-Error "No desktop with name part '$Desktop' found"
  1837.                 return -1
  1838.             }
  1839.         }
  1840.         else
  1841.         {
  1842.             Write-Error "Parameter -Desktop has to be a desktop object or string"
  1843.             return -1
  1844.         }
  1845.     }
  1846. }
  1847.  
  1848.  
  1849. function Get-DesktopName
  1850. {
  1851. <#
  1852. .SYNOPSIS
  1853. Get name of virtual desktop
  1854. .DESCRIPTION
  1855. Get name of virtual desktop
  1856. .PARAMETER Desktop
  1857. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  1858. .INPUTS
  1859. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  1860. .OUTPUTS
  1861. String (name of desktop)
  1862. .EXAMPLE
  1863. Get-DesktopName 0
  1864.  
  1865. Get name of first desktop
  1866. .EXAMPLE
  1867. Get-DesktopName $Desktop
  1868.  
  1869. Get name of virtual desktop $Desktop
  1870. .EXAMPLE
  1871. "desktop" | Get-DesktopName
  1872.  
  1873. Get name of first virtual desktop whose name contains "desktop"
  1874. .EXAMPLE
  1875. New-Desktop | Get-DesktopName
  1876.  
  1877. Create virtual desktop and show its name
  1878. .LINK
  1879. https://github.com/MScholtes/PSVirtualDesktop
  1880. .LINK
  1881. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  1882. .LINK
  1883. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  1884. .NOTES
  1885. Author: Markus Scholtes
  1886. Created: 2020/06/27
  1887. Updated: 2021/02/28
  1888. #>
  1889.     [Cmdletbinding()]
  1890.     Param([Parameter(ValueFromPipeline = $TRUE)] $Desktop)
  1891.  
  1892.     if ($NULL -eq $Desktop)
  1893.     {
  1894.         $Desktop = [VirtualDesktop.Desktop]::Current
  1895.     }
  1896.  
  1897.     if ($Desktop -is [VirtualDesktop.Desktop])
  1898.     {
  1899.         Write-Verbose "Get name of desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')"
  1900.         return ([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))
  1901.     }
  1902.     else
  1903.     {
  1904.         if ($Desktop -is [ValueType])
  1905.         {
  1906.             $TempDesktop = [VirtualDesktop.Desktop]::FromIndex($Desktop)
  1907.             if ($TempDesktop)
  1908.             {
  1909.                 Write-Verbose "Get name of desktop number $([VirtualDesktop.Desktop]::FromDesktop($TempDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($TempDesktop))')"
  1910.                 return ([VirtualDesktop.Desktop]::DesktopNameFromDesktop($TempDesktop))
  1911.             }
  1912.         }
  1913.         else
  1914.         {
  1915.             if ($Desktop -is [STRING])
  1916.             {
  1917.                 $TempIndex = [VirtualDesktop.Desktop]::SearchDesktop($Desktop)
  1918.                 if ($TempIndex -ge 0)
  1919.                 {
  1920.                     Write-Verbose "Get name of desktop number $([VirtualDesktop.Desktop]::FromDesktop(([VirtualDesktop.Desktop]::FromIndex($TempIndex)))) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop([VirtualDesktop.Desktop]::FromIndex($TempIndex)))')"
  1921.                     return ([VirtualDesktop.Desktop]::DesktopNameFromDesktop([VirtualDesktop.Desktop]::FromIndex($TempIndex)))
  1922.                 }
  1923.                 else
  1924.                 {
  1925.                     Write-Error "No desktop with name part '$Desktop' found"
  1926.                 }
  1927.             }
  1928.             else
  1929.             {
  1930.                 Write-Error "Parameter -Desktop has to be a desktop object, an integer or a string"
  1931.             }
  1932.         }
  1933.     }
  1934. }
  1935.  
  1936.  
  1937. function Set-DesktopName
  1938. {
  1939. <#
  1940. .SYNOPSIS
  1941. Set name of virtual desktop
  1942. .DESCRIPTION
  1943. Set name of virtual desktop.
  1944. If parameter Desktop is not set, the name of the current desktop is used.
  1945. .PARAMETER Desktop
  1946. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  1947. .PARAMETER Name
  1948. Name of desktop. If omitted or empty or $NULL, a name will be removed from the desktop.
  1949. .PARAMETER PassThru
  1950. Return virtual desktop
  1951. .INPUTS
  1952. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  1953. .OUTPUTS
  1954. None or [VirtualDesktop.Desktop]
  1955. .EXAMPLE
  1956. Set-DesktopName 0 "The first desktop"
  1957.  
  1958. Set name of first desktop
  1959. .EXAMPLE
  1960. Set-DesktopName $Desktop
  1961.  
  1962. Remove name of virtual desktop $Desktop
  1963. .EXAMPLE
  1964. "desktop" | Set-DesktopName -Name "First found"
  1965.  
  1966. Set name of first virtual desktop whose name contains "desktop"
  1967. .EXAMPLE
  1968. Set-DesktopName -Name "This is the current desktop"
  1969.  
  1970. Set name of the current virtual desktop
  1971. .EXAMPLE
  1972. New-Desktop | Set-DesktopName -Name "The new one" -PassThru | Get-DesktopName
  1973.  
  1974. Create virtual desktop, set its name and return the new name
  1975. .LINK
  1976. https://github.com/MScholtes/PSVirtualDesktop
  1977. .LINK
  1978. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  1979. .LINK
  1980. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  1981. .NOTES
  1982. Author: Markus Scholtes
  1983. Created: 2020/06/27
  1984. Updated: 2021/10/18
  1985. #>
  1986.     [Cmdletbinding()]
  1987.     Param([Parameter(ValueFromPipeline = $TRUE)] $Desktop, [Parameter(ValueFromPipeline = $FALSE)] $Name, [SWITCH]$PassThru)
  1988.  
  1989.     if ($NULL -eq $Name) { $Name = "" }
  1990.     if ($NULL -eq $Desktop) { $Desktop = [VirtualDesktop.Desktop]::Current }
  1991.  
  1992.     if ($Desktop -is [VirtualDesktop.Desktop])
  1993.     {
  1994.         if ($Name -ne "")
  1995.         { Write-Verbose "Set name of desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))') to '$Name'" }
  1996.         else
  1997.         { Write-Verbose "Remove name of desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')" }
  1998.         $Desktop.SetName($Name)
  1999.         $ActiveDesktop = $Desktop
  2000.     }
  2001.     else
  2002.     {
  2003.         if ($Desktop -is [ValueType])
  2004.         {
  2005.             $ActiveDesktop = [VirtualDesktop.Desktop]::FromIndex($Desktop)
  2006.             if ($ActiveDesktop)
  2007.             {
  2008.                 if ($Name -ne "")
  2009.                 { Write-Verbose "Set name of desktop number $([VirtualDesktop.Desktop]::FromDesktop($ActiveDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($ActiveDesktop))') to '$Name'" }
  2010.                 else
  2011.                 { Write-Verbose "Remove name of desktop number $([VirtualDesktop.Desktop]::FromDesktop($ActiveDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($ActiveDesktop))')" }
  2012.                 $ActiveDesktop.SetName($Name)
  2013.             }
  2014.         }
  2015.         else
  2016.         {
  2017.             if ($Desktop -is [STRING])
  2018.             {
  2019.                 $TempIndex = [VirtualDesktop.Desktop]::SearchDesktop($Desktop)
  2020.                 if ($TempIndex -ge 0)
  2021.                 {
  2022.                     $ActiveDesktop = [VirtualDesktop.Desktop]::FromIndex($TempIndex)
  2023.                     if ($Name -ne "")
  2024.                     { Write-Verbose "Set name of desktop number $([VirtualDesktop.Desktop]::FromDesktop($ActiveDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($ActiveDesktop))') to '$Name'" }
  2025.                     else
  2026.                     { Write-Verbose "Remove name of desktop number $([VirtualDesktop.Desktop]::FromDesktop($ActiveDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($ActiveDesktop))')" }
  2027.                     $ActiveDesktop.SetName($Name)
  2028.                 }
  2029.                 else
  2030.                 {
  2031.                     Write-Error "No desktop with name part '$Desktop' found"
  2032.                 }
  2033.             }
  2034.             else
  2035.             {
  2036.                 Write-Error "Parameter -Desktop has to be a desktop object, an integer or a string"
  2037.             }
  2038.         }
  2039.     }
  2040.     if ($PassThru)
  2041.     {
  2042.         return $ActiveDesktop
  2043.     }
  2044. }
  2045.  
  2046.  
  2047. if ($OSBuild -ge 22000)
  2048. {
  2049.     function Set-DesktopWallpaper
  2050.     {
  2051.     <#
  2052.     .SYNOPSIS
  2053.     Set wallpaper of virtual desktop
  2054.     .DESCRIPTION
  2055.     Set wallpaper of virtual desktop
  2056.     .PARAMETER Desktop
  2057.     Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  2058.     If parameter Desktop is not set, the name of the current desktop is used.
  2059.     .PARAMETER Path
  2060.     Path to wallpaper
  2061.     .PARAMETER PassThru
  2062.     Return virtual desktop
  2063.     .INPUTS
  2064.     Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  2065.     .OUTPUTS
  2066.     None or [VirtualDesktop.Desktop]
  2067.     .EXAMPLE
  2068.     Set-DesktopWallpaper 0 "C:\Users\VD\Pictures\NicePic.jpg"
  2069.  
  2070.     Set wallpaper of first desktop
  2071.     .EXAMPLE
  2072.     Set-DesktopWallpaper -Path "C:\Users\VD\Pictures\CurrentDesktopPic.jpg"
  2073.  
  2074.     Set wallpaper of current desktop
  2075.     .EXAMPLE
  2076.     "First found" | Set-DesktopWallpaper -Path "C:\Windows\Web\Wallpaper\Windows\img0.jpg"
  2077.  
  2078.     Set wallpaper of first virtual desktop whose name contains "First found"
  2079.     .EXAMPLE
  2080.     New-Desktop | Set-DesktopWallpaper -Path "Background.jpg" -PassThru | Get-DesktopName
  2081.  
  2082.     Create virtual desktop, set its wallpaper and return the name
  2083.     .LINK
  2084.     https://github.com/MScholtes/PSVirtualDesktop
  2085.     .LINK
  2086.     https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  2087.     .NOTES
  2088.     Author: Markus Scholtes
  2089.     Created: 2021/10/18
  2090.     #>
  2091.         [Cmdletbinding()]
  2092.         Param([Parameter(ValueFromPipeline = $TRUE)] $Desktop, [Parameter(ValueFromPipeline = $FALSE)] $Path, [SWITCH]$PassThru)
  2093.  
  2094.         if ($NULL -eq $Desktop) { $Desktop = [VirtualDesktop.Desktop]::Current }
  2095.  
  2096.         if ($Desktop -is [VirtualDesktop.Desktop])
  2097.         {
  2098.             if ([STRING]::IsNullOrEmpty($Path))
  2099.             { Write-Error "Wallpaper path is missing" }
  2100.             else
  2101.             { Write-Verbose "Set wallpaper of desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))') to '$Path'"
  2102.                 $Desktop.SetWallpaperPath($Path)
  2103.             }
  2104.             $ActiveDesktop = $Desktop
  2105.         }
  2106.         else
  2107.         {
  2108.             if ($Desktop -is [ValueType])
  2109.             {
  2110.                 $ActiveDesktop = [VirtualDesktop.Desktop]::FromIndex($Desktop)
  2111.                 if ($ActiveDesktop)
  2112.                 {
  2113.                     if ([STRING]::IsNullOrEmpty($Path))
  2114.                     { Write-Error "Wallpaper path is missing" }
  2115.                     else
  2116.                     { Write-Verbose "Set name of desktop number $([VirtualDesktop.Desktop]::FromDesktop($ActiveDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($ActiveDesktop))') to '$Path'"
  2117.                         $ActiveDesktop.SetWallpaperPath($Path)
  2118.                     }
  2119.                 }
  2120.             }
  2121.             else
  2122.             {
  2123.                 if ($Desktop -is [STRING])
  2124.                 {
  2125.                     $TempIndex = [VirtualDesktop.Desktop]::SearchDesktop($Desktop)
  2126.                     if ($TempIndex -ge 0)
  2127.                     {
  2128.                         $ActiveDesktop = [VirtualDesktop.Desktop]::FromIndex($TempIndex)
  2129.                         if ([STRING]::IsNullOrEmpty($Path))
  2130.                         { Write-Error "Wallpaper path is missing" }
  2131.                         else
  2132.                         { Write-Verbose "Set name of desktop number $([VirtualDesktop.Desktop]::FromDesktop($ActiveDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($ActiveDesktop))') to '$Path'"
  2133.                             $ActiveDesktop.SetWallpaperPath($Path)
  2134.                         }
  2135.                     }
  2136.                     else
  2137.                     {
  2138.                         Write-Error "No desktop with name part '$Desktop' found"
  2139.                     }
  2140.                 }
  2141.                 else
  2142.                 {
  2143.                     Write-Error "Parameter -Desktop has to be a desktop object, an integer or a string"
  2144.                 }
  2145.             }
  2146.         }
  2147.         if ($PassThru)
  2148.         {
  2149.             return $ActiveDesktop
  2150.         }
  2151.     }
  2152.  
  2153.  
  2154.     function Set-AllDesktopWallpapers
  2155.     {
  2156.     <#
  2157.     .SYNOPSIS
  2158.     Set wallpaper of all virtual desktops
  2159.     .DESCRIPTION
  2160.     Set wallpaper of all virtual desktops
  2161.     .PARAMETER Path
  2162.     Path to wallpaper
  2163.     .INPUTS
  2164.     String
  2165.     .OUTPUTS
  2166.     None
  2167.     .EXAMPLE
  2168.     Set-AllDesktopWallpapers -Path "C:\Users\VD\Pictures\NicePic.jpg"
  2169.  
  2170.     Set wallpaper of all desktops
  2171.     .EXAMPLE
  2172.     "C:\Windows\Web\Wallpaper\Windows\img0.jpg" | Set-AllDesktopWallpapers
  2173.  
  2174.     Set wallpaper of all desktops
  2175.     .LINK
  2176.     https://github.com/MScholtes/PSVirtualDesktop
  2177.     .LINK
  2178.     https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  2179.     .NOTES
  2180.     Author: Markus Scholtes
  2181.     Created: 2021/10/17
  2182.     #>
  2183.         [Cmdletbinding()]
  2184.         Param([Parameter(ValueFromPipeline = $TRUE)] $Path)
  2185.  
  2186.         if ([STRING]::IsNullOrEmpty($Path))
  2187.         { Write-Error "Wallpaper path is missing" }
  2188.         else
  2189.         { Write-Verbose "Set wallpaper of all desktops to '$Path'"
  2190.             [VirtualDesktop.Desktop]::SetAllWallpaperPaths($Path)
  2191.         }
  2192.     }
  2193. }
  2194.  
  2195.  
  2196. function Get-DesktopFromWindow
  2197. {
  2198. <#
  2199. .SYNOPSIS
  2200. Get virtual desktop of window
  2201. .DESCRIPTION
  2202. Get virtual desktop of window whose window handle is given.
  2203. Returns $NULL if window handle is unknown.
  2204. .PARAMETER Hwnd
  2205. Window handle
  2206. .INPUTS
  2207. IntPtr
  2208. .OUTPUTS
  2209. Desktop object
  2210. .EXAMPLE
  2211. Get-DesktopFromWindow ((Get-Process "notepad")[0].MainWindowHandle) | Switch-Desktop
  2212.  
  2213. Switch to virtual desktop with notepad window
  2214. .EXAMPLE
  2215. Find-WindowHandle "notepad" | Get-DesktopFromWindow | Switch-Desktop
  2216.  
  2217. Switch to virtual desktop with notepad window
  2218. .LINK
  2219. https://github.com/MScholtes/PSVirtualDesktop
  2220. .LINK
  2221. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  2222. .LINK
  2223. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  2224. .NOTES
  2225. Author: Markus Scholtes
  2226. Created: 2017/05/08
  2227. Updated: 2020/06/27
  2228. #>
  2229.     [OutputType([VirtualDesktop.Desktop])]
  2230.     [Cmdletbinding()]
  2231.     Param([Parameter(ValueFromPipeline = $TRUE)] $Hwnd)
  2232.  
  2233.     if ($Hwnd -is [IntPtr])
  2234.     {
  2235.         $Desktop = [VirtualDesktop.Desktop]::FromWindow($Hwnd)
  2236.         if ($NULL -ne $Desktop) { Write-Verbose "Window is on desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')" }
  2237.         return $Desktop
  2238.     }
  2239.     else
  2240.     {
  2241.         if ($Hwnd -is [ValueType])
  2242.         {
  2243.             $Desktop = [VirtualDesktop.Desktop]::FromWindow([IntPtr]$Hwnd)
  2244.             if ($NULL -ne $Desktop) { Write-Verbose "Window is on desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')" }
  2245.             return $Desktop
  2246.         }
  2247.         else
  2248.         {
  2249.             Write-Error "Parameter -Hwnd has to be an IntPtr or an integer"
  2250.             return $NULL
  2251.         }
  2252.     }
  2253. }
  2254.  
  2255.  
  2256. function Test-CurrentDesktop
  2257. {
  2258. <#
  2259. .SYNOPSIS
  2260. Checks whether a desktop is the displayed virtual desktop
  2261. .DESCRIPTION
  2262. Checks whether a desktop is the displayed virtual desktop
  2263. .PARAMETER Desktop
  2264. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  2265. .INPUTS
  2266. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  2267. .OUTPUTS
  2268. Boolean
  2269. .EXAMPLE
  2270. Get-DesktopIndex 1 | Test-CurrentDesktop
  2271.  
  2272. Checks whether the desktop with count number 1 is the displayed virtual desktop
  2273. .EXAMPLE
  2274. Test-CurrentDesktop "desktop 2"
  2275.  
  2276. Checks whether the desktop with string "desktop 2" in name is the displayed virtual desktop
  2277. .LINK
  2278. https://github.com/MScholtes/PSVirtualDesktop
  2279. .LINK
  2280. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  2281. .LINK
  2282. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  2283. .NOTES
  2284. Author: Markus Scholtes
  2285. Created: 2017/05/08
  2286. Updated: 2020/06/27
  2287. #>
  2288.     [OutputType([BOOLEAN])]
  2289.     [Cmdletbinding()]
  2290.     Param([Parameter(ValueFromPipeline = $TRUE)] $Desktop)
  2291.  
  2292.     if ($Desktop -is [VirtualDesktop.Desktop])
  2293.     {
  2294.         Write-Verbose "Check visibility of desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')"
  2295.         return $Desktop.IsVisible
  2296.     }
  2297.     else
  2298.     {
  2299.         if ($Desktop -is [ValueType])
  2300.         {
  2301.             $TempDesktop = [VirtualDesktop.Desktop]::FromIndex($Desktop)
  2302.             if ($TempDesktop)
  2303.             {
  2304.                 Write-Verbose "Check visibility of desktop number $([VirtualDesktop.Desktop]::FromDesktop($TempDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($TempDesktop))')"
  2305.                 return $TempDesktop.IsVisible
  2306.             }
  2307.         }
  2308.         else
  2309.         {
  2310.             if ($Desktop -is [STRING])
  2311.             {
  2312.                 $TempIndex = [VirtualDesktop.Desktop]::SearchDesktop($Desktop)
  2313.                 if ($TempIndex -ge 0)
  2314.                 {
  2315.                     Write-Verbose "Check visibility of desktop number $([VirtualDesktop.Desktop]::FromDesktop(([VirtualDesktop.Desktop]::FromIndex($TempIndex)))) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop([VirtualDesktop.Desktop]::FromIndex($TempIndex)))')"
  2316.                     return ([VirtualDesktop.Desktop]::FromIndex($TempIndex)).IsVisible
  2317.                 }
  2318.                 else
  2319.                 {
  2320.                     Write-Error "No desktop with name part '$Desktop' found"
  2321.                 }
  2322.             }
  2323.             else
  2324.             {
  2325.                 Write-Error "Parameter -Desktop has to be a desktop object, an integer or a string"
  2326.             }
  2327.         }
  2328.         return $FALSE
  2329.     }
  2330. }
  2331.  
  2332.  
  2333. function Get-LeftDesktop
  2334. {
  2335. <#
  2336. .SYNOPSIS
  2337. Get the desktop object on the "left" side
  2338. .DESCRIPTION
  2339. Get the desktop object on the "left" side
  2340. If there is no desktop on the "left" side $NULL is returned.
  2341. Returns desktop "left" to current desktop if parameter desktop is omitted.
  2342. .PARAMETER Desktop
  2343. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  2344. .INPUTS
  2345. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  2346. .OUTPUTS
  2347. Desktop object
  2348. .EXAMPLE
  2349. Get-CurrentDesktop | Get-LeftDesktop | Switch-Desktop
  2350.  
  2351. Switch to the desktop left of the displayed virtual desktop
  2352. .EXAMPLE
  2353. Get-LeftDesktop 1
  2354.  
  2355. Get desktop left to second desktop
  2356. .LINK
  2357. https://github.com/MScholtes/PSVirtualDesktop
  2358. .LINK
  2359. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  2360. .LINK
  2361. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  2362. .NOTES
  2363. Author: Markus Scholtes
  2364. Created: 2017/05/08
  2365. Updated: 2020/06/27
  2366. #>
  2367.     [Cmdletbinding()]
  2368.     Param([Parameter(ValueFromPipeline = $TRUE)] $Desktop)
  2369.  
  2370.     if ($NULL -eq $Desktop)
  2371.     {
  2372.         $Desktop = [VirtualDesktop.Desktop]::Current
  2373.     }
  2374.  
  2375.     if ($Desktop -is [VirtualDesktop.Desktop])
  2376.     {
  2377.         Write-Verbose "Returning desktop left of desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')"
  2378.         return $Desktop.Left
  2379.     }
  2380.     else
  2381.     {
  2382.         if ($Desktop -is [ValueType])
  2383.         {
  2384.             $TempDesktop = [VirtualDesktop.Desktop]::FromIndex($Desktop)
  2385.             if ($TempDesktop)
  2386.             {
  2387.                 Write-Verbose "Returning desktop left of desktop number $([VirtualDesktop.Desktop]::FromDesktop($TempDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($TempDesktop))')"
  2388.                 return $TempDesktop.Left
  2389.             }
  2390.         }
  2391.         else
  2392.         {
  2393.             if ($Desktop -is [STRING])
  2394.             {
  2395.                 $TempIndex = [VirtualDesktop.Desktop]::SearchDesktop($Desktop)
  2396.                 if ($TempIndex -ge 0)
  2397.                 {
  2398.                     Write-Verbose "Returning desktop left of desktop number $([VirtualDesktop.Desktop]::FromDesktop(([VirtualDesktop.Desktop]::FromIndex($TempIndex)))) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop([VirtualDesktop.Desktop]::FromIndex($TempIndex)))')"
  2399.                     return ([VirtualDesktop.Desktop]::FromIndex($TempIndex)).Left
  2400.                 }
  2401.                 else
  2402.                 {
  2403.                     Write-Error "No desktop with name part '$Desktop' found"
  2404.                 }
  2405.             }
  2406.             else
  2407.             {
  2408.                 Write-Error "Parameter -Desktop has to be a desktop object, an integer or a string"
  2409.             }
  2410.         }
  2411.  
  2412.         return $NULL
  2413.     }
  2414. }
  2415.  
  2416.  
  2417. function Get-RightDesktop
  2418. {
  2419. <#
  2420. .SYNOPSIS
  2421. Get the desktop object on the "right" side
  2422. .DESCRIPTION
  2423. Get the desktop object on the "right" side
  2424. If there is no desktop on the "right" side $NULL is returned.
  2425. Returns desktop "right" to current desktop if parameter desktop is omitted.
  2426. .PARAMETER Desktop
  2427. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  2428. .INPUTS
  2429. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  2430. .OUTPUTS
  2431. Desktop object
  2432. .EXAMPLE
  2433. Get-CurrentDesktop | Get-RightDesktop | Switch-Desktop
  2434.  
  2435. Switch to the desktop right of the displayed virtual desktop
  2436. .EXAMPLE
  2437. Get-RightDesktop 1
  2438.  
  2439. Get desktop right to second desktop
  2440. .LINK
  2441. https://github.com/MScholtes/PSVirtualDesktop
  2442. .LINK
  2443. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  2444. .LINK
  2445. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  2446. .NOTES
  2447. Author: Markus Scholtes
  2448. Created: 2017/05/08
  2449. Updated: 2020/06/27
  2450. #>
  2451.     [Cmdletbinding()]
  2452.     Param([Parameter(ValueFromPipeline = $TRUE)] $Desktop)
  2453.  
  2454.     if ($NULL -eq $Desktop)
  2455.     {
  2456.         $Desktop = [VirtualDesktop.Desktop]::Current
  2457.     }
  2458.  
  2459.     if ($Desktop -is [VirtualDesktop.Desktop])
  2460.     {
  2461.         Write-Verbose "Returning desktop right of desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')"
  2462.         return $Desktop.Right
  2463.     }
  2464.     else
  2465.     {
  2466.         if ($Desktop -is [ValueType])
  2467.         {
  2468.             $TempDesktop = [VirtualDesktop.Desktop]::FromIndex($Desktop)
  2469.             if ($TempDesktop)
  2470.             {
  2471.                 Write-Verbose "Returning desktop right of desktop number $([VirtualDesktop.Desktop]::FromDesktop($TempDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($TempDesktop))')"
  2472.                 return $TempDesktop.Right
  2473.             }
  2474.         }
  2475.         else
  2476.         {
  2477.             if ($Desktop -is [STRING])
  2478.             {
  2479.                 $TempIndex = [VirtualDesktop.Desktop]::SearchDesktop($Desktop)
  2480.                 if ($TempIndex -ge 0)
  2481.                 {
  2482.                     Write-Verbose "Returning desktop right of desktop number $([VirtualDesktop.Desktop]::FromDesktop(([VirtualDesktop.Desktop]::FromIndex($TempIndex)))) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop([VirtualDesktop.Desktop]::FromIndex($TempIndex)))')"
  2483.                     return ([VirtualDesktop.Desktop]::FromIndex($TempIndex)).Right
  2484.                 }
  2485.                 else
  2486.                 {
  2487.                     Write-Error "No desktop with name part '$Desktop' found"
  2488.                 }
  2489.             }
  2490.             else
  2491.             {
  2492.                 Write-Error "Parameter -Desktop has to be a desktop object, an integer or a string"
  2493.             }
  2494.         }
  2495.  
  2496.         return $NULL
  2497.     }
  2498. }
  2499.  
  2500.  
  2501. if ($OSBuild -ge 22000)
  2502. {
  2503.     function Move-Desktop
  2504.     {
  2505.     <#
  2506.     .SYNOPSIS
  2507.     Move current desktop to other virtual desktop
  2508.     .DESCRIPTION
  2509.     Move current desktop to other virtual desktop.
  2510.     .PARAMETER Desktop
  2511.     Desktop object to move current desktop to
  2512.     .INPUTS
  2513.     None
  2514.     .OUTPUTS
  2515.     Desktop object
  2516.     .EXAMPLE
  2517.     Move-Window -Desktop (Get-Desktop "Other Desktop")
  2518.  
  2519.     Move current virtual desktop to desktop "Other Desktop"
  2520.     .EXAMPLE
  2521.     Move-Window -Desktop (Get-RightDesktop)
  2522.  
  2523.     Move current virtual desktop to the "right"
  2524.     .LINK
  2525.     https://github.com/MScholtes/PSVirtualDesktop
  2526.     .LINK
  2527.     https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  2528.     .NOTES
  2529.     Author: Markus Scholtes
  2530.     Created: 2021/10/17
  2531.     #>
  2532.         [Cmdletbinding()]
  2533.         Param([Parameter(ValueFromPipeline = $TRUE)] $Desktop)
  2534.  
  2535.         if ($NULL -eq $Desktop)
  2536.         {
  2537.             Write-Error "Parameter -Desktop missing"
  2538.             return $NULL
  2539.         }
  2540.  
  2541.         if ($Desktop -is [VirtualDesktop.Desktop])
  2542.         {
  2543.             Write-Verbose "Moving current desktop to desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')"
  2544.             ([VirtualDesktop.Desktop]::Current).Move([VirtualDesktop.Desktop]::FromDesktop($Desktop))
  2545.             return ([VirtualDesktop.Desktop]::Current)
  2546.         }
  2547.  
  2548.         Write-Error "Parameter -Desktop has to be a desktop object"
  2549.         return $NULL
  2550.     }
  2551. }
  2552.  
  2553. function Move-Window
  2554. {
  2555. <#
  2556. .SYNOPSIS
  2557. Move window to virtual desktop
  2558. .DESCRIPTION
  2559. Move window whose window handle is given to virtual desktop.
  2560. The parameter values are auto detected and can change places. The desktop object is handed to the output pipeline for further use.
  2561. If parameter desktop is omitted, the current desktop is used.
  2562. .PARAMETER Desktop
  2563. Desktop object
  2564. .PARAMETER Hwnd
  2565. Window handle
  2566. .INPUTS
  2567. Desktop object
  2568. .OUTPUTS
  2569. Desktop object
  2570. .EXAMPLE
  2571. Move-Window -Desktop (Get-CurrentDesktop) -Hwnd ((Get-Process "notepad")[0].MainWindowHandle)
  2572.  
  2573. Move notepad window to current virtual desktop
  2574. .EXAMPLE
  2575. New-Desktop | Move-Window (Get-ConsoleHandle) | Switch-Desktop
  2576.  
  2577. Create virtual desktop and move powershell console window to it, then activate new desktop.
  2578. .LINK
  2579. https://github.com/MScholtes/PSVirtualDesktop
  2580. .LINK
  2581. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  2582. .LINK
  2583. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  2584. .NOTES
  2585. Author: Markus Scholtes
  2586. Created: 2017/05/08
  2587. Updated: 2020/06/27
  2588. #>
  2589.     [Cmdletbinding()]
  2590.     Param([Parameter(ValueFromPipeline = $FALSE)] $Desktop, [Parameter(ValueFromPipeline = $TRUE)] $Hwnd)
  2591.  
  2592.     if ($NULL -eq $Desktop)
  2593.     {
  2594.         $Desktop = [VirtualDesktop.Desktop]::Current
  2595.     }
  2596.     else
  2597.     {
  2598.         if ($NULL -eq $Hwnd)
  2599.         {
  2600.             $Hwnd = [VirtualDesktop.Desktop]::Current
  2601.         }
  2602.     }
  2603.  
  2604.     if (($Hwnd -is [IntPtr]) -And ($Desktop -is [VirtualDesktop.Desktop]))
  2605.     {
  2606.         Write-Verbose "Moving window to desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')"
  2607.         $Desktop.MoveWindow($Hwnd)
  2608.         return $Desktop
  2609.     }
  2610.  
  2611.     if (($Hwnd -is [ValueType]) -And ($Desktop -is [VirtualDesktop.Desktop]))
  2612.     {
  2613.         Write-Verbose "Moving window to desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')"
  2614.         $Desktop.MoveWindow([IntPtr]$Hwnd)
  2615.         return $Desktop
  2616.     }
  2617.  
  2618.     if (($Desktop -is [IntPtr]) -And ($Hwnd -is [VirtualDesktop.Desktop]))
  2619.     {
  2620.         Write-Verbose "Moving window to desktop number $([VirtualDesktop.Desktop]::FromDesktop($Hwnd)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Hwnd))')"
  2621.         $Hwnd.MoveWindow($Desktop)
  2622.         return $Hwnd
  2623.     }
  2624.  
  2625.     if (($Desktop -is [ValueType]) -And ($Hwnd -is [VirtualDesktop.Desktop]))
  2626.     {
  2627.         Write-Verbose "Moving window to desktop number $([VirtualDesktop.Desktop]::FromDesktop($Hwnd)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Hwnd))')"
  2628.         $Hwnd.MoveWindow([IntPtr]$Desktop)
  2629.         return $Hwnd
  2630.     }
  2631.  
  2632.     Write-Error "Parameters -Desktop and -Hwnd have to be a desktop object and an IntPtr/integer pair"
  2633.     return $NULL
  2634. }
  2635.  
  2636.  
  2637. function Move-ActiveWindow
  2638. {
  2639. <#
  2640. .SYNOPSIS
  2641. Move active window to virtual desktop
  2642. .DESCRIPTION
  2643. Move active window to virtual desktop. The desktop object is handed to the output pipeline for further use.
  2644. If parameter desktop is omitted, the current desktop is used.
  2645. .PARAMETER Desktop
  2646. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  2647. .INPUTS
  2648. Number of desktop (starting with 0 to count-1), desktop object or string (part of desktop name)
  2649. .OUTPUTS
  2650. Desktop object
  2651. .EXAMPLE
  2652. Move-ActiveWindow -Desktop (Get-CurrentDesktop)
  2653.  
  2654. Move active window to current virtual desktop
  2655. .EXAMPLE
  2656. New-Desktop | Move-ActiveWindow | Switch-Desktop
  2657.  
  2658. Create virtual desktop and move activate window to it, then activate new desktop.
  2659. .EXAMPLE
  2660. Move-ActiveWindow "Desktop 2"
  2661.  
  2662. Move activate window to second desktop
  2663. .LINK
  2664. https://github.com/MScholtes/PSVirtualDesktop
  2665. .LINK
  2666. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  2667. .LINK
  2668. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  2669. .NOTES
  2670. Author: Markus Scholtes
  2671. Created: 2019/02/13
  2672. Updated: 2020/06/27
  2673. #>
  2674.     [Cmdletbinding()]
  2675.     Param([Parameter(ValueFromPipeline = $TRUE)] $Desktop)
  2676.  
  2677.     if ($NULL -eq $Desktop)
  2678.     {
  2679.         $Desktop = [VirtualDesktop.Desktop]::Current
  2680.     }
  2681.  
  2682.     if ($Desktop -is [VirtualDesktop.Desktop])
  2683.     {
  2684.         Write-Verbose "Moving active window to desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')"
  2685.         $Desktop.MoveWindow((Get-ActiveWindowHandle))
  2686.         return $Desktop
  2687.     }
  2688.     else
  2689.     {
  2690.         if ($Desktop -is [ValueType])
  2691.         {
  2692.             $TempDesktop = [VirtualDesktop.Desktop]::FromIndex($Desktop)
  2693.             if ($TempDesktop)
  2694.             {
  2695.                 Write-Verbose "Moving active window to desktop number $([VirtualDesktop.Desktop]::FromDesktop($TempDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($TempDesktop))')"
  2696.                 $TempDesktop.MoveWindow((Get-ActiveWindowHandle))
  2697.                 return $TempDesktop
  2698.             }
  2699.         }
  2700.         else
  2701.         {
  2702.             if ($Desktop -is [STRING])
  2703.             {
  2704.                 $TempIndex = [VirtualDesktop.Desktop]::SearchDesktop($Desktop)
  2705.                 if ($TempIndex -ge 0)
  2706.                 {
  2707.                     $TempDesktop = [VirtualDesktop.Desktop]::FromIndex($TempIndex)
  2708.                     Write-Verbose "Moving active window to desktop number $([VirtualDesktop.Desktop]::FromDesktop($TempDesktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($TempDesktop))')"
  2709.                     $TempDesktop.MoveWindow((Get-ActiveWindowHandle))
  2710.                     return $TempDesktop
  2711.                 }
  2712.                 else
  2713.                 {
  2714.                     Write-Error "No desktop with name part '$Desktop' found"
  2715.                 }
  2716.             }
  2717.             else
  2718.             {
  2719.                 Write-Error "Parameter -Desktop has to be a desktop object, an integer or a string"
  2720.             }
  2721.         }
  2722.  
  2723.     return $NULL
  2724.     }
  2725. }
  2726.  
  2727.  
  2728. function Test-Window
  2729. {
  2730. <#
  2731. .SYNOPSIS
  2732. Check if window is displayed on virtual desktop
  2733. .DESCRIPTION
  2734. Check if window  whose window handle is given is displayed on virtual desktop.
  2735. The parameter values are auto detected and can change places. If parameter desktop is not supplied, the current desktop is used.
  2736. .PARAMETER Desktop
  2737. Desktop object. If omitted the current desktop is used.
  2738. .PARAMETER Hwnd
  2739. Window handle
  2740. .INPUTS
  2741. Desktop object
  2742. .OUTPUTS
  2743. Boolean
  2744. .EXAMPLE
  2745. Test-Window -Hwnd ((Get-Process "notepad")[0].MainWindowHandle)
  2746.  
  2747. Check if notepad window is displayed on current virtual desktop
  2748. .EXAMPLE
  2749. Get-Desktop 1 | Test-Window (Get-ConsoleHandle)
  2750.  
  2751. Check if powershell console window is displayed on virtual desktop with number 1 (second desktop)
  2752. .LINK
  2753. https://github.com/MScholtes/PSVirtualDesktop
  2754. .LINK
  2755. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  2756. .LINK
  2757. https://github.com/MScholtes/PSVirtualDesktop
  2758. .LINK
  2759. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  2760. .LINK
  2761. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  2762. .NOTES
  2763. Author: Markus Scholtes
  2764. Created: 2017/05/08
  2765. Updated: 2020/06/27
  2766. #>
  2767.     [OutputType([BOOLEAN])]
  2768.     [Cmdletbinding()]
  2769.     Param([Parameter(ValueFromPipeline = $FALSE)] $Desktop, [Parameter(ValueFromPipeline = $TRUE)] $Hwnd)
  2770.  
  2771.     if ($Hwnd -is [IntPtr])
  2772.     {
  2773.         if ($Desktop -is [VirtualDesktop.Desktop])
  2774.         {
  2775.             Write-Verbose "Checking window on desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')"
  2776.             return $Desktop.HasWindow($Hwnd)
  2777.         }
  2778.         else
  2779.         {
  2780.             Write-Verbose "Checking window on desktop number $([VirtualDesktop.Desktop]::FromDesktop([VirtualDesktop.Desktop]::Current)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop([VirtualDesktop.Desktop]::Current))')"
  2781.             return ([VirtualDesktop.Desktop]::Current).HasWindow($Hwnd)
  2782.         }
  2783.     }
  2784.  
  2785.     if ($Hwnd -is [ValueType])
  2786.     {
  2787.         if ($Desktop -is [VirtualDesktop.Desktop])
  2788.         {
  2789.             Write-Verbose "Checking window on desktop number $([VirtualDesktop.Desktop]::FromDesktop($Desktop)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Desktop))')"
  2790.             return $Desktop.HasWindow([IntPtr]$Hwnd)
  2791.         }
  2792.         else
  2793.         {
  2794.             Write-Verbose "Checking window on desktop number $([VirtualDesktop.Desktop]::FromDesktop([VirtualDesktop.Desktop]::Current)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop([VirtualDesktop.Desktop]::Current))')"
  2795.             return ([VirtualDesktop.Desktop]::Current).HasWindow([IntPtr]$Hwnd)
  2796.         }
  2797.     }
  2798.  
  2799.     if ($Desktop -is [IntPtr])
  2800.     {
  2801.         if ($Hwnd -is [VirtualDesktop.Desktop])
  2802.         {
  2803.             Write-Verbose "Checking window on desktop number $([VirtualDesktop.Desktop]::FromDesktop($Hwnd)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Hwnd))')"
  2804.             return $Hwnd.HasWindow($Desktop)
  2805.         }
  2806.         else
  2807.         {
  2808.             Write-Verbose "Checking window on desktop number $([VirtualDesktop.Desktop]::FromDesktop([VirtualDesktop.Desktop]::Current)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop([VirtualDesktop.Desktop]::Current))')"
  2809.             return ([VirtualDesktop.Desktop]::Current).HasWindow($Desktop)
  2810.         }
  2811.     }
  2812.  
  2813.     if ($Desktop -is [ValueType])
  2814.     {
  2815.         if ($Hwnd -is [VirtualDesktop.Desktop])
  2816.         {
  2817.             Write-Verbose "Checking window on desktop number $([VirtualDesktop.Desktop]::FromDesktop($Hwnd)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop($Hwnd))')"
  2818.             return $Hwnd.HasWindow([IntPtr]$Desktop)
  2819.         }
  2820.         else
  2821.         {
  2822.             Write-Verbose "Checking window on desktop number $([VirtualDesktop.Desktop]::FromDesktop([VirtualDesktop.Desktop]::Current)) ('$([VirtualDesktop.Desktop]::DesktopNameFromDesktop([VirtualDesktop.Desktop]::Current))')"
  2823.             return ([VirtualDesktop.Desktop]::Current).HasWindow([IntPtr]$Desktop)
  2824.         }
  2825.     }
  2826.  
  2827.     Write-Error "Parameters -Desktop and -Hwnd have to be a desktop object and an IntPtr/integer pair"
  2828.     return $FALSE
  2829. }
  2830.  
  2831.  
  2832. function Pin-Window
  2833. {
  2834. <#
  2835. .SYNOPSIS
  2836. Pin window to all desktops
  2837. .DESCRIPTION
  2838. Pin window whose window handle is given to all desktops.
  2839. .PARAMETER Hwnd
  2840. Window handle
  2841. .INPUTS
  2842. IntPtr
  2843. .OUTPUTS
  2844. None
  2845. .EXAMPLE
  2846. Pin-Window ((Get-Process "notepad")[0].MainWindowHandle)
  2847.  
  2848. Pin notepad window to all desktops
  2849. .LINK
  2850. https://github.com/MScholtes/PSVirtualDesktop
  2851. .LINK
  2852. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  2853. .LINK
  2854. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  2855. .NOTES
  2856. Author: Markus Scholtes
  2857. Created: 2017/05/08
  2858. Updated: 2020/06/27
  2859. #>
  2860.     [Cmdletbinding()]
  2861.     Param([Parameter(ValueFromPipeline = $TRUE)] $Hwnd)
  2862.  
  2863.     if ($Hwnd -is [IntPtr])
  2864.     {
  2865.         [VirtualDesktop.Desktop]::PinWindow($Hwnd)
  2866.         Write-Verbose "Pinned window with handle $Hwnd to all desktops"
  2867.     }
  2868.     else
  2869.     {
  2870.         if ($Hwnd -is [ValueType])
  2871.         {
  2872.             [VirtualDesktop.Desktop]::PinWindow([IntPtr]$Hwnd)
  2873.             Write-Verbose "Pinned window with handle $Hwnd to all desktops"
  2874.         }
  2875.         else
  2876.         {
  2877.             Write-Error "Parameter -Hwnd has to be an IntPtr or an integer"
  2878.         }
  2879.     }
  2880. }
  2881.  
  2882.  
  2883. function Unpin-Window
  2884. {
  2885. <#
  2886. .SYNOPSIS
  2887. Unpin window from all desktops
  2888. .DESCRIPTION
  2889. Unpin window whose window handle is given from all desktops.
  2890. .PARAMETER Hwnd
  2891. Window handle
  2892. .INPUTS
  2893. IntPtr
  2894. .OUTPUTS
  2895. None
  2896. .EXAMPLE
  2897. Unpin-Window ((Get-Process "notepad")[0].MainWindowHandle)
  2898.  
  2899. Unpin notepad window from all desktops
  2900. .LINK
  2901. https://github.com/MScholtes/PSVirtualDesktop
  2902. .LINK
  2903. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  2904. .LINK
  2905. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  2906. .NOTES
  2907. Author: Markus Scholtes
  2908. Created: 2017/05/08
  2909. Updated: 2020/06/27
  2910. #>
  2911.     [Cmdletbinding()]
  2912.     Param([Parameter(ValueFromPipeline = $TRUE)] $Hwnd)
  2913.  
  2914.     if ($Hwnd -is [IntPtr])
  2915.     {
  2916.         [VirtualDesktop.Desktop]::UnpinWindow($Hwnd)
  2917.         Write-Verbose "Unpinned window with handle $Hwnd from all desktops"
  2918.     }
  2919.     else
  2920.     {
  2921.         if ($Hwnd -is [ValueType])
  2922.         {
  2923.             [VirtualDesktop.Desktop]::UnpinWindow([IntPtr]$Hwnd)
  2924.             Write-Verbose "Unpinned window with handle $Hwnd from all desktops"
  2925.         }
  2926.         else
  2927.         {
  2928.             Write-Error "Parameter -Hwnd has to be an IntPtr or an integer"
  2929.         }
  2930.     }
  2931. }
  2932.  
  2933.  
  2934. function Test-WindowPinned
  2935. {
  2936. <#
  2937. .SYNOPSIS
  2938. Checks whether a window is pinned to all desktops
  2939. .DESCRIPTION
  2940. Checks whether a window whose window handle is given is pinned to all desktops.
  2941. .PARAMETER Hwnd
  2942. Window handle
  2943. .INPUTS
  2944. IntPtr
  2945. .OUTPUTS
  2946. Boolean
  2947. .EXAMPLE
  2948. Test-WindowPinned ((Get-Process "notepad")[0].MainWindowHandle)
  2949.  
  2950. Checks whether notepad window is pinned to all virtual desktops
  2951. .LINK
  2952. https://github.com/MScholtes/PSVirtualDesktop
  2953. .LINK
  2954. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  2955. .LINK
  2956. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  2957. .NOTES
  2958. Author: Markus Scholtes
  2959. Created: 2017/05/08
  2960. Updated: 2020/06/27
  2961. #>
  2962.     [OutputType([BOOLEAN])]
  2963.     [Cmdletbinding()]
  2964.     Param([Parameter(ValueFromPipeline = $TRUE)] $Hwnd)
  2965.  
  2966.     if ($Hwnd -is [IntPtr])
  2967.     {
  2968.         Write-Verbose "Check if window with handle $Hwnd is pinned to all desktops"
  2969.         return [VirtualDesktop.Desktop]::IsWindowPinned($Hwnd)
  2970.     }
  2971.     else
  2972.     {
  2973.         if ($Hwnd -is [ValueType])
  2974.         {
  2975.             Write-Verbose "Check if window with handle $Hwnd is pinned to all desktops"
  2976.             return [VirtualDesktop.Desktop]::IsWindowPinned([IntPtr]$Hwnd)
  2977.         }
  2978.         else
  2979.         {
  2980.             Write-Error "Parameter -Hwnd has to be an IntPtr or an integer"
  2981.             return $FALSE
  2982.         }
  2983.     }
  2984. }
  2985.  
  2986.  
  2987. function Pin-Application
  2988. {
  2989. <#
  2990. .SYNOPSIS
  2991. Pin application to all desktops
  2992. .DESCRIPTION
  2993. Pin application whose window handle is given to all desktops.
  2994. .PARAMETER Hwnd
  2995. Window handle
  2996. .INPUTS
  2997. IntPtr
  2998. .OUTPUTS
  2999. None
  3000. .EXAMPLE
  3001. Pin-Application ((Get-Process "notepad")[0].MainWindowHandle)
  3002.  
  3003. Pin all notepad windows to all desktops
  3004. .LINK
  3005. https://github.com/MScholtes/PSVirtualDesktop
  3006. .LINK
  3007. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  3008. .LINK
  3009. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  3010. .NOTES
  3011. Author: Markus Scholtes
  3012. Created: 2017/05/08
  3013. Updated: 2020/06/27
  3014. #>
  3015.     [Cmdletbinding()]
  3016.     Param([Parameter(ValueFromPipeline = $TRUE)] $Hwnd)
  3017.  
  3018.     if ($Hwnd -is [IntPtr])
  3019.     {
  3020.         [VirtualDesktop.Desktop]::PinApplication($Hwnd)
  3021.         Write-Verbose "Pinned application with window handle $Hwnd to all desktops"
  3022.     }
  3023.     else
  3024.     {
  3025.         if ($Hwnd -is [ValueType])
  3026.         {
  3027.             [VirtualDesktop.Desktop]::PinApplication([IntPtr]$Hwnd)
  3028.             Write-Verbose "Pinned application with window handle $Hwnd to all desktops"
  3029.         }
  3030.         else
  3031.         {
  3032.             Write-Error "Parameter -Hwnd has to be an IntPtr or an integer"
  3033.         }
  3034.     }
  3035. }
  3036.  
  3037.  
  3038. function Unpin-Application
  3039. {
  3040. <#
  3041. .SYNOPSIS
  3042. Unpin application from all desktops
  3043. .DESCRIPTION
  3044. Unpin application whose window handle is given from all desktops.
  3045. .PARAMETER Hwnd
  3046. Window handle
  3047. .INPUTS
  3048. IntPtr
  3049. .OUTPUTS
  3050. None
  3051. .EXAMPLE
  3052. Unpin-Application ((Get-Process "notepad")[0].MainWindowHandle)
  3053.  
  3054. Unpin all notepad windows from all desktops
  3055. .LINK
  3056. https://github.com/MScholtes/PSVirtualDesktop
  3057. .LINK
  3058. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  3059. .LINK
  3060. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  3061. .NOTES
  3062. Author: Markus Scholtes
  3063. Created: 2017/05/08
  3064. Updated: 2020/06/27
  3065. #>
  3066.     [Cmdletbinding()]
  3067.     Param([Parameter(ValueFromPipeline = $TRUE)] $Hwnd)
  3068.  
  3069.     if ($Hwnd -is [IntPtr])
  3070.     {
  3071.         [VirtualDesktop.Desktop]::UnpinApplication($Hwnd)
  3072.         Write-Verbose "Unpinned application with window handle $Hwnd from all desktops"
  3073.     }
  3074.     else
  3075.     {
  3076.         if ($Hwnd -is [ValueType])
  3077.         {
  3078.             [VirtualDesktop.Desktop]::UnpinApplication([IntPtr]$Hwnd)
  3079.             Write-Verbose "Unpinned application with window handle $Hwnd from all desktops"
  3080.         }
  3081.         else
  3082.         {
  3083.             Write-Error "Parameter -Hwnd has to be an IntPtr or an integer"
  3084.         }
  3085.     }
  3086. }
  3087.  
  3088.  
  3089. function Test-ApplicationPinned
  3090. {
  3091. <#
  3092. .SYNOPSIS
  3093. Checks whether an application is pinned to all desktops
  3094. .DESCRIPTION
  3095. Checks whether an application whose window handle is given is pinned to all desktops.
  3096. .PARAMETER Hwnd
  3097. Window handle
  3098. .INPUTS
  3099. IntPtr
  3100. .OUTPUTS
  3101. Boolean
  3102. .EXAMPLE
  3103. Test-ApplicationPinned ((Get-Process "notepad")[0].MainWindowHandle)
  3104.  
  3105. Checks whether notepad windows are pinned to all virtual desktops
  3106. .LINK
  3107. https://github.com/MScholtes/PSVirtualDesktop
  3108. .LINK
  3109. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  3110. .LINK
  3111. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  3112. .NOTES
  3113. Author: Markus Scholtes
  3114. Created: 2017/05/08
  3115. Updated: 2020/06/27
  3116. #>
  3117.     [OutputType([BOOLEAN])]
  3118.     [Cmdletbinding()]
  3119.     Param([Parameter(ValueFromPipeline = $TRUE)] $Hwnd)
  3120.  
  3121.     if ($Hwnd -is [IntPtr])
  3122.     {
  3123.         Write-Verbose "Check if application with window handle $Hwnd is pinned to all desktops"
  3124.         return [VirtualDesktop.Desktop]::IsApplicationPinned($Hwnd)
  3125.     }
  3126.     else
  3127.     {
  3128.         if ($Hwnd -is [ValueType])
  3129.         {
  3130.             Write-Verbose "Check if application with window handle $Hwnd is pinned to all desktops"
  3131.             return [VirtualDesktop.Desktop]::IsApplicationPinned([IntPtr]$Hwnd)
  3132.         }
  3133.         else
  3134.         {
  3135.             Write-Error "Parameter -Hwnd has to be an IntPtr or an integer"
  3136.             return $FALSE
  3137.         }
  3138.     }
  3139. }
  3140.  
  3141.  
  3142. function Get-ConsoleHandle
  3143. {
  3144. <#
  3145. .SYNOPSIS
  3146. Get window handle of powershell console
  3147. .DESCRIPTION
  3148. Get window handle of powershell console in a safe way (means: if powershell is started in a cmd window, the cmd window handled is returned).
  3149. .INPUTS
  3150. None
  3151. .OUTPUTS
  3152. IntPtr
  3153. .EXAMPLE
  3154. Get-ConsoleHandle
  3155.  
  3156. Get window handle of powershell console
  3157. .LINK
  3158. https://github.com/MScholtes/PSVirtualDesktop
  3159. .LINK
  3160. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  3161. .LINK
  3162. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  3163. .NOTES
  3164. Author: Markus Scholtes
  3165. Created: 2018/10/22
  3166. Updated: 2022/02/25
  3167. #>
  3168.     [Cmdletbinding()]
  3169.     Param()
  3170.  
  3171.     Write-Verbose "Retrieving console window handle"
  3172.     if ($NULL -ne $ENV:wt_session)
  3173.     { # seems to be running in Windows Terminal
  3174.         $HANDLE = (Get-Process -PID ((Get-WmiObject -Class win32_process -Filter "processid='$PID'").ParentProcessId)).MainWindowHandle
  3175.     }
  3176.     else
  3177.     { # Powershell in own console
  3178.         $HANDLE = [VirtualDesktop.Desktop]::GetConsoleWindow()
  3179.         if ($HANDLE -eq 0)
  3180.         { # maybe script is started in ISE
  3181.             $HANDLE = (Get-Process -PID $PID).MainWindowHandle
  3182.         }
  3183.     }
  3184.     return $HANDLE
  3185. }
  3186.  
  3187.  
  3188. function Get-ActiveWindowHandle
  3189. {
  3190. <#
  3191. .SYNOPSIS
  3192. Get window handle of foreground window
  3193. .DESCRIPTION
  3194. Get window handle of foreground window (the foreground window is always on the current virtual desktop).
  3195. .INPUTS
  3196. None
  3197. .OUTPUTS
  3198. IntPtr
  3199. .EXAMPLE
  3200. Get-ActiveWindowHandle
  3201.  
  3202. Get window handle of foreground window
  3203. .LINK
  3204. https://github.com/MScholtes/PSVirtualDesktop
  3205. .LINK
  3206. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  3207. .LINK
  3208. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  3209. .NOTES
  3210. Author: Markus Scholtes
  3211. Created: 2019/02/13
  3212. Updated: 2020/06/27
  3213. #>
  3214.     [Cmdletbinding()]
  3215.     Param()
  3216.  
  3217.     Write-Verbose "Retrieving handle of active window"
  3218.     return [VirtualDesktop.Desktop]::GetForegroundWindow()
  3219. }
  3220.  
  3221. function Find-WindowHandle
  3222. {
  3223. <#
  3224. .SYNOPSIS
  3225. Find window handle to title text or retrieve list of windows with title
  3226. .DESCRIPTION
  3227. Find first window handle to partial title text (not case sensitive) or retrieve list of windows with title if *
  3228. is supplied as title
  3229. .PARAMETER Title
  3230. Partial window title or *. The search is not case sensitive.
  3231. .INPUTS
  3232. STRING
  3233. .OUTPUTS
  3234. Int or Array of WindowInformation
  3235. .EXAMPLE
  3236. Find-WindowHandle powershell
  3237.  
  3238. Get window handle of first powershell window
  3239. .EXAMPLE
  3240. Find-WindowHandle *
  3241.  
  3242. Get a list of all windows with title
  3243. .EXAMPLE
  3244. Find-WindowHandle * | ? { $_.Title -match "firefox" }
  3245.  
  3246. Find all windows that contain the text "firefox" in their title
  3247. .LINK
  3248. https://github.com/MScholtes/PSVirtualDesktop
  3249. .LINK
  3250. https://github.com/MScholtes/TechNet-Gallery/tree/master/VirtualDesktop
  3251. .LINK
  3252. https://gallery.technet.microsoft.com/scriptcenter/Powershell-commands-to-d0e79cc5
  3253. .NOTES
  3254. Author: Markus Scholtes
  3255. Created: 2019/09/04
  3256. Updated: 2020/06/27
  3257. #>
  3258.     [Cmdletbinding()]
  3259.     Param([Parameter(ValueFromPipeline = $TRUE)] $Title)
  3260.  
  3261.     if ($Title -eq "*")
  3262.     {
  3263.         Write-Verbose "Retrieving window titles and handles of all windows with titles"
  3264.         return [VirtualDesktop.Desktop]::GetWindows()
  3265.     }
  3266.     else
  3267.     {
  3268.         Write-Verbose "Retrieving window handles of first window with '$Title' in title"
  3269.         $RESULT = [VirtualDesktop.Desktop]::FindWindow($Title)
  3270.         if ($RESULT)
  3271.         {
  3272.             Write-Verbose "Window '$($RESULT.Title)' found"
  3273.             return $RESULT.Handle
  3274.         }
  3275.         else
  3276.         {
  3277.             Write-Verbose "No window found"
  3278.             return 0
  3279.         }
  3280.     }
  3281. }
  3282.  
  3283. # Clean up variables
  3284. Remove-Variable -Name OSVer,OSBuild
  3285. #:: VirtualDesktop.ps1 bundle end >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  3286.  
  3287. #:: AveYo: prepare dedicated virtual desktop - clear previously created if game not running
  3288. Get-DesktopList | where Name -match "[\w -]+ -fullscreen" | foreach {
  3289.   if ($null -eq (get-process $_.Name.Replace(' -fullscreen','') -ea 0)) { $_.Name | Remove-Desktop -ea 0 }
  3290. }
  3291. $game = (gi $env:PROGR).BaseName
  3292. $desk = Get-CurrentDesktop
  3293. $full = New-Desktop | Set-DesktopName -Name "$game -fullscreen" -PassThru
  3294.  
  3295. #:: start game (and steam if not already running) or switch to it
  3296. start "$env:STEAM\steam.exe" -args "$steam_options"
  3297.  
  3298. #:: wait for game to start
  3299. while ($null -eq (get-process $game -ea 0)) { sleep -m 100 }
  3300. " switching to '$game -fullscreen' virtual desktop...`n"
  3301. $full | Move-Window (Get-ConsoleHandle) | Switch-Desktop
  3302. " $game is loading...`n"
  3303. $wait = get-process $game -ea 0; while ($wait.MainWindowHandle -eq 0 -or $wait.Responding -eq !1) {$wait.Refresh(); sleep -m 100}
  3304.  
  3305. #:: flip to a temporary virtual desktop so that the game updates the res if scaled
  3306. $flip = New-Desktop | Set-DesktopName -Name "$game -flip" -PassThru | Switch-Desktop
  3307. sleep 1
  3308. $full | Switch-Desktop
  3309. $flip | Remove-Desktop -ea 0
  3310.  
  3311. #:: done, script closes
  3312.  
Add Comment
Please, Sign In to add comment