Freigeben über


Erstellen einer einzelinstanzierten WinUI-App mit C#

In dieser Vorgehensweise wird veranschaulicht, wie Sie eine winUI 3-App mit einer Instanz mit C# und dem Windows App SDK erstellen. Einzelinstanzen-Apps erlauben nur jeweils eine Instanz der App, die ausgeführt wird. WinUI-Apps sind standardmäßig multiinstanziert. Sie ermöglichen es Ihnen, mehrere Instanzen derselben App gleichzeitig zu starten. Das wird auf mehrere Instanzen verwiesen. Sie können jedoch die Einzelinstanzerstellung basierend auf dem Anwendungsfall Ihrer App implementieren. Der Versuch, eine zweite Instanz einer einzelinstanzierten App zu starten, führt nur dazu, dass stattdessen das Hauptfenster der ersten Instanz aktiviert wird. In diesem Lernprogramm wird die Implementierung einer Einzelinstanzerstellung in einer WinUI-App veranschaulicht.

In diesem Artikel wird Folgendes behandelt:

  • Deaktivieren des generierten Program XAML-Codes
  • Definieren einer benutzerdefinierten Main Methode für die Umleitung
  • Testen der Einzelinstanzerstellung nach der App-Bereitstellung

Voraussetzungen

In diesem Lernprogramm wird Visual Studio verwendet und auf der WinUI-Vorlage für leere Apps erstellt. Wenn Sie mit der WinUI-Entwicklung noch nicht vertraut sind, können Sie die Einrichtung ausführen, indem Sie die Anweisungen in "Erste Schritte mit WinUI" befolgen. Dort installieren Sie Visual Studio, konfigurieren sie für die Entwicklung von Apps mit WinUI und stellen sicher, dass Sie über die neueste Version von WinUI und das Windows App SDK verfügen und ein Hallo Welt Projekt erstellen.

Wenn Sie dies getan haben, kehren Sie hierher zurück, um zu erfahren, wie Sie Ihr "Hallo Welt"-Projekt in eine einzelinstanzierte App umwandeln.

Hinweis

Diese Vorgehensweise basiert auf dem Blogbeitrag "Erstellen der Einzelinstanzen" (Teil 3) der App aus einer Windows-Blogreihe unter WinUI 3. Der Code für diese Artikel ist auf GitHub verfügbar.

Automatisch generierten Programmcode deaktivieren

Wir müssen die Umleitung so früh wie möglich überprüfen, bevor wir Fenster erstellen. Dazu müssen wir das Symbol "DISABLE_XAML_GENERATED_MAIN" in der Projektdatei definieren. Führen Sie die folgenden Schritte aus, um den automatisch generierten Programmcode zu deaktivieren:

  1. Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf den Projektnamen, und wählen Sie "Projektdatei bearbeiten" aus.

  2. Definieren Sie das DISABLE_XAML_GENERATED_MAIN-Symbol für jede Konfiguration und Plattform. Fügen Sie der Projektdatei die folgende XML hinzu:

    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
      <DefineConstants>DISABLE_XAML_GENERATED_MAIN</DefineConstants>
    </PropertyGroup>
    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
      <DefineConstants>DISABLE_XAML_GENERATED_MAIN</DefineConstants>
    </PropertyGroup>
    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
      <DefineConstants>DISABLE_XAML_GENERATED_MAIN</DefineConstants>
    </PropertyGroup>
    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
      <DefineConstants>DISABLE_XAML_GENERATED_MAIN</DefineConstants>
    </PropertyGroup>
    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|arm64'">
      <DefineConstants>DISABLE_XAML_GENERATED_MAIN</DefineConstants>
    </PropertyGroup>
    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|arm64'">
      <DefineConstants>DISABLE_XAML_GENERATED_MAIN</DefineConstants>
    </PropertyGroup>
    

Wenn Sie das Symbol DISABLE_XAML_GENERATED_MAIN hinzufügen, wird der automatisch generierte Programmcode für Ihr Projekt deaktiviert.

Definieren einer Program-Klasse mit einer Main-Methode

Eine angepasste Program.cs Datei muss erstellt werden, anstatt die Standard-Main-Methode auszuführen. Der Code, der der Program-Klasse hinzugefügt wurde, ermöglicht der App die Überprüfung auf Umleitung, was nicht das Standardverhalten von WinUI-Apps ist.

  1. Navigieren Sie zu Projektmappen-Explorer, klicken Sie mit der rechten Maustaste auf den Projektnamen, und wählen Sie "Hinzufügen" | aus. Klasse.

  2. Benennen Sie die neue KlasseProgram.cs, und wählen Sie "Hinzufügen" aus.

  3. Fügen Sie der Program-Klasse die folgenden Namespaces hinzu, und ersetzen Sie alle vorhandenen Namespaces:

    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.UI.Dispatching;
    using Microsoft.UI.Xaml;
    using Microsoft.Windows.AppLifecycle;
    
  4. Ersetzen Sie die leere Program-Klasse durch Folgendes:

    public class Program
    {
        [STAThread]
        static int Main(string[] args)
        {
            WinRT.ComWrappersSupport.InitializeComWrappers();
            bool isRedirect = DecideRedirection();
    
            if (!isRedirect)
            {
                Application.Start((p) =>
                {
                    var context = new DispatcherQueueSynchronizationContext(
                        DispatcherQueue.GetForCurrentThread());
                    SynchronizationContext.SetSynchronizationContext(context);
                    _ = new App();
                });
            }
    
            return 0;
        }
    }
    

    Die Main-Methode bestimmt, ob die App zur ersten Instanz umgeleitet werden soll, oder eine neue Instanz nach dem Aufrufen von "DecideRedirection", die als nächstes definiert wird.

  5. Definieren Sie die DecideRedirection-Methode unter der Main-Methode :

    private static bool DecideRedirection()
    {
        bool isRedirect = false;
        AppActivationArguments args = AppInstance.GetCurrent().GetActivatedEventArgs();
        ExtendedActivationKind kind = args.Kind;
        AppInstance keyInstance = AppInstance.FindOrRegisterForKey("MySingleInstanceApp");
    
        if (keyInstance.IsCurrent)
        {
            keyInstance.Activated += OnActivated;
        }
        else
        {
            isRedirect = true;
            RedirectActivationTo(args, keyInstance);
        }
    
        return isRedirect;
    }
    

    DecideRedirection bestimmt, ob die App durch Registrieren eines eindeutigen Schlüssels registriert wurde, der Ihre App-Instanz darstellt. Basierend auf dem Ergebnis der Schlüsselregistrierung kann ermittelt werden, ob eine aktuelle Instanz der App ausgeführt wird. Nachdem Sie die Bestimmung vorgenommen haben, weiß die Methode, ob die App umgeleitet werden soll oder ob die App den Start der neuen Instanz fortsetzen soll. Die RedirectActivationTo-Methode wird aufgerufen, wenn die Umleitung erforderlich ist.

  6. Als Nächstes erstellen wir die RedirectActivationTo-Methode unterhalb der DecideRedirection-Methode zusammen mit den erforderlichen DllImport-Anweisungen. Fügen Sie der Program-Klasse den folgenden Code hinzu:

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    private static extern IntPtr CreateEvent(
        IntPtr lpEventAttributes, bool bManualReset,
        bool bInitialState, string lpName);
    
    [DllImport("kernel32.dll")]
    private static extern bool SetEvent(IntPtr hEvent);
    
    [DllImport("ole32.dll")]
    private static extern uint CoWaitForMultipleObjects(
        uint dwFlags, uint dwMilliseconds, ulong nHandles,
        IntPtr[] pHandles, out uint dwIndex);
    
    [DllImport("user32.dll")]
    static extern bool SetForegroundWindow(IntPtr hWnd);
    
    private static IntPtr redirectEventHandle = IntPtr.Zero;
    
    // Do the redirection on another thread, and use a non-blocking
    // wait method to wait for the redirection to complete.
    public static void RedirectActivationTo(AppActivationArguments args,
                                            AppInstance keyInstance)
    {
        redirectEventHandle = CreateEvent(IntPtr.Zero, true, false, null);
        Task.Run(() =>
        {
            keyInstance.RedirectActivationToAsync(args).AsTask().Wait();
            SetEvent(redirectEventHandle);
        });
    
        uint CWMO_DEFAULT = 0;
        uint INFINITE = 0xFFFFFFFF;
        _ = CoWaitForMultipleObjects(
           CWMO_DEFAULT, INFINITE, 1,
           [redirectEventHandle], out uint handleIndex);
    
        // Bring the window to the foreground
        Process process = Process.GetProcessById((int)keyInstance.ProcessId);
        SetForegroundWindow(process.MainWindowHandle);
    }
    

    Die RedirectActivationTo-Methode ist für die Umleitung der Aktivierung an die erste Instanz der App verantwortlich. Es erstellt ein Ereignishandle, startet einen neuen Thread, um die Aktivierung umzuleiten, und wartet auf den Abschluss der Umleitung. Nach Abschluss der Umleitung bringt die Methode das Fenster in den Vordergrund.

  7. Definieren Sie schließlich die Hilfsmethode OnActivated unter der DecideRedirection-Methode :

    private static void OnActivated(object sender, AppActivationArguments args)
    {
        ExtendedActivationKind kind = args.Kind;
    }
    

Testen der Einzelinstanzerstellung über die App-Bereitstellung

Bis zu diesem Punkt testen wir die App durch Debuggen in Visual Studio. Es kann jedoch nur ein Debugger gleichzeitig ausgeführt werden. Diese Einschränkung verhindert, dass wir wissen, ob die App eine Instanz ist, da wir dasselbe Projekt nicht zweimal gleichzeitig debuggen können. Für einen genauen Test stellen wir die Anwendung auf unserem lokalen Windows-Client bereit. Nach der Bereitstellung können wir die App wie jede app, die unter Windows installiert ist, über den Desktop starten.

  1. Navigieren Sie zu Projektmappen-Explorer, klicken Sie mit der rechten Maustaste auf den Projektnamen, und wählen Sie "Bereitstellen" aus.

  2. Öffnen Sie die Menü , und klicken Sie in das Suchfeld.

  3. Geben Sie den Namen Ihrer App in das Suchfeld ein.

  4. Klicken Sie auf das App-Symbol aus dem Suchergebnis, um Ihre App zu starten.

    Hinweis

    Wenn app im Releasemodus abstürzt, gibt es einige bekannte Probleme mit gekürzten Apps im Windows App SDK. Sie können die Kürzung im Projekt deaktivieren, indem Sie die PublishTrimmed-Eigenschaft für alle Buildkonfigurationen in den Projektdateien .pubxml auf "false" festlegen. Weitere Informationen finden Sie in diesem Problem auf GitHub.

  5. Wiederholen Sie die Schritte 2 bis 4, um dieselbe App erneut zu starten, und überprüfen Sie, ob eine andere Instanz geöffnet wird. Wenn die App eine Instanz ist, wird die erste Instanz anstelle einer neuen Instanz aktiviert.

    Tipp

    Optional können Sie der OnActivated-Methode einen Protokollierungscode hinzufügen, um zu überprüfen, ob die vorhandene Instanz aktiviert wurde. Versuchen Sie, Copilot um Hilfe beim Hinzufügen einer ILogger-Implementierung zu Ihrer WinUI-App zu bitten.

Zusammenfassung

Der gesamte hier behandelte Code befindet sich auf GitHub mit Verzweigungen für die verschiedenen Schritte in der ursprünglichen Windows-Blogreihe. Weitere Informationen zu diesem Vorgehensweisen finden Sie in der Verzweigung für die Einzelinstanzerstellung . Die main Filiale ist die umfassendste. Die anderen Verzweigungen sollen Ihnen zeigen, wie sich die App-Architektur weiterentwickelt hat.

App-Instanziierung mit der App-Lebenszyklus-API

Erstellen einer einzelinstanzierten App (Teil 3)

WinAppSDK-DrumPad-Beispiel auf GitHub