Fix notification activation and calendar bootstrap flow

This commit is contained in:
Burak Kaan Köse
2026-04-16 01:32:48 +02:00
parent 94675eee9a
commit e13aaadc78
15 changed files with 844 additions and 209 deletions
+114 -11
View File
@@ -13,38 +13,87 @@ namespace Wino.Mail.WinUI;
public class Program
{
private const string AppNotificationActivatedCommandLinePrefix = "----AppNotificationActivated:";
private const string SingleInstanceKey = "WinoMailSingleInstance";
private const string ForceAlternateModeSignalEventName = "Local\\WinoMailForceAlternateMode";
private const string MailHostRunningMutexName = "Local\\WinoMailMailHostRunning";
private const int VkControl = 0x11;
private static bool _forceAlternateModeOnLaunch;
private static EventWaitHandle? _forceAlternateModeSignalHandle;
private static Mutex? _mailHostRunningMutex;
private static PendingBootstrapActivation? _pendingBootstrapActivation;
private static bool _hasDeferredAppNotificationStartup;
private static bool _shouldRegisterAppNotifications;
[STAThread]
static int Main(string[] args)
{
WinRT.ComWrappersSupport.InitializeComWrappers();
bool isRedirect = DecideRedirection();
if (TryCaptureCommandLineToastActivation(args))
{
_shouldRegisterAppNotifications = true;
EnsureMailHostRunningMutex();
StartApplication();
return 0;
}
var activationArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
var shouldBootstrapCalendarEntry = CalendarEntryBootstrapActivation.ShouldBootstrapToMailHost(activationArgs);
_shouldRegisterAppNotifications = !shouldBootstrapCalendarEntry;
if (shouldBootstrapCalendarEntry && !IsMailHostRunning())
{
if (CalendarEntryBootstrapActivation.QueuePendingActivation(activationArgs) &&
CalendarEntryBootstrapActivation.LaunchMailHost())
{
return 0;
}
CalendarEntryBootstrapActivation.ClearPendingActivation();
}
_pendingBootstrapActivation = CalendarEntryBootstrapActivation.ConsumePendingActivation();
bool isRedirect = DecideRedirection(activationArgs);
if (!isRedirect)
{
Application.Start((p) =>
{
var context = new DispatcherQueueSynchronizationContext(
DispatcherQueue.GetForCurrentThread());
SynchronizationContext.SetSynchronizationContext(context);
var app = new App();
_ = app.HandleInitialActivationAsync();
});
EnsureMailHostRunningMutex();
StartApplication();
}
return 0;
}
private static bool DecideRedirection()
public static bool ShouldRegisterAppNotifications()
=> _shouldRegisterAppNotifications;
internal static bool TryConsumeDeferredAppNotificationStartup()
{
if (!_hasDeferredAppNotificationStartup)
return false;
_hasDeferredAppNotificationStartup = false;
return true;
}
internal static bool TryConsumePendingBootstrapActivation(out PendingBootstrapActivation activation)
{
if (_pendingBootstrapActivation == null)
{
activation = null!;
return false;
}
activation = _pendingBootstrapActivation;
_pendingBootstrapActivation = null;
return true;
}
private static bool DecideRedirection(AppActivationArguments args)
{
bool isRedirect = false;
AppActivationArguments args = AppInstance.GetCurrent().GetActivatedEventArgs();
_forceAlternateModeOnLaunch = args.Kind == ExtendedActivationKind.Launch && IsCtrlKeyDown();
AppInstance keyInstance = AppInstance.FindOrRegisterForKey(SingleInstanceKey);
@@ -69,6 +118,60 @@ public class Program
return isRedirect;
}
private static bool TryCaptureCommandLineToastActivation(string[] args)
{
var commandLine = Environment.CommandLine;
var prefixIndex = commandLine.IndexOf(AppNotificationActivatedCommandLinePrefix, StringComparison.OrdinalIgnoreCase);
if (prefixIndex < 0)
return false;
// Do not touch AppInstance.GetActivatedEventArgs here. For app-notification cold starts,
// Windows App SDK expects the app to register AppNotificationManager first and then
// resolve the activation inside App.OnLaunched.
_hasDeferredAppNotificationStartup = true;
return true;
}
private static void StartApplication()
{
Application.Start((p) =>
{
var context = new DispatcherQueueSynchronizationContext(
DispatcherQueue.GetForCurrentThread());
SynchronizationContext.SetSynchronizationContext(context);
_ = new App();
});
}
private static bool IsMailHostRunning()
{
try
{
if (!Mutex.TryOpenExisting(MailHostRunningMutexName, out var existingMutex))
return false;
existingMutex.Dispose();
return true;
}
catch
{
return false;
}
}
private static void EnsureMailHostRunningMutex()
{
try
{
_mailHostRunningMutex ??= new Mutex(false, MailHostRunningMutexName);
}
catch
{
_mailHostRunningMutex = null;
}
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr CreateEvent(
IntPtr lpEventAttributes, bool bManualReset,