using IBKRTrader.Core.Logging; using IBKRTrader.Core.Modularity; using IBKRTrader.Core.Settings; using IBKRTrader.Core.Trading; using IBKRTrader.Core.Workers; using IBKRTrader.Hosting; using IBKRTrader.UI; using IBKRTrader.UI.Views; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace IBKRTrader; /// /// Einstiegspunkt der WinForms-Shell (Windows). /// /// Der Host selbst kommt aus und ist plattformneutral – hier /// wird er nur um Fensterverwaltung und Launcher ergänzt. Derselbe Host trägt den kopflosen /// Linux-Dienst IBKRTrader.Daemon; so kann keine der beiden Varianten eine Registrierung /// vergessen. /// internal static class Program { public static IHost? AppHost { get; private set; } [STAThread] static void Main(string[] args) { // Headless-Smoke-Test der UI (konstruiert jede View + Launcher, ohne Message-Loop). if (args.Length > 0 && string.Equals(args[0], "--smoke-ui", StringComparison.OrdinalIgnoreCase)) { Environment.ExitCode = RunSmokeUi(); return; } ApplicationConfiguration.Initialize(); var modules = AppHostBuilder.CreateModules(); AppHost = AppHostBuilder.Build(modules, RegisterShellServices); AppHostBuilder.RunStartupChecks(AppHost.Services); // Host starten → alle Worker/Services (IHostedService) laufen an. AppHost.Start(); // Views registrieren (Core + Module), dann Launcher starten. var uiHost = AppHost.Services.GetRequiredService(); RegisterCoreViews(uiHost, AppHost.Services); foreach (var module in modules) module.RegisterUi(uiHost, AppHost.Services); ModuleViews.Register(uiHost, AppHost.Services); ViewIcons.AssignDefaults(uiHost); Application.Run(AppHost.Services.GetRequiredService()); AppHost.StopAsync().GetAwaiter().GetResult(); } /// Nur die Dienste, die es ohne Oberfläche nicht gibt. private static void RegisterShellServices(IServiceCollection services, IConfiguration configuration) { services.AddSingleton(); services.AddSingleton(sp => sp.GetRequiredService()); services.AddSingleton(); } /// Registriert die Core-Views (Dashboard, Workers, Logs, Settings) bei der Shell. private static void RegisterCoreViews(IModuleUiHost uiHost, IServiceProvider sp) { uiHost.RegisterView(new ModuleView { Id = "core.dashboard", Title = "Dashboard", Group = "Core", Order = 5, CreateView = () => new DashboardView( sp.GetRequiredService(), sp.GetRequiredService(), sp.GetServices(), sp.GetRequiredService(), sp.GetServices()) }); uiHost.RegisterView(new ModuleView { Id = "core.workers", Title = "Workers / Services", Group = "Core", Order = 10, CreateView = () => new WorkersView(sp.GetRequiredService()) }); uiHost.RegisterView(new ModuleView { Id = "core.logs", Title = "Logs", Group = "Core", Order = 20, CreateView = () => new LogsView(sp.GetRequiredService()) }); uiHost.RegisterView(new ModuleView { Id = "core.settings", Title = "Settings", Group = "Core", Order = 30, CreateView = () => new SettingsView(sp.GetRequiredService()) }); } /// /// Headless-Smoke-Test: baut den Host, registriert Views + Modul-UI und konstruiert jede /// registrierte View sowie den Launcher – ohne Message-Loop. Gibt die Fehleranzahl zurück. /// Der Host wird bewusst NICHT gestartet: sonst liefen Worker und Broker-Verbindungen an, /// was für eine reine Konstruktionsprüfung unerwünscht ist. /// private static int RunSmokeUi() { ApplicationConfiguration.Initialize(); var modules = AppHostBuilder.CreateModules(); using var host = AppHostBuilder.Build(modules, RegisterShellServices); AppHostBuilder.RunStartupChecks(host.Services); var uiHost = host.Services.GetRequiredService(); RegisterCoreViews(uiHost, host.Services); foreach (var module in modules) module.RegisterUi(uiHost, host.Services); ModuleViews.Register(uiHost, host.Services); ViewIcons.AssignDefaults(uiHost); var failures = 0; Console.WriteLine("=== Smoke-UI: View-Konstruktion ==="); foreach (var view in uiHost.Views) { try { using var form = (Form)view.CreateView(); Console.WriteLine($"[OK] {view.Id} ({view.Title})"); } catch (Exception ex) { failures++; Console.WriteLine($"[FEHLER] {view.Id}: {ex.GetType().Name}: {ex.Message}"); } } try { using var launcher = host.Services.GetRequiredService(); Console.WriteLine("[OK] LauncherForm konstruiert"); } catch (Exception ex) { failures++; Console.WriteLine($"[FEHLER] LauncherForm: {ex.GetType().Name}: {ex.Message}"); } Console.WriteLine(failures == 0 ? "=== Smoke-UI OK ===" : $"=== Smoke-UI: {failures} Fehler ==="); return failures; } }