using Duempelkas.Infrastructure; using Duempelkas.Infrastructure.Persistence; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Photino.Blazor; using System.Runtime.InteropServices; namespace Duempelkas.Desktop; class Program { [DllImport("shell32.dll", CharSet = CharSet.Unicode)] private static extern int SetCurrentProcessExplicitAppUserModelID(string appID); [STAThread] static void Main(string[] args) { if (OperatingSystem.IsWindows()) { // Ensure Windows taskbar does not group this process under generic dotnet host identity. SetCurrentProcessExplicitAppUserModelID("Duempelkas.Desktop"); } var appBuilder = PhotinoBlazorAppBuilder.CreateDefault(args); // Use a per-user writable DB location so debugging via dotnet host works reliably. var appDataDir = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Duempelkas"); var isolated = false; #if DEBUG var directoryIndex = Array.IndexOf(args, "--data-directory"); if (directoryIndex >= 0) { if (directoryIndex + 1 >= args.Length) throw new ArgumentException("Datenordner fehlt."); appDataDir = Path.GetFullPath(args[directoryIndex + 1]); isolated = true; } #endif Directory.CreateDirectory(appDataDir); var dbPath = Path.Combine(appDataDir, "duempelkas.db"); // One-time compatibility: keep existing data if older versions stored DB next to the app executable. var legacyExeDirectory = Path.GetDirectoryName(Environment.ProcessPath ?? AppContext.BaseDirectory) ?? AppContext.BaseDirectory; var legacyDbPath = Path.Combine(legacyExeDirectory, "duempelkas.db"); if (!isolated && !File.Exists(dbPath) && File.Exists(legacyDbPath)) { try { using var source = new Microsoft.Data.Sqlite.SqliteConnection($"Data Source={legacyDbPath};Mode=ReadOnly"); using var target = new Microsoft.Data.Sqlite.SqliteConnection($"Data Source={dbPath}"); source.Open(); target.Open(); source.BackupDatabase(target); } catch (Exception ex) { throw new InvalidOperationException("Die vorhandene Datenbank konnte nicht übernommen werden. Der Start wurde abgebrochen; die ursprüngliche Datei bleibt erhalten.", ex); } } appBuilder.Services.AddInfrastructure(new Microsoft.Data.Sqlite.SqliteConnectionStringBuilder { DataSource = dbPath }.ToString(), appDataDir); appBuilder.RootComponents.Add("app"); var app = appBuilder.Build(); app.MainWindow.StartUrl = PhotinoWebViewManager.AppBaseUri; // Force the native window icon for title bar + taskbar representation. var iconPath = Path.Combine(AppContext.BaseDirectory, "Assets", "app-icon.ico"); if (File.Exists(iconPath)) { app.MainWindow.SetIconFile(iconPath); } using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); db.Database.Migrate(); } // Load saved window size or use defaults var settingsPath = Path.Combine(appDataDir, "window.json"); int savedWidth = 1280, savedHeight = 800; if (File.Exists(settingsPath)) { try { var json = File.ReadAllText(settingsPath); var doc = System.Text.Json.JsonDocument.Parse(json); savedWidth = doc.RootElement.GetProperty("width").GetInt32(); savedHeight = doc.RootElement.GetProperty("height").GetInt32(); } catch { /* use defaults on parse error */ } } app.MainWindow .SetTitle(isolated ? "Dümpelkas – Testdaten" : "Dümpelkas – Kassenbuch") .SetSize(savedWidth, savedHeight) .SetUseOsDefaultSize(false); app.MainWindow.MinWidth = 768; app.MainWindow.MinHeight = 480; // Persist window size on resize app.MainWindow.RegisterSizeChangedHandler((_, size) => { try { var dir = Path.GetDirectoryName(settingsPath)!; Directory.CreateDirectory(dir); File.WriteAllText(settingsPath, System.Text.Json.JsonSerializer.Serialize(new { width = size.Width, height = size.Height })); } catch { /* ignore save errors */ } }); AppDomain.CurrentDomain.UnhandledException += (sender, error) => { app.MainWindow.ShowMessage("Schwerwiegender Fehler", error.ExceptionObject.ToString()); }; app.Run(); } }