Fix single instancing.
This commit is contained in:
@@ -351,4 +351,29 @@ public partial class App : WinoApplication, IRecipient<NewMailSynchronizationReq
|
|||||||
{
|
{
|
||||||
_synchronizationManager?.SynchronizeMailAsync(message.Options);
|
_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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
<UseWinUI>true</UseWinUI>
|
<UseWinUI>true</UseWinUI>
|
||||||
<EnableMsixTooling>true</EnableMsixTooling>
|
<EnableMsixTooling>true</EnableMsixTooling>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Remove="Assets\BadgeLogo.scale-100.png" />
|
<Content Remove="Assets\BadgeLogo.scale-100.png" />
|
||||||
|
|||||||
Reference in New Issue
Block a user