Dela via


Skapa en WinUI-app med endast en instans med C#

Den här instruktioner visar hur du skapar en WinUI 3-app med en enda instans med C# och Windows App SDK. Appar med enstaka instanser tillåter endast en instans av appen att köras åt gången. WinUI-appar är flerstansade som standard. De gör att du kan starta flera instanser av samma app samtidigt. Detta hänvisar till flera instanser. Du kanske dock vill implementera enkel instancing baserat på appens användningsfall. Om du försöker starta en andra instans av en app med en enda instans resulterar det bara i att den första instansens huvudfönster aktiveras i stället. Den här handledningen visar hur du implementerar enkel instansiering i en WinUI-app.

I den här artikeln får du lära dig att:

  • Inaktivera XAML:s genererade Program kod
  • Definiera anpassad Main metod för omdirigering
  • Testa enkel instansiering efter app-distribution

Förutsättningar

Den här handledningen använder Visual Studio och bygger på den tomma WinUI-appens mall. Om du är nybörjare på WinUI-utveckling kan du konfigureras genom att följa anvisningarna i Kom igång med WinUI. Där installerar du Visual Studio, konfigurerar det för att utveckla appar med WinUI samtidigt som du ser till att du har den senaste versionen av WinUI och Windows App SDK och skapar ett Hello World-projekt.

När du har gjort det kan du komma tillbaka hit för att lära dig hur du omvandlar ditt "Hello World"-projekt till en app med en enda instans.

Anteckning

Den här instruktionen baseras på att göra appen enkelinstansad (del 3) blogginlägg från en Windows-bloggserie i WinUI 3. Koden för dessa artiklar finns på GitHub.

Inaktivera kod för automatiskt genererat program

Vi måste söka efter omdirigering så tidigt som möjligt innan vi skapar några fönster. För att göra detta måste vi definiera symbolen "DISABLE_XAML_GENERATED_MAIN" i projektfilen. Följ dessa steg för att inaktivera den automatiskt genererade programkoden:

  1. Högerklicka på projektnamnet i Solution Explorer och välj Redigera projektfil.

  2. Definiera symbolen DISABLE_XAML_GENERATED_MAIN. Lägg till följande XML i projektfilen:

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

Om du lägger till symbolen DISABLE_XAML_GENERATED_MAIN inaktiveras den automatiskt genererade programkoden för projektet.

Definiera en Program-klass med en Main-metod

En anpassad Program.cs fil måste skapas i stället för att standardmetoden Main körs. Koden som läggs till i klassen Program gör att appen kan söka efter omdirigering, vilket inte är standardbeteendet för WinUI-appar.

  1. Gå till Solution Explorer, högerklicka på projektnamnet och välj Lägg till | Klass.

  2. Ge den nya klassen namnet Program.cs och välj Lägg till.

  3. Lägg till följande namnområden i klassen Program och ersätt alla befintliga namnområden:

    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. Ersätt den tomma klassen Program med följande:

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

    Metoden Main avgör om appen ska omdirigeras till den första instansen eller starta en ny instans efter att ha anropat DecideRedirection, som vi ska definiera härnäst.

  5. Definiera metoden DecideRedirection under metoden 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 avgör om appen har registrerats genom att registrera en unik nyckel som representerar din appinstans. Baserat på resultatet av nyckelregistreringen kan den avgöra om det finns en aktuell instans av appen som körs. Efter att ha gjort bedömningen vet metoden om den ska omdirigera eller tillåta appen att fortsätta att starta den nya instansen. Metoden RedirectActivationTo anropas om omdirigering krävs.

  6. Nu ska vi skapa metoden RedirectActivationTo under metoden DecideRedirection, tillsammans med nödvändiga DllImport-instruktioner. Lägg till följande kod i klassen 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);
    }
    

    Metoden RedirectActivationTo ansvarar för att omdirigera aktiveringen till den första instansen av appen. Den skapar en händelsereferens, startar en ny tråd för att omdirigera aktiveringen och väntar på att omdirigeringen ska slutföras. När omdirigeringen är klar tar metoden fönstret till förgrunden.

  7. Definiera slutligen hjälpmetoden OnActivated under metoden DecideRedirection:

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

Testa enkel instansering via applikationsdistribution

Hittills har vi testat appen genom att felsöka i Visual Studio. Vi kan dock bara ha ett felsökningsprogram som körs samtidigt. Den här begränsningen hindrar oss från att veta om appen är eninstans eftersom vi inte kan felsöka samma projekt två gånger samtidigt. För ett korrekt test distribuerar vi programmet till vår lokala Windows-klient. Efter distributionen kan vi starta appen från skrivbordet på samma sätt som med alla appar som är installerade i Windows.

  1. Gå till Solution Explorer, högerklicka på projektnamnet och välj Distribuera.

  2. Öppna Start-menyn och klicka i sökfältet.

  3. Skriv appens namn i sökfältet.

  4. Klicka på appikonen i sökresultatet för att starta appen.

    Anmärkning

    Om du upplever att appen kraschar i versionsläge finns det några kända problem med trimmade appar i Windows App SDK. Du kan inaktivera trimning i projektet genom att ange egenskapen PublishTrimmed till false för alla byggkonfigurationer i projektets .pubxml fil. Mer information finns i det här ärendet på GitHub.

  5. Upprepa steg 2 till 4 för att starta samma app igen och se om en annan instans öppnas. Om appen är eninstans aktiveras den första instansen i stället för att en ny instans öppnas.

    Tips

    Du kan också lägga till loggningskod i metoden OnActivated för att kontrollera att den befintliga instansen har aktiverats. Försök att be Copilot om hjälp med att lägga till en ILogger-implementering till din WinUI-app.

Sammanfattning

All kod som beskrivs här finns på GitHub, med grenar för de olika stegen i den ursprungliga Windows-bloggserien. Se -grenen för kod specifik för den här instruktionen om enkel instancing. Grenen main är den mest omfattande. De andra grenarna är avsedda att visa hur apparkitekturen utvecklades.

App-instansering med appens livscykel-API

Göra appen enkelinstansierad (del 3)

WinAppSDK-DrumPad exempel på GitHub