Fix single instancing.

This commit is contained in:
Burak Kaan Köse
2025-11-14 12:51:19 +01:00
parent 3d5da92c74
commit 540a4e5117
3 changed files with 130 additions and 0 deletions
+25
View File
@@ -351,4 +351,29 @@ public partial class App : WinoApplication, IRecipient<NewMailSynchronizationReq
{
_synchronizationManager?.SynchronizeMailAsync(message.Options);
}
/// <summary>
/// Handles activation redirected from another instance (single-instancing).
/// This is called when a second instance tries to launch and redirects to this existing instance.
/// </summary>
public void HandleRedirectedActivation(AppActivationArguments args)
{
// Dispatch to UI thread since this is called from Program.OnActivated
MainWindow?.DispatcherQueue.TryEnqueue(() =>
{
// Handle different activation kinds
if (args.Kind == ExtendedActivationKind.AppNotification)
{
// Handle toast notification activation
var toastArgs = (AppNotificationActivatedEventArgs)args.Data;
_ = HandleToastActivationAsync(toastArgs);
}
else
{
// For other activation types (Launch, Protocol, etc.), bring window to front
MainWindow?.BringToFront();
MainWindow?.Activate();
}
});
}
}
+104
View File
@@ -0,0 +1,104 @@
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;
namespace Wino.Mail.WinUI;
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;
}
private static bool DecideRedirection()
{
bool isRedirect = false;
AppActivationArguments args = AppInstance.GetCurrent().GetActivatedEventArgs();
ExtendedActivationKind kind = args.Kind;
AppInstance keyInstance = AppInstance.FindOrRegisterForKey("WinoMailSingleInstance");
if (keyInstance.IsCurrent)
{
keyInstance.Activated += OnActivated;
}
else
{
isRedirect = true;
RedirectActivationTo(args, keyInstance);
}
return isRedirect;
}
[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);
}
private static void OnActivated(object? sender, AppActivationArguments args)
{
// When a new instance tries to launch, this event fires in the existing instance.
// We need to notify the App to handle the activation (e.g., bring window to front, handle protocol).
if (Application.Current is App app)
{
app.HandleRedirectedActivation(args);
}
}
}
+1
View File
@@ -11,6 +11,7 @@
<UseWinUI>true</UseWinUI>
<EnableMsixTooling>true</EnableMsixTooling>
<Nullable>enable</Nullable>
<DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Content Remove="Assets\BadgeLogo.scale-100.png" />