Worklog SeeingSharp 2: Unterstützung für WinUI 3 Desktop

Im März 2021 wurde die erste stabile Version von WinUI 3 zusammen mit Project Reunion 0.5 veröffentlicht. Vorher gab es bereits mehrere Preview-Releases von WinUI 3, welche einen ersten Blick auf das Framework ermöglicht haben. Nun aber durch das erste stabile Release ist ein guter Zeitpunkt zur Umsetzung der WinUI 3 Desktop Unterstützung in SeeingSharp 2 erreicht. Mit WinUI allgemein beschäftige ich mich schon länger, so basiert die Beispielapplikation von SeeingSharp 2 für die Universal Windows Plattform (UWP) bereits auf Komponenten von WinUI 2 [1]. WinUI 2 ist allerdings noch auf die UWP beschränkt, mit WinUI 3 entfällt diese Abhängigkeit. Mit der neuen Version des Frameworks ist es nun möglich, WinUI Komponenten direkt in einer Desktop-Applikation zu verwenden.

Aller Anfang ist leicht bei WinUI 3

Der Start mit WinUI 3 ist zunächst einfach. Alle notwendigen Schritte und Voraussetzungen sind in der Dokumentation von Microsoft unter [2] zu finden. Anschließend verfügt Visual Studio über die entsprechenden Projekttemplates für eine neue WinUI Desktop Applikation oder für eine WinUI Class Library. Mit deren Hilfe lässt sich mit sehr wenig Aufwand eine WinUI 3 Applikation oder eine Komponente erstellen. Die API von WinUI 3, also etwa die verfügbaren Xaml-Elemente, erinnern sehr stark an UWP. Das ist auch kein Zufall, denn WinUI 3 ist kein gänzlich neues Framework, sondern vielmehr eine Weiterentwicklung der UWP. Für mich hieß das bei SeeingSharp 2, dass ich den Großteil des Codings von der UWP Unterstützung übernehmen konnte. Konkret heißt dass, dass sich im ersten Schritt lediglich der Namensraum von Windows.* auf Microsoft.* ändert. Nachfolgender Screenshot zeigt das anhand der Klasse SeeingSharpPanelPainter. Insgesamt bin ich bei allen Klassen bei SeeingSharp.UWP so vorgegangen und habe daraus das Projekt SeeingSharp.WinUI erstellt.

Namespace-Änderung von Windows.* auf Microsoft.*
Änderung der Namespaces von UWP zu WinUI

Nach dem Start kommen die kleinen Probleme

Statische Methoden gegen die aktuelle View

Neben der oben besprochenen Änderung der Namensräume gibt es noch andere kleinere Arbeiten, um das Projekt zum Kompilieren zu bringen. So hatte ich einen Aufruf auf die statische Methode DisplayInformation.GetForCurrentView [3] im Quellcode. Dieser Aufruf macht für die Desktop Version von WinUI 3 keinen Sinn. Unter UWP hat ein GUI-Thread genau ein Hauptfenster bzw. eine View, die ihm zugeordnet ist. Somit weiß die Methode DisplayInformation.GetForCurrentView anhand des aktuellen Threads sofort, welche View gemeint ist. In WinUI 3 für den Desktop dagegen ist das anders. Hier gibt es ähnlich wie bei den Frameworks Windows.Forms oder WPF beliebig viele Windows in einem GUI-Thread. Daher sollte der Aufruf entweder komplett raus oder sich zumindest eindeutig auf eine View beziehen. Die Lösung des Problems war relativ einfach, denn der Aufruf von DisplayInformation.GetForCurrentView hing bei SeeingSharp 2 mit der Unterstützung des Steuerelements SwapChainBackgroundPanel [4] zusammen. Dieses Steuerelement ist aber längst obsolete, weshalb ich die Unterstützung dafür direkt aus SeeingSharp 2 entfernt habe.

Debugging der WinUI 3 Applikation

Als sich schließlich die neuen Projekte auf Basis von WinUI 3 kompilieren ließen, stieß ich auf ein weiteres Problem. Nachfolgender Screenshot zeigt die Exception, die WinUI direkt beim Start mit aktiviertem Debugger ausgelöst hat. Zunächst habe ich die Ursache irgendwo im SwapChainPanel und der Schnittstelle zum Rendering mit Direct3D vermutet. Nach längerem Ausprobieren ließ sich an dieser Stelle aber nichts finden. Anschließend bin ich darauf gestoßen, dass das Problem bei mir erst dann auftritt, sobald ich das Steuerelement NavigationView [5] in der Benutzeroberfläche verwende. Hierzu gibt es sogar einen Issue auf Github [6], der entsprechend auch den Workaround für dieses Problem beschreibt: Bei den Einstellungen des C#-Projekts sollte der Debugger auf “Managed Only” anstelle von “Mixed” gestellt werden.

Start-Fehler beim Debuggen einer WinUI 3 Desktop Applikation
Workaround für das Problem beim Debuggen der WinUI 3 Desktop Applikation

Kommunikation zwischen WinUI 3 und SeeingSharp per ISwapChainPanelNative

Das letzte Problem in der Reihe war dann doch die Schnittstelle zwischen dem SwapChainPanel von WinUI 3 und dem Direct3D Rendering von SeeingSharp2. Microsoft stellt die COM-Schnittstelle ISwapChainPanelNative [7] bereit, um diese beiden Welten zusammen zu bringen. Ähnlich wie die meisten anderen Steuerelemente wurde auch diese COM-Schnittstelle direkt von der UWP übernommen. SharpDX stellt ISwapChainPanelNative direkt bereit. Beim Testen musste ich dann aber leider feststellen, dass beim Steuerelement SwapChainPanel keine Implementierung von ISwapChainPanelNative gefunden wird. Ein vergleichbares Problem lässt sich in einem Issue auf GitHub finden [8]. Hierbei handelt es sich zwar um C++, die Ursache des Problems bei SeeingSharp 2 lässt sich aber gut daraus ableiten: Es existieren zwei verschiedene Interfaces mit dem Namen ISwapChainPanelNative. Das Eine kommt von der UWP und wird auch von SharpDX bereitgestellt. Das Andere kommt von WinUI 3. Diese beiden Schnittstellen sind in C++ entsprechend in den folgenden verschiedenen Header-Dateien definiert:

  • windows.ui.xaml.media.dxinterop.h (UWP)
  • microsoft.ui.xaml.media.dxinterop.h (WinUI 3)

Die Auswirkung für SeeingSharp 2 ist relativ einfach. Um die WinUI 3 Variante der Schnittstelle zu unterstützen, musste ich lediglich ein eigenes Interface für den Zugriff auf das ISwapChainPanelNative Interface schreiben. Nachfolgend das Coding dazu. Der Inhalt der Schnittstelle ist dabei zwischen WinUI und UWP gleich, lediglich die Guid ist unterschiedlich.

namespace SeeingSharp.Multimedia.Views
{
    internal static class WinUIDesktopInterop
    {
        /// <summary>
        /// Interface from microsoft.ui.xaml.media.dxinterop.h
        /// </summary>
        [ComImport]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        [Guid("63aad0b8-7c24-40ff-85a8-640d944cc325")]
        public interface ISwapChainPanelNative
        {
            [PreserveSig] Result SetSwapChain([In]IntPtr swapChain);
        }
    }
}

Dann wären da noch die größeren Probleme

Nachdem alle Beispiele von SeeingSharp 2 auf Basis einer WinUI 3 Desktop Applikation laufen, bleiben noch ein paar grundsätzliche Probleme. Das für mich wichtigste wäre die Unterstützung von Unterfenstern. Zwar sollte WinUI 3 bereits beliebig viele Unterfenster unterstützen, nachfolgendes Coding führt bei mir aber noch zu einem Programmabsturz. Die Klasse ChildRenderWindow erbt von Window. Es knallt, sobald der Konstruktor dieser Klasse aufgerufen wird.

private void OnViewModel_NewChildWindowRequest(object sender, EventArgs eArgs)
{
    var childWindow = new ChildRenderWindow();
    _childPages.Add(childWindow);

    childWindow.Activate();
}

Fazit

Mein Fazit nach einem erneuten Ausflug in die Welt von WinUI 3 ist leider noch ernüchternd. Zwar funktioniert es mit der aktuellen stabilen Version von Visual Studio und .Net 5 grundsätzlich gut, im Detail scheint es aber auch noch einige Ecken und Kanten zu geben. Insbesondere das Problem mit dem Debugger (oben im Text das zweite) lässt das Framework noch nicht sehr ausgereift wirken. Bei dem letzten Problem mit dem Unterfenster möchte ich nicht ausschließen, dass ich einfach noch irgendetwas falsch mache. Bevor man mit WinUI 3 anfängt, lohnt auf jeden Fall ein Blick auf die Dokumentation unter [2]. Hier werden bereits implementierte und auch noch fehlende Features beschrieben.

Verweise

  1. https://www.rolandk.de/wp-posts/2020/09/worklog-seeing-2-winui/
  2. https://docs.microsoft.com/en-us/windows/apps/winui/winui3/
  3. https://docs.microsoft.com/en-us/uwp/api/windows.graphics.display.displayinformation.getforcurrentview?view=winrt-19041
  4. https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.swapchainbackgroundpanel?view=winrt-19041
  5. https://docs.microsoft.com/de-de/windows/uwp/design/controls-and-patterns/navigationview
  6. https://github.com/microsoft/microsoft-ui-xaml/issues/3741
  7. https://docs.microsoft.com/en-us/windows/win32/api/windows.ui.xaml.media.dxinterop/nn-windows-ui-xaml-media-dxinterop-iswapchainpanelnative
  8. https://github.com/microsoft/microsoft-ui-xaml/issues/3707

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.