Partager via


Créer une application WinUI à instance unique avec C#

Ce guide pratique montre comment créer une application WinUI 3 à instance unique avec C# et le Kit de développement logiciel (SDK) d’application Windows. Les applications à instance unique n’autorisent qu’une seule instance de l’application en cours d’exécution à la fois. Les applications WinUI sont multi-instances par défaut. Ils vous permettent de lancer plusieurs instances de la même application simultanément. Il s’agit d’une référence à plusieurs instances. Toutefois, vous pouvez implémenter l’instanciation unique en fonction du cas d’utilisation de votre application. La tentative de lancement d’une deuxième instance d’une application à instance unique entraîne uniquement l’activation de la fenêtre principale de la première instance. Ce tutoriel montre comment implémenter l’instanciation unique dans une application WinUI.

Dans cet article, vous allez apprendre à :

  • Désactiver le code généré Program par XAML
  • Définir une méthode personnalisée Main pour la redirection
  • Tester l’instanciation unique après le déploiement de l’application

Conditions préalables

Ce tutoriel utilise Visual Studio et s’appuie sur le modèle d’application vide WinUI. Si vous débutez avec le développement WinUI, vous pouvez être configuré en suivant les instructions de Prise en main de WinUI. Vous allez installer Visual Studio, le configurer pour le développement d’applications avec WinUI tout en vous assurant que vous disposez de la dernière version de WinUI et du Kit de développement logiciel (SDK) d’application Windows, puis créez un projet Hello World.

Lorsque vous avez terminé cela, revenez ici pour apprendre à transformer votre projet « Hello World » en une application à instance unique.

Remarque

Ce guide pratique est basé sur le billet de blog à instance unique de l’application (partie 3) d’une série de blog Windows sur WinUI 3. Le code de ces articles est disponible sur GitHub.

Désactiver le code du programme généré automatiquement

Nous devons vérifier la redirection dès que possible, avant de créer des fenêtres. Pour ce faire, nous devons définir le symbole « DISABLE_XAML_GENERATED_MAIN » dans le fichier projet. Procédez comme suit pour désactiver le code du programme généré automatiquement :

  1. Cliquez avec le bouton droit sur le nom du projet dans Explorateur de solutions, puis sélectionnez Modifier le fichier projet.

  2. Définissez le symbole DISABLE_XAML_GENERATED_MAIN pour chaque configuration et plateforme. Ajoutez le XML suivant au fichier de projet :

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

L’ajout du symbole DISABLE_XAML_GENERATED_MAIN désactive le code programme généré automatiquement pour votre projet.

Définir une classe Program avec une méthode Main

Un fichier Program.cs personnalisé doit être créé au lieu d’exécuter la méthode Main par défaut. Le code ajouté à la classe Program permet à l’application de vérifier la redirection, ce qui n’est pas le comportement par défaut des applications WinUI.

  1. Accédez à Explorateur de solutions, cliquez avec le bouton droit sur le nom du projet, puis sélectionnez Ajouter | Classe.

  2. Nommez la nouvelle classe Program.cs , puis sélectionnez Ajouter.

  3. Ajoutez les espaces de noms suivants à la classe Program, en remplaçant les espaces de noms existants :

    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. Remplacez la classe Program vide par ce qui suit :

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

    La méthode Main détermine si l’application doit rediriger vers la première instance ou démarrer une nouvelle instance après avoir appelé DecideRedirection, que nous allons définir ensuite.

  5. Définissez la méthode DecideRedirection sous la méthode 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 détermine si l’application a été inscrite en inscrivant une clé unique qui représente votre instance d’application. En fonction du résultat de l’inscription de clé, il peut déterminer s’il existe une instance actuelle de l’application en cours d’exécution. Après avoir effectué la détermination, la méthode sait s’il faut rediriger ou autoriser l’application à continuer à lancer la nouvelle instance. La méthode RedirectActivationTo est appelée si la redirection est nécessaire.

  6. Ensuite, nous allons créer la méthode RedirectActivationTo sous la méthode DecideRedirection, ainsi que les instructions DllImport requises. Ajoutez le code suivant à la classe 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);
    }
    

    La méthode RedirectActivationTo est chargée de rediriger l’activation vers la première instance de l’application. Il crée un handle d’événement, démarre un nouveau thread pour rediriger l’activation et attend la fin de la redirection. Une fois la redirection terminée, la méthode amène la fenêtre au premier plan.

  7. Enfin, définissez la méthode d’assistance OnActivated sous la méthode DecideRedirection :

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

Tester l’instanciation unique via le déploiement d’applications

Jusqu’à ce stade, nous avons testé l’application en débogueant dans Visual Studio. Toutefois, nous ne pouvons avoir qu’un seul débogueur en cours d’exécution en même temps. Cette limitation nous empêche de savoir si l’application est à instance unique, car nous ne pouvons pas déboguer le même projet deux fois en même temps. Pour un test précis, nous allons déployer l’application sur notre client Windows local. Après le déploiement, nous pouvons lancer l’application à partir du bureau comme vous le feriez avec n’importe quelle application installée sur Windows.

  1. Accédez à Explorateur de solutions, cliquez avec le bouton droit sur le nom du projet, puis sélectionnez Déployer.

  2. Ouvrez le menu Démarrer, puis cliquez dans le champ de recherche.

  3. Tapez le nom de votre application dans le champ de recherche.

  4. Cliquez sur l’icône de l’application à partir du résultat de recherche pour lancer votre application.

    Remarque

    Si vous rencontrez des blocages d’application en mode mise en production, il existe des problèmes connus liés aux applications rognées dans le KIT SDK d’application Windows. Vous pouvez désactiver le découpage dans le projet en définissant la propriété PublishTrimmed sur false pour toutes les configurations de build dans les fichiers de .pubxml votre projet. Pour plus d’informations, consultez ce problème sur GitHub.

  5. Répétez les étapes 2 à 4 pour lancer à nouveau la même application et voir si une autre instance s’ouvre. Si l’application est à instance unique, la première instance est activée au lieu d’une nouvelle instance ouvrante.

    Conseil

    Vous pouvez éventuellement ajouter du code de journalisation à la méthode OnActivated pour vérifier que l’instance existante a été activée. Essayez de demander de l’aide à Copilot pour ajouter une implémentation ILogger à votre application WinUI.

Résumé

Tout le code abordé ici est sur GitHub, avec des branches pour les différentes étapes de la série de blog Windows d’origine. Consultez la branche d’instanciation unique pour le code spécifique à ce guide pratique. La main branche est la plus complète. Les autres branches sont destinées à vous montrer comment l’architecture de l’application a évolué.

Instanciation d’application avec l’API de cycle de vie de l’application

Création de l’application à instance unique (partie 3)

Exemple WinAppSDK-DrumPad sur GitHub