Creare un'app WinUI a istanza singola con C#
Questa procedura illustra come creare un'app WinUI 3 a istanza singola con C# e il SDK per app di Windows. Le app a istanza singola consentono solo un'istanza dell'app in esecuzione alla volta. Le app WinUI sono multiistanza per impostazione predefinita. Consentono di avviare più istanze della stessa app contemporaneamente. Questo si riferisce a più istanze. Tuttavia, potresti voler implementare la creazione di istanze singole in base al caso d'uso della tua app. Se si tenta di avviare una seconda istanza di un'app a istanza singola, verrà attivata solo la finestra principale della prima istanza. Questa esercitazione illustra come implementare la creazione di istanze singole in un'app WinUI.
In questo articolo verrà spiegato come:
- Disattivare il codice generato da
Program
XAML - Definire un metodo personalizzato
Main
per il reindirizzamento - Testare la creazione di istanze singole dopo la distribuzione dell'app
Prerequisiti
Questa esercitazione usa Visual Studio e si basa sul modello di app vuota WinUI. Se non si ha familiarità con lo sviluppo winUI, è possibile configurare seguendo le istruzioni riportate in Introduzione a WinUI. È possibile installare Visual Studio, configurarlo per lo sviluppo di app con WinUI assicurando al tempo stesso la versione più recente di WinUI e il SDK per app di Windows e creare un progetto Hello World.
Al termine, tornare qui per informazioni su come trasformare il progetto "Hello World" in un'app a istanza singola.
Nota
Questa procedura si basa sul post di blog Creazione dell'app a istanza singola (parte 3) di una serie di blog di Windows su WinUI 3. Il codice per questi articoli è disponibile in GitHub.
Disabilitare il codice programma generato automaticamente
È necessario verificare la presenza di reindirizzamento il prima possibile, prima di creare qualsiasi finestra. A tale scopo, è necessario definire il simbolo "DISABLE_XAML_GENERATED_MAIN" nel file di progetto. Seguire questa procedura per disabilitare il codice programma generato automaticamente:
Fare clic con il pulsante destro del mouse sul nome del progetto in Esplora soluzioni e scegliere Modifica file di progetto.
Definire il simbolo DISABLE_XAML_GENERATED_MAIN. Aggiungere il codice XML seguente al file di progetto:
<PropertyGroup> <DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN</DefineConstants> </PropertyGroup>
L'aggiunta del simbolo DISABLE_XAML_GENERATED_MAIN disabiliterà il codice programma generato automaticamente per il progetto.
Definire una classe Program con un metodo Main
È necessario creare un file Program.cs personalizzato anziché eseguire il metodo Main predefinito. Il codice aggiunto alla classe Program consente all'app di verificare la presenza di reindirizzamento, che non è il comportamento predefinito delle app WinUI.
Passare a Esplora soluzioni, fare clic con il pulsante destro del mouse sul nome del progetto e scegliere Aggiungi | Classe.
Assegnare un nome alla nuova classe
Program.cs
e selezionare Aggiungi.Aggiungere gli spazi dei nomi seguenti alla classe Program, sostituendo eventuali spazi dei nomi esistenti:
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;
Sostituire la classe Program vuota con quanto segue:
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; } }
Il metodo Main determina se l'app deve reindirizzare alla prima istanza o avviare una nuova istanza dopo aver chiamato DecideRedirection, che verrà definita successivamente.
Definire il metodo DecideRedirection sotto il metodo 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 determina se l'app è stata registrata registrando una chiave univoca che rappresenta l'istanza dell'app. In base al risultato della registrazione della chiave, può determinare se è in esecuzione un'istanza corrente dell'app. Dopo aver effettuato la determinazione, il metodo sa se reindirizzare o consentire all'app di continuare l'avvio della nuova istanza. Il metodo RedirectActivationTo viene chiamato se è necessario il reindirizzamento.
Creare quindi il metodo RedirectActivationTo sotto il metodo DecideRedirection, insieme alle istruzioni DllImport necessarie. Aggiungere il codice seguente alla 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); }
Il metodo RedirectActivationTo è responsabile del reindirizzamento dell'attivazione alla prima istanza dell'app. Crea un handle di eventi, avvia un nuovo thread per reindirizzare l'attivazione e attende il completamento del reindirizzamento. Al termine del reindirizzamento, il metodo porta la finestra in primo piano.
Infine, definire il metodo helper OnActivated sotto il metodo DecideRedirection :
private static void OnActivated(object sender, AppActivationArguments args) { ExtendedActivationKind kind = args.Kind; }
Testare la creazione di istanze singole tramite la distribuzione di app
Fino a questo punto, l'app è stata testata eseguendo il debug all'interno di Visual Studio. Tuttavia, è possibile avere un solo debugger in esecuzione contemporaneamente. Questa limitazione impedisce a Microsoft di sapere se l'app è a istanza singola perché non è possibile eseguire il debug dello stesso progetto due volte contemporaneamente. Per un test accurato, l'applicazione verrà distribuita nel client Windows locale. Dopo la distribuzione, è possibile avviare l'app dal desktop come si farebbe con qualsiasi app installata in Windows.
Passare a Esplora soluzioni, fare clic con il pulsante destro del mouse sul nome del progetto e scegliere Distribuisci.
Aprire il menu Start e fare clic nel campo di ricerca.
Digitare il nome dell'app nel campo di ricerca.
Fare clic sull'icona dell'app nel risultato della ricerca per avviare l'app.
Nota
Se si verificano arresti anomali dell'app in modalità di rilascio, esistono alcuni problemi noti relativi alle app tagliate nella SDK per app di Windows. È possibile disabilitare il taglio nel progetto impostando la proprietà PublishTrimmed su false per tutte le configurazioni di compilazione nei file del
.pubxml
progetto. Per altre informazioni, vedere questo problema in GitHub.Ripetere i passaggi da 2 a 4 per avviare di nuovo la stessa app e verificare se viene aperta un'altra istanza. Se l'app è a istanza singola, la prima istanza verrà attivata anziché una nuova istanza aperta.
Suggerimento
Facoltativamente, è possibile aggiungere codice di registrazione al metodo OnActivated per verificare che l'istanza esistente sia stata attivata. Provare a chiedere aiuto a Copilot per aggiungere un'implementazione ILogger all'app WinUI.
Riepilogo
Tutto il codice trattato qui è disponibile in GitHub, con rami per i diversi passaggi della serie di blog di Windows originale. Vedere il ramo single-instancing per il codice specifico di questa procedura. Il main
ramo è il più completo. Gli altri rami sono progettati per mostrare come si è evoluta l'architettura dell'app.
Contenuto correlato
Creazione di istanze dell'app con l'API ciclo di vita dell'app