├── src ├── VirtualDesktop (LocalAppData).lnk ├── VirtualDesktop.WPF │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── VirtualDesktop.WPF.csproj │ ├── ApplicationExtensions.cs │ └── WindowExtensions.cs ├── VirtualDesktop.WinForms │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── FormExtensions.cs │ └── VirtualDesktop.WinForms.csproj ├── VirtualDesktop │ ├── Interop │ │ ├── Proxy │ │ │ ├── IApplicationViewCollection.cs │ │ │ ├── IVirtualDesktopNotificationService.cs │ │ │ ├── IApplicationView.cs │ │ │ ├── IVirtualDesktop.cs │ │ │ ├── IVirtualDesktopPinnedApps.cs │ │ │ ├── IVirtualDesktopManagerInternal.cs │ │ │ └── IVirtualDesktopNotification.cs │ │ ├── Build10240_0000 │ │ │ ├── .interfaces │ │ │ │ ├── IVirtualDesktop.cs │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ ├── IVirtualDesktopNotificationService.cs │ │ │ │ ├── IVirtualDesktopPinnedApps.cs │ │ │ │ ├── IVirtualDesktopManagerInternal.cs │ │ │ │ ├── IApplicationViewCollection.cs │ │ │ │ ├── IVirtualDesktopNotification.cs │ │ │ │ └── IApplicationView.cs │ │ │ ├── ApplicationView.cs │ │ │ ├── VirtualDesktop.cs │ │ │ ├── ApplicationViewCollection.cs │ │ │ ├── VirtualDesktopPinnedApps.cs │ │ │ ├── .Provider.cs │ │ │ ├── VirtualDesktopManagerInternal.cs │ │ │ └── VirtualDesktopNotificationService.cs │ │ ├── Build20348_0000 │ │ │ ├── .interfaces │ │ │ │ ├── IVirtualDesktopNotificationService.cs │ │ │ │ ├── IVirtualDesktop.cs │ │ │ │ ├── IVirtualDesktopManagerInternal.cs │ │ │ │ └── IVirtualDesktopNotification.cs │ │ │ ├── VirtualDesktop.cs │ │ │ ├── .Provider.cs │ │ │ ├── VirtualDesktopManagerInternal.cs │ │ │ └── VirtualDesktopNotificationService.cs │ │ ├── Build22000_0000 │ │ │ ├── .interfaces │ │ │ │ ├── IVirtualDesktopNotificationService.cs │ │ │ │ ├── IVirtualDesktop.cs │ │ │ │ ├── IVirtualDesktopManagerInternal.cs │ │ │ │ └── IVirtualDesktopNotification.cs │ │ │ ├── VirtualDesktop.cs │ │ │ ├── .Provider.cs │ │ │ ├── VirtualDesktopManagerInternal.cs │ │ │ └── VirtualDesktopNotificationService.cs │ │ ├── Build22621_2215 │ │ │ ├── .interfaces │ │ │ │ ├── IVirtualDesktopNotificationService.cs │ │ │ │ ├── IVirtualDesktop.cs │ │ │ │ ├── IVirtualDesktopManagerInternal.cs │ │ │ │ └── IVirtualDesktopNotification.cs │ │ │ ├── VirtualDesktop.cs │ │ │ ├── .Provider.cs │ │ │ ├── VirtualDesktopManagerInternal.cs │ │ │ └── VirtualDesktopNotificationService.cs │ │ ├── Build26100_0000 │ │ │ ├── .interfaces │ │ │ │ ├── IVirtualDesktopNotificationService.cs │ │ │ │ ├── IVirtualDesktop.cs │ │ │ │ ├── IVirtualDesktopManagerInternal.cs │ │ │ │ └── IVirtualDesktopNotification.cs │ │ │ ├── VirtualDesktop.cs │ │ │ ├── .Provider.cs │ │ │ ├── VirtualDesktopManagerInternal.cs │ │ │ └── VirtualDesktopNotificationService.cs │ │ ├── HResult.cs │ │ ├── CLSID.cs │ │ ├── HString.cs │ │ ├── ComWrapperFactory.cs │ │ ├── ComInterfaceAttribute.cs │ │ ├── Win32.cs │ │ ├── ComInterfaceAssembly.cs │ │ ├── VirtualDesktopProvider.cs │ │ ├── ComWrapperBase.cs │ │ ├── IID.cs │ │ └── ComInterfaceAssemblyBuilder.cs │ ├── VirtualDesktopExtensions.cs │ ├── Utils │ │ ├── TransparentWindow.cs │ │ ├── Disposable.cs │ │ ├── OS.cs │ │ ├── ExplorerRestartListenerWindow.cs │ │ └── RawWindow.cs │ ├── Properties │ │ ├── Configurations.cs │ │ └── AssemblyInfo.cs │ ├── VirtualDesktop.csproj.DotSettings │ ├── VirtualDesktopEventArgs.cs │ ├── VirtualDesktop.system.cs │ ├── VirtualDesktop.csproj │ ├── VirtualDesktop.notification.cs │ └── app.config ├── Directory.Build.props └── VirtualDesktop.sln ├── .github ├── pack.bat ├── workflows │ ├── build.yml │ └── publish.yml └── FUNDING.yml ├── .editorconfig ├── samples ├── VirtualDesktop.Showcase │ ├── App.xaml.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── App.xaml │ ├── VirtualDesktop.Showcase.csproj │ └── MainWindow.xaml └── README.md ├── LICENSE ├── .gitattributes ├── .gitignore └── README.md /src/VirtualDesktop (LocalAppData).lnk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Slion/VirtualDesktop/HEAD/src/VirtualDesktop (LocalAppData).lnk -------------------------------------------------------------------------------- /.github/pack.bat: -------------------------------------------------------------------------------- 1 | dotnet build ..\src\VirtualDesktop.sln -c Release 2 | dotnet pack ..\src\VirtualDesktop.sln -c Release --no-build -o %CD% 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{cs,xaml}] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = crlf 7 | insert_final_newline = true 8 | charset = utf-8-bom 9 | -------------------------------------------------------------------------------- /src/VirtualDesktop.WPF/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | [assembly: ComVisible(false)] 4 | [assembly: Guid("9dd597c6-065a-4764-a96c-1b18c4eded78")] 5 | -------------------------------------------------------------------------------- /src/VirtualDesktop.WinForms/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | [assembly: ComVisible(false)] 4 | [assembly: Guid("da586cec-2ffe-42c5-aeda-56d25984c7ad")] 5 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Proxy/IApplicationViewCollection.cs: -------------------------------------------------------------------------------- 1 | namespace WindowsDesktop.Interop.Proxy; 2 | 3 | [ComInterface] 4 | public interface IApplicationViewCollection 5 | { 6 | IApplicationView GetViewForHwnd(IntPtr hWnd); 7 | } 8 | -------------------------------------------------------------------------------- /samples/VirtualDesktop.Showcase/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace VirtualDesktopShowcase; 7 | 8 | partial class App 9 | { 10 | } 11 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Proxy/IVirtualDesktopNotificationService.cs: -------------------------------------------------------------------------------- 1 | namespace WindowsDesktop.Interop.Proxy; 2 | 3 | [ComInterface] 4 | public interface IVirtualDesktopNotificationService 5 | { 6 | IDisposable Register(IVirtualDesktopNotification proxy); 7 | } 8 | -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | ## Samples 2 | 3 | * [VirtualDesktop.Showcase](VirtualDesktop.Showcase) … A simple application uses the VirtualDesktop library, included in this repository. 4 | * [SylphyHorn](https://github.com/Grabacr07/SylphyHorn) … Utility app for Windows by the same author. 5 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Proxy/IApplicationView.cs: -------------------------------------------------------------------------------- 1 | namespace WindowsDesktop.Interop.Proxy; 2 | 3 | [ComInterface] 4 | public interface IApplicationView 5 | { 6 | IntPtr GetThumbnailWindow(); 7 | 8 | string GetAppUserModelId(); 9 | 10 | Guid GetVirtualDesktopId(); 11 | } 12 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Proxy/IVirtualDesktop.cs: -------------------------------------------------------------------------------- 1 | namespace WindowsDesktop.Interop.Proxy; 2 | 3 | [ComInterface] 4 | public interface IVirtualDesktop 5 | { 6 | bool IsViewVisible(IntPtr hWnd); 7 | 8 | Guid GetID(); 9 | 10 | string GetName(); 11 | 12 | string GetWallpaperPath(); 13 | } 14 | -------------------------------------------------------------------------------- /samples/VirtualDesktop.Showcase/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Windows; 3 | 4 | [assembly: ComVisible(false)] 5 | [assembly: Guid("5B4544B8-3EF0-4E9F-8D60-DD605AD99725")] 6 | [assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] 7 | -------------------------------------------------------------------------------- /src/VirtualDesktop/VirtualDesktopExtensions.cs: -------------------------------------------------------------------------------- 1 | using WindowsDesktop.Interop; 2 | using WindowsDesktop.Interop.Proxy; 3 | 4 | namespace WindowsDesktop; 5 | 6 | public static class VirtualDesktopExtensions 7 | { 8 | internal static VirtualDesktop ToVirtualDesktop(this IVirtualDesktop desktop) 9 | => VirtualDesktop.FromComObject(desktop); 10 | } 11 | -------------------------------------------------------------------------------- /samples/VirtualDesktop.Showcase/App.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Proxy/IVirtualDesktopPinnedApps.cs: -------------------------------------------------------------------------------- 1 | namespace WindowsDesktop.Interop.Proxy; 2 | 3 | [ComInterface] 4 | public interface IVirtualDesktopPinnedApps 5 | { 6 | bool IsAppIdPinned(string appId); 7 | 8 | void PinAppID(string appId); 9 | 10 | void UnpinAppID(string appId); 11 | 12 | bool IsViewPinned(IntPtr hWnd); 13 | 14 | void PinView(IntPtr hWnd); 15 | 16 | void UnpinView(IntPtr hWnd); 17 | } 18 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build10240_0000/.interfaces/IVirtualDesktop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace WindowsDesktop.Interop.Build10240 5 | { 6 | [ComImport] 7 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 8 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 9 | public interface IVirtualDesktop 10 | { 11 | bool IsViewVisible(IApplicationView view); 12 | 13 | Guid GetID(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build10240_0000/.interfaces/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("VirtualDesktop.{OS_BUILD}.generated")] 5 | [assembly: AssemblyCompany("grabacr.net")] 6 | [assembly: AssemblyProduct("VirtualDesktop")] 7 | [assembly: AssemblyDescription("COM interface definitions for virtual desktop on Windows 11 build {VERSION}.")] 8 | [assembly: AssemblyCopyright("Copyright © 2022 Manato KAMEYA")] 9 | 10 | [assembly: AssemblyVersion("{ASSEMBLY_VERSION}")] 11 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Utils/TransparentWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Interop; 5 | 6 | namespace WindowsDesktop.Utils; 7 | 8 | internal class TransparentWindow : RawWindow 9 | { 10 | public override void Show() 11 | { 12 | var parameters = new HwndSourceParameters(this.Name) 13 | { 14 | Width = 1, 15 | Height = 1, 16 | WindowStyle = 0x800000, 17 | }; 18 | 19 | this.Show(parameters); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build10240_0000/.interfaces/IVirtualDesktopNotificationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace WindowsDesktop.Interop.Build10240 5 | { 6 | [ComImport] 7 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 8 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 9 | public interface IVirtualDesktopNotificationService 10 | { 11 | uint Register(IVirtualDesktopNotification pNotification); 12 | 13 | void Unregister(uint dwCookie); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build20348_0000/.interfaces/IVirtualDesktopNotificationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace WindowsDesktop.Interop.Build20348 5 | { 6 | [ComImport] 7 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 8 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 9 | public interface IVirtualDesktopNotificationService 10 | { 11 | uint Register(IVirtualDesktopNotification pNotification); 12 | 13 | void Unregister(uint dwCookie); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22000_0000/.interfaces/IVirtualDesktopNotificationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace WindowsDesktop.Interop.Build22000 5 | { 6 | [ComImport] 7 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 8 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 9 | public interface IVirtualDesktopNotificationService 10 | { 11 | uint Register(IVirtualDesktopNotification pNotification); 12 | 13 | void Unregister(uint dwCookie); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22621_2215/.interfaces/IVirtualDesktopNotificationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace WindowsDesktop.Interop.Build22621 5 | { 6 | [ComImport] 7 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 8 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 9 | public interface IVirtualDesktopNotificationService 10 | { 11 | uint Register(IVirtualDesktopNotification pNotification); 12 | 13 | void Unregister(uint dwCookie); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build26100_0000/.interfaces/IVirtualDesktopNotificationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace WindowsDesktop.Interop.Build26100 5 | { 6 | [ComImport] 7 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 8 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 9 | public interface IVirtualDesktopNotificationService 10 | { 11 | uint Register(IVirtualDesktopNotification pNotification); 12 | 13 | void Unregister(uint dwCookie); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build20348_0000/.interfaces/IVirtualDesktop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using WindowsDesktop.Interop.Build10240; 4 | 5 | namespace WindowsDesktop.Interop.Build20348 6 | { 7 | [ComImport] 8 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 9 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 10 | public interface IVirtualDesktop 11 | { 12 | bool IsViewVisible(IApplicationView view); 13 | 14 | Guid GetID(); 15 | 16 | IntPtr GetMonitor(IntPtr monitor); 17 | 18 | HString GetName(); 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Utils/Disposable.cs: -------------------------------------------------------------------------------- 1 | namespace WindowsDesktop.Utils 2 | { 3 | internal class Disposable 4 | { 5 | public static IDisposable Create(Action dispose) 6 | { 7 | return new AnonymousDisposable(dispose); 8 | } 9 | 10 | private class AnonymousDisposable : IDisposable 11 | { 12 | private bool _isDisposed; 13 | private readonly Action _dispose; 14 | 15 | public AnonymousDisposable(Action dispose) 16 | { 17 | this._dispose = dispose; 18 | } 19 | 20 | public void Dispose() 21 | { 22 | if (this._isDisposed) return; 23 | 24 | this._isDisposed = true; 25 | this._dispose(); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22000_0000/.interfaces/IVirtualDesktop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using WindowsDesktop.Interop.Build10240; 4 | 5 | namespace WindowsDesktop.Interop.Build22000 6 | { 7 | [ComImport] 8 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 9 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 10 | public interface IVirtualDesktop 11 | { 12 | bool IsViewVisible(IApplicationView view); 13 | 14 | Guid GetID(); 15 | 16 | IntPtr Proc5(); 17 | 18 | HString GetName(); 19 | 20 | HString GetWallpaperPath(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /samples/VirtualDesktop.Showcase/VirtualDesktop.Showcase.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WinExe 5 | net8.0-windows10.0.19041.0;net7.0-windows10.0.19041.0;net6.0-windows10.0.19041.0 6 | true 7 | enable 8 | VirtualDesktopShowcase 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22621_2215/.interfaces/IVirtualDesktop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using WindowsDesktop.Interop.Build10240; 4 | 5 | namespace WindowsDesktop.Interop.Build22621 6 | { 7 | [ComImport] 8 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 9 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 10 | public interface IVirtualDesktop 11 | { 12 | bool IsViewVisible(IApplicationView view); 13 | 14 | Guid GetID(); 15 | 16 | HString GetName(); 17 | 18 | HString GetWallpaperPath(); 19 | 20 | bool IsRemote(); 21 | 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build26100_0000/.interfaces/IVirtualDesktop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using WindowsDesktop.Interop.Build10240; 4 | 5 | namespace WindowsDesktop.Interop.Build26100 6 | { 7 | [ComImport] 8 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 9 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 10 | public interface IVirtualDesktop 11 | { 12 | bool IsViewVisible(IApplicationView view); 13 | 14 | Guid GetID(); 15 | 16 | HString GetName(); 17 | 18 | HString GetWallpaperPath(); 19 | 20 | bool IsRemote(); 21 | 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/HResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace WindowsDesktop.Interop; 5 | 6 | internal enum HResult : uint 7 | { 8 | // ReSharper disable IdentifierTypo 9 | TYPE_E_OUTOFBOUNDS = 0x80028CA1, 10 | TYPE_E_ELEMENTNOTFOUND = 0x8002802B, 11 | REGDB_E_CLASSNOTREG = 0x80040154, 12 | RPC_S_SERVER_UNAVAILABLE = 0x800706BA, 13 | // ReSharper restore IdentifierTypo 14 | } 15 | 16 | internal static class HResultExtensions 17 | { 18 | public static bool Match(this Exception ex, params HResult[] hResult) 19 | { 20 | return hResult 21 | .Cast() 22 | .Any(x => (uint)ex.HResult == x); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/CLSID.cs: -------------------------------------------------------------------------------- 1 | namespace WindowsDesktop.Interop; 2 | 3 | // ReSharper disable once InconsistentNaming 4 | internal static class CLSID 5 | { 6 | public static Guid ImmersiveShell { get; } = new("c2f03a33-21f5-47fa-b4bb-156362a2f239"); 7 | 8 | public static Guid VirtualDesktopManager { get; } = new("aa509086-5ca9-4c25-8f95-589d3c07b48a"); 9 | 10 | public static Guid VirtualDesktopManagerInternal { get; } = new("c5e0cdca-7b6e-41b2-9fc4-d93975cc467b"); 11 | 12 | public static Guid VirtualDesktopNotificationService { get; } = new Guid("a501fdec-4a09-464c-ae4e-1b9c21b84918"); 13 | 14 | public static Guid VirtualDesktopPinnedApps { get; } = new("b5a399e7-1c87-46b8-88e9-fc5747b171bd"); 15 | } 16 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build10240_0000/.interfaces/IVirtualDesktopPinnedApps.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace WindowsDesktop.Interop.Build10240 5 | { 6 | [ComImport] 7 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 8 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 9 | public interface IVirtualDesktopPinnedApps 10 | { 11 | bool IsAppIdPinned(string appId); 12 | 13 | void PinAppID(string appId); 14 | 15 | void UnpinAppID(string appId); 16 | 17 | bool IsViewPinned(IApplicationView applicationView); 18 | 19 | void PinView(IApplicationView applicationView); 20 | 21 | void UnpinView(IApplicationView applicationView); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | 10 | env: 11 | DOTNET_VERSION: 8.0.x 12 | BUILD_TARGET: .\src\VirtualDesktop.sln 13 | 14 | 15 | jobs: 16 | build: 17 | name: .NET Build 18 | runs-on: windows-latest 19 | # Don't build for release the publish workflow will do that to 20 | if: "!startsWith(github.event.head_commit.message, 'Release v')" 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | 25 | - name: Use .NET ${{ env.DOTNET_VERSION }} 26 | uses: actions/setup-dotnet@v4 27 | with: 28 | dotnet-version: ${{ env.DOTNET_VERSION }} 29 | 30 | - name: Build 31 | run: dotnet build ${{ env.BUILD_TARGET }} -c Release 32 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build10240_0000/ApplicationView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using WindowsDesktop.Interop.Proxy; 5 | 6 | namespace WindowsDesktop.Interop.Build10240; 7 | 8 | internal class ApplicationView : ComWrapperBase, IApplicationView 9 | { 10 | public ApplicationView(ComInterfaceAssembly assembly, object comObject) 11 | : base(assembly, comObject) 12 | { 13 | } 14 | 15 | public IntPtr GetThumbnailWindow() 16 | => this.InvokeMethod(); 17 | 18 | public string GetAppUserModelId() 19 | => this.InvokeMethod() ?? throw new Exception("Failed to get AppUserModelId."); 20 | 21 | public Guid GetVirtualDesktopId() 22 | => this.InvokeMethod(); 23 | } 24 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/HString.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using WinRT; 3 | 4 | namespace WindowsDesktop.Interop; 5 | 6 | // ## Note 7 | // .NET 5 has removed WinRT support, so HString cannot marshal to System.String. 8 | // Since marshalling with UnmanagedType.HString fails, use IntPtr to get the string via C#/WinRT MarshalString. 9 | // 10 | // see also: https://github.com/microsoft/CsWinRT/blob/master/docs/interop.md 11 | 12 | [StructLayout(LayoutKind.Sequential)] 13 | public struct HString 14 | { 15 | private readonly IntPtr _abi; 16 | 17 | internal HString(string str) 18 | { 19 | this._abi = MarshalString.GetAbi(MarshalString.CreateMarshaler(str)); 20 | } 21 | 22 | public static implicit operator string(HString hStr) 23 | => MarshalString.FromAbi(hStr._abi); 24 | } 25 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build10240_0000/VirtualDesktop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using WindowsDesktop.Interop.Proxy; 5 | 6 | namespace WindowsDesktop.Interop.Build10240; 7 | 8 | internal class VirtualDesktop : ComWrapperBase, IVirtualDesktop 9 | { 10 | private Guid? _id; 11 | 12 | public VirtualDesktop(ComInterfaceAssembly assembly, object comObject) 13 | : base(assembly, comObject) 14 | { 15 | } 16 | 17 | public bool IsViewVisible(IntPtr hWnd) 18 | => this.InvokeMethod(Args(hWnd)); 19 | 20 | public Guid GetID() 21 | => this._id ?? (Guid)(this._id = this.InvokeMethod()); 22 | 23 | public string GetName() 24 | => ""; 25 | 26 | public string GetWallpaperPath() 27 | => ""; 28 | } 29 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build10240_0000/ApplicationViewCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using WindowsDesktop.Interop.Proxy; 3 | 4 | namespace WindowsDesktop.Interop.Build10240; 5 | 6 | internal class ApplicationViewCollection : ComWrapperBase, IApplicationViewCollection 7 | { 8 | public ApplicationViewCollection(ComInterfaceAssembly assembly) 9 | : base(assembly) 10 | { 11 | } 12 | 13 | public ApplicationView GetViewForHwnd(IntPtr hWnd) 14 | { 15 | var view = this.InvokeMethod(Args(hWnd)) 16 | ?? new ArgumentException("ApplicationView is not found.", nameof(hWnd)); 17 | 18 | return new ApplicationView(this.ComInterfaceAssembly, view); 19 | } 20 | 21 | IApplicationView IApplicationViewCollection.GetViewForHwnd(IntPtr hWnd) 22 | => this.GetViewForHwnd(hWnd); 23 | } 24 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22000_0000/VirtualDesktop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using WindowsDesktop.Interop.Proxy; 5 | 6 | namespace WindowsDesktop.Interop.Build22000; 7 | 8 | internal class VirtualDesktop : ComWrapperBase, IVirtualDesktop 9 | { 10 | private Guid? _id; 11 | 12 | public VirtualDesktop(ComInterfaceAssembly assembly, object comObject) 13 | : base(assembly, comObject) 14 | { 15 | } 16 | 17 | public bool IsViewVisible(IntPtr hWnd) 18 | => this.InvokeMethod(Args(hWnd)); 19 | 20 | public Guid GetID() 21 | => this._id ?? (Guid)(this._id = this.InvokeMethod()); 22 | 23 | public string GetName() 24 | => this.InvokeMethod(); 25 | 26 | public string GetWallpaperPath() 27 | => this.InvokeMethod(); 28 | } 29 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22621_2215/VirtualDesktop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using WindowsDesktop.Interop.Proxy; 5 | 6 | namespace WindowsDesktop.Interop.Build22621; 7 | 8 | internal class VirtualDesktop : ComWrapperBase, IVirtualDesktop 9 | { 10 | private Guid? _id; 11 | 12 | public VirtualDesktop(ComInterfaceAssembly assembly, object comObject) 13 | : base(assembly, comObject) 14 | { 15 | } 16 | 17 | public bool IsViewVisible(IntPtr hWnd) 18 | => this.InvokeMethod(Args(hWnd)); 19 | 20 | public Guid GetID() 21 | => this._id ?? (Guid)(this._id = this.InvokeMethod()); 22 | 23 | public string GetName() 24 | => this.InvokeMethod(); 25 | 26 | public string GetWallpaperPath() 27 | => this.InvokeMethod(); 28 | 29 | public bool IsRemote() 30 | => this.InvokeMethod(); 31 | } 32 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build26100_0000/VirtualDesktop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using WindowsDesktop.Interop.Proxy; 5 | 6 | namespace WindowsDesktop.Interop.Build26100; 7 | 8 | internal class VirtualDesktop : ComWrapperBase, IVirtualDesktop 9 | { 10 | private Guid? _id; 11 | 12 | public VirtualDesktop(ComInterfaceAssembly assembly, object comObject) 13 | : base(assembly, comObject) 14 | { 15 | } 16 | 17 | public bool IsViewVisible(IntPtr hWnd) 18 | => this.InvokeMethod(Args(hWnd)); 19 | 20 | public Guid GetID() 21 | => this._id ?? (Guid)(this._id = this.InvokeMethod()); 22 | 23 | public string GetName() 24 | => this.InvokeMethod(); 25 | 26 | public string GetWallpaperPath() 27 | => this.InvokeMethod(); 28 | 29 | public bool IsRemote() 30 | => this.InvokeMethod(); 31 | } 32 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [Slion] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build20348_0000/VirtualDesktop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using WindowsDesktop.Interop.Proxy; 5 | 6 | namespace WindowsDesktop.Interop.Build20348; 7 | 8 | internal class VirtualDesktop : ComWrapperBase, IVirtualDesktop 9 | { 10 | private Guid? _id; 11 | 12 | public VirtualDesktop(ComInterfaceAssembly assembly, object comObject) 13 | : base(assembly, comObject) 14 | { 15 | } 16 | 17 | public bool IsViewVisible(IntPtr hWnd) 18 | => this.InvokeMethod(Args(hWnd)); 19 | 20 | public Guid GetID() 21 | => this._id ?? (Guid)(this._id = this.InvokeMethod()); 22 | 23 | public IntPtr GetMonitor(IntPtr monitor) 24 | => this.InvokeMethod(Args(monitor)); 25 | 26 | public string GetName() 27 | => this.InvokeMethod(); 28 | 29 | public string GetWallpaperPath() 30 | { 31 | return ""; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/ComWrapperFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using WindowsDesktop.Interop.Proxy; 5 | 6 | namespace WindowsDesktop.Interop; 7 | 8 | internal class ComWrapperFactory 9 | { 10 | public Func> ApplicationView { get; } 11 | 12 | public Func> ApplicationViewFromHwnd { get; } 13 | 14 | public Func> VirtualDesktop { get; } 15 | 16 | public ComWrapperFactory( 17 | Func> applicationView, 18 | Func> applicationViewFromHwnd, 19 | Func> virtualDesktop) 20 | { 21 | this.ApplicationView = applicationView; 22 | this.ApplicationViewFromHwnd = applicationViewFromHwnd; 23 | this.VirtualDesktop = virtualDesktop; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Utils/OS.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace WindowsDesktop.Utils 9 | { 10 | internal class OS 11 | { 12 | /// 13 | /// Return the OS Build number such as: 22621.2215 14 | /// 15 | /// 16 | public static Version Build 17 | { 18 | get 19 | { 20 | Version v = Environment.OSVersion.Version; 21 | Version actual = new(v.Major, v.Minor, v.Build, int.Parse(Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion").GetValue("UBR").ToString())); 22 | return actual; 23 | } 24 | } 25 | 26 | /// 27 | /// 28 | /// 29 | public static readonly String VersionPrefix = "10.0."; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Utils/ExplorerRestartListenerWindow.cs: -------------------------------------------------------------------------------- 1 | using WindowsDesktop.Interop; 2 | 3 | namespace WindowsDesktop.Utils; 4 | 5 | internal class ExplorerRestartListenerWindow : TransparentWindow 6 | { 7 | private uint _explorerRestartedMessage; 8 | private readonly Action _action; 9 | 10 | public ExplorerRestartListenerWindow(Action action) 11 | { 12 | this.Name = nameof(ExplorerRestartListenerWindow); 13 | this._action = action; 14 | } 15 | 16 | public override void Show() 17 | { 18 | base.Show(); 19 | this._explorerRestartedMessage = PInvoke.RegisterWindowMessage("TaskbarCreated"); 20 | } 21 | 22 | protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 23 | { 24 | if (msg == this._explorerRestartedMessage) 25 | { 26 | this._action(); 27 | return IntPtr.Zero; 28 | } 29 | 30 | return base.WndProc(hwnd, msg, wParam, lParam, ref handled); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/ComInterfaceAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace WindowsDesktop.Interop; 7 | 8 | [AttributeUsage(AttributeTargets.Interface)] 9 | internal class ComInterfaceAttribute : Attribute 10 | { 11 | public string? InterfaceName { get; } 12 | 13 | public ComInterfaceAttribute() 14 | { 15 | } 16 | 17 | public ComInterfaceAttribute(string interfaceName) 18 | { 19 | this.InterfaceName = interfaceName; 20 | } 21 | } 22 | 23 | internal static class ComInterfaceAttributeExtensions 24 | { 25 | /// 26 | /// Gets COM interface name if specific type has '' attribute. 27 | /// 28 | public static string? GetComInterfaceNameIfWrapper(this Type type) 29 | { 30 | var attr = type.GetCustomAttribute(); 31 | if (attr == null) return null; 32 | 33 | return attr.InterfaceName ?? type.Name; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6.9.2 4 | Grabacr07, maciej-makowski, mzomparelli, Slion 5 | 6 | v6.9.2 7 | Documentation update 8 | 9 | v6.9.1 10 | Description update 11 | 12 | v6.9.0 13 | Minor documentation fix 14 | 15 | v6.8.0 16 | Documentation updates 17 | 18 | v6.7.0 19 | Support OS Build 26100.0000 20 | Support single file app 21 | 22 | v6.6.0 23 | Support OS Build 22631.3155 24 | Support OS Build 22621.3155 25 | 26 | v6.5.0 27 | Support for .NET 8.0 28 | 29 | v6.4.0 30 | Fix crash when changing desktop 31 | 32 | v6.3.0 33 | Desktop move support 34 | 35 | v6.2.1 36 | Add mzomparelli to list of authors 37 | 38 | v6.2.0 39 | Better OS version resolution 40 | 41 | v6.1.0 42 | Support OS Build 20348.1906 43 | 44 | v6.0.0 45 | Support OS Build 22621.2215 46 | 47 | v5.2.0 48 | Support for .NET 7.0 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/VirtualDesktop.WinForms/FormExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | namespace WindowsDesktop; 7 | 8 | public static class FormExtensions 9 | { 10 | /// 11 | /// Determines whether this form is on the current virtual desktop. 12 | /// 13 | public static bool IsCurrentVirtualDesktop(this Form form) 14 | { 15 | return VirtualDesktop.IsCurrentVirtualDesktop(form.Handle); 16 | } 17 | 18 | /// 19 | /// Moves a form to the specified virtual desktop. 20 | /// 21 | public static void MoveToDesktop(this Form form, VirtualDesktop virtualDesktop) 22 | { 23 | VirtualDesktop.MoveToDesktop(form.Handle, virtualDesktop); 24 | } 25 | 26 | /// 27 | /// Returns the virtual desktop this form is located on. 28 | /// 29 | public static VirtualDesktop? GetCurrentDesktop(this Form form) 30 | { 31 | return VirtualDesktop.FromHwnd(form.Handle); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build10240_0000/.interfaces/IVirtualDesktopManagerInternal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace WindowsDesktop.Interop.Build10240 5 | { 6 | [ComImport] 7 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 8 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 9 | public interface IVirtualDesktopManagerInternal 10 | { 11 | int GetCount(); 12 | 13 | void MoveViewToDesktop(IApplicationView pView, IVirtualDesktop desktop); 14 | 15 | bool CanViewMoveDesktops(IApplicationView pView); 16 | 17 | IVirtualDesktop GetCurrentDesktop(); 18 | 19 | IObjectArray GetDesktops(); 20 | 21 | IVirtualDesktop GetAdjacentDesktop(IVirtualDesktop pDesktopReference, int uDirection); 22 | 23 | void SwitchDesktop(IVirtualDesktop desktop); 24 | 25 | IVirtualDesktop CreateDesktop(); 26 | 27 | void RemoveDesktop(IVirtualDesktop pRemove, IVirtualDesktop pFallbackDesktop); 28 | 29 | IVirtualDesktop FindDesktop(in Guid desktopId); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build10240_0000/.interfaces/IApplicationViewCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace WindowsDesktop.Interop.Build10240 5 | { 6 | [ComImport] 7 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 8 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 9 | public interface IApplicationViewCollection 10 | { 11 | IObjectArray GetViews(); 12 | 13 | IObjectArray GetViewsByZOrder(); 14 | 15 | IObjectArray GetViewsByAppUserModelId(string id); 16 | 17 | IApplicationView GetViewForHwnd(IntPtr hwnd); 18 | 19 | IApplicationView GetViewForApplication(object application); 20 | 21 | IApplicationView GetViewForAppUserModelId(string id); 22 | 23 | IntPtr GetViewInFocus(); 24 | 25 | void RefreshCollection(); 26 | 27 | int RegisterForApplicationViewChanges(object listener); 28 | 29 | int RegisterForApplicationViewPositionChanges(object listener); 30 | 31 | void UnregisterForApplicationViewChanges(int cookie); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Proxy/IVirtualDesktopManagerInternal.cs: -------------------------------------------------------------------------------- 1 | namespace WindowsDesktop.Interop.Proxy; 2 | 3 | public enum AdjacentDesktop 4 | { 5 | LeftDirection = 3, 6 | 7 | RightDirection = 4, 8 | } 9 | 10 | [ComInterface] 11 | public interface IVirtualDesktopManagerInternal 12 | { 13 | IEnumerable GetDesktops(); 14 | 15 | IVirtualDesktop GetCurrentDesktop(); 16 | 17 | IVirtualDesktop GetAdjacentDesktop(IVirtualDesktop pDesktopReference, AdjacentDesktop uDirection); 18 | 19 | IVirtualDesktop FindDesktop(Guid desktopId); 20 | 21 | IVirtualDesktop CreateDesktop(); 22 | 23 | void MoveDesktop(IVirtualDesktop pMove, int nIndex); 24 | 25 | void RemoveDesktop(IVirtualDesktop pRemove, IVirtualDesktop pFallbackDesktop); 26 | 27 | void SwitchDesktop(IVirtualDesktop desktop); 28 | 29 | void MoveViewToDesktop(IntPtr hWnd, IVirtualDesktop desktop); 30 | 31 | void SetDesktopName(IVirtualDesktop desktop, string name); 32 | 33 | void SetDesktopWallpaper(IVirtualDesktop desktop, string path); 34 | 35 | void UpdateWallpaperPathForAllDesktops(string path); 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Manato KAMEYA 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Proxy/IVirtualDesktopNotification.cs: -------------------------------------------------------------------------------- 1 | namespace WindowsDesktop.Interop.Proxy; 2 | 3 | [ComInterface] 4 | public interface IVirtualDesktopNotification 5 | { 6 | void VirtualDesktopCreated(IVirtualDesktop pDesktop); 7 | 8 | void VirtualDesktopDestroyBegin(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 9 | 10 | void VirtualDesktopDestroyFailed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 11 | 12 | void VirtualDesktopDestroyed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 13 | 14 | void VirtualDesktopIsPerMonitorChanged(int i); 15 | 16 | void VirtualDesktopMoved(IVirtualDesktop pDesktop, int nIndexFrom, int nIndexTo); 17 | 18 | void VirtualDesktopRenamed(IVirtualDesktop pDesktop, string chName); 19 | 20 | void ViewVirtualDesktopChanged(IApplicationView pView); 21 | 22 | void CurrentVirtualDesktopChanged(IVirtualDesktop pDesktopOld, IVirtualDesktop pDesktopNew); 23 | 24 | void VirtualDesktopWallpaperChanged(IVirtualDesktop pDesktop, string chPath); 25 | 26 | void VirtualDesktopSwitched(IVirtualDesktop pDesktop); 27 | 28 | void RemoteVirtualDesktopConnected(IVirtualDesktop pDesktop); 29 | } 30 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Utils/RawWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Interop; 3 | using System.Windows.Threading; 4 | using WindowsDesktop.Interop; 5 | 6 | namespace WindowsDesktop.Utils; 7 | 8 | internal abstract class RawWindow 9 | { 10 | private HwndSource? _source; 11 | 12 | public IntPtr Handle 13 | => this._source?.Handle ?? IntPtr.Zero; 14 | public string? Name { get; init; } 15 | 16 | public virtual void Show() 17 | { 18 | this.Show(new HwndSourceParameters(this.Name)); 19 | } 20 | 21 | protected void Show(HwndSourceParameters parameters) 22 | { 23 | this._source = new HwndSource(parameters); 24 | this._source.AddHook(this.WndProc); 25 | } 26 | 27 | public virtual void Close() 28 | { 29 | this._source?.RemoveHook(this.WndProc); 30 | // Source could have been created on a different thread, which means we 31 | // have to Dispose of it on the UI thread or it will crash. 32 | this._source?.Dispatcher?.BeginInvoke(DispatcherPriority.Send, () => this._source?.Dispose()); 33 | this._source = null; 34 | 35 | PInvoke.CloseWindow(this.Handle); 36 | } 37 | 38 | protected virtual IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 39 | { 40 | return IntPtr.Zero; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build10240_0000/VirtualDesktopPinnedApps.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using WindowsDesktop.Interop.Proxy; 5 | 6 | namespace WindowsDesktop.Interop.Build10240; 7 | 8 | internal class VirtualDesktopPinnedApps : ComWrapperBase, IVirtualDesktopPinnedApps 9 | { 10 | private readonly ComWrapperFactory _factory; 11 | 12 | public VirtualDesktopPinnedApps(ComInterfaceAssembly assembly, ComWrapperFactory factory) 13 | : base(assembly, CLSID.VirtualDesktopPinnedApps) 14 | { 15 | this._factory = factory; 16 | } 17 | 18 | public bool IsViewPinned(IntPtr hWnd) 19 | => this.InvokeMethod(this.ArgsWithApplicationView(hWnd)); 20 | 21 | public void PinView(IntPtr hWnd) 22 | => this.InvokeMethod(this.ArgsWithApplicationView(hWnd)); 23 | 24 | public void UnpinView(IntPtr hWnd) 25 | => this.InvokeMethod(this.ArgsWithApplicationView(hWnd)); 26 | 27 | public bool IsAppIdPinned(string appId) 28 | => this.InvokeMethod(Args(appId)); 29 | 30 | public void PinAppID(string appId) 31 | => this.InvokeMethod(Args(appId)); 32 | 33 | public void UnpinAppID(string appId) 34 | => this.InvokeMethod(Args(appId)); 35 | 36 | private object?[] ArgsWithApplicationView(IntPtr hWnd) 37 | => Args(this._factory.ApplicationViewFromHwnd(hWnd).ComObject); 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | env: 8 | DOTNET_VERSION: 8.0.x 9 | BUILD_TARGET: .\src\VirtualDesktop.sln 10 | 11 | jobs: 12 | build: 13 | name: .NET Build 14 | runs-on: windows-latest 15 | if: "startsWith(github.event.head_commit.message, 'Release v')" 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: Use .NET ${{ env.DOTNET_VERSION }} 21 | uses: actions/setup-dotnet@v4 22 | with: 23 | dotnet-version: ${{ env.DOTNET_VERSION }} 24 | 25 | - name: Build 26 | run: dotnet build ${{ env.BUILD_TARGET }} -c Release 27 | 28 | - name: Pack 29 | run: dotnet pack ${{ env.BUILD_TARGET }} -c Release --no-build 30 | 31 | - name: Upload artifacts 32 | uses: actions/upload-artifact@v4 33 | with: 34 | name: nuget-packages 35 | path: "**/*.nupkg" 36 | 37 | 38 | publish: 39 | name: Publish to NuGet 40 | runs-on: windows-latest 41 | needs: build 42 | if: "startsWith(github.event.head_commit.message, 'Release v')" 43 | 44 | steps: 45 | - name: Use NuGet 46 | uses: NuGet/setup-nuget@v2 47 | with: 48 | nuget-version: latest 49 | nuget-api-key: ${{ secrets.NUGET_API_KEY }} 50 | 51 | - name: Fetch artifacts 52 | uses: actions/download-artifact@v4 53 | with: 54 | name: nuget-packages 55 | 56 | - name: Publish 57 | run: nuget push **\*.nupkg -Source https://api.nuget.org/v3/index.json 58 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build20348_0000/.interfaces/IVirtualDesktopManagerInternal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using WindowsDesktop.Interop.Build10240; 4 | 5 | namespace WindowsDesktop.Interop.Build20348 6 | { 7 | [ComImport] 8 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 9 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 10 | public interface IVirtualDesktopManagerInternal 11 | { 12 | int GetCount(IntPtr hWndOrMon); 13 | 14 | void MoveViewToDesktop(IApplicationView pView, IVirtualDesktop desktop); 15 | 16 | bool CanViewMoveDesktops(IApplicationView pView); 17 | 18 | IVirtualDesktop GetCurrentDesktop(IntPtr hWndOrMon); 19 | 20 | IObjectArray GetDesktops(IntPtr hWndOrMon); 21 | 22 | IVirtualDesktop GetAdjacentDesktop(IVirtualDesktop pDesktopReference, int uDirection); 23 | 24 | void SwitchDesktop(IntPtr hWndOrMon, IVirtualDesktop desktop); 25 | 26 | IVirtualDesktop CreateDesktop(IntPtr hWndOrMon); 27 | 28 | void RemoveDesktop(IVirtualDesktop pRemove, IVirtualDesktop pFallbackDesktop); 29 | 30 | IVirtualDesktop FindDesktop(in Guid desktopId); 31 | 32 | void GetDesktopSwitchIncludeExcludeViews(IVirtualDesktop desktop, out IObjectArray o1, out IObjectArray o2); 33 | 34 | void SetDesktopName(IVirtualDesktop desktop, HString name); 35 | 36 | void CopyDesktopState(IApplicationView pView0, IApplicationView pView1); 37 | 38 | bool GetDesktopIsPerMonitor(); 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Win32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace WindowsDesktop.Interop; 7 | 8 | [ComImport] 9 | [Guid("6d5140c1-7436-11ce-8034-00aa006009fa")] 10 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 11 | internal interface IServiceProvider 12 | { 13 | [return: MarshalAs(UnmanagedType.IUnknown)] 14 | object QueryService(in Guid guidService, in Guid riid); 15 | } 16 | 17 | [ComImport] 18 | [Guid("92ca9dcd-5622-4bba-a805-5e9f541bd8c9")] 19 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 20 | public interface IObjectArray 21 | { 22 | uint GetCount(); 23 | 24 | [return: MarshalAs(UnmanagedType.Interface)] 25 | object GetAt(uint iIndex, in Guid riid); 26 | } 27 | 28 | [ComImport] 29 | [Guid("a5cd92ff-29be-454c-8d04-d82879fb3f1b")] 30 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 31 | internal interface IVirtualDesktopManager 32 | { 33 | bool IsWindowOnCurrentVirtualDesktop(IntPtr topLevelWindow); 34 | 35 | Guid GetWindowDesktopId(IntPtr topLevelWindow); 36 | 37 | void MoveWindowToDesktop(IntPtr topLevelWindow, ref Guid desktopId); 38 | } 39 | 40 | internal static class PInvoke 41 | { 42 | [DllImport("user32.dll")] 43 | public static extern bool CloseWindow(IntPtr hWnd); 44 | 45 | [DllImport("user32.dll")] 46 | public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); 47 | 48 | [DllImport("user32.dll", CharSet = CharSet.Unicode)] 49 | public static extern uint RegisterWindowMessage(string lpProcName); 50 | } 51 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Properties/Configurations.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace WindowsDesktop.Properties; 4 | 5 | public record VirtualDesktopConfiguration : VirtualDesktopCompilerConfiguration 6 | { 7 | } 8 | 9 | public record VirtualDesktopCompilerConfiguration 10 | { 11 | /// 12 | /// Gets or sets a value indicating whether the compiled assembly should be saved or not. 13 | /// 14 | /// 15 | /// This library uses the non-public Windows API.
16 | /// Since the ID of the COM Interface may differ depending on the build of Windows, it works by checking the ID in that environment at runtime and generating the assembly.
17 | ///
18 | /// Here you can set whether to save the assembly.
19 | /// Saving will improve the speed of the next launch. 20 | ///
21 | /// 22 | /// A value indicating whether the compiled assembly should be saved or not. Default is . 23 | /// 24 | public bool SaveCompiledAssembly { get; init; } = true; 25 | 26 | /// 27 | /// Gets or sets a value indicating the directory where the assembly will be saved. 28 | /// 29 | /// 30 | /// See property for details. 31 | /// 32 | /// 33 | /// A value indicating whether the compiled assembly should be saved or not. Default is %LocalAppData%. 34 | /// 35 | public DirectoryInfo CompiledAssemblySaveDirectory { get; init; } = new(Path.Combine(LocationInfo.LocalAppData.FullName, "assemblies")); 36 | } 37 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22000_0000/.interfaces/IVirtualDesktopManagerInternal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using WindowsDesktop.Interop.Build10240; 4 | 5 | namespace WindowsDesktop.Interop.Build22000 6 | { 7 | [ComImport] 8 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 9 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 10 | public interface IVirtualDesktopManagerInternal 11 | { 12 | int GetCount(IntPtr hWndOrMon); 13 | 14 | void MoveViewToDesktop(IApplicationView pView, IVirtualDesktop desktop); 15 | 16 | bool CanViewMoveDesktops(IApplicationView pView); 17 | 18 | IVirtualDesktop GetCurrentDesktop(IntPtr hWndOrMon); 19 | 20 | IObjectArray GetAllCurrentDesktops(); 21 | 22 | IObjectArray GetDesktops(IntPtr hWndOrMon); 23 | 24 | IVirtualDesktop GetAdjacentDesktop(IVirtualDesktop pDesktopReference, int uDirection); 25 | 26 | void SwitchDesktop(IntPtr hWndOrMon, IVirtualDesktop desktop); 27 | 28 | IVirtualDesktop CreateDesktop(IntPtr hWndOrMon); 29 | 30 | void MoveDesktop(IVirtualDesktop desktop, IntPtr hWndOrMon, int nIndex); 31 | 32 | void RemoveDesktop(IVirtualDesktop pRemove, IVirtualDesktop pFallbackDesktop); 33 | 34 | IVirtualDesktop FindDesktop(in Guid desktopId); 35 | 36 | void GetDesktopSwitchIncludeExcludeViews(IVirtualDesktop desktop, out IObjectArray o1, out IObjectArray o2); 37 | 38 | void SetDesktopName(IVirtualDesktop desktop, HString name); 39 | 40 | void SetDesktopWallpaper(IVirtualDesktop desktop, HString path); 41 | 42 | void UpdateWallpaperPathForAllDesktops(HString path); 43 | 44 | void CopyDesktopState(IApplicationView pView0, IApplicationView pView1); 45 | 46 | bool GetDesktopIsPerMonitor(); 47 | 48 | void SetDesktopIsPerMonitor(bool state); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/VirtualDesktop/VirtualDesktop.csproj.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | True 3 | True 4 | True 5 | True 6 | True 7 | True 8 | True 9 | True 10 | True 11 | True -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22621_2215/.interfaces/IVirtualDesktopManagerInternal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using WindowsDesktop.Interop.Build10240; 4 | 5 | namespace WindowsDesktop.Interop.Build22621 6 | { 7 | [ComImport] 8 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 9 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 10 | public interface IVirtualDesktopManagerInternal 11 | { 12 | int GetCount(); 13 | 14 | void MoveViewToDesktop(IApplicationView pView, IVirtualDesktop desktop); 15 | 16 | bool CanViewMoveDesktops(IApplicationView pView); 17 | 18 | IVirtualDesktop GetCurrentDesktop(); 19 | 20 | IObjectArray GetDesktops(); 21 | 22 | IVirtualDesktop GetAdjacentDesktop(IVirtualDesktop pDesktopReference, int uDirection); 23 | 24 | void SwitchDesktop(IVirtualDesktop desktop); 25 | 26 | IVirtualDesktop CreateDesktop(); 27 | 28 | void MoveDesktop(IVirtualDesktop desktop, int nIndex); 29 | 30 | void RemoveDesktop(IVirtualDesktop pRemove, IVirtualDesktop pFallbackDesktop); 31 | 32 | IVirtualDesktop FindDesktop(in Guid desktopId); 33 | 34 | void GetDesktopSwitchIncludeExcludeViews(IVirtualDesktop desktop, out IObjectArray o1, out IObjectArray o2); 35 | 36 | void SetDesktopName(IVirtualDesktop desktop, HString name); 37 | 38 | void SetDesktopWallpaper(IVirtualDesktop desktop, HString path); 39 | 40 | void UpdateWallpaperPathForAllDesktops(HString path); 41 | 42 | void CopyDesktopState(IApplicationView pView0, IApplicationView pView1); 43 | 44 | IVirtualDesktop CreateRemoteDesktop(HString name); 45 | 46 | void SwitchRemoteDesktop(IVirtualDesktop desktop); 47 | 48 | void SwitchDesktopWithAnimation(IVirtualDesktop desktop); 49 | 50 | IVirtualDesktop GetLastActiveDesktop(); 51 | 52 | void WaitForAnimationToComplete(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/ComInterfaceAssembly.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | 7 | namespace WindowsDesktop.Interop 8 | { 9 | internal class ComInterfaceAssembly 10 | { 11 | private readonly Dictionary _knownTypes = new(); 12 | private readonly Assembly _compiledAssembly; 13 | 14 | public DirectoryInfo AssemblyLocation 15 | => new(this._compiledAssembly.Location); 16 | 17 | public ComInterfaceAssembly(Assembly compiledAssembly) 18 | { 19 | this._compiledAssembly = compiledAssembly; 20 | } 21 | 22 | internal Type GetType(string typeName) 23 | { 24 | if (this._knownTypes.TryGetValue(typeName, out var type) == false) 25 | { 26 | type = this._knownTypes[typeName] = this._compiledAssembly 27 | .GetTypes() 28 | .Single(x => x.Name.Split('.').Last() == typeName); 29 | } 30 | 31 | return type; 32 | } 33 | 34 | internal (Type type, object instance) CreateInstance(string comInterfaceName) 35 | { 36 | var type = this.GetType(comInterfaceName); 37 | var instance = CreateInstance(type, null); 38 | 39 | return (type, instance); 40 | } 41 | 42 | internal (Type type, object instance) CreateInstance(string comInterfaceName, Guid clsid) 43 | { 44 | var type = this.GetType(comInterfaceName); 45 | var instance = CreateInstance(type, clsid); 46 | 47 | return (type, instance); 48 | } 49 | 50 | private static object CreateInstance(Type type, Guid? guidService) 51 | { 52 | var shellType = Type.GetTypeFromCLSID(CLSID.ImmersiveShell) 53 | ?? throw new Exception($"Type of ImmersiveShell ('{CLSID.ImmersiveShell}') is not found."); 54 | var shell = Activator.CreateInstance(shellType) as IServiceProvider 55 | ?? throw new Exception("Failed to create an instance of ImmersiveShell."); 56 | 57 | return shell.QueryService(guidService ?? type.GUID, type.GUID); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build26100_0000/.interfaces/IVirtualDesktopManagerInternal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using WindowsDesktop.Interop.Build10240; 4 | 5 | namespace WindowsDesktop.Interop.Build26100 6 | { 7 | [ComImport] 8 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 9 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 10 | public interface IVirtualDesktopManagerInternal 11 | { 12 | int GetCount(); 13 | 14 | void MoveViewToDesktop(IApplicationView pView, IVirtualDesktop desktop); 15 | 16 | bool CanViewMoveDesktops(IApplicationView pView); 17 | 18 | IVirtualDesktop GetCurrentDesktop(); 19 | 20 | IObjectArray GetDesktops(); 21 | 22 | IVirtualDesktop GetAdjacentDesktop(IVirtualDesktop pDesktopReference, int uDirection); 23 | 24 | void SwitchDesktop(IVirtualDesktop desktop); 25 | 26 | void SwitchDesktopAndMoveForegroundView(IVirtualDesktop desktop); 27 | 28 | IVirtualDesktop CreateDesktop(); 29 | 30 | void MoveDesktop(IVirtualDesktop desktop, int nIndex); 31 | 32 | void RemoveDesktop(IVirtualDesktop pRemove, IVirtualDesktop pFallbackDesktop); 33 | 34 | IVirtualDesktop FindDesktop(in Guid desktopId); 35 | 36 | void GetDesktopSwitchIncludeExcludeViews(IVirtualDesktop desktop, out IObjectArray o1, out IObjectArray o2); 37 | 38 | void SetDesktopName(IVirtualDesktop desktop, HString name); 39 | 40 | void SetDesktopWallpaper(IVirtualDesktop desktop, HString path); 41 | 42 | void UpdateWallpaperPathForAllDesktops(HString path); 43 | 44 | void CopyDesktopState(IApplicationView pView0, IApplicationView pView1); 45 | 46 | IVirtualDesktop CreateRemoteDesktop(HString name); 47 | 48 | void SwitchRemoteDesktop(IVirtualDesktop desktop); 49 | 50 | void SwitchDesktopWithAnimation(IVirtualDesktop desktop); 51 | 52 | IVirtualDesktop GetLastActiveDesktop(); 53 | 54 | void WaitForAnimationToComplete(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: ComVisible(false)] 6 | [assembly: Guid("ab848ecd-76aa-41c0-b63d-86a8591b25aa")] 7 | 8 | namespace WindowsDesktop.Properties; 9 | 10 | internal static class AssemblyInfo 11 | { 12 | private static readonly Assembly _assembly = Assembly.GetExecutingAssembly(); 13 | private static string? _title; 14 | private static string? _description; 15 | private static string? _company; 16 | private static string? _product; 17 | private static string? _copyright; 18 | private static string? _trademark; 19 | private static string? _versionString; 20 | 21 | public static string Title 22 | => _title ??= Prop(x => x.Title); 23 | 24 | public static string Description 25 | => _description ??= Prop(x => x.Description); 26 | 27 | public static string Company 28 | => _company ??= Prop(x => x.Company); 29 | 30 | public static string Product 31 | => _product ??= Prop(x => x.Product); 32 | 33 | public static string Copyright 34 | => _copyright ??= Prop(x => x.Copyright); 35 | 36 | public static string Trademark 37 | => _trademark ??= Prop(x => x.Trademark); 38 | 39 | public static Version Version 40 | => _assembly.GetName().Version ?? new Version(); 41 | 42 | public static string VersionString 43 | => _versionString ??= Version.ToString(3); 44 | 45 | private static string Prop(Func propSelector) 46 | where T : Attribute 47 | { 48 | var attribute = _assembly.GetCustomAttribute(); 49 | return attribute != null ? propSelector(attribute) : ""; 50 | } 51 | } 52 | 53 | internal static class LocationInfo 54 | { 55 | private static DirectoryInfo? _localAppData; 56 | 57 | internal static DirectoryInfo LocalAppData 58 | => _localAppData ??= new DirectoryInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), AssemblyInfo.Company, AssemblyInfo.Product)); 59 | } 60 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/VirtualDesktopProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using WindowsDesktop.Interop.Proxy; 5 | using WindowsDesktop.Properties; 6 | 7 | namespace WindowsDesktop.Interop; 8 | 9 | internal abstract class VirtualDesktopProvider 10 | { 11 | public virtual bool IsSupported 12 | => true; 13 | 14 | public abstract IApplicationViewCollection ApplicationViewCollection { get; } 15 | 16 | public abstract IVirtualDesktopManager VirtualDesktopManager { get; } 17 | 18 | public abstract IVirtualDesktopManagerInternal VirtualDesktopManagerInternal { get; } 19 | 20 | public abstract IVirtualDesktopPinnedApps VirtualDesktopPinnedApps { get; } 21 | 22 | public abstract IVirtualDesktopNotificationService VirtualDesktopNotificationService { get; } 23 | 24 | public bool IsInitialized { get; internal set; } 25 | 26 | internal void Initialize(ComInterfaceAssembly assembly) 27 | { 28 | if (this.IsInitialized) return; 29 | 30 | this.InitializeCore(assembly); 31 | this.IsInitialized = true; 32 | } 33 | 34 | private protected abstract void InitializeCore(ComInterfaceAssembly assembly); 35 | 36 | internal class NotSupported : VirtualDesktopProvider 37 | { 38 | public override bool IsSupported 39 | => false; 40 | 41 | public override IApplicationViewCollection ApplicationViewCollection 42 | => throw new NotSupportedException(); 43 | 44 | public override IVirtualDesktopManager VirtualDesktopManager 45 | => throw new NotSupportedException(); 46 | 47 | public override IVirtualDesktopManagerInternal VirtualDesktopManagerInternal 48 | => throw new NotSupportedException(); 49 | 50 | public override IVirtualDesktopPinnedApps VirtualDesktopPinnedApps 51 | => throw new NotSupportedException(); 52 | 53 | public override IVirtualDesktopNotificationService VirtualDesktopNotificationService 54 | => throw new NotSupportedException(); 55 | 56 | private protected override void InitializeCore(ComInterfaceAssembly assembly) 57 | => throw new NotSupportedException(); 58 | } 59 | 60 | protected static InvalidOperationException InitializationIsRequired 61 | => new("Initialization is required."); 62 | } 63 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build10240_0000/.interfaces/IVirtualDesktopNotification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace WindowsDesktop.Interop.Build10240 5 | { 6 | [ComImport] 7 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 8 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 9 | public interface IVirtualDesktopNotification 10 | { 11 | void VirtualDesktopCreated(IVirtualDesktop pDesktop); 12 | 13 | void VirtualDesktopDestroyBegin(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 14 | 15 | void VirtualDesktopDestroyFailed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 16 | 17 | void VirtualDesktopDestroyed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 18 | 19 | void ViewVirtualDesktopChanged(IApplicationView pView); 20 | 21 | void CurrentVirtualDesktopChanged(IVirtualDesktop pDesktopOld, IVirtualDesktop pDesktopNew); 22 | } 23 | 24 | internal class VirtualDesktopNotification : VirtualDesktopNotificationService.EventListenerBase, IVirtualDesktopNotification 25 | { 26 | public void VirtualDesktopCreated(IVirtualDesktop pDesktop) 27 | { 28 | this.CreatedCore(pDesktop); 29 | } 30 | 31 | public void VirtualDesktopDestroyBegin(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 32 | { 33 | this.DestroyBeginCore(pDesktopDestroyed, pDesktopFallback); 34 | } 35 | 36 | public void VirtualDesktopDestroyFailed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 37 | { 38 | this.DestroyFailedCore(pDesktopDestroyed, pDesktopFallback); 39 | } 40 | 41 | public void VirtualDesktopDestroyed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 42 | { 43 | this.DestroyedCore(pDesktopDestroyed, pDesktopFallback); 44 | } 45 | 46 | public void ViewVirtualDesktopChanged(IApplicationView pView) 47 | { 48 | this.ViewChangedCore(pView); 49 | } 50 | 51 | public void CurrentVirtualDesktopChanged(IVirtualDesktop pDesktopOld, IVirtualDesktop pDesktopNew) 52 | { 53 | this.CurrentChangedCore(pDesktopOld, pDesktopNew); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/VirtualDesktop.WPF/VirtualDesktop.WPF.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0-windows10.0.19041.0;net7.0-windows10.0.19041.0;net6.0-windows10.0.19041.0 5 | Slions.VirtualDesktop.WPF 6 | latest 7 | true 8 | enable 9 | enable 10 | $(PublishVersion) 11 | VirtualDesktop 12 | $(PublishAuthors) 13 | slions.net 14 | Copyright © 2022 Manato KAMEYA 15 | C# Wrapper for the Virtual Desktop API on Windows 11 (and Windows 10). 16 | https://github.com/Slion/VirtualDesktop 17 | https://github.com/Slion/VirtualDesktop 18 | LICENSE 19 | README.md 20 | Windows;Windows10;Windows11;Desktop;VirtualDesktop; 21 | True 22 | WindowsDesktop 23 | 24 | 25 | 26 | 1701;1702;1591 27 | 28 | 29 | 30 | 1701;1702;1591 31 | 32 | 33 | 34 | 1701;1702;1591 35 | 36 | 37 | 38 | 1701;1702;1591 39 | 40 | 41 | 42 | 43 | True 44 | 45 | 46 | 47 | True 48 | \ 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/VirtualDesktop.WPF/ApplicationExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows; 5 | 6 | namespace WindowsDesktop; 7 | 8 | public static class ApplicationExtensions 9 | { 10 | /// 11 | /// Determines whether this application is pinned. 12 | /// 13 | /// if pinned, otherwise. 14 | public static bool IsPinned(this Application app) 15 | { 16 | return VirtualDesktop.TryGetAppUserModelId(app.GetWindowHandle(), out var appId) 17 | && VirtualDesktop.IsPinnedApplication(appId); 18 | } 19 | 20 | /// 21 | /// Pins an application, showing it on all virtual desktops. 22 | /// 23 | /// if already pinned or successfully pinned, otherwise (most of the time, main window is not found). 24 | public static bool Pin(this Application app) 25 | { 26 | return VirtualDesktop.TryGetAppUserModelId(app.GetWindowHandle(), out var appId) 27 | && VirtualDesktop.PinApplication(appId); 28 | } 29 | 30 | /// 31 | /// Unpins an application. 32 | /// 33 | /// if already unpinned or successfully unpinned, otherwise (most of the time, main window is not found). 34 | public static bool Unpin(this Application app) 35 | { 36 | return VirtualDesktop.TryGetAppUserModelId(app.GetWindowHandle(), out var appId) 37 | && VirtualDesktop.UnpinApplication(appId); 38 | } 39 | 40 | /// 41 | /// Toggles an application between being pinned and unpinned. 42 | /// 43 | /// if successfully toggled, otherwise (most of the time, main window is not found). 44 | public static bool TogglePin(this Application app) 45 | { 46 | if (VirtualDesktop.TryGetAppUserModelId(app.GetWindowHandle(), out var appId) == false) return false; 47 | 48 | return VirtualDesktop.IsPinnedApplication(appId) 49 | ? VirtualDesktop.UnpinApplication(appId) 50 | : VirtualDesktop.PinApplication(appId); 51 | } 52 | 53 | private static IntPtr GetWindowHandle(this Application app) 54 | => app.MainWindow?.GetHandle() 55 | ?? throw new InvalidOperationException(); 56 | } 57 | -------------------------------------------------------------------------------- /src/VirtualDesktop.WinForms/VirtualDesktop.WinForms.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0-windows10.0.19041.0;net7.0-windows10.0.19041.0;net6.0-windows10.0.19041.0 5 | Slions.VirtualDesktop.WinForms 6 | latest 7 | true 8 | enable 9 | enable 10 | $(PublishVersion) 11 | VirtualDesktop 12 | $(PublishAuthors) 13 | slions.net 14 | Copyright © 2022 Manato KAMEYA 15 | C# Wrapper for the Virtual Desktop API on Windows 11 (and Windows 10). 16 | https://github.com/Slion/VirtualDesktop 17 | https://github.com/Slion/VirtualDesktop 18 | LICENSE 19 | README.md 20 | Windows;Windows10;Windows11;Desktop;VirtualDesktop; 21 | True 22 | WindowsDesktop 23 | 24 | 25 | 26 | 1701;1702;1591 27 | 28 | 29 | 30 | 1701;1702;1591 31 | 32 | 33 | 34 | 1701;1702;1591 35 | 36 | 37 | 38 | 1701;1702;1591 39 | 40 | 41 | 42 | 43 | True 44 | 45 | 46 | 47 | True 48 | \ 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build20348_0000/.Provider.cs: -------------------------------------------------------------------------------- 1 | using WindowsDesktop.Interop.Build10240; 2 | using WindowsDesktop.Interop.Proxy; 3 | 4 | namespace WindowsDesktop.Interop.Build20348; 5 | 6 | internal class VirtualDesktopProvider20348 : VirtualDesktopProvider 7 | { 8 | private IVirtualDesktopManager? _virtualDesktopManager; 9 | private ApplicationViewCollection? _applicationViewCollection; 10 | private VirtualDesktopManagerInternal? _virtualDesktopManagerInternal; 11 | private VirtualDesktopPinnedApps? _virtualDesktopPinnedApps; 12 | private VirtualDesktopNotificationService? _virtualDesktopNotificationService; 13 | 14 | public override IApplicationViewCollection ApplicationViewCollection 15 | => this._applicationViewCollection ?? throw InitializationIsRequired; 16 | 17 | public override IVirtualDesktopManager VirtualDesktopManager 18 | => this._virtualDesktopManager ?? throw InitializationIsRequired; 19 | 20 | public override IVirtualDesktopManagerInternal VirtualDesktopManagerInternal 21 | => this._virtualDesktopManagerInternal ?? throw InitializationIsRequired; 22 | 23 | public override IVirtualDesktopPinnedApps VirtualDesktopPinnedApps 24 | => this._virtualDesktopPinnedApps ?? throw InitializationIsRequired; 25 | 26 | public override IVirtualDesktopNotificationService VirtualDesktopNotificationService 27 | => this._virtualDesktopNotificationService ?? throw InitializationIsRequired; 28 | 29 | private protected override void InitializeCore(ComInterfaceAssembly assembly) 30 | { 31 | var type = Type.GetTypeFromCLSID(CLSID.VirtualDesktopManager) 32 | ?? throw new Exception($"No type found for CLSID '{CLSID.VirtualDesktopManager}'."); 33 | this._virtualDesktopManager = Activator.CreateInstance(type) is IVirtualDesktopManager manager 34 | ? manager 35 | : throw new Exception($"Failed to create instance of Type '{typeof(IVirtualDesktopManager)}'."); 36 | 37 | this._applicationViewCollection = new ApplicationViewCollection(assembly); 38 | var factory = new ComWrapperFactory( 39 | x => new ApplicationView(assembly, x), 40 | x => this._applicationViewCollection.GetViewForHwnd(x), 41 | x => new VirtualDesktop(assembly, x)); 42 | this._virtualDesktopManagerInternal = new VirtualDesktopManagerInternal(assembly, factory); 43 | this._virtualDesktopPinnedApps = new VirtualDesktopPinnedApps(assembly, factory); 44 | this._virtualDesktopNotificationService = new VirtualDesktopNotificationService(assembly, factory); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22000_0000/.Provider.cs: -------------------------------------------------------------------------------- 1 | using WindowsDesktop.Interop.Build10240; 2 | using WindowsDesktop.Interop.Proxy; 3 | 4 | namespace WindowsDesktop.Interop.Build22000; 5 | 6 | internal class VirtualDesktopProvider22000 : VirtualDesktopProvider 7 | { 8 | private IVirtualDesktopManager? _virtualDesktopManager; 9 | private ApplicationViewCollection? _applicationViewCollection; 10 | private VirtualDesktopManagerInternal? _virtualDesktopManagerInternal; 11 | private VirtualDesktopPinnedApps? _virtualDesktopPinnedApps; 12 | private VirtualDesktopNotificationService? _virtualDesktopNotificationService; 13 | 14 | public override IApplicationViewCollection ApplicationViewCollection 15 | => this._applicationViewCollection ?? throw InitializationIsRequired; 16 | 17 | public override IVirtualDesktopManager VirtualDesktopManager 18 | => this._virtualDesktopManager ?? throw InitializationIsRequired; 19 | 20 | public override IVirtualDesktopManagerInternal VirtualDesktopManagerInternal 21 | => this._virtualDesktopManagerInternal ?? throw InitializationIsRequired; 22 | 23 | public override IVirtualDesktopPinnedApps VirtualDesktopPinnedApps 24 | => this._virtualDesktopPinnedApps ?? throw InitializationIsRequired; 25 | 26 | public override IVirtualDesktopNotificationService VirtualDesktopNotificationService 27 | => this._virtualDesktopNotificationService ?? throw InitializationIsRequired; 28 | 29 | private protected override void InitializeCore(ComInterfaceAssembly assembly) 30 | { 31 | var type = Type.GetTypeFromCLSID(CLSID.VirtualDesktopManager) 32 | ?? throw new Exception($"No type found for CLSID '{CLSID.VirtualDesktopManager}'."); 33 | this._virtualDesktopManager = Activator.CreateInstance(type) is IVirtualDesktopManager manager 34 | ? manager 35 | : throw new Exception($"Failed to create instance of Type '{typeof(IVirtualDesktopManager)}'."); 36 | 37 | this._applicationViewCollection = new ApplicationViewCollection(assembly); 38 | var factory = new ComWrapperFactory( 39 | x => new ApplicationView(assembly, x), 40 | x => this._applicationViewCollection.GetViewForHwnd(x), 41 | x => new VirtualDesktop(assembly, x)); 42 | this._virtualDesktopManagerInternal = new VirtualDesktopManagerInternal(assembly, factory); 43 | this._virtualDesktopPinnedApps = new VirtualDesktopPinnedApps(assembly, factory); 44 | this._virtualDesktopNotificationService = new VirtualDesktopNotificationService(assembly, factory); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22621_2215/.Provider.cs: -------------------------------------------------------------------------------- 1 | using WindowsDesktop.Interop.Build10240; 2 | using WindowsDesktop.Interop.Proxy; 3 | 4 | namespace WindowsDesktop.Interop.Build22621; 5 | 6 | internal class VirtualDesktopProvider22621 : VirtualDesktopProvider 7 | { 8 | private IVirtualDesktopManager? _virtualDesktopManager; 9 | private ApplicationViewCollection? _applicationViewCollection; 10 | private VirtualDesktopManagerInternal? _virtualDesktopManagerInternal; 11 | private VirtualDesktopPinnedApps? _virtualDesktopPinnedApps; 12 | private VirtualDesktopNotificationService? _virtualDesktopNotificationService; 13 | 14 | public override IApplicationViewCollection ApplicationViewCollection 15 | => this._applicationViewCollection ?? throw InitializationIsRequired; 16 | 17 | public override IVirtualDesktopManager VirtualDesktopManager 18 | => this._virtualDesktopManager ?? throw InitializationIsRequired; 19 | 20 | public override IVirtualDesktopManagerInternal VirtualDesktopManagerInternal 21 | => this._virtualDesktopManagerInternal ?? throw InitializationIsRequired; 22 | 23 | public override IVirtualDesktopPinnedApps VirtualDesktopPinnedApps 24 | => this._virtualDesktopPinnedApps ?? throw InitializationIsRequired; 25 | 26 | public override IVirtualDesktopNotificationService VirtualDesktopNotificationService 27 | => this._virtualDesktopNotificationService ?? throw InitializationIsRequired; 28 | 29 | private protected override void InitializeCore(ComInterfaceAssembly assembly) 30 | { 31 | var type = Type.GetTypeFromCLSID(CLSID.VirtualDesktopManager) 32 | ?? throw new Exception($"No type found for CLSID '{CLSID.VirtualDesktopManager}'."); 33 | this._virtualDesktopManager = Activator.CreateInstance(type) is IVirtualDesktopManager manager 34 | ? manager 35 | : throw new Exception($"Failed to create instance of Type '{typeof(IVirtualDesktopManager)}'."); 36 | 37 | this._applicationViewCollection = new ApplicationViewCollection(assembly); 38 | var factory = new ComWrapperFactory( 39 | x => new ApplicationView(assembly, x), 40 | x => this._applicationViewCollection.GetViewForHwnd(x), 41 | x => new VirtualDesktop(assembly, x)); 42 | this._virtualDesktopManagerInternal = new VirtualDesktopManagerInternal(assembly, factory); 43 | this._virtualDesktopPinnedApps = new VirtualDesktopPinnedApps(assembly, factory); 44 | this._virtualDesktopNotificationService = new VirtualDesktopNotificationService(assembly, factory); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build26100_0000/.Provider.cs: -------------------------------------------------------------------------------- 1 | using WindowsDesktop.Interop.Build10240; 2 | using WindowsDesktop.Interop.Proxy; 3 | 4 | namespace WindowsDesktop.Interop.Build26100; 5 | 6 | internal class VirtualDesktopProvider26100 : VirtualDesktopProvider 7 | { 8 | private IVirtualDesktopManager? _virtualDesktopManager; 9 | private ApplicationViewCollection? _applicationViewCollection; 10 | private VirtualDesktopManagerInternal? _virtualDesktopManagerInternal; 11 | private VirtualDesktopPinnedApps? _virtualDesktopPinnedApps; 12 | private VirtualDesktopNotificationService? _virtualDesktopNotificationService; 13 | 14 | public override IApplicationViewCollection ApplicationViewCollection 15 | => this._applicationViewCollection ?? throw InitializationIsRequired; 16 | 17 | public override IVirtualDesktopManager VirtualDesktopManager 18 | => this._virtualDesktopManager ?? throw InitializationIsRequired; 19 | 20 | public override IVirtualDesktopManagerInternal VirtualDesktopManagerInternal 21 | => this._virtualDesktopManagerInternal ?? throw InitializationIsRequired; 22 | 23 | public override IVirtualDesktopPinnedApps VirtualDesktopPinnedApps 24 | => this._virtualDesktopPinnedApps ?? throw InitializationIsRequired; 25 | 26 | public override IVirtualDesktopNotificationService VirtualDesktopNotificationService 27 | => this._virtualDesktopNotificationService ?? throw InitializationIsRequired; 28 | 29 | private protected override void InitializeCore(ComInterfaceAssembly assembly) 30 | { 31 | var type = Type.GetTypeFromCLSID(CLSID.VirtualDesktopManager) 32 | ?? throw new Exception($"No type found for CLSID '{CLSID.VirtualDesktopManager}'."); 33 | this._virtualDesktopManager = Activator.CreateInstance(type) is IVirtualDesktopManager manager 34 | ? manager 35 | : throw new Exception($"Failed to create instance of Type '{typeof(IVirtualDesktopManager)}'."); 36 | 37 | this._applicationViewCollection = new ApplicationViewCollection(assembly); 38 | var factory = new ComWrapperFactory( 39 | x => new ApplicationView(assembly, x), 40 | x => this._applicationViewCollection.GetViewForHwnd(x), 41 | x => new VirtualDesktop(assembly, x)); 42 | this._virtualDesktopManagerInternal = new VirtualDesktopManagerInternal(assembly, factory); 43 | this._virtualDesktopPinnedApps = new VirtualDesktopPinnedApps(assembly, factory); 44 | this._virtualDesktopNotificationService = new VirtualDesktopNotificationService(assembly, factory); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/ComWrapperBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace WindowsDesktop.Interop; 8 | 9 | public abstract class ComWrapperBase 10 | { 11 | private readonly Dictionary _methods = new(); 12 | 13 | private protected ComInterfaceAssembly ComInterfaceAssembly { get; } 14 | 15 | public TInterface Interface 16 | => (TInterface)(object)this; 17 | 18 | public Type ComInterfaceType { get; } 19 | 20 | public object ComObject { get; } 21 | 22 | private protected ComWrapperBase(ComInterfaceAssembly assembly) 23 | { 24 | var (type, instance) = assembly.CreateInstance(typeof(TInterface).Name); 25 | 26 | this.ComInterfaceAssembly = assembly; 27 | this.ComInterfaceType = type; 28 | this.ComObject = instance; 29 | } 30 | 31 | private protected ComWrapperBase(ComInterfaceAssembly assembly, Guid clsid) 32 | { 33 | var (type, instance) = assembly.CreateInstance(typeof(TInterface).Name, clsid); 34 | 35 | this.ComInterfaceAssembly = assembly; 36 | this.ComInterfaceType = type; 37 | this.ComObject = instance; 38 | } 39 | 40 | private protected ComWrapperBase(ComInterfaceAssembly assembly, object comObject) 41 | { 42 | this.ComInterfaceAssembly = assembly; 43 | this.ComInterfaceType = assembly.GetType(typeof(TInterface).Name); 44 | this.ComObject = comObject; 45 | } 46 | 47 | protected static object?[] Args(params object?[] args) 48 | => args; 49 | 50 | protected void InvokeMethod(object?[]? parameters = null, [CallerMemberName] string methodName = "") 51 | => this.InvokeMethod(parameters, methodName); 52 | 53 | protected T? InvokeMethod(object?[]? parameters = null, [CallerMemberName] string methodName = "") 54 | { 55 | if (this._methods.TryGetValue(methodName, out var methodInfo) 56 | || (methodInfo = this.ComInterfaceType.GetMethod(methodName)) != null) 57 | { 58 | this._methods[methodName] = methodInfo; 59 | } 60 | else throw new NotSupportedException($"Method '{methodName}' is not supported in COM interface '{typeof(TInterface).Name}'."); 61 | 62 | try 63 | { 64 | return (T?)methodInfo.Invoke(this.ComObject, parameters); 65 | } 66 | catch (TargetInvocationException ex) when (ex.InnerException != null) 67 | { 68 | throw ex.InnerException; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build10240_0000/.Provider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using WindowsDesktop.Interop.Proxy; 5 | using WindowsDesktop.Properties; 6 | 7 | namespace WindowsDesktop.Interop.Build10240; 8 | 9 | internal class VirtualDesktopProvider10240 : VirtualDesktopProvider 10 | { 11 | private IVirtualDesktopManager? _virtualDesktopManager; 12 | private ApplicationViewCollection? _applicationViewCollection; 13 | private VirtualDesktopManagerInternal? _virtualDesktopManagerInternal; 14 | private VirtualDesktopPinnedApps? _virtualDesktopPinnedApps; 15 | private VirtualDesktopNotificationService? _virtualDesktopNotificationService; 16 | 17 | public override IApplicationViewCollection ApplicationViewCollection 18 | => this._applicationViewCollection ?? throw InitializationIsRequired; 19 | 20 | public override IVirtualDesktopManager VirtualDesktopManager 21 | => this._virtualDesktopManager ?? throw InitializationIsRequired; 22 | 23 | public override IVirtualDesktopManagerInternal VirtualDesktopManagerInternal 24 | => this._virtualDesktopManagerInternal ?? throw InitializationIsRequired; 25 | 26 | public override IVirtualDesktopPinnedApps VirtualDesktopPinnedApps 27 | => this._virtualDesktopPinnedApps ?? throw InitializationIsRequired; 28 | 29 | public override IVirtualDesktopNotificationService VirtualDesktopNotificationService 30 | => this._virtualDesktopNotificationService ?? throw InitializationIsRequired; 31 | 32 | private protected override void InitializeCore(ComInterfaceAssembly assembly) 33 | { 34 | var type = Type.GetTypeFromCLSID(CLSID.VirtualDesktopManager) 35 | ?? throw new Exception($"No type found for CLSID '{CLSID.VirtualDesktopManager}'."); 36 | this._virtualDesktopManager = Activator.CreateInstance(type) is IVirtualDesktopManager manager 37 | ? manager 38 | : throw new Exception($"Failed to create instance of Type '{typeof(IVirtualDesktopManager)}'."); 39 | 40 | this._applicationViewCollection = new ApplicationViewCollection(assembly); 41 | var factory = new ComWrapperFactory( 42 | x => new ApplicationView(assembly, x), 43 | x => this._applicationViewCollection.GetViewForHwnd(x), 44 | x => new VirtualDesktop(assembly, x)); 45 | this._virtualDesktopManagerInternal = new VirtualDesktopManagerInternal(assembly, factory); 46 | this._virtualDesktopPinnedApps = new VirtualDesktopPinnedApps(assembly, factory); 47 | this._virtualDesktopNotificationService = new VirtualDesktopNotificationService(assembly, factory); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | #* text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build20348_0000/.interfaces/IVirtualDesktopNotification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using WindowsDesktop.Interop.Build10240; 4 | 5 | namespace WindowsDesktop.Interop.Build20348 6 | { 7 | [ComImport] 8 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 9 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 10 | public interface IVirtualDesktopNotification 11 | { 12 | void VirtualDesktopCreated(IVirtualDesktop pDesktop); 13 | 14 | void VirtualDesktopDestroyBegin(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 15 | 16 | void VirtualDesktopDestroyFailed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 17 | 18 | void VirtualDesktopDestroyed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 19 | 20 | void VirtualDesktopIsPerMonitorChanged(int i); 21 | 22 | void VirtualDesktopRenamed(IVirtualDesktop pDesktop, HString chName); 23 | 24 | void ViewVirtualDesktopChanged(IApplicationView pView); 25 | 26 | void CurrentVirtualDesktopChanged(IVirtualDesktop pDesktopOld, IVirtualDesktop pDesktopNew); 27 | } 28 | 29 | internal class VirtualDesktopNotification : VirtualDesktopNotificationService.EventListenerBase, IVirtualDesktopNotification 30 | { 31 | public void VirtualDesktopCreated(IVirtualDesktop pDesktop) 32 | { 33 | this.CreatedCore(pDesktop); 34 | } 35 | 36 | public void VirtualDesktopDestroyBegin(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 37 | { 38 | this.DestroyBeginCore(pDesktopDestroyed, pDesktopFallback); 39 | } 40 | 41 | public void VirtualDesktopDestroyFailed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 42 | { 43 | this.DestroyFailedCore(pDesktopDestroyed, pDesktopFallback); 44 | } 45 | 46 | public void VirtualDesktopDestroyed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 47 | { 48 | this.DestroyedCore(pDesktopDestroyed, pDesktopFallback); 49 | } 50 | 51 | public void VirtualDesktopIsPerMonitorChanged(int i) 52 | { 53 | this.IsPerMonitorChangedCore(i); 54 | } 55 | 56 | public void VirtualDesktopRenamed(IVirtualDesktop pDesktop, HString chName) 57 | { 58 | this.RenamedCore(pDesktop, chName); 59 | } 60 | 61 | public void ViewVirtualDesktopChanged(IApplicationView pView) 62 | { 63 | this.ViewChangedCore(pView); 64 | } 65 | 66 | public void CurrentVirtualDesktopChanged(IVirtualDesktop pDesktopOld, IVirtualDesktop pDesktopNew) 67 | { 68 | this.CurrentChangedCore(pDesktopOld, pDesktopNew); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build10240_0000/VirtualDesktopManagerInternal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using WindowsDesktop.Interop.Proxy; 5 | 6 | namespace WindowsDesktop.Interop.Build10240; 7 | 8 | internal class VirtualDesktopManagerInternal : ComWrapperBase, IVirtualDesktopManagerInternal 9 | { 10 | private readonly ComWrapperFactory _factory; 11 | 12 | public VirtualDesktopManagerInternal(ComInterfaceAssembly assembly, ComWrapperFactory factory) 13 | : base(assembly, CLSID.VirtualDesktopManagerInternal) 14 | { 15 | this._factory = factory; 16 | } 17 | 18 | public IEnumerable GetDesktops() 19 | { 20 | var array = this.InvokeMethod(); 21 | if (array == null) yield break; 22 | 23 | var count = array.GetCount(); 24 | var vdType = this.ComInterfaceAssembly.GetType(nameof(IVirtualDesktop)); 25 | 26 | for (var i = 0u; i < count; i++) 27 | { 28 | var ppvObject = array.GetAt(i, vdType.GUID); 29 | yield return new VirtualDesktop(this.ComInterfaceAssembly, ppvObject); 30 | } 31 | } 32 | 33 | public IVirtualDesktop GetCurrentDesktop() 34 | => this.InvokeMethodAndWrap(); 35 | 36 | public IVirtualDesktop GetAdjacentDesktop(IVirtualDesktop pDesktopReference, AdjacentDesktop uDirection) 37 | => this.InvokeMethodAndWrap(Args(((VirtualDesktop)pDesktopReference).ComObject, uDirection)); 38 | 39 | public IVirtualDesktop FindDesktop(Guid desktopId) 40 | => this.InvokeMethodAndWrap(Args(desktopId)); 41 | 42 | public IVirtualDesktop CreateDesktop() 43 | => this.InvokeMethodAndWrap(); 44 | 45 | public void SwitchDesktop(IVirtualDesktop desktop) 46 | => this.InvokeMethod(Args(((VirtualDesktop)desktop).ComObject)); 47 | 48 | public void MoveDesktop(IVirtualDesktop pMove, int nIndex) 49 | => throw new NotSupportedException(); 50 | 51 | public void RemoveDesktop(IVirtualDesktop pRemove, IVirtualDesktop pFallbackDesktop) 52 | => this.InvokeMethod(Args(((VirtualDesktop)pRemove).ComObject, ((VirtualDesktop)pFallbackDesktop).ComObject)); 53 | 54 | public void MoveViewToDesktop(IntPtr hWnd, IVirtualDesktop desktop) 55 | => this.InvokeMethod(Args(this._factory.ApplicationViewFromHwnd(hWnd).ComObject, ((VirtualDesktop)desktop).ComObject)); 56 | 57 | public void SetDesktopName(IVirtualDesktop desktop, string name) 58 | => throw new NotSupportedException(); 59 | 60 | public void SetDesktopWallpaper(IVirtualDesktop desktop, string path) 61 | => throw new NotSupportedException(); 62 | 63 | public void UpdateWallpaperPathForAllDesktops(string path) 64 | => throw new NotSupportedException(); 65 | 66 | private VirtualDesktop InvokeMethodAndWrap(object?[]? parameters = null, [CallerMemberName] string methodName = "") 67 | => new(this.ComInterfaceAssembly, this.InvokeMethod(parameters, methodName) ?? throw new Exception("Failed to get IVirtualDesktop instance.")); 68 | } 69 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22621_2215/VirtualDesktopManagerInternal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using WindowsDesktop.Interop.Proxy; 5 | 6 | namespace WindowsDesktop.Interop.Build22621; 7 | 8 | internal class VirtualDesktopManagerInternal : ComWrapperBase, IVirtualDesktopManagerInternal 9 | { 10 | private readonly ComWrapperFactory _factory; 11 | 12 | public VirtualDesktopManagerInternal(ComInterfaceAssembly assembly, ComWrapperFactory factory) 13 | : base(assembly, CLSID.VirtualDesktopManagerInternal) 14 | { 15 | this._factory = factory; 16 | } 17 | 18 | public IEnumerable GetDesktops() 19 | { 20 | var array = this.InvokeMethod(); 21 | if (array == null) yield break; 22 | 23 | var count = array.GetCount(); 24 | var vdType = this.ComInterfaceAssembly.GetType(nameof(IVirtualDesktop)); 25 | 26 | for (var i = 0u; i < count; i++) 27 | { 28 | var ppvObject = array.GetAt(i, vdType.GUID); 29 | yield return new VirtualDesktop(this.ComInterfaceAssembly, ppvObject); 30 | } 31 | } 32 | 33 | public IVirtualDesktop GetCurrentDesktop() 34 | => this.InvokeMethodAndWrap(); 35 | 36 | public IVirtualDesktop GetAdjacentDesktop(IVirtualDesktop pDesktopReference, AdjacentDesktop uDirection) 37 | => this.InvokeMethodAndWrap(Args(((VirtualDesktop)pDesktopReference).ComObject, uDirection)); 38 | 39 | public IVirtualDesktop FindDesktop(Guid desktopId) 40 | => this.InvokeMethodAndWrap(Args(desktopId)); 41 | 42 | public IVirtualDesktop CreateDesktop() 43 | => this.InvokeMethodAndWrap(); 44 | 45 | public void SwitchDesktop(IVirtualDesktop desktop) 46 | => this.InvokeMethod(Args(((VirtualDesktop)desktop).ComObject)); 47 | 48 | public void MoveDesktop(IVirtualDesktop pMove, int nIndex) 49 | => this.InvokeMethod(Args(((VirtualDesktop)pMove).ComObject, nIndex)); 50 | 51 | public void RemoveDesktop(IVirtualDesktop pRemove, IVirtualDesktop pFallbackDesktop) 52 | => this.InvokeMethod(Args(((VirtualDesktop)pRemove).ComObject, ((VirtualDesktop)pFallbackDesktop).ComObject)); 53 | 54 | public void MoveViewToDesktop(IntPtr hWnd, IVirtualDesktop desktop) 55 | => this.InvokeMethod(Args(this._factory.ApplicationViewFromHwnd(hWnd).ComObject, ((VirtualDesktop)desktop).ComObject)); 56 | 57 | public void SetDesktopName(IVirtualDesktop desktop, string name) 58 | => this.InvokeMethod(Args(((VirtualDesktop)desktop).ComObject, new HString(name))); 59 | 60 | public void SetDesktopWallpaper(IVirtualDesktop desktop, string path) 61 | => this.InvokeMethod(Args(((VirtualDesktop)desktop).ComObject, new HString(path))); 62 | 63 | public void UpdateWallpaperPathForAllDesktops(string path) 64 | => this.InvokeMethod(Args(new HString(path))); 65 | 66 | private VirtualDesktop InvokeMethodAndWrap(object?[]? parameters = null, [CallerMemberName] string methodName = "") 67 | => new(this.ComInterfaceAssembly, this.InvokeMethod(parameters, methodName) ?? throw new Exception("Failed to get IVirtualDesktop instance.")); 68 | } 69 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build26100_0000/VirtualDesktopManagerInternal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using WindowsDesktop.Interop.Proxy; 5 | 6 | namespace WindowsDesktop.Interop.Build26100; 7 | 8 | internal class VirtualDesktopManagerInternal : ComWrapperBase, IVirtualDesktopManagerInternal 9 | { 10 | private readonly ComWrapperFactory _factory; 11 | 12 | public VirtualDesktopManagerInternal(ComInterfaceAssembly assembly, ComWrapperFactory factory) 13 | : base(assembly, CLSID.VirtualDesktopManagerInternal) 14 | { 15 | this._factory = factory; 16 | } 17 | 18 | public IEnumerable GetDesktops() 19 | { 20 | var array = this.InvokeMethod(); 21 | if (array == null) yield break; 22 | 23 | var count = array.GetCount(); 24 | var vdType = this.ComInterfaceAssembly.GetType(nameof(IVirtualDesktop)); 25 | 26 | for (var i = 0u; i < count; i++) 27 | { 28 | var ppvObject = array.GetAt(i, vdType.GUID); 29 | yield return new VirtualDesktop(this.ComInterfaceAssembly, ppvObject); 30 | } 31 | } 32 | 33 | public IVirtualDesktop GetCurrentDesktop() 34 | => this.InvokeMethodAndWrap(); 35 | 36 | public IVirtualDesktop GetAdjacentDesktop(IVirtualDesktop pDesktopReference, AdjacentDesktop uDirection) 37 | => this.InvokeMethodAndWrap(Args(((VirtualDesktop)pDesktopReference).ComObject, uDirection)); 38 | 39 | public IVirtualDesktop FindDesktop(Guid desktopId) 40 | => this.InvokeMethodAndWrap(Args(desktopId)); 41 | 42 | public IVirtualDesktop CreateDesktop() 43 | => this.InvokeMethodAndWrap(); 44 | 45 | public void SwitchDesktop(IVirtualDesktop desktop) 46 | => this.InvokeMethod(Args(((VirtualDesktop)desktop).ComObject)); 47 | 48 | public void MoveDesktop(IVirtualDesktop pMove, int nIndex) 49 | => this.InvokeMethod(Args(((VirtualDesktop)pMove).ComObject, nIndex)); 50 | 51 | public void RemoveDesktop(IVirtualDesktop pRemove, IVirtualDesktop pFallbackDesktop) 52 | => this.InvokeMethod(Args(((VirtualDesktop)pRemove).ComObject, ((VirtualDesktop)pFallbackDesktop).ComObject)); 53 | 54 | public void MoveViewToDesktop(IntPtr hWnd, IVirtualDesktop desktop) 55 | => this.InvokeMethod(Args(this._factory.ApplicationViewFromHwnd(hWnd).ComObject, ((VirtualDesktop)desktop).ComObject)); 56 | 57 | public void SetDesktopName(IVirtualDesktop desktop, string name) 58 | => this.InvokeMethod(Args(((VirtualDesktop)desktop).ComObject, new HString(name))); 59 | 60 | public void SetDesktopWallpaper(IVirtualDesktop desktop, string path) 61 | => this.InvokeMethod(Args(((VirtualDesktop)desktop).ComObject, new HString(path))); 62 | 63 | public void UpdateWallpaperPathForAllDesktops(string path) 64 | => this.InvokeMethod(Args(new HString(path))); 65 | 66 | private VirtualDesktop InvokeMethodAndWrap(object?[]? parameters = null, [CallerMemberName] string methodName = "") 67 | => new(this.ComInterfaceAssembly, this.InvokeMethod(parameters, methodName) ?? throw new Exception("Failed to get IVirtualDesktop instance.")); 68 | } 69 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build10240_0000/VirtualDesktopNotificationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using WindowsDesktop.Interop.Proxy; 6 | using WindowsDesktop.Utils; 7 | 8 | namespace WindowsDesktop.Interop.Build10240; 9 | 10 | public class VirtualDesktopNotificationService : ComWrapperBase, IVirtualDesktopNotificationService 11 | { 12 | private readonly ComWrapperFactory _factory; 13 | 14 | internal VirtualDesktopNotificationService(ComInterfaceAssembly assembly, ComWrapperFactory factory) 15 | : base(assembly, CLSID.VirtualDesktopNotificationService) 16 | { 17 | this._factory = factory; 18 | } 19 | 20 | public IDisposable Register(IVirtualDesktopNotification notification) 21 | { 22 | var type = this.ComInterfaceAssembly.GetType("VirtualDesktopNotification"); 23 | var listener = Activator.CreateInstance(type) as EventListenerBase 24 | ?? throw new Exception($"{nameof(EventListenerBase)} inheritance type is not found in the COM interface assembly."); 25 | 26 | listener.Notification = notification; 27 | listener.Factory = this._factory; 28 | 29 | var dwCookie = this.InvokeMethod(Args(listener)); 30 | return Disposable.Create(() => this.Unregister(dwCookie)); 31 | } 32 | 33 | private void Unregister(uint dwCookie) 34 | { 35 | try 36 | { 37 | this.InvokeMethod(Args(dwCookie)); 38 | } 39 | catch (COMException ex) when (ex.Match(HResult.RPC_S_SERVER_UNAVAILABLE)) 40 | { 41 | // Nothing particular to do. 42 | } 43 | } 44 | 45 | public abstract class EventListenerBase 46 | { 47 | internal ComWrapperFactory Factory { get; set; } = null!; 48 | 49 | internal IVirtualDesktopNotification Notification { get; set; } = null!; 50 | 51 | protected void CreatedCore(object pDesktop) 52 | => this.Notification.VirtualDesktopCreated(this.Wrap(pDesktop)); 53 | 54 | protected void DestroyBeginCore(object pDesktopDestroyed, object pDesktopFallback) 55 | => this.Notification.VirtualDesktopDestroyBegin(this.Wrap(pDesktopDestroyed), this.Wrap(pDesktopFallback)); 56 | 57 | protected void DestroyFailedCore(object pDesktopDestroyed, object pDesktopFallback) 58 | => this.Notification.VirtualDesktopDestroyFailed(this.Wrap(pDesktopDestroyed), this.Wrap(pDesktopFallback)); 59 | 60 | protected void DestroyedCore(object pDesktopDestroyed, object pDesktopFallback) 61 | => this.Notification.VirtualDesktopDestroyed(this.Wrap(pDesktopDestroyed), this.Wrap(pDesktopFallback)); 62 | 63 | protected void ViewChangedCore(object view) 64 | => this.Notification.ViewVirtualDesktopChanged(this.Factory.ApplicationView(view).Interface); 65 | 66 | protected void CurrentChangedCore(object pDesktopOld, object pDesktopNew) 67 | => this.Notification.CurrentVirtualDesktopChanged(this.Wrap(pDesktopOld), this.Wrap(pDesktopNew)); 68 | 69 | private IVirtualDesktop Wrap(object desktop) 70 | => this.Factory.VirtualDesktop(desktop).Interface; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build20348_0000/VirtualDesktopManagerInternal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using WindowsDesktop.Interop.Proxy; 5 | 6 | namespace WindowsDesktop.Interop.Build20348; 7 | 8 | internal class VirtualDesktopManagerInternal : ComWrapperBase, IVirtualDesktopManagerInternal 9 | { 10 | private readonly ComWrapperFactory _factory; 11 | 12 | public VirtualDesktopManagerInternal(ComInterfaceAssembly assembly, ComWrapperFactory factory) 13 | : base(assembly, CLSID.VirtualDesktopManagerInternal) 14 | { 15 | this._factory = factory; 16 | } 17 | 18 | public IEnumerable GetDesktops() 19 | { 20 | var array = this.InvokeMethod(Args(IntPtr.Zero)); 21 | if (array == null) yield break; 22 | 23 | var count = array.GetCount(); 24 | var vdType = this.ComInterfaceAssembly.GetType(nameof(IVirtualDesktop)); 25 | 26 | for (var i = 0u; i < count; i++) 27 | { 28 | var ppvObject = array.GetAt(i, vdType.GUID); 29 | yield return new VirtualDesktop(this.ComInterfaceAssembly, ppvObject); 30 | } 31 | } 32 | 33 | public IVirtualDesktop GetCurrentDesktop() 34 | => this.InvokeMethodAndWrap(Args(IntPtr.Zero)); 35 | 36 | public IVirtualDesktop GetAdjacentDesktop(IVirtualDesktop pDesktopReference, AdjacentDesktop uDirection) 37 | => this.InvokeMethodAndWrap(Args(((VirtualDesktop)pDesktopReference).ComObject, uDirection)); 38 | 39 | public IVirtualDesktop FindDesktop(Guid desktopId) 40 | => this.InvokeMethodAndWrap(Args(desktopId)); 41 | 42 | public IVirtualDesktop CreateDesktop() 43 | => this.InvokeMethodAndWrap(Args(IntPtr.Zero)); 44 | 45 | public void SwitchDesktop(IVirtualDesktop desktop) 46 | => this.InvokeMethod(Args(IntPtr.Zero, ((VirtualDesktop)desktop).ComObject)); 47 | 48 | 49 | public void MoveDesktop(IVirtualDesktop pMove, int nIndex) 50 | => throw new NotSupportedException(); 51 | 52 | public void RemoveDesktop(IVirtualDesktop pRemove, IVirtualDesktop pFallbackDesktop) 53 | => this.InvokeMethod(Args(((VirtualDesktop)pRemove).ComObject, ((VirtualDesktop)pFallbackDesktop).ComObject)); 54 | 55 | public void MoveViewToDesktop(IntPtr hWnd, IVirtualDesktop desktop) 56 | => this.InvokeMethod(Args(this._factory.ApplicationViewFromHwnd(hWnd).ComObject, ((VirtualDesktop)desktop).ComObject)); 57 | 58 | public void SetDesktopName(IVirtualDesktop desktop, string name) 59 | => this.InvokeMethod(Args(((VirtualDesktop)desktop).ComObject, new HString(name))); 60 | 61 | public void SetDesktopWallpaper(IVirtualDesktop desktop, string path) 62 | { 63 | //not available in server 2022 64 | } 65 | 66 | public void UpdateWallpaperPathForAllDesktops(string path) 67 | { 68 | //not available in server 2022 69 | } 70 | 71 | private VirtualDesktop InvokeMethodAndWrap(object?[]? parameters = null, [CallerMemberName] string methodName = "") 72 | => new(this.ComInterfaceAssembly, this.InvokeMethod(parameters, methodName) ?? throw new Exception("Failed to get IVirtualDesktop instance.")); 73 | } 74 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22000_0000/VirtualDesktopManagerInternal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using WindowsDesktop.Interop.Proxy; 5 | 6 | namespace WindowsDesktop.Interop.Build22000; 7 | 8 | internal class VirtualDesktopManagerInternal : ComWrapperBase, IVirtualDesktopManagerInternal 9 | { 10 | private readonly ComWrapperFactory _factory; 11 | 12 | public VirtualDesktopManagerInternal(ComInterfaceAssembly assembly, ComWrapperFactory factory) 13 | : base(assembly, CLSID.VirtualDesktopManagerInternal) 14 | { 15 | this._factory = factory; 16 | } 17 | 18 | public IEnumerable GetDesktops() 19 | { 20 | var array = this.InvokeMethod(Args(IntPtr.Zero)); 21 | if (array == null) yield break; 22 | 23 | var count = array.GetCount(); 24 | var vdType = this.ComInterfaceAssembly.GetType(nameof(IVirtualDesktop)); 25 | 26 | for (var i = 0u; i < count; i++) 27 | { 28 | var ppvObject = array.GetAt(i, vdType.GUID); 29 | yield return new VirtualDesktop(this.ComInterfaceAssembly, ppvObject); 30 | } 31 | } 32 | 33 | public IVirtualDesktop GetCurrentDesktop() 34 | => this.InvokeMethodAndWrap(Args(IntPtr.Zero)); 35 | 36 | public IVirtualDesktop GetAdjacentDesktop(IVirtualDesktop pDesktopReference, AdjacentDesktop uDirection) 37 | => this.InvokeMethodAndWrap(Args(((VirtualDesktop)pDesktopReference).ComObject, uDirection)); 38 | 39 | public IVirtualDesktop FindDesktop(Guid desktopId) 40 | => this.InvokeMethodAndWrap(Args(desktopId)); 41 | 42 | public IVirtualDesktop CreateDesktop() 43 | => this.InvokeMethodAndWrap(Args(IntPtr.Zero)); 44 | 45 | public void SwitchDesktop(IVirtualDesktop desktop) 46 | => this.InvokeMethod(Args(IntPtr.Zero, ((VirtualDesktop)desktop).ComObject)); 47 | 48 | public void MoveDesktop(IVirtualDesktop pMove, int nIndex) 49 | => this.InvokeMethod(Args(((VirtualDesktop)pMove).ComObject, IntPtr.Zero, nIndex)); 50 | 51 | public void RemoveDesktop(IVirtualDesktop pRemove, IVirtualDesktop pFallbackDesktop) 52 | => this.InvokeMethod(Args(((VirtualDesktop)pRemove).ComObject, ((VirtualDesktop)pFallbackDesktop).ComObject)); 53 | 54 | public void MoveViewToDesktop(IntPtr hWnd, IVirtualDesktop desktop) 55 | => this.InvokeMethod(Args(this._factory.ApplicationViewFromHwnd(hWnd).ComObject, ((VirtualDesktop)desktop).ComObject)); 56 | 57 | public void SetDesktopName(IVirtualDesktop desktop, string name) 58 | => this.InvokeMethod(Args(((VirtualDesktop)desktop).ComObject, new HString(name))); 59 | 60 | public void SetDesktopWallpaper(IVirtualDesktop desktop, string path) 61 | => this.InvokeMethod(Args(((VirtualDesktop)desktop).ComObject, new HString(path))); 62 | 63 | public void UpdateWallpaperPathForAllDesktops(string path) 64 | => this.InvokeMethod(Args(new HString(path))); 65 | 66 | private VirtualDesktop InvokeMethodAndWrap(object?[]? parameters = null, [CallerMemberName] string methodName = "") 67 | => new(this.ComInterfaceAssembly, this.InvokeMethod(parameters, methodName) ?? throw new Exception("Failed to get IVirtualDesktop instance.")); 68 | } 69 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22000_0000/.interfaces/IVirtualDesktopNotification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using WindowsDesktop.Interop.Build10240; 4 | 5 | namespace WindowsDesktop.Interop.Build22000 6 | { 7 | [ComImport] 8 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 9 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 10 | public interface IVirtualDesktopNotification 11 | { 12 | void VirtualDesktopCreated(IObjectArray p0, IVirtualDesktop pDesktop); 13 | 14 | void VirtualDesktopDestroyBegin(IObjectArray p0, IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 15 | 16 | void VirtualDesktopDestroyFailed(IObjectArray p0, IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 17 | 18 | void VirtualDesktopDestroyed(IObjectArray p0, IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 19 | 20 | void Proc7(int p0); 21 | 22 | void VirtualDesktopMoved(IObjectArray p0, IVirtualDesktop pDesktop, int nIndexFrom, int nIndexTo); 23 | 24 | void VirtualDesktopRenamed(IVirtualDesktop pDesktop, HString chName); 25 | 26 | void ViewVirtualDesktopChanged(IApplicationView pView); 27 | 28 | void CurrentVirtualDesktopChanged(IObjectArray p0, IVirtualDesktop pDesktopOld, IVirtualDesktop pDesktopNew); 29 | 30 | void VirtualDesktopWallpaperChanged(IVirtualDesktop pDesktop, HString chPath); 31 | } 32 | 33 | internal class VirtualDesktopNotification : VirtualDesktopNotificationService.EventListenerBase, IVirtualDesktopNotification 34 | { 35 | public void VirtualDesktopCreated(IObjectArray p0, IVirtualDesktop pDesktop) 36 | { 37 | this.CreatedCore(pDesktop); 38 | } 39 | 40 | public void VirtualDesktopDestroyBegin(IObjectArray p0, IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 41 | { 42 | this.DestroyBeginCore(pDesktopDestroyed, pDesktopFallback); 43 | } 44 | 45 | public void VirtualDesktopDestroyFailed(IObjectArray p0, IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 46 | { 47 | this.DestroyFailedCore(pDesktopDestroyed, pDesktopFallback); 48 | } 49 | 50 | public void VirtualDesktopDestroyed(IObjectArray p0, IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 51 | { 52 | this.DestroyedCore(pDesktopDestroyed, pDesktopFallback); 53 | } 54 | 55 | public void Proc7(int p0) 56 | { 57 | } 58 | 59 | public void VirtualDesktopMoved(IObjectArray p0, IVirtualDesktop pDesktop, int nIndexFrom, int nIndexTo) 60 | { 61 | this.MovedCore(p0, pDesktop, nIndexFrom, nIndexTo); 62 | } 63 | 64 | public void VirtualDesktopRenamed(IVirtualDesktop pDesktop, HString chName) 65 | { 66 | this.RenamedCore(pDesktop, chName); 67 | } 68 | 69 | public void ViewVirtualDesktopChanged(IApplicationView pView) 70 | { 71 | this.ViewChangedCore(pView); 72 | } 73 | 74 | public void CurrentVirtualDesktopChanged(IObjectArray p0, IVirtualDesktop pDesktopOld, IVirtualDesktop pDesktopNew) 75 | { 76 | this.CurrentChangedCore(pDesktopOld, pDesktopNew); 77 | } 78 | 79 | public void VirtualDesktopWallpaperChanged(IVirtualDesktop pDesktop, HString chPath) 80 | { 81 | this.WallpaperChangedCore(pDesktop, chPath); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build20348_0000/VirtualDesktopNotificationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using WindowsDesktop.Interop.Proxy; 6 | using WindowsDesktop.Utils; 7 | 8 | namespace WindowsDesktop.Interop.Build20348; 9 | 10 | public class VirtualDesktopNotificationService : ComWrapperBase, IVirtualDesktopNotificationService 11 | { 12 | private readonly ComWrapperFactory _factory; 13 | 14 | internal VirtualDesktopNotificationService(ComInterfaceAssembly assembly, ComWrapperFactory factory) 15 | : base(assembly, CLSID.VirtualDesktopNotificationService) 16 | { 17 | this._factory = factory; 18 | } 19 | 20 | public IDisposable Register(IVirtualDesktopNotification notification) 21 | { 22 | var type = this.ComInterfaceAssembly.GetType("VirtualDesktopNotification"); 23 | var listener = Activator.CreateInstance(type) as EventListenerBase 24 | ?? throw new Exception($"{nameof(EventListenerBase)} inheritance type is not found in the COM interface assembly."); 25 | 26 | listener.Notification = notification; 27 | listener.Factory = this._factory; 28 | 29 | var dwCookie = this.InvokeMethod(Args(listener)); 30 | return Disposable.Create(() => this.Unregister(dwCookie)); 31 | } 32 | 33 | private void Unregister(uint dwCookie) 34 | { 35 | try 36 | { 37 | this.InvokeMethod(Args(dwCookie)); 38 | } 39 | catch (COMException ex) when (ex.Match(HResult.RPC_S_SERVER_UNAVAILABLE)) 40 | { 41 | // Nothing particular to do. 42 | } 43 | } 44 | 45 | public abstract class EventListenerBase 46 | { 47 | internal ComWrapperFactory Factory { get; set; } = null!; 48 | 49 | internal IVirtualDesktopNotification Notification { get; set; } = null!; 50 | 51 | protected void CreatedCore(object pDesktop) 52 | => this.Notification.VirtualDesktopCreated(this.Wrap(pDesktop)); 53 | 54 | protected void DestroyBeginCore(object pDesktopDestroyed, object pDesktopFallback) 55 | => this.Notification.VirtualDesktopDestroyBegin(this.Wrap(pDesktopDestroyed), this.Wrap(pDesktopFallback)); 56 | 57 | protected void DestroyFailedCore(object pDesktopDestroyed, object pDesktopFallback) 58 | => this.Notification.VirtualDesktopDestroyFailed(this.Wrap(pDesktopDestroyed), this.Wrap(pDesktopFallback)); 59 | 60 | protected void DestroyedCore(object pDesktopDestroyed, object pDesktopFallback) 61 | => this.Notification.VirtualDesktopDestroyed(this.Wrap(pDesktopDestroyed), this.Wrap(pDesktopFallback)); 62 | 63 | protected void IsPerMonitorChangedCore(int i) 64 | => this.Notification.VirtualDesktopIsPerMonitorChanged(i); 65 | 66 | protected void RenamedCore(object pDesktop, HString chName) 67 | => this.Notification.VirtualDesktopRenamed(this.Wrap(pDesktop), chName); 68 | 69 | protected void ViewChangedCore(object view) 70 | => this.Notification.ViewVirtualDesktopChanged(this.Factory.ApplicationView(view).Interface); 71 | 72 | protected void CurrentChangedCore(object pDesktopOld, object pDesktopNew) 73 | => this.Notification.CurrentVirtualDesktopChanged(this.Wrap(pDesktopOld), this.Wrap(pDesktopNew)); 74 | 75 | private IVirtualDesktop Wrap(object desktop) 76 | => this.Factory.VirtualDesktop(desktop).Interface; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22621_2215/.interfaces/IVirtualDesktopNotification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using WindowsDesktop.Interop.Build10240; 4 | 5 | namespace WindowsDesktop.Interop.Build22621 6 | { 7 | [ComImport] 8 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 9 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 10 | public interface IVirtualDesktopNotification 11 | { 12 | void VirtualDesktopCreated(IVirtualDesktop pDesktop); 13 | 14 | void VirtualDesktopDestroyBegin(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 15 | 16 | void VirtualDesktopDestroyFailed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 17 | 18 | void VirtualDesktopDestroyed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 19 | 20 | void VirtualDesktopMoved(IVirtualDesktop pDesktop, int nIndexFrom, int nIndexTo); 21 | 22 | void VirtualDesktopRenamed(IVirtualDesktop pDesktop, HString chName); 23 | 24 | void ViewVirtualDesktopChanged(IApplicationView pView); 25 | 26 | void CurrentVirtualDesktopChanged(IVirtualDesktop pDesktopOld, IVirtualDesktop pDesktopNew); 27 | 28 | void VirtualDesktopWallpaperChanged(IVirtualDesktop pDesktop, HString chPath); 29 | 30 | void VirtualDesktopSwitched(IVirtualDesktop pDesktop); 31 | 32 | void RemoteVirtualDesktopConnected(IVirtualDesktop pDesktop); 33 | } 34 | 35 | internal class VirtualDesktopNotification : VirtualDesktopNotificationService.EventListenerBase, IVirtualDesktopNotification 36 | { 37 | public void VirtualDesktopCreated(IVirtualDesktop pDesktop) 38 | { 39 | this.CreatedCore(pDesktop); 40 | } 41 | 42 | public void VirtualDesktopDestroyBegin(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 43 | { 44 | this.DestroyBeginCore(pDesktopDestroyed, pDesktopFallback); 45 | } 46 | 47 | public void VirtualDesktopDestroyFailed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 48 | { 49 | this.DestroyFailedCore(pDesktopDestroyed, pDesktopFallback); 50 | } 51 | 52 | public void VirtualDesktopDestroyed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 53 | { 54 | this.DestroyedCore(pDesktopDestroyed, pDesktopFallback); 55 | } 56 | 57 | public void VirtualDesktopMoved(IVirtualDesktop pDesktop, int nIndexFrom, int nIndexTo) 58 | { 59 | this.MovedCore(pDesktop, nIndexFrom, nIndexTo); 60 | } 61 | 62 | public void VirtualDesktopRenamed(IVirtualDesktop pDesktop, HString chName) 63 | { 64 | this.RenamedCore(pDesktop, chName); 65 | } 66 | 67 | public void ViewVirtualDesktopChanged(IApplicationView pView) 68 | { 69 | this.ViewChangedCore(pView); 70 | } 71 | 72 | public void CurrentVirtualDesktopChanged(IVirtualDesktop pDesktopOld, IVirtualDesktop pDesktopNew) 73 | { 74 | this.CurrentChangedCore(pDesktopOld, pDesktopNew); 75 | } 76 | 77 | public void VirtualDesktopWallpaperChanged(IVirtualDesktop pDesktop, HString chPath) 78 | { 79 | this.WallpaperChangedCore(pDesktop, chPath); 80 | } 81 | 82 | public void VirtualDesktopSwitched(IVirtualDesktop pDesktop) 83 | { 84 | this.SwitchedCore(pDesktop); 85 | } 86 | 87 | public void RemoteVirtualDesktopConnected(IVirtualDesktop pDesktop) 88 | { 89 | this.RemoteConnectedCore(pDesktop); 90 | } 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build26100_0000/.interfaces/IVirtualDesktopNotification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using WindowsDesktop.Interop.Build10240; 4 | 5 | namespace WindowsDesktop.Interop.Build26100 6 | { 7 | [ComImport] 8 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 9 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 10 | public interface IVirtualDesktopNotification 11 | { 12 | void VirtualDesktopCreated(IVirtualDesktop pDesktop); 13 | 14 | void VirtualDesktopDestroyBegin(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 15 | 16 | void VirtualDesktopDestroyFailed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 17 | 18 | void VirtualDesktopDestroyed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); 19 | 20 | void VirtualDesktopMoved(IVirtualDesktop pDesktop, int nIndexFrom, int nIndexTo); 21 | 22 | void VirtualDesktopRenamed(IVirtualDesktop pDesktop, HString chName); 23 | 24 | void ViewVirtualDesktopChanged(IApplicationView pView); 25 | 26 | void CurrentVirtualDesktopChanged(IVirtualDesktop pDesktopOld, IVirtualDesktop pDesktopNew); 27 | 28 | void VirtualDesktopWallpaperChanged(IVirtualDesktop pDesktop, HString chPath); 29 | 30 | void VirtualDesktopSwitched(IVirtualDesktop pDesktop); 31 | 32 | void RemoteVirtualDesktopConnected(IVirtualDesktop pDesktop); 33 | } 34 | 35 | internal class VirtualDesktopNotification : VirtualDesktopNotificationService.EventListenerBase, IVirtualDesktopNotification 36 | { 37 | public void VirtualDesktopCreated(IVirtualDesktop pDesktop) 38 | { 39 | this.CreatedCore(pDesktop); 40 | } 41 | 42 | public void VirtualDesktopDestroyBegin(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 43 | { 44 | this.DestroyBeginCore(pDesktopDestroyed, pDesktopFallback); 45 | } 46 | 47 | public void VirtualDesktopDestroyFailed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 48 | { 49 | this.DestroyFailedCore(pDesktopDestroyed, pDesktopFallback); 50 | } 51 | 52 | public void VirtualDesktopDestroyed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 53 | { 54 | this.DestroyedCore(pDesktopDestroyed, pDesktopFallback); 55 | } 56 | 57 | public void VirtualDesktopMoved(IVirtualDesktop pDesktop, int nIndexFrom, int nIndexTo) 58 | { 59 | this.MovedCore(pDesktop, nIndexFrom, nIndexTo); 60 | } 61 | 62 | public void VirtualDesktopRenamed(IVirtualDesktop pDesktop, HString chName) 63 | { 64 | this.RenamedCore(pDesktop, chName); 65 | } 66 | 67 | public void ViewVirtualDesktopChanged(IApplicationView pView) 68 | { 69 | this.ViewChangedCore(pView); 70 | } 71 | 72 | public void CurrentVirtualDesktopChanged(IVirtualDesktop pDesktopOld, IVirtualDesktop pDesktopNew) 73 | { 74 | this.CurrentChangedCore(pDesktopOld, pDesktopNew); 75 | } 76 | 77 | public void VirtualDesktopWallpaperChanged(IVirtualDesktop pDesktop, HString chPath) 78 | { 79 | this.WallpaperChangedCore(pDesktop, chPath); 80 | } 81 | 82 | public void VirtualDesktopSwitched(IVirtualDesktop pDesktop) 83 | { 84 | this.SwitchedCore(pDesktop); 85 | } 86 | 87 | public void RemoteVirtualDesktopConnected(IVirtualDesktop pDesktop) 88 | { 89 | this.RemoteConnectedCore(pDesktop); 90 | } 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22000_0000/VirtualDesktopNotificationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using WindowsDesktop.Interop.Proxy; 6 | using WindowsDesktop.Utils; 7 | 8 | namespace WindowsDesktop.Interop.Build22000; 9 | 10 | public class VirtualDesktopNotificationService : ComWrapperBase, IVirtualDesktopNotificationService 11 | { 12 | private readonly ComWrapperFactory _factory; 13 | 14 | internal VirtualDesktopNotificationService(ComInterfaceAssembly assembly, ComWrapperFactory factory) 15 | : base(assembly, CLSID.VirtualDesktopNotificationService) 16 | { 17 | this._factory = factory; 18 | } 19 | 20 | public IDisposable Register(IVirtualDesktopNotification notification) 21 | { 22 | var type = this.ComInterfaceAssembly.GetType("VirtualDesktopNotification"); 23 | var listener = Activator.CreateInstance(type) as EventListenerBase 24 | ?? throw new Exception($"{nameof(EventListenerBase)} inheritance type is not found in the COM interface assembly."); 25 | 26 | listener.Notification = notification; 27 | listener.Factory = this._factory; 28 | 29 | var dwCookie = this.InvokeMethod(Args(listener)); 30 | return Disposable.Create(() => this.Unregister(dwCookie)); 31 | } 32 | 33 | private void Unregister(uint dwCookie) 34 | { 35 | try 36 | { 37 | this.InvokeMethod(Args(dwCookie)); 38 | } 39 | catch (COMException ex) when (ex.Match(HResult.RPC_S_SERVER_UNAVAILABLE)) 40 | { 41 | // Nothing particular to do. 42 | } 43 | } 44 | 45 | public abstract class EventListenerBase 46 | { 47 | internal ComWrapperFactory Factory { get; set; } = null!; 48 | 49 | internal IVirtualDesktopNotification Notification { get; set; } = null!; 50 | 51 | protected void CreatedCore(object pDesktop) 52 | => this.Notification.VirtualDesktopCreated(this.Wrap(pDesktop)); 53 | 54 | protected void DestroyBeginCore(object pDesktopDestroyed, object pDesktopFallback) 55 | => this.Notification.VirtualDesktopDestroyBegin(this.Wrap(pDesktopDestroyed), this.Wrap(pDesktopFallback)); 56 | 57 | protected void DestroyFailedCore(object pDesktopDestroyed, object pDesktopFallback) 58 | => this.Notification.VirtualDesktopDestroyFailed(this.Wrap(pDesktopDestroyed), this.Wrap(pDesktopFallback)); 59 | 60 | protected void DestroyedCore(object pDesktopDestroyed, object pDesktopFallback) 61 | => this.Notification.VirtualDesktopDestroyed(this.Wrap(pDesktopDestroyed), this.Wrap(pDesktopFallback)); 62 | 63 | protected void MovedCore(object p0, object pDesktop, int nIndexFrom, int nIndexTo) 64 | => this.Notification.VirtualDesktopMoved(this.Wrap(pDesktop), nIndexFrom, nIndexTo); 65 | 66 | protected void RenamedCore(object pDesktop, HString chName) 67 | => this.Notification.VirtualDesktopRenamed(this.Wrap(pDesktop), chName); 68 | 69 | protected void ViewChangedCore(object view) 70 | => this.Notification.ViewVirtualDesktopChanged(this.Factory.ApplicationView(view).Interface); 71 | 72 | protected void CurrentChangedCore(object pDesktopOld, object pDesktopNew) 73 | => this.Notification.CurrentVirtualDesktopChanged(this.Wrap(pDesktopOld), this.Wrap(pDesktopNew)); 74 | 75 | protected void WallpaperChangedCore(object pDesktop, HString chPath) 76 | => this.Notification.VirtualDesktopWallpaperChanged(this.Wrap(pDesktop), chPath); 77 | 78 | private IVirtualDesktop Wrap(object desktop) 79 | => this.Factory.VirtualDesktop(desktop).Interface; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/VirtualDesktop.WPF/WindowExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows; 5 | using System.Windows.Interop; 6 | using System.Windows.Media; 7 | 8 | namespace WindowsDesktop; 9 | 10 | public static class WindowExtensions 11 | { 12 | /// 13 | /// Determines whether this window is on the current virtual desktop. 14 | /// 15 | public static bool IsCurrentVirtualDesktop(this Window window) 16 | { 17 | return VirtualDesktop.IsCurrentVirtualDesktop(window.GetHandle()); 18 | } 19 | 20 | /// 21 | /// Returns the virtual desktop this window is located on. 22 | /// 23 | public static VirtualDesktop? GetCurrentDesktop(this Window window) 24 | { 25 | return VirtualDesktop.FromHwnd(window.GetHandle()); 26 | } 27 | 28 | /// 29 | /// Moves a window to the specified virtual desktop. 30 | /// 31 | public static void MoveToDesktop(this Window window, VirtualDesktop virtualDesktop) 32 | { 33 | VirtualDesktop.MoveToDesktop(window.GetHandle(), virtualDesktop); 34 | } 35 | 36 | /// 37 | /// Switches to a virtual desktop and moves the specified window to that desktop. 38 | /// 39 | /// The virtual desktop to move the window to. 40 | /// The window to move. 41 | public static void SwitchAndMove(this VirtualDesktop virtualDesktop, Window window) 42 | { 43 | if (window.IsPinned() == false) window.MoveToDesktop(virtualDesktop); 44 | virtualDesktop.Switch(); 45 | } 46 | 47 | /// 48 | /// Determines whether this window is pinned. 49 | /// 50 | /// if pinned, otherwise. 51 | public static bool IsPinned(this Window window) 52 | { 53 | return VirtualDesktop.IsPinnedWindow(window.GetHandle()); 54 | } 55 | 56 | /// 57 | /// Pins a window, showing it on all virtual desktops. 58 | /// 59 | /// if already pinned or successfully pinned, otherwise (most of the time, the target window is not found or not ready). 60 | public static bool Pin(this Window window) 61 | { 62 | return VirtualDesktop.PinWindow(window.GetHandle()); 63 | } 64 | 65 | /// 66 | /// Unpins a window. 67 | /// 68 | /// if already unpinned or successfully unpinned, otherwise (most of the time, the target window is not found or not ready). 69 | public static bool Unpin(this Window window) 70 | { 71 | return VirtualDesktop.UnpinWindow(window.GetHandle()); 72 | } 73 | 74 | /// 75 | /// Toggles a window between being pinned and unpinned. 76 | /// 77 | /// if successfully toggled, otherwise (most of the time, the target window is not found or not ready). 78 | public static bool TogglePin(this Window window) 79 | { 80 | var handle = window.GetHandle(); 81 | 82 | return VirtualDesktop.IsPinnedWindow(handle) 83 | ? VirtualDesktop.UnpinWindow(handle) 84 | : VirtualDesktop.PinWindow(handle); 85 | } 86 | 87 | /// 88 | /// Returns the window handle for this . 89 | /// 90 | public static IntPtr GetHandle(this Visual visual) 91 | => PresentationSource.FromVisual(visual) is HwndSource hwndSource 92 | ? hwndSource.Handle 93 | : throw new ArgumentException("Unable to get a window handle. Call it after the Window.SourceInitialized event is fired.", nameof(visual)); 94 | } 95 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/IID.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Specialized; 4 | using System.Configuration; 5 | using System.Linq; 6 | using System.Text.RegularExpressions; 7 | using WindowsDesktop.Properties; 8 | using Microsoft.Win32; 9 | using System.Globalization; 10 | using WindowsDesktop.Utils; 11 | 12 | namespace WindowsDesktop.Interop; 13 | 14 | internal record OsBuildSettings( 15 | Version osBuild, 16 | SettingsProperty prop); 17 | 18 | internal static class IID 19 | { 20 | private static readonly Regex _osBuildRegex = new(@"v_(?\d+_\d+)"); 21 | 22 | // ReSharper disable once InconsistentNaming 23 | public static Dictionary GetIIDs(string[] interfaceNames) 24 | { 25 | var result = new Dictionary(); 26 | 27 | // Order configuration props by build version 28 | var orderedProps = Settings.Default.Properties.OfType() 29 | .Select(prop => 30 | { 31 | if (Version.TryParse(OS.VersionPrefix + _osBuildRegex.Match(prop.Name).Groups["build"].ToString().Replace('_','.'), out var build)) 32 | { 33 | return new OsBuildSettings(build, prop); 34 | } 35 | 36 | return null; 37 | }) 38 | .Where(s => s != null) 39 | .OrderByDescending(s => s.osBuild) 40 | .ToArray(); 41 | 42 | // TODO: Select per major version first? 43 | // Find first prop with build version <= current OS version 44 | var selectedSettings = orderedProps.FirstOrDefault(p => 45 | p.osBuild <= OS.Build 46 | ); 47 | 48 | if (selectedSettings == null) 49 | { 50 | var supportedBuilds = orderedProps.Select(v => v.osBuild).ToArray(); 51 | throw new ConfigurationException( 52 | "Invalid application configuration. Unable to determine interop interfaces for " + 53 | $"current OS Build: {OS.Build}. All configured OS Builds " + 54 | $"have build version greater than current OS: {supportedBuilds}"); 55 | } 56 | 57 | foreach (var str in (StringCollection)Settings.Default[selectedSettings.prop.Name]) 58 | { 59 | if (str == null) continue; 60 | 61 | var pair = str.Split(','); 62 | if (pair.Length != 2) continue; 63 | if (interfaceNames.Contains(pair[0]) == false || result.ContainsKey(pair[0])) continue; 64 | if (Guid.TryParse(pair[1], out var guid) == false) continue; 65 | 66 | result.Add(pair[0], guid); 67 | } 68 | 69 | var except = interfaceNames.Except(result.Keys).ToArray(); 70 | if (except.Length > 0) 71 | { 72 | foreach (var (key, value) in GetIIDsFromRegistry(except)) result.Add(key, value); 73 | } 74 | 75 | return result; 76 | } 77 | 78 | // ReSharper disable once InconsistentNaming 79 | private static Dictionary GetIIDsFromRegistry(string[] targets) 80 | { 81 | using var interfaceKey = Registry.ClassesRoot.OpenSubKey("Interface") 82 | ?? throw new Exception(@"Registry key '\HKEY_CLASSES_ROOT\Interface' is missing."); 83 | 84 | var result = new Dictionary(); 85 | 86 | foreach (var name in interfaceKey.GetSubKeyNames()) 87 | { 88 | using var key = interfaceKey.OpenSubKey(name); 89 | 90 | if (key?.GetValue("") is string value) 91 | { 92 | var match = targets.FirstOrDefault(x => x == value); 93 | if (match != null && Guid.TryParse(key.Name.Split('\\').Last(), out var guid)) 94 | { 95 | result[match] = guid; 96 | } 97 | } 98 | } 99 | 100 | return result; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/VirtualDesktop.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.32112.339 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".samples", ".samples", "{17B4686F-1AFC-4D5E-BCD8-408DE4CEC3C3}" 7 | ProjectSection(SolutionItems) = preProject 8 | ..\samples\README.md = ..\samples\README.md 9 | EndProjectSection 10 | EndProject 11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VirtualDesktop.WPF", "VirtualDesktop.WPF\VirtualDesktop.WPF.csproj", "{0C17C595-1669-4333-8153-A04676AA3EF3}" 12 | EndProject 13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VirtualDesktop.Showcase", "..\samples\VirtualDesktop.Showcase\VirtualDesktop.Showcase.csproj", "{EEF46F33-3DDB-4D77-9054-4273332745B4}" 14 | EndProject 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VirtualDesktop.WinForms", "VirtualDesktop.WinForms\VirtualDesktop.WinForms.csproj", "{4B6FB0EB-943E-42C9-8CC9-990D84A2EEDB}" 16 | EndProject 17 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VirtualDesktop", "VirtualDesktop\VirtualDesktop.csproj", "{B8A37B59-0F48-4A44-ADF7-96C54AC6CE61}" 18 | EndProject 19 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".config", ".config", "{335CE9BA-A467-4667-8707-D4B779EC8005}" 20 | ProjectSection(SolutionItems) = preProject 21 | ..\.editorconfig = ..\.editorconfig 22 | ..\.gitattributes = ..\.gitattributes 23 | ..\.gitignore = ..\.gitignore 24 | EndProjectSection 25 | EndProject 26 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{A58CDA13-2F14-4F77-AFF3-6F91C7993E5B}" 27 | ProjectSection(SolutionItems) = preProject 28 | ..\.github\workflows\build.yml = ..\.github\workflows\build.yml 29 | ..\.github\workflows\publish.yml = ..\.github\workflows\publish.yml 30 | EndProjectSection 31 | EndProject 32 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docs", ".docs", "{8A097A9D-BF69-424A-8477-24FD5FE334EF}" 33 | ProjectSection(SolutionItems) = preProject 34 | ..\README.md = ..\README.md 35 | EndProjectSection 36 | EndProject 37 | Global 38 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 39 | Debug|Any CPU = Debug|Any CPU 40 | Release|Any CPU = Release|Any CPU 41 | EndGlobalSection 42 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 43 | {0C17C595-1669-4333-8153-A04676AA3EF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {0C17C595-1669-4333-8153-A04676AA3EF3}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {0C17C595-1669-4333-8153-A04676AA3EF3}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {0C17C595-1669-4333-8153-A04676AA3EF3}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {EEF46F33-3DDB-4D77-9054-4273332745B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {EEF46F33-3DDB-4D77-9054-4273332745B4}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {EEF46F33-3DDB-4D77-9054-4273332745B4}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {4B6FB0EB-943E-42C9-8CC9-990D84A2EEDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {4B6FB0EB-943E-42C9-8CC9-990D84A2EEDB}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {4B6FB0EB-943E-42C9-8CC9-990D84A2EEDB}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {4B6FB0EB-943E-42C9-8CC9-990D84A2EEDB}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {B8A37B59-0F48-4A44-ADF7-96C54AC6CE61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {B8A37B59-0F48-4A44-ADF7-96C54AC6CE61}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {B8A37B59-0F48-4A44-ADF7-96C54AC6CE61}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {B8A37B59-0F48-4A44-ADF7-96C54AC6CE61}.Release|Any CPU.Build.0 = Release|Any CPU 58 | EndGlobalSection 59 | GlobalSection(SolutionProperties) = preSolution 60 | HideSolutionNode = FALSE 61 | EndGlobalSection 62 | GlobalSection(NestedProjects) = preSolution 63 | {EEF46F33-3DDB-4D77-9054-4273332745B4} = {17B4686F-1AFC-4D5E-BCD8-408DE4CEC3C3} 64 | EndGlobalSection 65 | GlobalSection(ExtensibilityGlobals) = postSolution 66 | SolutionGuid = {0EDF8EBE-5CA7-414F-B7EA-04FC9D5D0FE7} 67 | EndGlobalSection 68 | EndGlobal 69 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studo 2015 cache/options directory 26 | .vs/ 27 | 28 | # Visual Studo Code cache/options directory 29 | .vscode/ 30 | 31 | # MSTest test Results 32 | [Tt]est[Rr]esult*/ 33 | [Bb]uild[Ll]og.* 34 | 35 | # NUNIT 36 | *.VisualState.xml 37 | TestResult.xml 38 | 39 | # Build Results of an ATL Project 40 | [Dd]ebugPS/ 41 | [Rr]eleasePS/ 42 | dlldata.c 43 | 44 | *_i.c 45 | *_p.c 46 | *_i.h 47 | *.ilk 48 | *.meta 49 | *.obj 50 | *.pch 51 | *.pdb 52 | *.pgc 53 | *.pgd 54 | *.rsp 55 | *.sbr 56 | *.tlb 57 | *.tli 58 | *.tlh 59 | *.tmp 60 | *.tmp_proj 61 | *.log 62 | *.vspscc 63 | *.vssscc 64 | .builds 65 | *.pidb 66 | *.svclog 67 | *.scc 68 | 69 | # Chutzpah Test files 70 | _Chutzpah* 71 | 72 | # Visual C++ cache files 73 | ipch/ 74 | *.aps 75 | *.ncb 76 | *.opensdf 77 | *.sdf 78 | *.cachefile 79 | 80 | # Visual Studio profiler 81 | *.psess 82 | *.vsp 83 | *.vspx 84 | 85 | # TFS 2012 Local Workspace 86 | $tf/ 87 | 88 | # Guidance Automation Toolkit 89 | *.gpState 90 | 91 | # ReSharper is a .NET coding add-in 92 | _ReSharper*/ 93 | *.[Rr]e[Ss]harper 94 | *.DotSettings.user 95 | 96 | # JustCode is a .NET coding addin-in 97 | .JustCode 98 | 99 | # TeamCity is a build add-in 100 | _TeamCity* 101 | 102 | # DotCover is a Code Coverage Tool 103 | *.dotCover 104 | 105 | # NCrunch 106 | _NCrunch_* 107 | .*crunch*.local.xml 108 | 109 | # MightyMoose 110 | *.mm.* 111 | AutoTest.Net/ 112 | 113 | # Web workbench (sass) 114 | .sass-cache/ 115 | 116 | # Installshield output folder 117 | [Ee]xpress/ 118 | 119 | # DocProject is a documentation generator add-in 120 | DocProject/buildhelp/ 121 | DocProject/Help/*.HxT 122 | DocProject/Help/*.HxC 123 | DocProject/Help/*.hhc 124 | DocProject/Help/*.hhk 125 | DocProject/Help/*.hhp 126 | DocProject/Help/Html2 127 | DocProject/Help/html 128 | 129 | # Click-Once directory 130 | publish/ 131 | 132 | # Publish Web Output 133 | *.[Pp]ublish.xml 134 | *.azurePubxml 135 | # TODO: Comment the next line if you want to checkin your web deploy settings 136 | # but database connection strings (with potential passwords) will be unencrypted 137 | *.pubxml 138 | *.publishproj 139 | 140 | # NuGet Packages 141 | *.nupkg 142 | # The packages folder can be ignored because of Package Restore 143 | **/packages/* 144 | # except build/, which is used as an MSBuild target. 145 | !**/packages/build/ 146 | # Uncomment if necessary however generally it will be regenerated when needed 147 | #!**/packages/repositories.config 148 | 149 | # Windows Azure Build Output 150 | csx/ 151 | *.build.csdef 152 | 153 | # Windows Store app package directory 154 | AppPackages/ 155 | 156 | # Others 157 | *.[Cc]ache 158 | ClientBin/ 159 | [Ss]tyle[Cc]op.* 160 | ~$* 161 | *~ 162 | *.dbmdl 163 | *.dbproj.schemaview 164 | *.pfx 165 | *.publishsettings 166 | node_modules/ 167 | bower_components/ 168 | 169 | # RIA/Silverlight projects 170 | Generated_Code/ 171 | 172 | # Backup & report files from converting an old project file 173 | # to a newer Visual Studio version. Backup files are not needed, 174 | # because we have git ;-) 175 | _UpgradeReport_Files/ 176 | Backup*/ 177 | UpgradeLog*.XML 178 | UpgradeLog*.htm 179 | 180 | # SQL Server files 181 | *.mdf 182 | *.ldf 183 | 184 | # Business Intelligence projects 185 | *.rdl.data 186 | *.bim.layout 187 | *.bim_*.settings 188 | 189 | # Microsoft Fakes 190 | FakesAssemblies/ 191 | 192 | # Node.js Tools for Visual Studio 193 | .ntvs_analysis.dat 194 | 195 | # Visual Studio 6 build log 196 | *.plg 197 | 198 | # Visual Studio 6 workspace options file 199 | *.opt 200 | 201 | # Rider settings directory 202 | .idea -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build22621_2215/VirtualDesktopNotificationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using WindowsDesktop.Interop.Proxy; 6 | using WindowsDesktop.Utils; 7 | 8 | namespace WindowsDesktop.Interop.Build22621; 9 | 10 | public class VirtualDesktopNotificationService : ComWrapperBase, IVirtualDesktopNotificationService 11 | { 12 | private readonly ComWrapperFactory _factory; 13 | 14 | internal VirtualDesktopNotificationService(ComInterfaceAssembly assembly, ComWrapperFactory factory) 15 | : base(assembly, CLSID.VirtualDesktopNotificationService) 16 | { 17 | this._factory = factory; 18 | } 19 | 20 | public IDisposable Register(IVirtualDesktopNotification notification) 21 | { 22 | var type = this.ComInterfaceAssembly.GetType("VirtualDesktopNotification"); 23 | var listener = Activator.CreateInstance(type) as EventListenerBase 24 | ?? throw new Exception($"{nameof(EventListenerBase)} inheritance type is not found in the COM interface assembly."); 25 | 26 | listener.Notification = notification; 27 | listener.Factory = this._factory; 28 | 29 | var dwCookie = this.InvokeMethod(Args(listener)); 30 | return Disposable.Create(() => this.Unregister(dwCookie)); 31 | } 32 | 33 | private void Unregister(uint dwCookie) 34 | { 35 | try 36 | { 37 | this.InvokeMethod(Args(dwCookie)); 38 | } 39 | catch (COMException ex) when (ex.Match(HResult.RPC_S_SERVER_UNAVAILABLE)) 40 | { 41 | // Nothing particular to do. 42 | } 43 | } 44 | 45 | public abstract class EventListenerBase 46 | { 47 | internal ComWrapperFactory Factory { get; set; } = null!; 48 | 49 | internal IVirtualDesktopNotification Notification { get; set; } = null!; 50 | 51 | protected void CreatedCore(object pDesktop) 52 | => this.Notification.VirtualDesktopCreated(this.Wrap(pDesktop)); 53 | 54 | protected void DestroyBeginCore(object pDesktopDestroyed, object pDesktopFallback) 55 | => this.Notification.VirtualDesktopDestroyBegin(this.Wrap(pDesktopDestroyed), this.Wrap(pDesktopFallback)); 56 | 57 | protected void DestroyFailedCore(object pDesktopDestroyed, object pDesktopFallback) 58 | => this.Notification.VirtualDesktopDestroyFailed(this.Wrap(pDesktopDestroyed), this.Wrap(pDesktopFallback)); 59 | 60 | protected void DestroyedCore(object pDesktopDestroyed, object pDesktopFallback) 61 | => this.Notification.VirtualDesktopDestroyed(this.Wrap(pDesktopDestroyed), this.Wrap(pDesktopFallback)); 62 | 63 | protected void MovedCore(object pDesktop, int nIndexFrom, int nIndexTo) 64 | => this.Notification.VirtualDesktopMoved(this.Wrap(pDesktop), nIndexFrom, nIndexTo); 65 | 66 | protected void RenamedCore(object pDesktop, HString chName) 67 | => this.Notification.VirtualDesktopRenamed(this.Wrap(pDesktop), chName); 68 | 69 | protected void ViewChangedCore(object view) 70 | => this.Notification.ViewVirtualDesktopChanged(this.Factory.ApplicationView(view).Interface); 71 | 72 | protected void CurrentChangedCore(object pDesktopOld, object pDesktopNew) 73 | => this.Notification.CurrentVirtualDesktopChanged(this.Wrap(pDesktopOld), this.Wrap(pDesktopNew)); 74 | 75 | protected void WallpaperChangedCore(object pDesktop, HString chPath) 76 | => this.Notification.VirtualDesktopWallpaperChanged(this.Wrap(pDesktop), chPath); 77 | 78 | protected void SwitchedCore(object pDesktop) 79 | => this.Notification.VirtualDesktopSwitched(this.Wrap(pDesktop)); 80 | 81 | protected void RemoteConnectedCore(object pDesktop) 82 | => this.Notification.RemoteVirtualDesktopConnected(this.Wrap(pDesktop)); 83 | 84 | private IVirtualDesktop Wrap(object desktop) 85 | => this.Factory.VirtualDesktop(desktop).Interface; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build26100_0000/VirtualDesktopNotificationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using WindowsDesktop.Interop.Proxy; 6 | using WindowsDesktop.Utils; 7 | 8 | namespace WindowsDesktop.Interop.Build26100; 9 | 10 | public class VirtualDesktopNotificationService : ComWrapperBase, IVirtualDesktopNotificationService 11 | { 12 | private readonly ComWrapperFactory _factory; 13 | 14 | internal VirtualDesktopNotificationService(ComInterfaceAssembly assembly, ComWrapperFactory factory) 15 | : base(assembly, CLSID.VirtualDesktopNotificationService) 16 | { 17 | this._factory = factory; 18 | } 19 | 20 | public IDisposable Register(IVirtualDesktopNotification notification) 21 | { 22 | var type = this.ComInterfaceAssembly.GetType("VirtualDesktopNotification"); 23 | var listener = Activator.CreateInstance(type) as EventListenerBase 24 | ?? throw new Exception($"{nameof(EventListenerBase)} inheritance type is not found in the COM interface assembly."); 25 | 26 | listener.Notification = notification; 27 | listener.Factory = this._factory; 28 | 29 | var dwCookie = this.InvokeMethod(Args(listener)); 30 | return Disposable.Create(() => this.Unregister(dwCookie)); 31 | } 32 | 33 | private void Unregister(uint dwCookie) 34 | { 35 | try 36 | { 37 | this.InvokeMethod(Args(dwCookie)); 38 | } 39 | catch (COMException ex) when (ex.Match(HResult.RPC_S_SERVER_UNAVAILABLE)) 40 | { 41 | // Nothing particular to do. 42 | } 43 | } 44 | 45 | public abstract class EventListenerBase 46 | { 47 | internal ComWrapperFactory Factory { get; set; } = null!; 48 | 49 | internal IVirtualDesktopNotification Notification { get; set; } = null!; 50 | 51 | protected void CreatedCore(object pDesktop) 52 | => this.Notification.VirtualDesktopCreated(this.Wrap(pDesktop)); 53 | 54 | protected void DestroyBeginCore(object pDesktopDestroyed, object pDesktopFallback) 55 | => this.Notification.VirtualDesktopDestroyBegin(this.Wrap(pDesktopDestroyed), this.Wrap(pDesktopFallback)); 56 | 57 | protected void DestroyFailedCore(object pDesktopDestroyed, object pDesktopFallback) 58 | => this.Notification.VirtualDesktopDestroyFailed(this.Wrap(pDesktopDestroyed), this.Wrap(pDesktopFallback)); 59 | 60 | protected void DestroyedCore(object pDesktopDestroyed, object pDesktopFallback) 61 | => this.Notification.VirtualDesktopDestroyed(this.Wrap(pDesktopDestroyed), this.Wrap(pDesktopFallback)); 62 | 63 | protected void MovedCore(object pDesktop, int nIndexFrom, int nIndexTo) 64 | => this.Notification.VirtualDesktopMoved(this.Wrap(pDesktop), nIndexFrom, nIndexTo); 65 | 66 | protected void RenamedCore(object pDesktop, HString chName) 67 | => this.Notification.VirtualDesktopRenamed(this.Wrap(pDesktop), chName); 68 | 69 | protected void ViewChangedCore(object view) 70 | => this.Notification.ViewVirtualDesktopChanged(this.Factory.ApplicationView(view).Interface); 71 | 72 | protected void CurrentChangedCore(object pDesktopOld, object pDesktopNew) 73 | => this.Notification.CurrentVirtualDesktopChanged(this.Wrap(pDesktopOld), this.Wrap(pDesktopNew)); 74 | 75 | protected void WallpaperChangedCore(object pDesktop, HString chPath) 76 | => this.Notification.VirtualDesktopWallpaperChanged(this.Wrap(pDesktop), chPath); 77 | 78 | protected void SwitchedCore(object pDesktop) 79 | => this.Notification.VirtualDesktopSwitched(this.Wrap(pDesktop)); 80 | 81 | protected void RemoteConnectedCore(object pDesktop) 82 | => this.Notification.RemoteVirtualDesktopConnected(this.Wrap(pDesktop)); 83 | 84 | private IVirtualDesktop Wrap(object desktop) 85 | => this.Factory.VirtualDesktop(desktop).Interface; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/Build10240_0000/.interfaces/IApplicationView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace WindowsDesktop.Interop.Build10240 5 | { 6 | // ## Fixes for breaking changes in .NET 5 7 | // * InterfaceIsIInspectable -> InterfaceIsIUnknown 8 | // * Add three dummy entries to the start of the interface; Proc3() - Proc5() 9 | // 10 | // see also: https://docs.microsoft.com/en-us/dotnet/core/compatibility/interop/5.0/casting-rcw-to-inspectable-interface-throws-exception 11 | 12 | [ComImport] 13 | [Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */] 14 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 15 | public interface IApplicationView 16 | { 17 | void GetIids(out ulong iidCount, out IntPtr iids); 18 | 19 | HString GetRuntimeClassName(); 20 | 21 | IntPtr GetTrustLevel(); 22 | 23 | void SetFocus(); 24 | 25 | void SwitchTo(); 26 | 27 | void TryInvokeBack(IntPtr callback); 28 | 29 | IntPtr GetThumbnailWindow(); 30 | 31 | IntPtr GetMonitor(); 32 | 33 | int GetVisibility(); 34 | 35 | void SetCloak(ApplicationViewCloakType cloakType, int unknown); 36 | 37 | IntPtr GetPosition(in Guid guid, out IntPtr position); 38 | 39 | void SetPosition(in IntPtr position); 40 | 41 | void InsertAfterWindow(IntPtr hwnd); 42 | 43 | Rect GetExtendedFramePosition(); 44 | 45 | [return: MarshalAs(UnmanagedType.LPWStr)] 46 | string GetAppUserModelId(); 47 | 48 | void SetAppUserModelId([MarshalAs(UnmanagedType.LPWStr)] string id); 49 | 50 | bool IsEqualByAppUserModelId(string id); 51 | 52 | uint GetViewState(); 53 | 54 | void SetViewState(uint state); 55 | 56 | int GetNeediness(); 57 | 58 | ulong GetLastActivationTimestamp(); 59 | 60 | void SetLastActivationTimestamp(ulong timestamp); 61 | 62 | Guid GetVirtualDesktopId(); 63 | 64 | void SetVirtualDesktopId(in Guid guid); 65 | 66 | int GetShowInSwitchers(); 67 | 68 | void SetShowInSwitchers(int flag); 69 | 70 | int GetScaleFactor(); 71 | 72 | bool CanReceiveInput(); 73 | 74 | ApplicationViewCompatibilityPolicy GetCompatibilityPolicyType(); 75 | 76 | void SetCompatibilityPolicyType(ApplicationViewCompatibilityPolicy flags); 77 | 78 | IntPtr GetPositionPriority(); 79 | 80 | void SetPositionPriority(IntPtr priority); 81 | 82 | void GetSizeConstraints(IntPtr monitor, out Size size1, out Size size2); 83 | 84 | void GetSizeConstraintsForDpi(uint uint1, out Size size1, out Size size2); 85 | 86 | void SetSizeConstraintsForDpi(ref uint uint1, in Size size1, in Size size2); 87 | 88 | int QuerySizeConstraintsFromApp(); 89 | 90 | void OnMinSizePreferencesUpdated(IntPtr hwnd); 91 | 92 | void ApplyOperation(IntPtr operation); 93 | 94 | bool IsTray(); 95 | 96 | bool IsInHighZOrderBand(); 97 | 98 | bool IsSplashScreenPresented(); 99 | 100 | void Flash(); 101 | 102 | IApplicationView GetRootSwitchableOwner(); 103 | 104 | IObjectArray EnumerateOwnershipTree(); 105 | 106 | [return: MarshalAs(UnmanagedType.LPWStr)] 107 | string GetEnterpriseId(); 108 | 109 | bool IsMirrored(); 110 | } 111 | 112 | [StructLayout(LayoutKind.Sequential)] 113 | public struct Size 114 | { 115 | public int X; 116 | public int Y; 117 | } 118 | 119 | [StructLayout(LayoutKind.Sequential)] 120 | public struct Rect 121 | { 122 | public int Left; 123 | public int Top; 124 | public int Right; 125 | public int Bottom; 126 | } 127 | 128 | public enum ApplicationViewCloakType 129 | { 130 | AVCT_NONE = 0, 131 | AVCT_DEFAULT = 1, 132 | AVCT_VIRTUAL_DESKTOP = 2 133 | } 134 | 135 | public enum ApplicationViewCompatibilityPolicy 136 | { 137 | AVCP_NONE = 0, 138 | AVCP_SMALL_SCREEN = 1, 139 | AVCP_TABLET_SMALL_SCREEN = 2, 140 | AVCP_VERY_SMALL_SCREEN = 3, 141 | AVCP_HIGH_SCALE_FACTOR = 4 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/VirtualDesktop/VirtualDesktopEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using WindowsDesktop.Interop.Proxy; 3 | 4 | namespace WindowsDesktop; 5 | 6 | /// 7 | /// Provides data for the event. 8 | /// 9 | public class VirtualDesktopRenamedEventArgs : EventArgs 10 | { 11 | public VirtualDesktop Desktop { get; } 12 | 13 | public string Name { get; } 14 | 15 | public VirtualDesktopRenamedEventArgs(VirtualDesktop desktop, string name) 16 | { 17 | this.Desktop = desktop; 18 | this.Name = name; 19 | } 20 | } 21 | 22 | /// 23 | /// Provides data for the event. 24 | /// 25 | public class VirtualDesktopWallpaperChangedEventArgs : EventArgs 26 | { 27 | public VirtualDesktop Desktop { get; } 28 | 29 | public string Path { get; } 30 | 31 | public VirtualDesktopWallpaperChangedEventArgs(VirtualDesktop desktop, string path) 32 | { 33 | this.Desktop = desktop; 34 | this.Path = path; 35 | } 36 | } 37 | 38 | /// 39 | /// Provides data for the event. 40 | /// 41 | public class VirtualDesktopChangedEventArgs : EventArgs 42 | { 43 | public VirtualDesktop OldDesktop { get; } 44 | 45 | public VirtualDesktop NewDesktop { get; } 46 | 47 | public VirtualDesktopChangedEventArgs(VirtualDesktop oldDesktop, VirtualDesktop newDesktop) 48 | { 49 | this.OldDesktop = oldDesktop; 50 | this.NewDesktop = newDesktop; 51 | } 52 | 53 | internal VirtualDesktopChangedEventArgs(IVirtualDesktop oldDesktop, IVirtualDesktop newDesktop) 54 | : this(oldDesktop.ToVirtualDesktop(), newDesktop.ToVirtualDesktop()) 55 | { 56 | } 57 | } 58 | 59 | /// 60 | /// Provides data for the event. 61 | /// 62 | public class VirtualDesktopMovedEventArgs : EventArgs 63 | { 64 | public VirtualDesktop Desktop { get; } 65 | 66 | public int OldIndex { get; } 67 | 68 | public int NewIndex { get; } 69 | 70 | public VirtualDesktopMovedEventArgs(VirtualDesktop desktop, int oldIndex, int newIndex) 71 | { 72 | this.Desktop = desktop; 73 | this.OldIndex = oldIndex; 74 | this.NewIndex = newIndex; 75 | } 76 | 77 | internal VirtualDesktopMovedEventArgs(IVirtualDesktop desktop, int oldIndex, int newIndex) 78 | : this(desktop.ToVirtualDesktop(), oldIndex, newIndex) 79 | { 80 | } 81 | } 82 | 83 | /// 84 | /// Provides data for the , , and events. 85 | /// 86 | public class VirtualDesktopDestroyEventArgs : EventArgs 87 | { 88 | /// 89 | /// Gets the virtual desktop that was destroyed. 90 | /// 91 | public VirtualDesktop Destroyed { get; } 92 | 93 | /// 94 | /// Gets the virtual desktop to be displayed after is destroyed. 95 | /// 96 | public VirtualDesktop Fallback { get; } 97 | 98 | public VirtualDesktopDestroyEventArgs(VirtualDesktop destroyed, VirtualDesktop fallback) 99 | { 100 | this.Destroyed = destroyed; 101 | this.Fallback = fallback; 102 | } 103 | 104 | internal VirtualDesktopDestroyEventArgs(IVirtualDesktop destroyed, IVirtualDesktop fallback) 105 | : this(destroyed.ToVirtualDesktop(), fallback.ToVirtualDesktop()) 106 | { 107 | } 108 | } 109 | 110 | /// 111 | /// Provides data for the event. 112 | /// 113 | public class VirtualDesktopSwitchedEventArgs: EventArgs 114 | { 115 | public VirtualDesktop Desktop { get; } 116 | 117 | 118 | public VirtualDesktopSwitchedEventArgs(VirtualDesktop desktop) 119 | { 120 | this.Desktop = desktop; 121 | } 122 | 123 | internal VirtualDesktopSwitchedEventArgs(IVirtualDesktop desktop) 124 | : this(desktop.ToVirtualDesktop()) 125 | { 126 | } 127 | } 128 | 129 | /// 130 | /// Provides data for the event. 131 | /// 132 | public class RemoteVirtualDesktopConnectedEventArgs: EventArgs 133 | { 134 | public VirtualDesktop Desktop { get; } 135 | 136 | 137 | public RemoteVirtualDesktopConnectedEventArgs(VirtualDesktop desktop) 138 | { 139 | this.Desktop = desktop; 140 | } 141 | 142 | internal RemoteVirtualDesktopConnectedEventArgs(IVirtualDesktop desktop) 143 | : this(desktop.ToVirtualDesktop()) 144 | { 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/VirtualDesktop/VirtualDesktop.system.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using WindowsDesktop.Interop; 8 | using WindowsDesktop.Interop.Build10240; 9 | using WindowsDesktop.Interop.Build20348; 10 | using WindowsDesktop.Interop.Build22000; 11 | using WindowsDesktop.Interop.Build22621; 12 | using WindowsDesktop.Interop.Build26100; 13 | using WindowsDesktop.Interop.Proxy; 14 | using WindowsDesktop.Properties; 15 | using WindowsDesktop.Utils; 16 | 17 | namespace WindowsDesktop; 18 | 19 | partial class VirtualDesktop 20 | { 21 | private static readonly VirtualDesktopProvider _provider; 22 | private static readonly ConcurrentDictionary _knownDesktops = new(); 23 | private static readonly ExplorerRestartListenerWindow _explorerRestartListener = new(() => HandleExplorerRestarted()); 24 | private static VirtualDesktopConfiguration _configuration = new(); 25 | private static ComInterfaceAssembly? _assembly; 26 | private static IDisposable? _notificationListener; 27 | private readonly IVirtualDesktop _source; 28 | private string _name; 29 | private string _wallpaperPath; 30 | 31 | /// 32 | /// Gets a value indicating virtual desktops are supported by the host. 33 | /// 34 | public static bool IsSupported 35 | => _provider.IsSupported; 36 | 37 | static VirtualDesktop() 38 | { 39 | 40 | _provider = CreateProvider(); 41 | 42 | Debug.WriteLine($"*** {AssemblyInfo.Title} Library ***"); 43 | Debug.WriteLine($"Version: {AssemblyInfo.VersionString}"); 44 | Debug.WriteLine($"OS Build: {OS.Build}"); 45 | Debug.WriteLine($"Provider: {_provider.GetType().Name}"); 46 | } 47 | 48 | /// 49 | /// Create provider for current OS version. 50 | /// Test order matters. Make sure you test the highest version first. 51 | /// 52 | /// 53 | private static VirtualDesktopProvider CreateProvider() 54 | { 55 | Version v = OS.Build; 56 | 57 | if (v >= new Version(10, 0, 26100, 0)) 58 | { 59 | return new VirtualDesktopProvider26100(); 60 | } 61 | 62 | if (v >= new Version(10, 0, 22621, 2215)) 63 | { 64 | return new VirtualDesktopProvider22621(); 65 | } 66 | 67 | if (v >= new Version(10, 0, 22000, 0)) 68 | { 69 | return new VirtualDesktopProvider22000(); 70 | } 71 | 72 | if (v >= new Version(10, 0, 20348, 0)) 73 | { 74 | return new VirtualDesktopProvider20348(); 75 | } 76 | 77 | if (v >= new Version(10, 0, 10240, 0)) 78 | { 79 | return new VirtualDesktopProvider10240(); 80 | } 81 | 82 | return new VirtualDesktopProvider.NotSupported(); 83 | } 84 | 85 | 86 | private VirtualDesktop(IVirtualDesktop source) 87 | { 88 | this._source = source; 89 | this._name = source.GetName(); 90 | this._wallpaperPath = source.GetWallpaperPath(); 91 | this.Id = source.GetID(); 92 | } 93 | 94 | /// 95 | /// Initialize using the default settings. This method should always be called first. 96 | /// 97 | public static void Configure() 98 | { 99 | InitializeIfNeeded(); 100 | } 101 | 102 | /// 103 | /// Sets the behavior for compiling the assembly. This method should always be called first. 104 | /// 105 | public static void Configure(VirtualDesktopConfiguration configuration) 106 | { 107 | _configuration = configuration; 108 | InitializeIfNeeded(); 109 | } 110 | 111 | internal static VirtualDesktop FromComObject(IVirtualDesktop desktop) 112 | => _knownDesktops.GetOrAdd(desktop.GetID(), _ => new VirtualDesktop(desktop)); 113 | 114 | internal static void InitializeIfNeeded() 115 | { 116 | if (IsSupported == false) throw new NotSupportedException("You must target Windows 10 or later in your 'app.manifest' and run without debugging."); 117 | if (_provider.IsInitialized) return; 118 | 119 | _explorerRestartListener.Show(); 120 | InitializeCore(); 121 | } 122 | 123 | private static void HandleExplorerRestarted() 124 | { 125 | _knownDesktops.Clear(); 126 | _provider.IsInitialized = false; 127 | InitializeCore(); 128 | } 129 | 130 | private static void InitializeCore() 131 | { 132 | _provider.Initialize(_assembly ??= new ComInterfaceAssemblyBuilder(_configuration).GetAssembly()); 133 | 134 | _notificationListener?.Dispose(); 135 | _notificationListener = _provider.VirtualDesktopNotificationService.Register(new EventProxy()); 136 | } 137 | 138 | private static T? SafeInvoke(Func action, params HResult[] hResult) 139 | { 140 | try 141 | { 142 | return action(); 143 | } 144 | catch (COMException ex) when (ex.Match(hResult is { Length: 0 } ? new[] { HResult.TYPE_E_ELEMENTNOTFOUND, } : hResult)) 145 | { 146 | return default; 147 | } 148 | } 149 | 150 | private static bool SafeInvoke(Action action, params HResult[] hResult) 151 | { 152 | try 153 | { 154 | action(); 155 | return true; 156 | } 157 | catch (COMException ex) when (ex.Match(hResult is { Length: 0 } ? new[] { HResult.TYPE_E_ELEMENTNOTFOUND, } : hResult)) 158 | { 159 | return false; 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/VirtualDesktop/VirtualDesktop.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0-windows10.0.19041.0;net7.0-windows10.0.19041.0;net6.0-windows10.0.19041.0 5 | Slions.VirtualDesktop 6 | latest 7 | true 8 | enable 9 | enable 10 | $(PublishVersion) 11 | VirtualDesktop 12 | $(PublishAuthors) 13 | slions.net 14 | Copyright © 2022 Manato KAMEYA 15 | .NET library for Windows multiple desktop operations 16 | https://github.com/Slion/VirtualDesktop 17 | https://github.com/Slion/VirtualDesktop 18 | LICENSE 19 | README.md 20 | Windows;Windows10;Windows11;Desktop;VirtualDesktop; 21 | True 22 | WindowsDesktop 23 | Virtual Desktop API for C# 24 | git 25 | $(PublishReleaseNotes) 26 | true 27 | 28 | 29 | 30 | 1701;1702;1591 31 | 32 | 33 | 34 | 1701;1702;1591 35 | 36 | 37 | 38 | 1701;1702;1591 39 | 40 | 41 | 42 | 1701;1702;1591 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | True 77 | 78 | 79 | 80 | True 81 | \ 82 | 83 | 84 | False 85 | 86 | 87 | 88 | 89 | 90 | True 91 | True 92 | Settings.settings 93 | 94 | 95 | 96 | 97 | 98 | SettingsSingleFileGenerator 99 | Settings.Designer.cs 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/VirtualDesktop/VirtualDesktop.notification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using WindowsDesktop.Interop.Proxy; 6 | using WindowsDesktop.Utils; 7 | 8 | namespace WindowsDesktop; 9 | 10 | partial class VirtualDesktop 11 | { 12 | private static readonly ConcurrentDictionary _viewChangedEventListeners = new(); 13 | 14 | /// 15 | /// Occurs when a virtual desktop is created. 16 | /// 17 | /// 18 | /// See for details. 19 | /// 20 | public static event EventHandler? Created; 21 | 22 | public static event EventHandler? DestroyBegin; 23 | 24 | public static event EventHandler? DestroyFailed; 25 | 26 | /// 27 | /// Occurs when a virtual desktop is destroyed. 28 | /// 29 | /// 30 | /// See for details. 31 | /// 32 | public static event EventHandler? Destroyed; 33 | 34 | /// 35 | /// Occurs when the current virtual desktop is changed. 36 | /// 37 | /// 38 | /// The internal initialization is triggered by the call of a static property/method.
39 | /// Therefore, events are not fired just by subscribing to them.
40 | ///
41 | /// If you want to use only event subscription, the following code is recommended.
42 | /// 43 | /// VirtualDesktop.Configuration(); 44 | /// 45 | ///
46 | public static event EventHandler? CurrentChanged; 47 | 48 | /// 49 | /// Occurs when the virtual desktop is moved. 50 | /// 51 | /// 52 | /// See for details. 53 | /// 54 | public static event EventHandler? Moved; 55 | 56 | /// 57 | /// Occurs when a virtual desktop is renamed. 58 | /// 59 | /// 60 | /// See for details. 61 | /// 62 | public static event EventHandler? Renamed; 63 | 64 | /// 65 | /// Occurs when a virtual desktop wallpaper is changed. 66 | /// 67 | /// 68 | /// See for details. 69 | /// 70 | public static event EventHandler? WallpaperChanged; 71 | 72 | /// 73 | /// Occurs when a virtual desktop is switched. Seems duplicate to ViewVirtualDesktopChanged, the difference is not yet known. Both are fired when swtiching. 74 | /// 75 | /// 76 | /// See for details. 77 | /// 78 | public static event EventHandler? Switched; 79 | 80 | /// 81 | /// Occurs when a remote desktop is connected. Should be related to Windows 365 Cloud PC: https://www.microsoft.com/store/productId/9N1F85V9T8BN. 82 | /// 83 | /// 84 | /// See for details. 85 | /// 86 | public static event EventHandler? RemoteConnected; 87 | 88 | /// 89 | /// Register a listener to receive changes in the application view. 90 | /// 91 | /// The target window handle to receive events from. If specify , all changes will be delivered. 92 | /// Action to be performed. 93 | /// instance for unsubscribing. 94 | public static IDisposable RegisterViewChanged(IntPtr targetHwnd, Action action) 95 | { 96 | InitializeIfNeeded(); 97 | 98 | var listener = _viewChangedEventListeners.GetOrAdd(targetHwnd, x => new ViewChangedListener(x)); 99 | listener.Listeners.Add(action); 100 | 101 | return Disposable.Create(() => listener.Listeners.Remove(action)); 102 | } 103 | 104 | private class EventProxy : IVirtualDesktopNotification 105 | { 106 | public void VirtualDesktopCreated(IVirtualDesktop pDesktop) 107 | => Created?.Invoke(this, pDesktop.ToVirtualDesktop()); 108 | 109 | public void VirtualDesktopDestroyBegin(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 110 | => DestroyBegin?.Invoke(this, new VirtualDesktopDestroyEventArgs(pDesktopDestroyed, pDesktopFallback)); 111 | 112 | public void VirtualDesktopDestroyFailed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 113 | => DestroyFailed?.Invoke(this, new VirtualDesktopDestroyEventArgs(pDesktopDestroyed, pDesktopFallback)); 114 | 115 | public void VirtualDesktopDestroyed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback) 116 | => Destroyed?.Invoke(this, new VirtualDesktopDestroyEventArgs(pDesktopDestroyed, pDesktopFallback)); 117 | 118 | public void VirtualDesktopIsPerMonitorChanged(int i) 119 | { 120 | 121 | } 122 | 123 | 124 | public void VirtualDesktopMoved(IVirtualDesktop pDesktop, int nIndexFrom, int nIndexTo) 125 | => Moved?.Invoke(this, new VirtualDesktopMovedEventArgs(pDesktop, nIndexFrom, nIndexTo)); 126 | 127 | public void ViewVirtualDesktopChanged(IApplicationView pView) 128 | { 129 | if (_viewChangedEventListeners.TryGetValue(IntPtr.Zero, out var all)) all.Call(); 130 | if (_viewChangedEventListeners.TryGetValue(pView.GetThumbnailWindow(), out var listener)) listener.Call(); 131 | } 132 | 133 | public void CurrentVirtualDesktopChanged(IVirtualDesktop pDesktopOld, IVirtualDesktop pDesktopNew) 134 | => CurrentChanged?.Invoke(this, new VirtualDesktopChangedEventArgs(pDesktopOld, pDesktopNew)); 135 | 136 | public void VirtualDesktopRenamed(IVirtualDesktop pDesktop, string chName) 137 | { 138 | var desktop = pDesktop.ToVirtualDesktop(); 139 | desktop._name = chName; 140 | 141 | Renamed?.Invoke(this, new VirtualDesktopRenamedEventArgs(desktop, chName)); 142 | } 143 | 144 | public void VirtualDesktopWallpaperChanged(IVirtualDesktop pDesktop, string chPath) 145 | { 146 | var desktop = pDesktop.ToVirtualDesktop(); 147 | desktop._wallpaperPath = chPath; 148 | 149 | WallpaperChanged?.Invoke(this, new VirtualDesktopWallpaperChangedEventArgs(desktop, chPath)); 150 | } 151 | 152 | public void VirtualDesktopSwitched(IVirtualDesktop pDesktop) => 153 | Switched?.Invoke(this, new VirtualDesktopSwitchedEventArgs(pDesktop)); 154 | 155 | public void RemoteVirtualDesktopConnected(IVirtualDesktop pDesktop) => 156 | RemoteConnected?.Invoke(this, new RemoteVirtualDesktopConnectedEventArgs(pDesktop)); 157 | } 158 | 159 | private class ViewChangedListener 160 | { 161 | private readonly IntPtr _targetHandle; 162 | 163 | public List> Listeners { get; } = new(); 164 | 165 | public ViewChangedListener(IntPtr targetHandle) 166 | { 167 | this._targetHandle = targetHandle; 168 | } 169 | 170 | public void Call() 171 | { 172 | foreach (var listener in this.Listeners) listener(this._targetHandle); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VirtualDesktop 2 | 3 | .NET library for Windows multiple desktop operations 4 | 5 | [![Windows 11](https://img.shields.io/badge/Windows_11-supported-dodgerblue)](https://www.microsoft.com/en-us/windows/learning-center/stay-organized-using-multiple-desktops-windows-11) 6 | [![Windows 10](https://img.shields.io/badge/Windows_10-supported-dodgerblue)](https://support.microsoft.com/en-us/windows/configure-multiple-desktops-in-windows-36f52e38-5b4a-557b-2ff9-e1a60c976434) 7 | 8 | [![Build](https://github.com/Slion/VirtualDesktop/workflows/Build/badge.svg)](https://github.com/Slion/VirtualDesktop/actions/workflows/build.yml) 9 | [![Publish](https://github.com/Slion/VirtualDesktop/workflows/Publish/badge.svg)](https://github.com/Slion/VirtualDesktop/actions/workflows/publish.yml) 10 | [![License](https://img.shields.io/github/license/Slion/VirtualDesktop)](LICENSE) 11 | 12 | | Platform | NuGet | Downloads | 13 | | -- | -- | -- | 14 | | Core | [![NuGet Badge](https://img.shields.io/nuget/v/Slions.VirtualDesktop)](https://www.nuget.org/packages/Slions.VirtualDesktop/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Slions.VirtualDesktop)](https://www.nuget.org/packages/Slions.VirtualDesktop/) | 15 | | Forms | [![NuGet Badge](https://img.shields.io/nuget/v/Slions.VirtualDesktop.WinForms)](https://www.nuget.org/packages/Slions.VirtualDesktop.WinForms/)| [![NuGet Downloads](https://img.shields.io/nuget/dt/Slions.VirtualDesktop.WinForms)](https://www.nuget.org/packages/Slions.VirtualDesktop.WinForms/) | 16 | | WPF | [![NuGet Badge](https://img.shields.io/nuget/v/Slions.VirtualDesktop.WPF)](https://www.nuget.org/packages/Slions.VirtualDesktop.WPF/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/Slions.VirtualDesktop.WPF)](https://www.nuget.org/packages/Slions.VirtualDesktop.WPF/) | 17 | 18 | 19 | ## Features 20 | 21 | * Switch, add, and remove a virtual desktop 22 | * Move any window to any virtual desktop 23 | * Pin any window or application for them to show on all desktops 24 | * Notifications when switching, deleting, or renaming virtual desktops 25 | * Change the wallpaper for each desktop 26 | 27 | ### Sample app 28 | 29 | ![](https://user-images.githubusercontent.com/1779073/152605684-2d872356-1882-4bfd-821d-d4211ccac069.gif) 30 | [samples/VirtualDesktop.Showcase](samples/VirtualDesktop.Showcase) 31 | 32 | 33 | ## Requirements 34 | 35 | ```xml 36 | net6.0-windows10.0.19041.0 37 | ``` 38 | * .NET 6.0 39 | * .NET 7.0 40 | * .NET 8.0 41 | * Windows 10 build 19041 (20H1) or later 42 | 43 | 44 | ## Installation 45 | 46 | Install NuGet package(s). 47 | 48 | ```powershell 49 | PM> Install-Package VirtualDesktop 50 | ``` 51 | 52 | * [VirtualDesktop] - Core classes for VirtualDesktop. 53 | * [VirtualDesktop.WPF] - Provides extension methods for [WPF Window class]. 54 | * [VirtualDesktop.WinForms] - Provides extension methods for [Form class]. 55 | 56 | 57 | ## How to use 58 | 59 | ### Preparation 60 | Because of the dependency on [C#/WinRT], the target framework must be set to `net6.0-windows10.0.19041.0` or later. 61 | ```xml 62 | net6.0-windows10.0.19041.0 63 | ``` 64 | 65 | If it does not work, try creating an `app.manifest` file optimized to work on Windows 10. 66 | ```xml 67 | 68 | 69 | 70 | 71 | 72 | 73 | ``` 74 | 75 | The namespace to use is `WindowsDesktop`. 76 | ```csharp 77 | using WindowsDesktop; 78 | ``` 79 | 80 | ### Get instance of VirtualDesktop class 81 | ```csharp 82 | // Get all virtual desktops 83 | var desktops = VirtualDesktop.GetDesktops(); 84 | 85 | // Get Virtual Desktop for specific window 86 | var desktop = VirtualDesktop.FromHwnd(hwnd); 87 | 88 | // Get the left/right desktop 89 | var left = desktop.GetLeft(); 90 | var right = desktop.GetRight(); 91 | ``` 92 | 93 | ### Manage virtual desktops 94 | ```csharp 95 | // Create new 96 | var desktop = VirtualDesktop.Create(); 97 | 98 | // Remove 99 | desktop.Remove(); 100 | 101 | // Switch 102 | desktop.GetLeft().Switch(); 103 | ``` 104 | 105 | ### Subscribe virtual desktop events 106 | ```csharp 107 | // Notification of desktop switching 108 | VirtualDesktop.CurrentChanged += (_, args) => Console.WriteLine($"Switched: {args.NewDesktop.Name}"); 109 | 110 | // Notification of desktop creating 111 | VirtualDesktop.Created += (_, desktop) => desktop.Switch(); 112 | ``` 113 | 114 | ### for WPF window 115 | ```csharp 116 | // Need to install 'VirtualDesktop.WPF' package 117 | 118 | // Check whether a window is on the current desktop. 119 | var isCurrent = window.IsCurrentVirtualDesktop(); 120 | 121 | // Get Virtual Desktop for WPF window 122 | var desktop = window.GetCurrentDesktop(); 123 | 124 | // Move window to specific Virtual Desktop 125 | window.MoveToDesktop(desktop); 126 | 127 | // Pin window 128 | window.Pin() 129 | ``` 130 | 131 | ### Windows version support 132 | 133 | Since this library is using undocumented interfaces, you need to [reverse engineer] your Windows version to support it. 134 | 135 | The class IDs of undocumented interfaces tend to change between different OS versions. 136 | If the demo application crashes on start-up, you will need to provide the interfaces' IDs matching your Windows version. 137 | 138 | Here are the interfaces we need: 139 | 140 | - `IApplicationView` 141 | - `IApplicationViewCollection` 142 | - `IObjectArray` 143 | - `IServiceProvider` 144 | - `IVirtualDesktop` 145 | - `IVirtualDesktopManager` 146 | - `IVirtualDesktopManagerInternal` 147 | - `IVirtualDesktopNotification` 148 | - `IVirtualDesktopNotificationService` 149 | - `IVirtualDesktopPinnedApps` 150 | 151 | Once you have those IDs, add them in a new `setting` element in [app.config]. 152 | Make sure to specify the correct 9 digits Windows build and cumulative update version. 153 | You can get it using one of those methods: 154 | - From the UI run: `winver` 155 | - From shell run: `ver` 156 | - From powershell run: `cmd /c ver` 157 | 158 | Don't forget to contribute back your changes. 159 | 160 | ## Publish 161 | 162 | To publish a new release specify your version in [Directory.Build.props] and push the changes with a commit description such as: 163 | `Release vx.y.z` where `x`, `y`, `z` form your version number. That should publish it on NuGet providing that your secret `NUGET_API_KEY` is still valid. 164 | 165 | ## Internals 166 | 167 | This library is essentially a C# wrapper for [IVirtualDesktopManager] and related undocumented interfaces. 168 | In order to support breaking binary changes between Windows versions we perform runtime compilation of a DLL providing access to the COM interfaces matching your OS build version. 169 | 170 | ## Resources 171 | * [samples/README.md](samples/README.md) 172 | * [StackOverflow](https://stackoverflow.com/questions/32416843/programmatic-control-of-virtual-desktops-in-windows-10) 173 | * [Upstream repository](https://github.com/Grabacr07/VirtualDesktop) - unmaintained 174 | * [VirtualDesktop command line tool](https://github.com/MScholtes/VirtualDesktop) - not using this library 175 | * [VirtualDesktop AutoHotKey DLL](https://github.com/Ciantic/VirtualDesktopAccessor) 176 | * [C#/WinRT repository](https://github.com/microsoft/CsWinRT) 177 | 178 | ## License 179 | 180 | This library is under the [MIT License]. 181 | 182 | 183 | [app.config]: src/VirtualDesktop/app.config 184 | [Directory.Build.props]: src/Directory.Build.props 185 | 186 | ## Credits 187 | 188 | * Thanks [@Grabacr07] for creating this great piece of software 189 | * All contributors for sharing your work with the community 190 | 191 | 192 | [VirtualDesktop]: https://www.nuget.org/packages/Slions.VirtualDesktop/ 193 | [VirtualDesktop.WPF]: https://www.nuget.org/packages/Slions.VirtualDesktop.WPF/ 194 | [VirtualDesktop.WinForms]: https://www.nuget.org/packages/Slions.VirtualDesktop.WinForms/ 195 | [WPF Window class]: https://msdn.microsoft.com/en-us/library/system.windows.window(v=vs.110).aspx 196 | [Form class]: https://msdn.microsoft.com/en-us/library/system.windows.forms.form(v=vs.110).aspx 197 | [C#/WinRT]: https://aka.ms/cswinrt 198 | [reverse engineer]: https://github.com/Slion/VirtualDesktop/issues/14 199 | [IVirtualDesktopManager]: https://msdn.microsoft.com/en-us/library/windows/desktop/mt186440%28v%3Dvs.85%29.aspx 200 | [MIT License]: https://github.com/Grabacr07/VirtualDesktop/blob/master/LICENSE 201 | [@Grabacr07]: https://github.com/Grabacr07 -------------------------------------------------------------------------------- /src/VirtualDesktop/Interop/ComInterfaceAssemblyBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Reflection.Metadata; 8 | using System.Runtime.Loader; 9 | using System.Text; 10 | using System.Text.RegularExpressions; 11 | using Microsoft.CodeAnalysis; 12 | using Microsoft.CodeAnalysis.CSharp; 13 | using Microsoft.Win32; 14 | using WindowsDesktop.Properties; 15 | using WindowsDesktop.Utils; 16 | 17 | namespace WindowsDesktop.Interop; 18 | 19 | internal class ComInterfaceAssemblyBuilder 20 | { 21 | private const string _assemblyName = "VirtualDesktop.{0}.generated.dll"; 22 | private const string _placeholderOsBuild = "{OS_BUILD}"; 23 | private const string _placeholderAssemblyVersion = "{ASSEMBLY_VERSION}"; 24 | private const string _placeholderInterfaceId = "00000000-0000-0000-0000-000000000000"; 25 | 26 | // Now using assembly version even though regenerating our DLL won't strictly be needed for every new version this is the safest option 27 | // Otherwise people will surely forget to increment a specific version here 28 | private static readonly Version? _requireVersion = Assembly.GetExecutingAssembly().GetName().Version; 29 | private static readonly Regex _assemblyRegex = new(@"VirtualDesktop\.10\.0\.(?\d+\.\d+)(\.\w*|)\.dll"); 30 | private static readonly Regex _buildNumberRegex = new(@"\.Build(?\d+\.\d+)\."); 31 | private static readonly Version osBuild = OS.Build; 32 | 33 | private static ComInterfaceAssembly? _assembly; 34 | 35 | private readonly VirtualDesktopCompilerConfiguration _configuration; 36 | 37 | public ComInterfaceAssemblyBuilder(VirtualDesktopCompilerConfiguration configuration) 38 | { 39 | this._configuration = configuration; 40 | } 41 | 42 | public ComInterfaceAssembly GetAssembly() 43 | => _assembly ??= new ComInterfaceAssembly(this.LoadExistingAssembly() ?? this.CreateAssembly()); 44 | 45 | private Assembly? LoadExistingAssembly() 46 | { 47 | if (this._configuration.CompiledAssemblySaveDirectory.Exists) 48 | { 49 | foreach (var file in this._configuration.CompiledAssemblySaveDirectory.GetFiles()) 50 | { 51 | if (Version.TryParse(OS.VersionPrefix + _assemblyRegex.Match(file.Name).Groups["build"].ToString(), out var build) 52 | && build == osBuild) 53 | { 54 | try 55 | { 56 | var name = AssemblyName.GetAssemblyName(file.FullName); 57 | if (name.Version >= _requireVersion) 58 | { 59 | Debug.WriteLine($"Assembly found: {file.FullName}"); 60 | #if !DEBUG 61 | return Assembly.LoadFile(file.FullName); 62 | #else 63 | Debug.WriteLine($"Debug force assembly creation"); 64 | #endif 65 | } 66 | else 67 | { 68 | Debug.WriteLine($"Outdated assembly: {name.Version} < {_requireVersion}"); 69 | } 70 | } 71 | catch (Exception ex) 72 | { 73 | Debug.WriteLine("Failed to load assembly: "); 74 | Debug.WriteLine(ex); 75 | 76 | File.Delete(file.FullName); 77 | } 78 | } 79 | } 80 | } 81 | 82 | return null; 83 | } 84 | 85 | private Assembly CreateAssembly() 86 | { 87 | var executingAssembly = Assembly.GetExecutingAssembly(); 88 | var compileTargets = new List(); 89 | { 90 | var assemblyInfo = executingAssembly.GetManifestResourceNames().Single(x => x.Contains("AssemblyInfo.cs")); 91 | var stream = executingAssembly.GetManifestResourceStream(assemblyInfo); 92 | if (stream != null) 93 | { 94 | using var reader = new StreamReader(stream, Encoding.UTF8); 95 | var sourceCode = reader 96 | .ReadToEnd() 97 | .Replace(_placeholderOsBuild, osBuild.ToString()) 98 | .Replace(_placeholderAssemblyVersion, _requireVersion.ToString(3)); 99 | compileTargets.Add(sourceCode); 100 | } 101 | } 102 | 103 | var interfaceNames = executingAssembly 104 | .GetTypes() 105 | .Select(x => x.GetComInterfaceNameIfWrapper()) 106 | .Where(x => string.IsNullOrEmpty(x) == false) 107 | .Cast() 108 | .ToArray(); 109 | var iids = IID.GetIIDs(interfaceNames); 110 | 111 | // e.g. 112 | // IVirtualDesktop 113 | // ├── 10240, VirtualDesktop.Interop.Build10240..interfaces.IVirtualDesktop.cs 114 | // └── 22000, VirtualDesktop.Interop.Build22000..interfaces.IVirtualDesktop.cs 115 | // IVirtualDesktopPinnedApps 116 | // └── 10240, VirtualDesktop.Interop.Build10240..interfaces.IVirtualDesktopPinnedApps.cs 117 | var interfaceSourceFiles = new Dictionary>(); 118 | 119 | // This is where we decide which interface variant goes into our generated DLL assembly 120 | foreach (var name in executingAssembly.GetManifestResourceNames()) 121 | { 122 | var interfaceName = Path.GetFileNameWithoutExtension(name).Split('.').LastOrDefault(); 123 | if (interfaceName != null 124 | && interfaceNames.Contains(interfaceName) 125 | && Version.TryParse(OS.VersionPrefix + _buildNumberRegex.Match(name.Replace('_','.')).Groups["build"].ToString(), out var build)) 126 | { 127 | if (interfaceSourceFiles.TryGetValue(interfaceName, out var sourceFiles) == false) 128 | { 129 | sourceFiles = new SortedList(); 130 | interfaceSourceFiles.Add(interfaceName, sourceFiles); 131 | } 132 | 133 | sourceFiles.Add(build, name); 134 | } 135 | } 136 | 137 | foreach (var (interfaceName, sourceFiles) in interfaceSourceFiles) 138 | { 139 | var resourceName = sourceFiles.Aggregate("", (current, kvp) => 140 | { 141 | var (build, resourceName) = kvp; 142 | return build <= osBuild ? resourceName : current; 143 | }); 144 | 145 | var stream = executingAssembly.GetManifestResourceStream(resourceName); 146 | if (stream == null) continue; 147 | 148 | using var reader = new StreamReader(stream, Encoding.UTF8); 149 | var sourceCode = reader.ReadToEnd().Replace(_placeholderInterfaceId, iids[interfaceName].ToString()); 150 | compileTargets.Add(sourceCode); 151 | } 152 | 153 | return this.Compile(compileTargets.ToArray()); 154 | } 155 | 156 | private Assembly Compile(IEnumerable sources) 157 | { 158 | try 159 | { 160 | var name = string.Format(_assemblyName, osBuild); 161 | var syntaxTrees = sources.Select(x => SyntaxFactory.ParseSyntaxTree(x)); 162 | var references = AppDomain.CurrentDomain.GetAssemblies() 163 | .Concat(new[] { Assembly.GetExecutingAssembly(), }) 164 | .Where(x => x.IsDynamic == false) 165 | .Select(x => { 166 | if (!string.IsNullOrEmpty(x.Location)) 167 | { 168 | return MetadataReference.CreateFromFile(x.Location); 169 | } 170 | else 171 | { 172 | unsafe 173 | { 174 | x.TryGetRawMetadata(out byte* blob, out int length); 175 | return AssemblyMetadata.Create(ModuleMetadata.CreateFromMetadata((IntPtr)blob, length)).GetReference(); 176 | } 177 | } 178 | }); 179 | var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); 180 | var compilation = CSharpCompilation.Create(name) 181 | .WithOptions(options) 182 | .WithReferences(references) 183 | .AddSyntaxTrees(syntaxTrees); 184 | 185 | string? errorMessage; 186 | 187 | if (this._configuration.SaveCompiledAssembly) 188 | { 189 | var dir = this._configuration.CompiledAssemblySaveDirectory; 190 | if (dir.Exists == false) dir.Create(); 191 | 192 | var path = Path.Combine(dir.FullName, name); 193 | var result = compilation.Emit(path); 194 | if (result.Success) return AssemblyLoadContext.Default.LoadFromAssemblyPath(path); 195 | 196 | File.Delete(path); 197 | errorMessage = string.Join(Environment.NewLine, result.Diagnostics.Select(x => $" {x.GetMessage()}")); 198 | } 199 | else 200 | { 201 | using var stream = new MemoryStream(); 202 | var result = compilation.Emit(stream); 203 | if (result.Success) 204 | { 205 | stream.Seek(0, SeekOrigin.Begin); 206 | return AssemblyLoadContext.Default.LoadFromStream(stream); 207 | } 208 | 209 | errorMessage = string.Join(Environment.NewLine, result.Diagnostics.Select(x => $" {x.GetMessage()}")); 210 | } 211 | 212 | throw new Exception("Failed to compile COM interfaces assembly." + Environment.NewLine + errorMessage); 213 | } 214 | finally 215 | { 216 | GC.Collect(); 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /samples/VirtualDesktop.Showcase/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 16 | 17 | 18 | 28 | 29 | 32 | 33 | 147 | 148 | 149 | 150 | 152 | 153 | 156 | 159 | 162 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /src/VirtualDesktop/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | IApplicationView,{9AC0B5C8-1484-4C5B-9533-4134A0F97CEA} 14 | IApplicationViewCollection,{2C08ADF0-A386-4B35-9250-0FE183476FCC} 15 | IObjectArray,{92CA9DCD-5622-4BBA-A805-5E9F541BD8C9} 16 | IServiceProvider,{6D5140C1-7436-11CE-8034-00AA006009FA} 17 | IVirtualDesktop,{FF72FFDD-BE7E-43FC-9C03-AD81681E88E4} 18 | IVirtualDesktopManager,{A5CD92FF-29BE-454C-8D04-D82879FB3F1B} 19 | IVirtualDesktopManagerInternal,{F31574D6-B682-4CDC-BD56-1827860ABEC6} 20 | IVirtualDesktopNotification,{C179334C-4295-40D3-BEA1-C654D965605A} 21 | IVirtualDesktopNotificationService,{0CD45E71-D927-4F15-8B0A-8FEF525337BF} 22 | IVirtualDesktopPinnedApps,{4CE81583-1E4C-4632-A621-07A53543148F} 23 | 24 | 25 | 26 | 27 | 28 | 29 | IApplicationView,{871F602A-2B58-42B4-8C4B-6C43D642C06F} 30 | IApplicationViewCollection,{2C08ADF0-A386-4B35-9250-0FE183476FCC} 31 | IObjectArray,{92CA9DCD-5622-4BBA-A805-5E9F541BD8C9} 32 | IServiceProvider,{6D5140C1-7436-11CE-8034-00AA006009FA} 33 | IVirtualDesktop,{FF72FFDD-BE7E-43FC-9C03-AD81681E88E4} 34 | IVirtualDesktopManager,{A5CD92FF-29BE-454C-8D04-D82879FB3F1B} 35 | IVirtualDesktopManagerInternal,{F31574D6-B682-4CDC-BD56-1827860ABEC6} 36 | IVirtualDesktopNotification,{C179334C-4295-40D3-BEA1-C654D965605A} 37 | IVirtualDesktopNotificationService,{0CD45E71-D927-4F15-8B0A-8FEF525337BF} 38 | IVirtualDesktopPinnedApps,{4CE81583-1E4C-4632-A621-07A53543148F} 39 | 40 | 41 | 42 | 43 | 44 | 45 | IApplicationView,{372E1D3B-38D3-42E4-A15B-8AB2B178F513} 46 | IApplicationViewCollection,{1841C6D7-4F9D-42C0-AF41-8747538F10E5} 47 | IObjectArray,{92CA9DCD-5622-4BBA-A805-5E9F541BD8C9} 48 | IServiceProvider,{6D5140C1-7436-11CE-8034-00AA006009FA} 49 | IVirtualDesktop,{FF72FFDD-BE7E-43FC-9C03-AD81681E88E4} 50 | IVirtualDesktopManager,{A5CD92FF-29BE-454C-8D04-D82879FB3F1B} 51 | IVirtualDesktopManagerInternal,{F31574D6-B682-4CDC-BD56-1827860ABEC6} 52 | IVirtualDesktopNotification,{C179334C-4295-40D3-BEA1-C654D965605A} 53 | IVirtualDesktopNotificationService,{0CD45E71-D927-4F15-8B0A-8FEF525337BF} 54 | IVirtualDesktopPinnedApps,{4CE81583-1E4C-4632-A621-07A53543148F} 55 | 56 | 57 | 58 | 59 | 60 | 61 | IApplicationView,{372E1D3B-38D3-42E4-A15B-8AB2B178F513} 62 | IApplicationViewCollection,{1841C6D7-4F9D-42C0-AF41-8747538F10E5} 63 | IObjectArray,{92CA9DCD-5622-4BBA-A805-5E9F541BD8C9} 64 | IServiceProvider,{6D5140C1-7436-11CE-8034-00AA006009FA} 65 | IVirtualDesktop,{62FDF88B-11CA-4AFB-8BD8-2296DFAE49E2} 66 | IVirtualDesktopManager,{A5CD92FF-29BE-454C-8D04-D82879FB3F1B} 67 | IVirtualDesktopManagerInternal,{094AFE11-44F2-4BA0-976F-29A97E263EE0} 68 | IVirtualDesktopNotification,{f3163e11-6b04-433c-a64b-6f82c9094257} 69 | IVirtualDesktopNotificationService,{0cd45e71-d927-4f15-8b0a-8fef525337bf} 70 | IVirtualDesktopPinnedApps,{4CE81583-1E4C-4632-A621-07A53543148F} 71 | 72 | 73 | 74 | 75 | 76 | 77 | IApplicationView,{372E1D3B-38D3-42E4-A15B-8AB2B178F513} 78 | IApplicationViewCollection,{1841C6D7-4F9D-42C0-AF41-8747538F10E5} 79 | IObjectArray,{92CA9DCD-5622-4BBA-A805-5E9F541BD8C9} 80 | IServiceProvider,{6D5140C1-7436-11CE-8034-00AA006009FA} 81 | IVirtualDesktop,{536D3495-B208-4CC9-AE26-DE8111275BF8} 82 | IVirtualDesktopManager,{A5CD92FF-29BE-454C-8D04-D82879FB3F1B} 83 | IVirtualDesktopManagerInternal,{B2F925B9-5A0F-4D2E-9F4D-2B1507593C10} 84 | IVirtualDesktopNotification,{cd403e52-deed-4c13-b437-b98380f2b1e8} 85 | IVirtualDesktopNotificationService,{0cd45e71-d927-4f15-8b0a-8fef525337bf} 86 | IVirtualDesktopPinnedApps,{4CE81583-1E4C-4632-A621-07A53543148F} 87 | 88 | 89 | 90 | 91 | 92 | 93 | IApplicationView,{372E1D3B-38D3-42E4-A15B-8AB2B178F513} 94 | IApplicationViewCollection,{1841C6D7-4F9D-42C0-AF41-8747538F10E5} 95 | IObjectArray,{92CA9DCD-5622-4BBA-A805-5E9F541BD8C9} 96 | IServiceProvider,{6D5140C1-7436-11CE-8034-00AA006009FA} 97 | IVirtualDesktop,{3F07F4BE-B107-441A-AF0F-39D82529072C} 98 | IVirtualDesktopManager,{A5CD92FF-29BE-454C-8D04-D82879FB3F1B} 99 | IVirtualDesktopManagerInternal,{A3175F2D-239C-4BD2-8AA0-EEBA8B0B138E} 100 | IVirtualDesktopNotification,{B287FA1C-7771-471A-A2DF-9B6B21F0D675} 101 | IVirtualDesktopNotificationService,{0cd45e71-d927-4f15-8b0a-8fef525337bf} 102 | IVirtualDesktopPinnedApps,{4CE81583-1E4C-4632-A621-07A53543148F} 103 | 104 | 105 | 106 | 107 | 108 | 109 | IApplicationView,{372E1D3B-38D3-42E4-A15B-8AB2B178F513} 110 | IApplicationViewCollection,{1841C6D7-4F9D-42C0-AF41-8747538F10E5} 111 | IObjectArray,{92CA9DCD-5622-4BBA-A805-5E9F541BD8C9} 112 | IServiceProvider,{6D5140C1-7436-11CE-8034-00AA006009FA} 113 | IVirtualDesktop,{3F07F4BE-B107-441A-AF0F-39D82529072C} 114 | IVirtualDesktopManager,{A5CD92FF-29BE-454C-8D04-D82879FB3F1B} 115 | IVirtualDesktopManagerInternal,{53F5CA0B-158F-4124-900C-057158060B27} 116 | IVirtualDesktopNotification,{B9E5E94D-233E-49AB-AF5C-2B4541C3AADE} 117 | IVirtualDesktopNotificationService,{0cd45e71-d927-4f15-8b0a-8fef525337bf} 118 | IVirtualDesktopPinnedApps,{4CE81583-1E4C-4632-A621-07A53543148F} 119 | 120 | 121 | 122 | 123 | 124 | 125 | IApplicationView,{372E1D3B-38D3-42E4-A15B-8AB2B178F513} 126 | IApplicationViewCollection,{1841C6D7-4F9D-42C0-AF41-8747538F10E5} 127 | IObjectArray,{92CA9DCD-5622-4BBA-A805-5E9F541BD8C9} 128 | IServiceProvider,{6D5140C1-7436-11CE-8034-00AA006009FA} 129 | IVirtualDesktop,{3F07F4BE-B107-441A-AF0F-39D82529072C} 130 | IVirtualDesktopManager,{A5CD92FF-29BE-454C-8D04-D82879FB3F1B} 131 | IVirtualDesktopManagerInternal,{A3175F2D-239C-4BD2-8AA0-EEBA8B0B138E} 132 | IVirtualDesktopNotification,{B287FA1C-7771-471A-A2DF-9B6B21F0D675} 133 | IVirtualDesktopNotificationService,{0cd45e71-d927-4f15-8b0a-8fef525337bf} 134 | IVirtualDesktopPinnedApps,{4CE81583-1E4C-4632-A621-07A53543148F} 135 | 136 | 137 | 138 | 139 | 140 | 141 | IApplicationView,{372E1D3B-38D3-42E4-A15B-8AB2B178F513} 142 | IApplicationViewCollection,{1841C6D7-4F9D-42C0-AF41-8747538F10E5} 143 | IObjectArray,{92CA9DCD-5622-4BBA-A805-5E9F541BD8C9} 144 | IServiceProvider,{6D5140C1-7436-11CE-8034-00AA006009FA} 145 | IVirtualDesktop,{3F07F4BE-B107-441A-AF0F-39D82529072C} 146 | IVirtualDesktopManager,{A5CD92FF-29BE-454C-8D04-D82879FB3F1B} 147 | IVirtualDesktopManagerInternal,{53F5CA0B-158F-4124-900C-057158060B27} 148 | IVirtualDesktopNotification,{B9E5E94D-233E-49AB-AF5C-2B4541C3AADE} 149 | IVirtualDesktopNotificationService,{0cd45e71-d927-4f15-8b0a-8fef525337bf} 150 | IVirtualDesktopPinnedApps,{4CE81583-1E4C-4632-A621-07A53543148F} 151 | 152 | 153 | 154 | 155 | 156 | 157 | IApplicationView,{372E1D3B-38D3-42E4-A15B-8AB2B178F513} 158 | IApplicationViewCollection,{1841C6D7-4F9D-42C0-AF41-8747538F10E5} 159 | IObjectArray,{92CA9DCD-5622-4BBA-A805-5E9F541BD8C9} 160 | IServiceProvider,{6D5140C1-7436-11CE-8034-00AA006009FA} 161 | IVirtualDesktop,{3F07F4BE-B107-441A-AF0F-39D82529072C} 162 | IVirtualDesktopManager,{A5CD92FF-29BE-454C-8D04-D82879FB3F1B} 163 | IVirtualDesktopManagerInternal,{4970BA3D-FD4E-4647-BEA3-D89076EF4B9C} 164 | IVirtualDesktopNotification,{B9E5E94D-233E-49AB-AF5C-2B4541C3AADE} 165 | IVirtualDesktopNotificationService,{0cd45e71-d927-4f15-8b0a-8fef525337bf} 166 | IVirtualDesktopPinnedApps,{4CE81583-1E4C-4632-A621-07A53543148F} 167 | 168 | 169 | 170 | 171 | 172 | --------------------------------------------------------------------------------