Udostępnij za pośrednictwem


Tworzenie aplikacji WinUI z pojedynczym wystąpieniem w języku C#

W tym przewodniku pokazano, jak utworzyć jednoinstancyjną aplikację WinUI 3 przy użyciu języka C# i Windows SDK aplikacji. Aplikacje jednoinstancyjne pozwalają tylko na jedno uruchomienie aplikacji w danym czasie. Aplikacje WinUI są domyślnie wieloinstancyjne. Pozwalają one na jednoczesne uruchamianie wielu wystąpień tej samej aplikacji. To dotyczy wielu wystąpień. Możesz jednak zdecydować się na implementację pojedynczego wystąpienia w zależności od przypadku użycia aplikacji. Próba uruchomienia drugiego wystąpienia pojedynczej aplikacji spowoduje aktywowanie tylko głównego okna pierwszego wystąpienia. W tym samouczku pokazano, jak zaimplementować pojedyncze wystąpienia w aplikacji WinUI.

Z tego artykułu dowiesz się, jak wykonywać następujące działania:

  • Wyłącz wygenerowany przez XAML kod Program
  • Definiowanie dostosowanej metody Main dla przekierowania
  • Testowanie pojedynczego wystąpienia po wdrożeniu aplikacji

Wymagania wstępne

W tym samouczku jest używany program Visual Studio i jest oparty na szablonie pustej aplikacji WinUI. Jeśli dopiero zaczynasz tworzenie aplikacji WinUI, możesz się przygotować, postępując zgodnie z instrukcjami w Rozpoczynanie pracy w WinUI. W tym miejscu zainstalujesz program Visual Studio, skonfigurujesz go do tworzenia aplikacji za pomocą interfejsu WinUI, zapewniając jednocześnie, że masz najnowszą wersję pakietu WinUI i zestawu SDK aplikacji systemu Windows, a następnie utworzysz projekt Hello World.

Po wykonaniu tej czynności wróć tutaj, aby dowiedzieć się, jak przekształcić projekt "Hello World" w jednowątkową aplikację.

Notatka

Ten poradnik jest oparty na Uczynienie aplikacji jednokrotną instancją (część 3) wpis na blogu z serii systemu Windows dotyczącej WinUI 3. Kod tych artykułów jest dostępny w witrynie GitHub.

Wyłączanie wygenerowanego automatycznie kodu programu

Przed utworzeniem jakichkolwiek okien musimy sprawdzić, czy przekierowanie jest możliwe. W tym celu musimy zdefiniować symbol "DISABLE_XAML_GENERATED_MAIN" w pliku projektu. Wykonaj następujące kroki, aby wyłączyć automatycznie wygenerowany kod programu:

  1. Kliknij prawym przyciskiem myszy nazwę projektu w Eksploratorze rozwiązań i wybierz pozycję Edytuj plik projektu.

  2. Zdefiniuj symbol DISABLE_XAML_GENERATED_MAIN. Dodaj następujący kod XML do pliku projektu:

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

Dodanie symbolu DISABLE_XAML_GENERATED_MAIN spowoduje wyłączenie automatycznie wygenerowanego kodu programu dla projektu.

Definiowanie klasy Program przy użyciu metody Main

Należy utworzyć dostosowany plik Program.cs zamiast uruchamiać domyślną metodę Main. Kod dodany do klasy Program umożliwia aplikacji sprawdzanie przekierowania, co nie jest domyślnym zachowaniem aplikacji WinUI.

  1. Przejdź do Eksploratora rozwiązań, kliknij prawym przyciskiem myszy nazwę projektu i wybierz pozycję Dodaj | Klasa.

  2. Nadaj nowej klasie nazwę Program.cs i wybierz pozycję Dodaj.

  3. Dodaj następujące przestrzenie nazw do klasy Program, zastępując wszystkie istniejące przestrzenie nazw:

    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. Zastąp pustą klasę Program następującym kodem:

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

    Metoda Main określa, czy aplikacja powinna przekierowywać do pierwszego wystąpienia, czy uruchamiać nowe wystąpienie po wywołaniu DecideRedirection, które zdefiniujemy dalej.

  5. Zdefiniuj metodę DecideRedirection poniżej metody 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 określa, czy aplikacja została zarejestrowana, poprzez rejestrację unikatowego klucza, który reprezentuje instancję aplikacji. Na podstawie wyniku rejestracji klucza można określić, czy uruchomiona jest bieżąca instancja aplikacji. Po dokonaniu ustalenia metoda wie, czy przekierować, czy też zezwolić aplikacji na uruchomienie nowego wystąpienia. Metoda RedirectActivationTo jest wywoływana, jeśli jest konieczne przekierowanie.

  6. Następnie utwórzmy metodę RedirectActivationTo poniżej metody DecideRedirection wraz z wymaganymi instrukcjami DllImport. Dodaj następujący kod do klasy 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);
    }
    

    Metoda RedirectActivationTo odpowiada za przekierowanie aktywacji do pierwszego wystąpienia aplikacji. Tworzy uchwyt zdarzenia, rozpoczyna nowy wątek, aby przekierować aktywację, i czeka na zakończenie przekierowania. Po zakończeniu przekierowania metoda przenosi okno na pierwszy plan.

  7. Na koniec zdefiniuj metodę pomocnika OnActivated poniżej metody DecideRedirection:

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

Testowanie pojedynczej instancji za pomocą wdrożenia aplikacji

Do tego momentu testowaliśmy aplikację przez debugowanie w programie Visual Studio. Możemy jednak mieć uruchomiony jednocześnie tylko jeden debuger. To ograniczenie uniemożliwia nam sprawdzenie, czy aplikacja jest pojedyncza, ponieważ nie możemy debugować tego samego projektu dwa razy w tym samym czasie. Aby przeprowadzić dokładny test, wdrożymy aplikację na lokalnym kliencie systemu Windows. Po wdrożeniu można uruchomić aplikację z poziomu pulpitu, tak jak w przypadku dowolnej aplikacji zainstalowanej w systemie Windows.

  1. Przejdź do Eksploratora rozwiązań, kliknij prawym przyciskiem myszy nazwę projektu i wybierz pozycję Wdróż.

  2. Otwórz menu Start i kliknij pole wyszukiwania.

  3. Wpisz nazwę aplikacji w polu wyszukiwania.

  4. Kliknij ikonę aplikacji z wyniku wyszukiwania, aby uruchomić aplikację.

    Notatka

    Jeśli doświadczasz awarii aplikacji w trybie produkcyjnym, istnieją znane problemy z przyciętymi aplikacjami w zestawie SDK aplikacji systemu Windows. Przycinanie w projekcie można wyłączyć, ustawiając właściwość PublishTrimmed na false dla wszystkich konfiguracji kompilacji w plikach .pubxml projektu. Aby uzyskać więcej informacji, zobacz to zgłoszenie na GitHub.

  5. Powtórz kroki od 2 do 4, aby ponownie uruchomić tę samą aplikację i sprawdzić, czy to otworzy inną instancję. Jeśli aplikacja jest z pojedynczym wystąpieniem, zamiast otwarcia nowego wystąpienia zostanie aktywowane pierwsze wystąpienie.

    Napiwek

    Opcjonalnie możesz dodać kod rejestrowania do metody OnActivated, aby sprawdzić, czy istniejące wystąpienie zostało aktywowane. Spróbuj poprosić Copilot o pomoc dotyczącą dodawania implementacji ILogger do aplikacji WinUI.

Streszczenie

Cały kod opisany tutaj jest dostępny na GitHub, z gałęziami dla różnych kroków w oryginalnej serii wpisów na blogu Windows. Zobacz gałąź związaną z pojedynczym instancjowaniem, aby uzyskać kod specyficzny dla tego poradnika. Gałąź main jest najbardziej kompleksowa. Pozostałe gałęzie mają na celu pokazanie, jak ewoluowała architektura aplikacji.

Instancjonowanie aplikacji za pomocą interfejsu API cyklu życia aplikacji

tworzenie pojedynczego wystąpienia aplikacji (część 3)

próbka WinAppSDK-DrumPad na GitHubie