Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 17 Min.

Virtuelle Arbeitsbereiche

Windows 10 bietet virtuelle Desktops an. Über API-Aufrufe lassen sich die Features auch unter .NET nutzen.
© dotnetpro
Ob Linux, macOS oder Windows: Alle Betriebssysteme bieten virtuelle Arbeitsbereiche in der ein oder anderen Ausprägung. Grundsätzlich versteht man darunter die Möglichkeit, mehrere Desktops mit Anwendungen und deren Fenstern zu erstellen und zwischen diesen hin- und herschalten zu können. Unter dem Begriff der virtuellen Desktops wird oft eine Virtualisierung von Systemen zusammengefasst, was aber eben nicht zutrifft und in diesem Artikel auch nicht behandelt wird. Deshalb ist der Begriff virtuelle Arbeitsbereiche die bessere Bezeichnung.Wie im weiteren Verlauf des Artikels noch zu sehen ist, lassen sich die virtuellen Arbeitsbereiche problemlos manuell ansteuern, also zum Beispiel durch Tastenkombinationen erstellen, wechseln und schließen, was aber nicht Inhalt dieses Artikels ist. Vielmehr liegt der Fokus dieser Episode von Frameworks und mehr auf der programmatischen Ansteuerung der virtuellen Arbeitsbereiche unter Windows 10 und 11.Microsoft bietet eine Schnittstelle in Form eines Interfaces an, um Zugriff auf typische Funktionen der virtuellen Arbeitsbereiche zu bekommen, ebenso wie konkreten Zugriff auf die einzelnen virtuellen Arbeitsbereiche. Das erlaubt es uns, die virtuellen Arbeitsbereiche zu verwalten, Fenster und Anwendungen zu verschieben und Funktionalitäten für ein Notification-System einzubauen. Zumindest ist das die Vermutung, die viele haben, wenn die von Microsoft angebotene Schnittstelle zum ersten Mal entdeckt wird. Dass es dabei erhebliche Probleme und Missstände gibt, zeigt der Artikel im weiteren Verlauf ebenfalls.Dabei ist der Inhalt des Artikels zweigeteilt. Zum einen geht es darum, wie COM-Interfaces und -Objekte in .NET eingebunden werden können. Das ist notwendig, denn die Schnittstelle zu den virtuellen Arbeitsbereichen liegt nicht als vorgefertigte .NET Assembly vor. Zumindest nicht von Microsoft, denn die Community hat Abhilfe geschaffen. Es gibt zahlreiche Bibliotheken und NuGet-Pakete, um die Features direkt in .NET-Projekten nutzen zu können. Eine umfassende Bibliothek stellt der Artikel vor und zeigt, wie die Funktionen zum Einsatz kommen können. Zudem werden die Begriffe virtuelle Arbeitsbereiche und virtuelle Desktops synonym verwendet. Um Virtualisierung geht es in diesem Artikel nicht.

Die Herausforderungen und mögliche Lösungen

Wenn eine Anwendung Zugriff auf die virtuellen Arbeitsbereiche von Windows haben möchte, müssen wir zunächst herausfinden, wie wir diesen Zugriff bekommen. In der Dokumentation zu .NET beziehungsweise C# tauchen das entsprechende Interface und das notwendige COM-Objekt nicht auf. Daher ist diese Funktionalität im C#-Universum gar nicht so ohne Weiteres bekannt. Ebenso wenig das Wissen, wie die Schnittstellen einzubinden sind. Generell ist gar nicht jedem bekannt, dass Windows mittlerweile virtuelle Arbeitsbereiche anbietet. Das Feature wird wenig beworben und die Funktionalität wurde in der Vergangenheit nur zögerlich ausgebaut.Um im Code auf die Arbeitsbereiche zugreifen zu können, lassen sich zwar Tastenkombinationen in Windows simulieren oder externe Drittanwendungen einbinden, um darüber Zugriff auf einen virtuellen Desktop zu bekommen. In den meisten Fällen wird dadurch aber keine zufriedenstellende Lösung herauskommen. Der Umweg über externe Anwendungen ist und bleibt eine Krücke. Die Schnittstelle von Windows zu nutzen ist daher die naheliegendste Lösung.

Welche Schnittstelle ist zuständig?

Das führt zur nächsten essenziellen Frage: Welche Schnittstelle ist konkret zuständig und wie wird diese eingebunden? Letzteres Thema behandelt ein dedizierter Abschnitt im weiteren Verlauf des Artikels. Klären wir zunächst die drängende Frage, welche Schnittstellen uns Microsoft in Windows anbietet, um auf virtuelle Arbeitsbereiche zuzugreifen.Im Fokus liegt das Interface IVirtualDesktopManager. Definiert ist das Interface im Header mit dem eingängigen Namen shobjidl_core.h [1]. Dieser Header gehört zu einem Satz APIs, die in verschiedenen Subsystemen von Windows zum Einsatz kommen, und steht ab Windows 10 beziehungsweise Windows Server 2016 für Desktop-Anwendungen zur Verfügung – zum Beispiel die Windows Shell [2], Windows Search [3] und Windows Sidebar.Das Interface IVirtualDesktopManager stellt Methoden bereit, die es unserer Anwendung ermöglichen, mit Gruppen von Fenstern zu interagieren, die virtuelle Arbeitsbereiche bilden. Das verdeutlicht noch einmal ein wichtiges Detail, nämlich dass virtuelle Arbeitsbereiche durch gruppierte Fenster von Anwendungen definiert sind. Die genannte Schnittstelle wird von VirtualDesktopManager implementiert. Das ist eine COM-Klasse, die durch CLSID_VirtualDesktopManager identifiziert wird. Über einen Aufruf von CoCreateInstance(CLSID_VirtualDesktopManager) erhalten wir eine Instanz dieses Objekts.Die Schnittstelle bietet die Methoden GetWindowDesktop­Id, IsWindowOnCurrentVirtualDesktop und MoveWindow­ToDesktop an. Die erste Methode ermöglicht es uns, den virtuellen Arbeitsbereich zu identifizieren, der das übergebene Top-Level-Fenster beinhaltet.Die zweite Methode prüft, ob sich das aktuelle Fenster auf dem aktuell aktiven Arbeitsbereich befindet, und die letzte Methode verschiebt ein Fenster zu einem angegebenen virtuellen Arbeitsbereich. Ein Blick in die Definition der Methode GetWindowDesktopId
<span class="hljs-function">HRESULT <span class="hljs-title">GetWindowDesktopId</span>(</span>
<span class="hljs-function"><span class="hljs-params">  [<span class="hljs-keyword">in</span>] HWND topLevelWindow,</span></span>
<span class="hljs-function"><span class="hljs-params">  [<span class="hljs-keyword">out</span>] GUID *desktopId</span></span>
<span class="hljs-function">)</span>; 
offenbart zudem, dass die Fenster als HWND anzugeben sind, also als sogenanntes Window Handle.

Virtueller Desktops unter Windows 10 und 11

Die ganze Magie hinter virtuellen Desktops: Benutzer können eine Sammlung von Fenstern zusammenfassen, um so einen virtuellen Desktop zu erstellen. Jedes Fenster ist implizit Teil eines virtuellen Desktops. Wenn wir nur einen virtuellen Arbeitsbereich haben, ist das eben der Standarddesktop. Windows sieht es aber trotzdem als virtuellen Desktop an.Wenn ein virtueller Desktop ausgeblendet wird, werden auch alle damit verbundenen Fenster ausgeblendet. Auf diese Weise kann der Benutzer mehrere Arbeitsumgebungen erstellen und zwischen ihnen wechseln. Wenn ein virtueller Desktop als aktiv ausgewählt wird, werden die mit diesem virtuellen Desktop verbundenen Fenster auf dem Bildschirm angezeigt. Dabei gibt es einige Richtlinien, die wir beim programmatischen Zugriff auf die virtuellen Umgebungen beachten sollten:Um dieses Konzept zu unterstützen, sollten Anwendungen es vermeiden, den Benutzer automatisch von einem virtuellen Desktop auf einen anderen umzuschalten. Nur der Benutzer sollte diesen Wechsel veranlassen. Um dies zu unterstützen, sollten neu erstellte Fenster auf dem gerade aktiven virtuellen Desktop erscheinen. Wenn eine Anwendung aktive Fenster wiederverwenden kann, sollte sie dies nur dann tun, wenn sie sich auf dem aktiven virtuellen Desktop befinden. Andernfalls sollte ein neues Fenster erstellt werden.Gerade der letzte Hinweis ist spannend, selbst wenn wir ansonsten gar nichts mit den virtuellen Arbeitsbereichen zu tun haben. Erstellen wir ein neues Fenster, dann immer auf dem aktuellen virtuellen Desktop – selbst wenn wir dieses Fenster unserer Anwendung schon einmal auf einem anderen Arbeitsbereich haben. Automatische Veränderungen sind insbesondere bei vielen Arbeitsbereichen sehr schnell sehr unübersichtlich und verwirrend.Ein neuer virtueller Arbeitsbereich lässt sich zunächst über ein Icon in der Taskleiste erstellen, das sich Taskansicht-Schaltfläche nennt und in den Einstellungen der Taskleiste aktiviert und deaktiviert werden kann. Alternativ funktioniert das über die Tastenkombination [Win]+[Strg]+[D]. Ab Version 1803 von Windows 10 befindet sich zudem die Schaltfläche für einen neuen Desktop links oben in der neuen Zeitleiste. Die Übersicht, zu sehen in Bild 1, zeigt alle Desktops und die Fenster auf dem aktuellen beziehungsweise dem ausgewählten virtuellen Arbeitsbereich. Mit [Win]+[Tab] werden die Desktops und die aktiven Programme angezeigt. [Alt]+[Tab] bleibt beim Standard und zeigt die Anwendungen der aktuellen virtuellen Arbeitsumgebung an.
Übersicht des aktivenund aller anderen virtuellen Desktops in Windows 10(Bild 1) © Autor
Mit der Tastenkombination [Win]+[Strg]+[F4] lässt sich
der aktive virtuelle Desktop beenden. Die geöffneten Fenster auf diesem Desktop werden auf den dann noch aktiven ­Desktop verschoben, von dem es immer einen gibt. Über [Win]+[Strg]+[Pfeil links/Pfeil rechts] lässt sich zwischen den Arbeitsbereichen hin- und herwechseln. Fenster lassen sich im Vorschau-Fenster ([Win]+[Tab]) zwischen den virtuellen Desktops hin- und herschieben. Auch das Anpinnen eines Fensters auf allen virtuellen Desktops ist möglich. Mittlerweile funktioniert es auch, den Arbeitsbereichen neue Icons und Wallpaper zuzuweisen – ein Feature, das recht spät nachgereicht wurde. Das Speichern dieser virtuellen Umgebungen wird bisher nicht angeboten. Abmelden, Neustarten oder Herunterfahren beendet alle virtuellen Arbeitsbereiche, die in der nächsten Session nicht wiederhergestellt werden.

Die Schnittstelle in .NET einbinden

Eine kurze Demo-Anwendung soll zeigen, wie man die vir­tuellen Desktops per Programm ansteuern kann. Sie ist mit .NET 6 und C# 10 erstellt. Die Integration von notwendigen Bibliotheken und Windows RT ist problemlos und funktioniert auch in Jetbrains Rider ohne Schwierigkeiten. Erstellt wurde zudem ein Fenster mit der Windows Presentation Foundation (WPF), um Erweiterungsmethoden der Bibliothek VirtualDesktop nutzen zu können, die im weiteren Verlauf vorgestellt wird.Zuerst legen wir ein Interface IVirtualDesktopManager in einem .NET-Projekt in unserer Solution an. Listing 1 zeigt den darin enthaltenen Code. Wir nutzen dabei viele Dinge aus dem Namensraum System.Runtime.InteropServices, um mit dem COM-System interagieren zu können. Zum Beispiel definieren wir das Interface mit dem ComImport-Attribut als Typ, der zuvor in COM definiert wurde. Das Attribut InterfaceType zeigt an, dass wir eine auf IUnknown beschränkte Schnittstelle haben, da diese für COM verfügbar gemacht wird. Innerhalb dieser Schnittstelle können wir nun die drei Methoden definieren, die Microsoft für Windows öffentlich zur Verfügung stellt.
Listing 1: Die Definitionen des Interfaces IVirtualDesktopManager:
[ComImport,&lt;br/&gt;  InterfaceType(ComInterfaceType.InterfaceIsIUnknown),&lt;br/&gt;  Guid(&lt;span class="hljs-string"&gt;"a5cd92ff-29be-454c-8d04-d82879fb3f1b"&lt;/span&gt;)]&lt;br/&gt;&lt;br/&gt;[System.Security.SuppressUnmanagedCodeSecurity]&lt;br/&gt;&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;interface&lt;/span&gt; IVirtualDesktopManager&lt;br/&gt;{&lt;br/&gt;  [PreserveSig]&lt;br/&gt;  &lt;span class="hljs-built_in"&gt;int&lt;/span&gt; IsWindowOnCurrentVirtualDesktop(&lt;br/&gt;    [&lt;span class="hljs-keyword"&gt;In&lt;/span&gt;] IntPtr TopLevelWindow,&lt;br/&gt;    [&lt;span class="hljs-keyword"&gt;Out&lt;/span&gt;] &lt;span class="hljs-keyword"&gt;out&lt;/span&gt; &lt;span class="hljs-built_in"&gt;int&lt;/span&gt; OnCurrentDesktop&lt;br/&gt;  );&lt;br/&gt;&lt;br/&gt;  [PreserveSig]&lt;br/&gt;  &lt;span class="hljs-built_in"&gt;int&lt;/span&gt; GetWindowDesktopId(&lt;br/&gt;    [&lt;span class="hljs-keyword"&gt;In&lt;/span&gt;] IntPtr TopLevelWindow,&lt;br/&gt;    [&lt;span class="hljs-keyword"&gt;Out&lt;/span&gt;] &lt;span class="hljs-keyword"&gt;out&lt;/span&gt; Guid CurrentDesktop&lt;br/&gt;  );&lt;br/&gt;&lt;br/&gt;  [PreserveSig]&lt;br/&gt;  &lt;span class="hljs-built_in"&gt;int&lt;/span&gt; MoveWindowToDesktop(&lt;br/&gt;    [&lt;span class="hljs-keyword"&gt;In&lt;/span&gt;] IntPtr TopLevelWindow,&lt;br/&gt;    [MarshalAs(UnmanagedType.LPStruct)]&lt;br/&gt;    [&lt;span class="hljs-keyword"&gt;In&lt;/span&gt;]Guid CurrentDesktop&lt;br/&gt;  );&lt;br/&gt;} 
Wir müssen die Parameter ordentlich spezifizieren und über das Attribut PerserveSig angeben, dass wir keine Umwandlung von HRESULT wünschen. Anschließend benötigen wir noch eine Klasse, um die ursprüngliche COM-Implementierung abzubilden. Die Definition der Klasse CVirtualDesktopManager lautet:
[ComImport, 
  Guid(<span class="hljs-string">"aa509086-5ca9-4c25-8f95-589d3c07b48a"</span>)]
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CVirtualDesktopManager</span> </span>{ } 
Wenn die Legacy-Schnittstellen implementiert sind, können wir eine eigene Klasse VirtualDesktopManager implementieren, um die COM-Schnittstellen zu nutzen.

Beispiele mit der öffentlichen Schnittstelle

In Listing 2 ist ein Ausschnitt der Implementierung der zuvor genannten Klasse VirtualDesktopManager zu sehen. Die Methode IsWindowOnCurrentVirtualDesktop, die als Beispiel vollständig abgebildet ist, zeigt, wie ein übergebenes Fenster-Handle genutzt wird, um zu prüfen, ob dieses Fenster auf der gerade aktiven virtuellen Arbeitsfläche vorhanden ist.
Listing 2: Ausschnitt aus der eigenen Implementierung der Klasse VirtualDesktopManager:
&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title"&gt;VirtualDesktopManager&lt;/span&gt;&lt;br/&gt;{&lt;br/&gt;  &lt;span class="hljs-keyword"&gt;private&lt;/span&gt; CVirtualDesktopManager? _cmanager;&lt;br/&gt;  &lt;span class="hljs-keyword"&gt;private&lt;/span&gt; IVirtualDesktopManager? _manager;&lt;br/&gt;&lt;br/&gt;  &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-title"&gt;VirtualDesktopManager&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-function"&gt;  &lt;/span&gt;{&lt;br/&gt;    _cmanager = &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; CVirtualDesktopManager();&lt;br/&gt;    _manager = _cmanager &lt;span class="hljs-keyword"&gt;as&lt;/span&gt; IVirtualDesktopManager;&lt;br/&gt;  }&lt;br/&gt;&lt;br/&gt;  ~VirtualDesktopManager()&lt;br/&gt;  {&lt;br/&gt;    _manager = &lt;span class="hljs-literal"&gt;null&lt;/span&gt;;&lt;br/&gt;    _cmanager = &lt;span class="hljs-literal"&gt;null&lt;/span&gt;;&lt;br/&gt;  }&lt;br/&gt;  &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;bool&lt;/span&gt; &lt;span class="hljs-title"&gt;IsWindowOnCurrentVirtualDesktop&lt;/span&gt;(&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-function"&gt;&lt;span class="hljs-params"&gt;      IntPtr topLevelWindow&lt;/span&gt;)&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-function"&gt;  &lt;/span&gt;{&lt;br/&gt;    &lt;span class="hljs-keyword"&gt;var&lt;/span&gt; result = &lt;span class="hljs-number"&gt;0&lt;/span&gt;;&lt;br/&gt;    &lt;span class="hljs-keyword"&gt;int&lt;/span&gt; hr;&lt;br/&gt;&lt;br/&gt;    &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (_manager != &lt;span class="hljs-literal"&gt;null&lt;/span&gt; &amp;amp;&amp;amp; &lt;br/&gt;        (hr = &lt;br/&gt;        _manager.IsWindowOnCurrentVirtualDesktop(&lt;br/&gt;        topLevelWindow, &lt;span class="hljs-keyword"&gt;out&lt;/span&gt; result)) != &lt;span class="hljs-number"&gt;0&lt;/span&gt;) &lt;br/&gt;    {&lt;br/&gt;      Marshal.ThrowExceptionForHR(hr);&lt;br/&gt;    }&lt;br/&gt;    &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; result != &lt;span class="hljs-number"&gt;0&lt;/span&gt;;&lt;br/&gt;  }&lt;br/&gt;} 
Wenn wir in unserer Demo-Solution nun ein weiteres Projekt mit Namen UI anlegen und dort eine Anwendung für die Windows Presentation Foundation (WPF) erzeugen, können wir eine Demo-Anwendung erstellen, um die Funktionalität unserer Klasse zu nutzen. In Listing 3 ist der Code zu sehen, der in einem Button ausgeführt wird. Wir brauchen unsere Klasse als Instanz und das Handle des aktuellen WPF-Fensters. Anschließend können wir testen, ob dieses Handle auf dem aktuellen virtuellen Desktop vorhanden ist. Wir beschaffen uns die ID in Form einer GUID für den aktiven Arbeitsbereich und verschieben das Fenster, wenn wir das Handle zur Verfügung haben und die GUID des virtuellen Arbeitsbereichs kennen. Die Ausgaben auf der Konsole der ersten beiden Aufrufe sieht als Beispiel wie folgt aus:
Listing 3: Test in einem Button-Handler für Funktionen des VirtualDesktopManager.
&lt;span class="hljs-keyword"&gt;private&lt;/span&gt; &lt;span class="hljs-literal"&gt;void&lt;/span&gt; ButtonBase_OnClick(object sender, &lt;br/&gt;    RoutedEventArgs e)&lt;br/&gt;{&lt;br/&gt;  &lt;span class="hljs-built_in"&gt;var&lt;/span&gt; virtualDesktopManager = &lt;br/&gt;    &lt;span class="hljs-literal"&gt;new&lt;/span&gt; VirtualDesktopManager();&lt;br/&gt;&lt;br/&gt;  &lt;span class="hljs-built_in"&gt;var&lt;/span&gt; &lt;span class="hljs-keyword"&gt;handle&lt;/span&gt; = &lt;span class="hljs-literal"&gt;new&lt;/span&gt; WindowInteropHelper(this).&lt;span class="hljs-keyword"&gt;Handle&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;  Debug.WriteLine(virtualDesktopManager&lt;br/&gt;    .IsWindowOnCurrentVirtualDesktop(&lt;span class="hljs-keyword"&gt;handle&lt;/span&gt;));&lt;br/&gt;  Debug.WriteLine(virtualDesktopManager&lt;br/&gt;    .GetWindowDesktopId(&lt;span class="hljs-keyword"&gt;handle&lt;/span&gt;));&lt;br/&gt;&lt;br/&gt;  virtualDesktopManager.MoveWindowToDesktop(&lt;span class="hljs-keyword"&gt;handle&lt;/span&gt;, &lt;br/&gt;    Guid.Parse(&lt;span class="hljs-string"&gt;"c3b61022-2092-489d-9d29-a6c26cf55774"&lt;/span&gt;));&lt;br/&gt;} 
True
c3b61022<span class="hljs-number">-2092</span><span class="hljs-number">-489</span>d<span class="hljs-number">-9</span>d29-a6c26cf55774 
Das funktioniert problemlos, wenn wir zum Beispiel die Methode GetWindowDesktopId nutzen, um die GUID des aktiven Desktops bekommen, um anschließend diese GUID in die Methode einzubauen. Dann starten wir die Anwendung, erzeugen einen neuen virtuellen Desktop und klicken auf unseren Button. Prompt taucht das Anwendungsfenster auf dem ursprünglichen virtuellen Arbeitsbereich auf.Unsere Implementierung funktioniert, aber ist das eine technisch gute Lösung? Ja, da wir uns auf eine offizielle Schnittstelle verlassen und diese sauber in unsere .NET-Anwendung einbinden. Ist das eine inhaltlich gute beziehungsweise ausreichende Lösung? Absolut nicht. Und das ist Micro­softs Schuld.

Microsoft: setzen, Sechs!

Auch wenn diese Schnittstelle gut funktioniert, ist sie mitnichten ausreichend, um etwas Sinnvolles damit umzusetzen. Zu prüfen, ob sich ein Fenster-Handle auf dem aktuell aktiven Desktop befindet, die GUID des virtuellen Arbeitsbereichs über ein Handle zu bekommen oder ein Fenster verschieben zu können, sind zwar sinnvolle und wichtige Aktionen, allerdings fehlen grundlegende Funktionen, um mit der Gesamtmenge an Desktops zu arbeiten – beispielsweise, um alle virtuellen Arbeitsbereiche aufzulisten, die Anzahl dieser Desktops, den benachbarten Desktop links oder rechts vom aktuellen Desktop zu ermitteln und dergleichen. Auch das Erstellen oder Löschen von Desktops ist mit der bisher beschriebenen Schnittstelle nicht realisierbar. Es ist unverständlich, wie diese Schnittstelle zu den virtuellen Desktops in diesem Zustand überhaupt veröffentlicht werden konnte.Wagen wir uns aber weiter in den Kaninchenbau, finden wir einige interne Schnittstellen, um die beschriebenen Missstände auszugleichen. Das passiert natürlich auf eigenes Risiko, da es sich um interne Implementierungen handelt. Daher an dieser Stelle die kurze Warnung, dass dieser Code nichts ist, was wir in ein Produkt einbauen möchten. Microsoft steht es immer frei, undokumentierte APIs zu ändern, wann immer ihnen danach ist. Und es besteht auch ein Laufzeitrisiko: Dieser Code funktioniert nicht unbedingt gut, wenn der Benutzer an den virtuellen Desktops Änderungen vornimmt.Um die internen Funktionen von Windows nutzen zu können, benötigen wir einige weitere Interfaces und Klassen. Neben IVirtualDesktopManager kommt noch das Interface IVirtualDesktop hinzu, das eine Methode zur Ermittlung der GUID als ID und die Methode IsViewVisible enthält. Zudem gibt es noch ein Interface IObjectArray mit den Methoden GetCount und GetAt. Dieses Interface wird bei der Defini­tion von IVirtualDesktopManagerInternal wichtig, das in Listing 4 zu sehen ist. Die Schnittstellendefinition zeigt zahlreiche Methoden, die genau das implementieren, was wir uns schon in der öffentlichen Schnittstelle gewünscht haben – zum Beispiel Methoden wie GetDesktops für eine Auflistung aller verfügbaren Desktops, die Methode GetAdjacentDesktop, um den vorhandenen virtuellen Desktop links oder rechts vom aktuell aktiven zu erhalten. Auch SwitchDesktop, Create­Desk­top und RemoveDesktop bieten wesentliche Opera­tio­nen an, um in Anwendungen tatsächlich mit den virtuellen Arbeitsbereichen arbeiten zu können.
Listing 4: Die umfangreiche interne Klasse für Funktionen des VirtualDesktopManager:
[ComImport]&lt;br/&gt;[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]&lt;br/&gt;[Guid(&lt;span class="hljs-string"&gt;"AF8DA486-95BB-4460-B3B7-6E7A6B2962B5"&lt;/span&gt;)]&lt;br/&gt;&lt;span class="hljs-keyword"&gt;internal&lt;/span&gt; &lt;span class="hljs-keyword"&gt;interface&lt;/span&gt; &lt;span class="hljs-title"&gt;IVirtualDesktopManagerInternal&lt;/span&gt; {&lt;br/&gt;  &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;int&lt;/span&gt; &lt;span class="hljs-title"&gt;GetCount&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;/span&gt;)&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;  &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;void&lt;/span&gt; &lt;span class="hljs-title"&gt;MoveViewToDesktop&lt;/span&gt;(&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-function"&gt;&lt;span class="hljs-params"&gt;    IApplicationView view, IVirtualDesktop desktop&lt;/span&gt;)&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;  &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;void&lt;/span&gt; &lt;span class="hljs-title"&gt;CanViewMoveDesktops&lt;/span&gt;(&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-function"&gt;&lt;span class="hljs-params"&gt;    IApplicationView view, &lt;span class="hljs-keyword"&gt;out&lt;/span&gt; &lt;span class="hljs-keyword"&gt;int&lt;/span&gt; itcan&lt;/span&gt;)&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;  &lt;span class="hljs-function"&gt;IVirtualDesktop &lt;span class="hljs-title"&gt;GetCurrentDesktop&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;/span&gt;)&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;  &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;void&lt;/span&gt; &lt;span class="hljs-title"&gt;GetDesktops&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;span class="hljs-keyword"&gt;out&lt;/span&gt; IObjectArray desktops&lt;/span&gt;)&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;  [PreserveSig]&lt;br/&gt;  &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;int&lt;/span&gt; &lt;span class="hljs-title"&gt;GetAdjacentDesktop&lt;/span&gt;(&lt;span class="hljs-params"&gt;IVirtualDesktop &lt;span class="hljs-keyword"&gt;from&lt;/span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-function"&gt;&lt;span class="hljs-params"&gt;    &lt;span class="hljs-keyword"&gt;int&lt;/span&gt; direction, &lt;span class="hljs-keyword"&gt;out&lt;/span&gt; IVirtualDesktop desktop&lt;/span&gt;)&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;  &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;void&lt;/span&gt; &lt;span class="hljs-title"&gt;SwitchDesktop&lt;/span&gt;(&lt;span class="hljs-params"&gt;IVirtualDesktop desktop&lt;/span&gt;)&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;  &lt;span class="hljs-function"&gt;IVirtualDesktop &lt;span class="hljs-title"&gt;CreateDesktop&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;/span&gt;)&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;  &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;void&lt;/span&gt; &lt;span class="hljs-title"&gt;RemoveDesktop&lt;/span&gt;(&lt;span class="hljs-params"&gt;IVirtualDesktop desktop, &lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-function"&gt;&lt;span class="hljs-params"&gt;    IVirtualDesktop fallback&lt;/span&gt;)&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;  &lt;span class="hljs-function"&gt;IVirtualDesktop &lt;span class="hljs-title"&gt;FindDesktop&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;span class="hljs-keyword"&gt;ref&lt;/span&gt; Guid desktopid&lt;/span&gt;)&lt;/span&gt;;&lt;br/&gt;} 
Mit den zusätzlichen Möglichkeiten aus Listing 4 lassen sich nun zahlreiche Anwendungsfälle umsetzen. Wir können zum Beispiel Dienstprogramme schreiben, um den aktuellen Desktop anzuzeigen, automatisch zwischen virtuellen Arbeitsbereichen zu wechseln und grundsätzlich Logik zu implementieren, um die Möglichkeiten der virtuellen Desktops so gut es geht auszuschöpfen.Für noch komplexere Anwendungsfälle fehlt aber weiterhin Funktionalität. Zum Beispiel wäre es gut, mitzubekommen, wenn sich die virtuellen Arbeitsbereiche verändern – wenn zum Beispiel ein Desktop hinzukommt oder entfernt wird. Auch das Wechseln von Anwendungen mitzubekommen wäre für viele Szenarien ein Vorteil. Und dann ist da noch das Feature mit den angepinnten Anwendungen, um ein bestimmtes Fenster oder alle Fenster dieser Anwendung auf allen virtuellen Desktops gleichzeitig anzuzeigen.Dazu sind weitere Interfaces aus der COM-Welt notwendig. Zusätzlich zu den bisher genannten kommen noch Vir­tualDesktopNotificationService und VirtualDesktopPinned­Apps hinzu. Das machen wir aber nicht mehr manuell, sondern nutzen eine fertige Implementierung. Diese Bibliothek abstrahiert zudem das Problem, dass sich die internen APIs von Microsoft zwischen den Windows-Builds verändern können und in der Vergangenheit auch schon verändert haben. Wir müssen daher auch diese verschiedenen Builds abstrahieren, wenn wir eine Anwendung mit Zugriff auf die virtuellen Arbeitsbereiche für zahlreiche Windows-Versionen neu bauen und pflegen möchten.

Was ist VirtualDesktop?

Das Projekt VirtualDesktop [4] ist ein C#-Wrapper um das Interface IVirtualDesktopManager. Dabei spielt es keine Rolle, ob Windows 10 oder Windows 11 als Unterbau zur Verfügung steht. Die Unterschiede bei den bisher genannten COM-Interfaces zwischen diesen Windows-Builds abstrahiert die Bibliothek weg und bietet zudem die erweiterten Funktionen wie das Anpinnen von Anwendungen und Benachrichtigungen bei Veränderungen in den Arbeitsbereichen an.Die Hauptfunktionen der Bibliothek lassen sich folgendermaßen zusammenfassen:
  • Wechseln, Hinzufügen und Entfernen eines virtuellen Desktops
  • Verschieben von Fenstern desselben Prozesses auf einen beliebigen virtuellen Desktop
  • Verschieben von Fenstern eines anderen Prozesses auf einen beliebigen virtuellen Desktop
  • Anheften eines Fensters oder einer Anwendung, um das Fenster oder die Anwendung auf allen virtuellen Desktops anzuzeigen
  • Benachrichtigung beim Umschalten, Löschen, Umbenennen von virtuellen Arbeitsbereichen
  • Ändern des Hintergrundbilds für jeden virtuellen Desktop
Die Integration und Nutzung der Bibliothek ist sehr einfach. Ein paar Anforderungen müssen erfüllt sein, die wir im Folgenden noch klären, aber ansonsten ist nicht viel zu berücksichtigen. Zudem bietet das Projekt eine Dreiteilung zwischen der Core-Funktionalität und Erweiterungsmethoden für Windows Forms und Windows Presentation Foundation an. Damit stehen Methoden wie zum Beispiel IsCurrentVirtualDesktop direkt auf den Fenster-Objekten von Windows Forms oder der WPF zur Verfügung, was etwas Aufwand bei der Implementierung spart.

Was ist WinRT?

Hinter der Abkürzung WinRT steckt die Windows Runtime, eine objektorientierte Schnittstelle (API) auf Basis von COM. Das API existiert bereits seit Windows 8 und kann unter anderem in klassischen .NET-Anwendungen zum Einsatz kommen. Spannend ist, dass WinRT als Ablösung für das bisherige Win32 API gedacht ist. Anders als das .NET Framework, das ein Aufsatz auf dem Win32 API ist, soll WinRT ein tatsächlicher Ersatz sein.

Installation von VirtualDesktop

Der Kasten Was ist WinRT? hat bereits erläutert, was dahintersteckt. Um Windows Runtime APIs aufrufen zu können, müssen wir die spezifische Version in unserer Solution konfigurieren. Für .NET 5 beziehungsweise 6 sind das die folgenden Einträge in den Projektdateien:
&lt;TargetFramework&gt;net5<span class="hljs-number">.0</span>-windows10<span class="hljs-number">.0</span><span class="hljs-number">.19041</span><span class="hljs-number">.0</span>
  &lt;/TargetFramework&gt;
&lt;TargetFramework&gt;net6<span class="hljs-number">.0</span>-windows10<span class="hljs-number">.0</span><span class="hljs-number">.19041</span><span class="hljs-number">.0</span>
  &lt;/TargetFramework&gt; 
In der Demo-Anwendung kommt der zweite Eintrag zum Einsatz, da .NET 6 genutzt wird. Anschließend lässt sich die Bibliothek VirtualDesktop installieren. Ohne die Anpassung erscheint bei der Installation eine Fehlermeldung. Funktioniert das alles nicht, lässt sich zum im app.manifest ein Eintrag erstellen, um die Anwendung für Windows 10 zu optimieren. Listing 5 zeigt, wie das funktioniert.
Listing 5: Optimierung der Demo-Anwendung für Windows 10/11:
&lt;span class="xml"&gt;&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;compatibility&lt;/span&gt; &lt;span class="hljs-attr"&gt;xmlns&lt;/span&gt;=&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="xml"&gt;&lt;span class="hljs-tag"&gt;    &lt;span class="hljs-string"&gt;"urn:schemas-microsoft-com:compatibility.v1"&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="xml"&gt;  &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;application&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="xml"&gt;  &lt;span class="hljs-comment"&gt;&amp;lt;!-- Windows 10 / 11--&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="xml"&gt;  &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;supportedOS&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="xml"&gt;&lt;span class="hljs-tag"&gt;    &lt;span class="hljs-attr"&gt;Id&lt;/span&gt;=&lt;span class="hljs-string"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="hljs-template-variable"&gt;{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}&lt;/span&gt;&lt;span class="xml"&gt;&lt;span class="hljs-tag"&gt;&lt;span class="hljs-string"&gt;"&lt;/span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="xml"&gt;  &lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;application&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="xml"&gt;&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;compatibility&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt; 

Virtuelle Desktops erstellen und löschen

Um auf alle Desktops zuzugreifen, reicht die folgende Zeile Code aus:
<span class="hljs-attribute">var desktops</span> = VirtualDesktop.GetDesktops(); 
In Bild 2 ist ein Screenshot von JetBrains Rider im Debug-Modus bei einem aktiven Breakpoint zu sehen. Abgebildet ist die Liste mit den zurückgelieferten virtuellen Arbeitsbereichen. Allein dieses Feature der Bibliothek ist bereits eine erhebliche Erleichterung, da es einfach funktioniert, die Windows-Builds abstrahiert sind und eine Funktion verfügbar ist, die in der offiziellen Schnittstelle von Windows gar nicht vorhanden ist.
Übersicht im Debug-Modusbeim Ermitteln aller virtueller Desktops(Bild 2) © Autor
Um einen bestimmten virtuellen Desktop zu erhalten, können Sie eine der beiden folgenden Varianten wählen:
<span class="hljs-attribute">var desktop</span> = VirtualDesktop.FromHwnd(hwnd);
<span class="hljs-attribute">v</span><span class="hljs-attribute">a</span><span class="hljs-attribute">r</span> <span class="hljs-attribute">d</span><span class="hljs-attribute">e</span><span class="hljs-attribute">s</span><span class="hljs-attribute">k</span><span class="hljs-attribute">t</span><span class="hljs-attribute">o</span><span class="hljs-attribute">p</span> = this.GetCurrentDesktop(); 
Die erste Code-Zeile zeigt die generische Variante, wenn Sie ein Fenster-Handle haben. Die zweite Variante nutzt die Erweiterungsmethode GetCurrentDesktop aus der Bibliothek VirtualDesktop.WPF. Den linken beziehungsweise rechten virtuellen Desktop erhalten Sie über die folgenden Aufrufe:
<span class="hljs-attribute">var left</span> = desktop.GetLeft();
<span class="hljs-attribute">v</span><span class="hljs-attribute">a</span><span class="hljs-attribute">r</span> <span class="hljs-attribute">r</span><span class="hljs-attribute">i</span><span class="hljs-attribute">g</span><span class="hljs-attribute">h</span><span class="hljs-attribute">t</span> = desktop.GetRight(); 
Wenn kein Desktop vorhanden ist, liefern die Methoden null zurück. Das Erstellen von virtuellen Arbeitsbereichen ist ebenso einfach wie deren Löschen. Dazu reichen die folgenden Aufrufe aus:
var desktop = VirtualDesktop.Create<span class="hljs-comment">()</span>;
desktop.Remove<span class="hljs-comment">()</span>; 
Das Erstellen eines Desktops ist beispielsweise so schnell, dass es in einer WPF-Anwendung überhaupt nicht auffällt, wenn eine Schaltfläche gedrückt wird, um den Code auszuführen. Der virtuelle Desktop wird im Hintergrund einfach erstellt und ist ab dem Moment verfügbar. Da die Ansicht nicht automatisch auf diesen gewechselt wird, bemerken wir vom neuen Desktop praktisch nichts.

Desktops wechseln und Fenster verschieben

Für das Wechseln steht bei der VirtualDesktop-Klasse eine Switch-Methode zur Verfügung. Sobald Sie also Zugriff auf ein Desktop-Objekt eines virtuellen Arbeitsbereichs haben, können Sie auch zu diesem wechseln. Wenn wir das Beispiel von zuvor nutzen, lässt sich schnell und unkompliziert die Ansicht auf den gerade neu erstellen virtuellen Desktop wechseln:
var desktop = VirtualDesktop.Create<span class="hljs-comment">()</span>;
desktop.Switch<span class="hljs-comment">()</span>; 
Auch das funktioniert sehr schnell, was bedeutet, dass wir uns direkt nach dem Klicken des Buttons auf dem neuen virtuellen Desktop befinden. Bevor wir den Klick auf die Schaltfläche realisiert haben, sind wir schon drüben. Der neue Desktop wird immer rechts vom aktuellen geöffnet. Damit lässt sich diese Funktionalität auch wie folgt implementieren:
<span class="hljs-keyword">var</span> desktop = VirtualDesktop.Create();
<span class="hljs-keyword">var</span> currentDesktop = <span class="hljs-keyword">this</span>.GetCurrentDesktop();
currentDesktop?.GetRight()?.Switch(); 
Das ist verständlicherweise keine direkte Alternative, weil sie über einen Umweg das identische Resultat erzeugt, verdeutlicht aber, dass wir das Wechseln von Desktops auch auf Basis des aktuellen virtuellen Desktops und den GetLeft- beziehungsweise GetRight-Methoden ausführen können.Ein Fenster auf einen virtuellen Desktop zu verschieben ist ebenso einfach. Wir erweitern das Beispiel mit dem Erstellen eines neuen Desktops, um das aktuelle Fenster unserer Demo-Anwendung auf diesen neuen virtuellen Desktop zu verschieben:
var desktop = VirtualDesktop.Create<span class="hljs-comment">()</span>;
this.MoveToDesktop<span class="hljs-comment">(desktop)</span>;
desktop.Switch<span class="hljs-comment">()</span>; 
Diese Aktion nutzt erneut die Erweiterungsmethoden für WPF. Dass ein Wechsel stattfindet, ist nur zu bemerken, wenn auf dem aktuell aktiven Desktop Fenster vorhanden sind. Diese verschwinden offensichtlich und das Fenster unserer Anwendung taucht auf dem neuen Desktop auf. Wenn wir anschließend auf dem neuen Desktop die Aktion erneut ausführen, scheint es fast so, als passiere nichts. In der Übersicht der virtuellen Desktops sehen wir aber, dass pro Klick ein neuer Desktop erstellt wird.Alternativ funktioniert auch der folgende Code:
<span class="hljs-keyword">var</span> desktop = VirtualDesktop.Create();
desktop.SwitchAndMove(<span class="hljs-keyword">this</span>); 
Die Methode SwitchAndMove realisiert genau den gewünschten Anwendungsfall, den virtuellen Desktop zu wechseln und das angegebene Fenster mitzunehmen.

Fenster oder Anwendungen anpinnen

Für das Anpinnen unserer Fenster oder Anwendungen stehen uns die Methoden IsPinned, Pin, Unpin und TogglePin zur Verfügung. Über die Erweiterungsmethoden ist es somit recht einfach, das aktuelle Fenster über einen Aufruf von this.Pin(); anzupinnen. Ein mehrmaliger Aufruf ist ebenso pro­blemlos. Nach dem Anpinnen ist unser Fenster auf allen virtuellen Desktops zu sehen – auch bei denen, die im Nachhi­nein erstellt werden.Diese Methode funktioniert auf Fenstern von Windows Forms oder WPF problemlos. Die Aktion bezieht sich aber auch nur auf das entsprechende Fenster-Objekt, das dadurch angepinnt oder eben wieder entfernt wird. Das mag in vielen Situationen erwünscht sein, da nicht alle Fenster einer Anwendung überall angepinnt sein müssen, aber hin und wieder ist genau das die Anforderung. Dann müssen Sie die Anwendung anpinnen und nicht nur ein einzelnes Fenster.Listing 6 zeigt dazu ein entsprechendes Beispiel. Wir erzeugen erst einen neuen virtuellen Desktop und holen uns dann das Handle vom aktuellen Fenster. Das ist nur ein Zwischenschritt, da wir zum Anpinnen einer vollständigen Anwendung die App User Model ID benötigen. Die bekommen wir über die Methode TryGetAppUserModelId, die leider nicht als Erweiterungsmethode zur Verfügung steht und damit ein Fenster-Handle benötigt. Diese ID reicht dann aus, um über die Methode PinApplication die Anwendung überall anzupinnen.
Listing 6: Beispiel zum Pinnen einer kompletten Anwendung:
&lt;span class="hljs-built_in"&gt;var&lt;/span&gt; desktop = VirtualDesktop.Create();&lt;br/&gt;&lt;br/&gt;&lt;span class="hljs-built_in"&gt;var&lt;/span&gt; &lt;span class="hljs-keyword"&gt;handle&lt;/span&gt; = &lt;span class="hljs-literal"&gt;new&lt;/span&gt; WindowInteropHelper(this).&lt;span class="hljs-keyword"&gt;Handle&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;&lt;span class="hljs-built_in"&gt;var&lt;/span&gt; success = VirtualDesktop.TryGetAppUserModelId(&lt;span class="hljs-keyword"&gt;handle&lt;/span&gt;, out &lt;span class="hljs-built_in"&gt;var&lt;/span&gt; result);&lt;br/&gt;&lt;br/&gt;VirtualDesktop.PinApplication(result);&lt;br/&gt;&lt;br/&gt; 

Benachrichtigungen verarbeiten

Als eine wesentliche Anforderung wurde zuvor im Artikel die Möglichkeit genannt, Benachrichtigungen zu erhalten, wenn sich etwas an den virtuellen Arbeitsbereichen verändert. Dieses Feature ist deshalb so elementar, weil wir ansonsten innerhalb unserer Anwendung oft den aktuellen Status der virtuellen Desktops nicht wissen. Wir müssten ansonsten die Aufzählung der vorhandenen virtuellen Desktops beständig abrufen und durchgehen, um Änderungen festzustellen..Erfreulicherweise bietet uns die Klasse VirtualDesktop einige Events an, an die wir die Anwendung hängen können. Diese Events sind unter anderem Created, Destroyed, Moved, Renamed und CurrentChanged. Sie bekommen dadurch mit, wann neue virtuelle Desktops erstellt wurden, wann ein Desktop gewechselt oder auch wieder gelöscht wird. In einer Anwendung, die mit den virtuellen Arbeitsbereichen interagiert, haben Sie so die Möglichkeit, Veränderungen von außen mitzubekommen – zum Beispiel, wenn der Benutzer einen Desktop schließt.Wir können uns daher in den Created-Event einhängen und mitbekommen, dass ein virtueller Desktop erstellt wird:
VirtualDesktop.Created += 
  <span class="hljs-function"><span class="hljs-params">(o, virtualDesktop)</span> =&gt;</span> Debug.WriteLine(virtualDesktop); 
Das funktioniert sowohl für virtuelle Desktops, die wir über unseren Code und die Bibliothek erstellen, als auch über die Windows-Bordmittel.Bei den anderen Events funktioniert das ebenso. Wichtig ist die Information, dass die interne Initialisierung erst stattfindet, wenn wir eine statische Eigenschaft oder Methode der Klasse VirtualDesktop nutzen.Machen wir das nicht, sondern möchten wir nur auf die Events hören und diese abonnieren, funktioniert es nicht. Wir bekommen keinen Event geliefert. Wenn wir nur auf Events hören möchten, ist zwingend die folgende Methode zuallererst aufzurufen:
VirtualDesktop.Configure()<span class="hljs-comment">;</span> 
Damit wird die interne Initialisierung abgeschlossen und wir bekommen auch etwas von den Events mit, wenn sie auftreten.

Eigenschaften von virtuellen Desktops verändern

Wenn wir mit virtuellen Arbeitsumgebungen in Form der Klasse VirtualDesktop zu tun haben, dann können wir einige Eigenschaften eines Desktops verändern. Zum Beispiel können wir über die Eigenschaft Name einen Namen vergeben, den wir auch in der Übersicht der virtuellen Desktops in Windows einstellen können.Diese Funktion steht allerdings laut Bibliothek nicht unter Windows 10 zur Verfügung, sondern lediglich ab der Version Windows 11.Zudem lässt sich das Hintergrundbild eines Desktops verändern. Dazu dient die Eigenschaft WallpaperPath, um den Pfad zu einem Bild zu erhalten oder zu setzen.Dies ist ebenfalls eine Option, die nur unter Windows 11 und nicht unter Windows 10 zur Verfügung steht. Möchten wir die Hintergründe aller virtuellen Desktops verändern, lässt sich das über eine statische Methode der Klasse VirtualDesktop regeln:
<span class="hljs-selector-tag">VirtualDesktop</span><span class="hljs-selector-class">.UpdateWallpaperForAllDesktops</span>(@"..."); 

Fazit

Alternative Bibliotheken gibt es zu VirtualDesktop nicht. Das Thema ist anscheinend immer noch ein Randthema, da sich insgesamt wenige Projekte bei GitHub oder Fragen auf StackOverflow und Co. damit befassen. Ein Grund ist sicherlich die Art und Weise, wie Microsoft die Möglichkeiten des API behandelt. Die offizielle Schnittstelle ist bis heute eher ein Witz als eine Möglichkeit. Alle Projekte, die sich annähernd mit dem Thema befassen, nutzen daher interne Möglichkeiten.Hier gibt es das angesprochene Problem, dass sich diese Schnittstellen einfach so ändern können. Gleichzeitig bietet das die Hoffnung, dass es in Zukunft mit der Entwicklung weitergeht. Die interne Schnittstelle wird beständig umfangreicher, wie zum Beispiel die Insider-Previews von Windows 11 zeigen. Das lässt hoffen, dass vielleicht irgendwann in ferner Zukunft auch die offizielle Schnittstelle erheblich erweitert wird.Zur Bibliothek VirtualDesktop bleibt gar nicht mehr viel zu sagen, außer der Tatsache, dass die Funktionen zahlreich sind und sich einfach in eigene Projekte einbinden lassen. Bleibt zu hoffen, dass das Projekt weiter gepflegt und an neue Windows-Builds angepasst wird.Eine Empfehlung bekommt die Bibliothek allemal und die Note „sehr gut“ – sowohl für die Funktionen und die Einfachheit, aber auch für die Recherchearbeiten, die in der Entwicklung stecken müssen.
Projektdateien herunterladen

Fussnoten

  1. Der Header shobjidl_core.h in der Windows-Entwickler-Dokumentation, http://www.dotnetpro.de/SL2206Frameworks1
  2. Informationen zur Windows Shell in der Entwickler-Dokumentation, http://www.dotnetpro.de/SL2206Frameworks2
  3. Informationen zur Windows-Suche in der Entwickler-Dokumentation, http://www.dotnetpro.de/SL2206Frameworks3
  4. Die Bibliothek VirtualDesktop auf GitHub, http://www.dotnetpro.de/SL2206Frameworks4

Neueste Beiträge

DWX hakt nach: Wie stellt man Daten besonders lesbar dar?
Dass das Design von Websites maßgeblich für die Lesbarkeit der Inhalte verantwortlich ist, ist klar. Das gleiche gilt aber auch für die Aufbereitung von Daten für Berichte. Worauf besonders zu achten ist, erklären Dr. Ina Humpert und Dr. Julia Norget.
3 Minuten
27. Jun 2025
DWX hakt nach: Wie gestaltet man intuitive User Experiences?
DWX hakt nach: Wie gestaltet man intuitive User Experiences? Intuitive Bedienbarkeit klingt gut – doch wie gelingt sie in der Praxis? UX-Expertin Vicky Pirker verrät auf der Developer Week, worauf es wirklich ankommt. Hier gibt sie vorab einen Einblick in ihre Session.
4 Minuten
27. Jun 2025
„Sieh die KI als Juniorentwickler“
CTO Christian Weyer fühlt sich jung wie schon lange nicht mehr. Woran das liegt und warum er keine Angst um seinen Job hat, erzählt er im dotnetpro-Interview.
15 Minuten
27. Jun 2025
Miscellaneous

Das könnte Dich auch interessieren

UIs für Linux - Bedienoberflächen entwickeln mithilfe von C#, .NET und Avalonia
Es gibt viele UI-Frameworks für .NET, doch nur sehr wenige davon unterstützen Linux. Avalonia schafft als etabliertes Open-Source-Projekt Abhilfe.
16 Minuten
16. Jun 2025
Mythos Motivation - Teamentwicklung
Entwickler bringen Arbeitsfreude und Engagement meist schon von Haus aus mit. Diesen inneren Antrieb zu erhalten sollte für Führungskräfte im Fokus stehen.
13 Minuten
19. Jan 2017
Evolutionäres Prototyping von Business-Apps - Low Code/No Code und KI mit Power Apps
Microsoft baut Power Apps zunehmend mit Features aus, um die Low-Code-/No-Code-Welt mit der KI und der professionellen Programmierung zu verbinden.
19 Minuten
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige