using System.IO; using System.Windows; using System.Windows.Threading; using VideoDownloader.App.Core; namespace VideoDownloader.App; public partial class App : Application { private static string CrashLogPath => Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "VideoDownloader", "crash.log"); // #14 글로벌 예외 핸들러: 잡히지 않은 예외를 crash.log 에 기록해 조용히 삼켜지는 것을 방지. protected override void OnStartup(StartupEventArgs e) { DispatcherUnhandledException += (_, de) => { LogCrash(de.Exception); de.Handled = true; }; AppDomain.CurrentDomain.UnhandledException += (_, ue) => LogCrash(ue.ExceptionObject as Exception); TaskScheduler.UnobservedTaskException += (_, ue) => { LogCrash(ue.Exception); ue.SetObserved(); }; base.OnStartup(e); ApplyTheme(ConfigStore.Load().Theme); new MainWindow().Show(); } /// 테마 색(dark/light) + Shared(스타일) 리소스사전을 App.Resources 에 주입. /// 주의: 반드시 색(brush) 사전을 스타일 사전보다 먼저 로드해야 Shared 의 템플릿이 /// {StaticResource GlassBg} 등을 해석할 수 있다. public static void ApplyTheme(string theme) { var dicts = Current.Resources.MergedDictionaries; dicts.Clear(); var themeSrc = theme.Equals("light", StringComparison.OrdinalIgnoreCase) ? "pack://application:,,,/Themes/Light.xaml" : "pack://application:,,,/Themes/Dark.xaml"; dicts.Add(new ResourceDictionary { Source = new Uri(themeSrc, UriKind.Absolute) }); dicts.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/Themes/Shared.xaml", UriKind.Absolute) }); } /// 현재 적용된 테마가 라이트인지 (DWM 타이틀바 색 연동용). public static bool IsLightTheme => ConfigStore.Load().Theme.Equals("light", StringComparison.OrdinalIgnoreCase); private static void LogCrash(Exception? ex) { if (ex == null) return; try { var dir = Path.GetDirectoryName(CrashLogPath); if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir); File.AppendAllText(CrashLogPath, $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {ex}{Environment.NewLine}{new string('-', 60)}{Environment.NewLine}"); } catch { } } }