- LaunchCommand: ShowMain / OpenWorkspace(path), doubles as IPC message - LaunchArguments: first non-blank arg wins, blank-only args -> ShowMain - IpcProtocol: strict newline-line codec (show / open+path), tolerant of trailing-newline framing only - SingleInstanceService: per-session named mutex decides primary; primary serves a named-pipe loop (decode -> dispatch -> ack) with handler failures contained; secondary retries send, exits on ack - Program/App wiring: secondary forwards command and exits; primary activates the main window for forwarded commands (workspace switch deferred to the workspace UI milestone) - 15 new tests (protocol codec, argument parsing); smoke-verified: instance 1 stays alive, instance 2 self-exits with code 0
67 lines
2.0 KiB
C#
67 lines
2.0 KiB
C#
using System.Linq;
|
|
using Avalonia;
|
|
using Avalonia.Controls.ApplicationLifetimes;
|
|
using Avalonia.Data.Core;
|
|
using Avalonia.Data.Core.Plugins;
|
|
using Avalonia.Markup.Xaml;
|
|
using Avalonia.Threading;
|
|
using YKanBan.Ipc;
|
|
using YKanBan.ViewModels;
|
|
using YKanBan.Views;
|
|
|
|
namespace YKanBan;
|
|
|
|
public partial class App : Application
|
|
{
|
|
public override void Initialize()
|
|
{
|
|
AvaloniaXamlLoader.Load(this);
|
|
}
|
|
|
|
public override void OnFrameworkInitializationCompleted()
|
|
{
|
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
|
{
|
|
DisableAvaloniaDataAnnotationValidation();
|
|
desktop.MainWindow = new MainWindow
|
|
{
|
|
DataContext = new MainWindowViewModel(),
|
|
};
|
|
|
|
// Primary instance: serve launch commands forwarded by secondary instances.
|
|
Program.SingleInstance?.StartServer(command => HandleLaunchCommand(desktop, command));
|
|
}
|
|
|
|
base.OnFrameworkInitializationCompleted();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Brings the main window to front for a forwarded launch command. Both
|
|
/// ShowMain and OpenWorkspace activate the window; switching to the
|
|
/// requested workspace lands together with the workspace UI.
|
|
/// </summary>
|
|
private static void HandleLaunchCommand(IClassicDesktopStyleApplicationLifetime desktop, LaunchCommand command)
|
|
{
|
|
// IPC delivery happens on a background thread; window access must hop to the UI thread.
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (desktop.MainWindow is { } window)
|
|
{
|
|
window.Show();
|
|
window.Activate();
|
|
}
|
|
});
|
|
}
|
|
|
|
private static void DisableAvaloniaDataAnnotationValidation()
|
|
{
|
|
var dataValidationPluginsToRemove =
|
|
BindingPlugins.DataValidators.OfType<DataAnnotationsValidationPlugin>().ToArray();
|
|
|
|
foreach (var plugin in dataValidationPluginsToRemove)
|
|
{
|
|
BindingPlugins.DataValidators.Remove(plugin);
|
|
}
|
|
}
|
|
}
|