Delen via


Een single-instanced WinUI-app maken met C#

Deze instructie laat zien hoe u een WinUI 3-app met één enkele instantie maakt met C# en de Windows App SDK. Apps met één exemplaar laten slechts één instantie van de app tegelijk draaien. WinUI-apps zijn standaard meervoudig uitvoerbaar. Hiermee kunt u meerdere exemplaren van dezelfde app tegelijk starten. Er wordt verwezen naar meerdere gevallen. Het is echter mogelijk dat u single-instancing wilt implementeren op basis van de use-case van uw app. Als u probeert een tweede instantie van een enkelvoudige instanties-app te starten, wordt in plaats daarvan alleen het hoofdvenster van de eerste instantie geactiveerd. In deze zelfstudie ziet u hoe u single-instancing implementeert in een WinUI-app.

In dit artikel leert u het volgende:

  • Gegenereerde Program code van XAML uitschakelen
  • Aangepaste Main-methode definiëren voor omleiding
  • Eenmalige instancing testen na de implementatie van de app

Voorwaarden

Deze zelfstudie maakt gebruik van Visual Studio en bouwt voort op de lege WinUI-app-sjabloon. Als u geen kennis hebt met WinUI-ontwikkeling, kunt u instellen door de instructies in Aan de slag met WinUIte volgen. Daar installeert u Visual Studio, configureert u deze voor het ontwikkelen van apps met WinUI en zorgt u ervoor dat u de nieuwste versie van WinUI en de Windows App SDK hebt en maakt u een Hello World-project.

Wanneer je dat hebt gedaan, kom dan hier terug om te leren hoe je je 'Hallo wereld'-project kunt omzetten in een app met een enkele instantie.

Notitie

Deze instructies zijn gebaseerd op de Het maken van de app met één exemplaar (deel 3) blogbericht van een Windows-blogserie op WinUI 3. De code voor deze artikelen is beschikbaar op GitHub-.

Automatisch gegenereerde programmacode uitschakelen

We moeten zo vroeg mogelijk controleren op redirectie, voordat we vensters maken. Hiervoor moeten we het symbool 'DISABLE_XAML_GENERATED_MAIN' definiëren in het projectbestand. Volg deze stappen om de automatisch gegenereerde programmacode uit te schakelen:

  1. Klik met de rechtermuisknop op de projectnaam in Solution Explorer en selecteer Projectbestand bewerken.

  2. Definieer het DISABLE_XAML_GENERATED_MAIN symbool. Voeg de volgende XML toe aan het projectbestand:

    <PropertyGroup>
      <DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN</DefineConstants>
    </PropertyGroup>
    

Als u het DISABLE_XAML_GENERATED_MAIN-symbool toevoegt, wordt de automatisch gegenereerde programmacode voor uw project uitgeschakeld.

Een Program-klasse definiëren met een Main-methode

Er moet een aangepast Program.cs-bestand worden gemaakt in plaats van de standaard main-methode uit te voeren. Met de code die is toegevoegd aan het Program-klasse, kan de app controleren op omleiding. Dit is niet het standaardgedrag van WinUI-apps.

  1. Navigeer naar Solution Explorer, klik met de rechtermuisknop op de projectnaam en selecteer Toevoegen | Klasse.

  2. Geef de nieuwe klasse een naam Program.cs en selecteer toevoegen.

  3. Voeg de volgende naamruimten toe aan de klasse Program, waarbij u bestaande naamruimten vervangt:

    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. Vervang de lege klasse Program door het volgende:

    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;
        }
    }
    

    De methode Main bepaalt of de app moet worden omgeleid naar het eerste exemplaar of een nieuw exemplaar moet starten nadat DecideRedirection-is aangeroepen, die we hierna gaan definiëren.

  5. Definieer de methode DecideRedirection onder de methode Main:

    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 bepaalt of de app is geregistreerd door een unieke sleutel te registreren die uw app-exemplaar vertegenwoordigt. Op basis van het resultaat van sleutelregistratie kan worden bepaald of er een huidig exemplaar van de app wordt uitgevoerd. Nadat de beslissing is genomen, weet de methode of de app moet worden omgeleid of toegestaan om het nieuwe exemplaar te starten. De methode RedirectActivationTo wordt aangeroepen als omleiding nodig is.

  6. Vervolgens gaan we de methode RedirectActivationTo maken onder de methode DecideRedirection, samen met de vereiste DllImport-instructies. Voeg de volgende code toe aan de klasse Program:

    [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);
    }
    

    De methode RedirectActivationTo is verantwoordelijk voor het omleiden van de activering naar het eerste exemplaar van de app. Er wordt een gebeurtenisgreep gemaakt, er wordt een nieuwe thread gestart om de activering om te leiden, en er wordt gewacht totdat de omleiding is voltooid. Nadat de omleiding is voltooid, brengt de methode het venster naar de voorgrond.

  7. Definieer ten slotte de helpermethode OnActivated onder de methode DecideRedirection:

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

Eenmalige instancing testen via app-implementatie

Tot nu toe hebben we de app getest door foutopsporing in Visual Studio uit te voeren. Er kan echter slechts één foutopsporingsprogramma tegelijk worden uitgevoerd. Deze beperking voorkomt dat we kunnen vaststellen of de app als een enkele instantie werkt, omdat we niet tweemaal tegelijkertijd hetzelfde project kunnen debuggen. Voor een nauwkeurige test implementeren we de toepassing op onze lokale Windows-client. Na de implementatie kunnen we de app starten vanaf het bureaublad, net zoals bij elke app die is geïnstalleerd in Windows.

  1. Navigeer naar Solution Explorer, klik met de rechtermuisknop op de projectnaam en selecteer Implementeren.

  2. Open het startmenu en klik in het zoekveld.

  3. Typ de naam van uw app in het zoekveld.

  4. Klik op het app-pictogram in het zoekresultaat om uw app te starten.

    Notitie

    Als de app vastloopt in de releasemodus, zijn er enkele bekende problemen met bijgesneden apps in de Windows App SDK. U kunt het bijsnijden in het project uitschakelen door de eigenschap PublishTrimmed in te stellen op false voor alle buildconfiguraties in de bestanden van uw project .pubxml. Zie dit probleem op GitHub voor meer informatie.

  5. Herhaal stap 2 tot en met 4 om dezelfde app opnieuw te starten en kijk of er een ander exemplaar wordt geopend. Als de app enkelvoudig is, wordt het eerste exemplaar geactiveerd in plaats van dat er een nieuw exemplaar wordt geopend.

    Tip

    U kunt eventueel wat logboekcode toevoegen aan de OnActivated methode om te controleren of het bestaande exemplaar is geactiveerd. Vraag Copilot- om hulp bij het toevoegen van een ILogger-implementatie aan uw WinUI-app.

Samenvatting

Alle hier behandelde code bevindt zich op GitHub-, met vertakkingen voor de verschillende stappen in de oorspronkelijke Windows-blogserie. Zie de single-instancing-branch voor code die specifiek is voor deze handleiding. De main vertakking is de meest uitgebreide. De andere vertakkingen zijn bedoeld om u te laten zien hoe de app-architectuur is ontwikkeld.

App-instancing met de levenscyclus-API van de app

Het enkelvoudig maken van de app (deel 3)

WinAppSDK-DrumPad voorbeeld op GitHub