diff --git a/src/EverythingToJpeg.App/App.xaml.cs b/src/EverythingToJpeg.App/App.xaml.cs index 4796741..e005df4 100644 --- a/src/EverythingToJpeg.App/App.xaml.cs +++ b/src/EverythingToJpeg.App/App.xaml.cs @@ -13,6 +13,8 @@ public partial class App : Application { base.OnStartup(e); + WireGlobalExceptionLogging(); + var parsed = CliRouter.Parse(e.Args); switch (parsed.Mode) @@ -116,4 +118,38 @@ public partial class App : Application try { File.WriteAllText(logPath, log.ToString()); } catch { } } } + + private static void WireGlobalExceptionLogging() + { + var path = Path.Combine(Path.GetTempPath(), "EverythingToJpeg_unhandled.log"); + + void Append(string source, Exception? ex) + { + try + { + File.AppendAllText(path, + $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {source}\n{ex}\n\n"); + } + catch { } + } + + AppDomain.CurrentDomain.UnhandledException += (_, e) => + Append("AppDomain.UnhandledException", e.ExceptionObject as Exception); + + Current.DispatcherUnhandledException += (_, e) => + { + Append("Application.DispatcherUnhandledException", e.Exception); + MessageBox.Show( + "예기치 못한 오류:\n\n" + e.Exception.Message + "\n\n로그: " + path, + "EverythingToJpeg", + MessageBoxButton.OK, MessageBoxImage.Error); + e.Handled = true; + }; + + System.Threading.Tasks.TaskScheduler.UnobservedTaskException += (_, e) => + { + Append("TaskScheduler.UnobservedTaskException", e.Exception); + e.SetObserved(); + }; + } } diff --git a/src/EverythingToJpeg.App/Views/ConvertWindow.xaml b/src/EverythingToJpeg.App/Views/ConvertWindow.xaml index c97a5f9..f896c18 100644 --- a/src/EverythingToJpeg.App/Views/ConvertWindow.xaml +++ b/src/EverythingToJpeg.App/Views/ConvertWindow.xaml @@ -147,7 +147,51 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/EverythingToJpeg.App/Views/ConvertWindow.xaml.cs b/src/EverythingToJpeg.App/Views/ConvertWindow.xaml.cs index ad409fd..e3ede57 100644 --- a/src/EverythingToJpeg.App/Views/ConvertWindow.xaml.cs +++ b/src/EverythingToJpeg.App/Views/ConvertWindow.xaml.cs @@ -22,7 +22,6 @@ public partial class ConvertWindow : FluentWindow InitializeComponent(); FilesList.ItemsSource = _entries; - FilesList.ItemTemplate = (DataTemplate)CreateFileEntryTemplate(); AddFiles(initialFiles); @@ -113,19 +112,51 @@ public partial class ConvertWindow : FluentWindow } } + private static readonly string DialogLogPath = + Path.Combine(Path.GetTempPath(), "EverythingToJpeg_dialog.log"); + + private static void DiagLog(string line) + { + try + { + File.AppendAllText(DialogLogPath, + $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {line}{Environment.NewLine}"); + } + catch { } + } + private async void OnConvertClick(object sender, RoutedEventArgs e) { + DiagLog($"OnConvertClick: entries={_entries.Count}"); + if (_entries.Count == 0) { + DiagLog(" → no entries, showing info"); ShowInfo("변환할 파일이 없습니다."); return; } ConvertButton.IsEnabled = false; CancelButton.Content = "취소"; + ProgressStatusText.Text = "준비 중…"; _cts = new CancellationTokenSource(); - var options = BuildOptions(); + ConvertOptions options; + try + { + options = BuildOptions(); + DiagLog($" options: Quality={options.Quality} OutputLocation={options.OutputLocation} Custom={options.CustomOutputDirectory} Collision={options.OnCollision} MaxLong={options.MaxLongEdgePixels} PdfDpi={options.PdfDpi}"); + } + catch (Exception ex) + { + DiagLog(" BuildOptions threw: " + ex); + ProgressStatusText.Text = "옵션 처리 오류: " + ex.Message; + ConvertButton.IsEnabled = true; + CancelButton.Content = "닫기"; + _cts = null; + return; + } + var reporter = new Progress(p => { var overall = p.Total == 0 ? 0 : (p.Index + p.FileProgress) / p.Total; @@ -137,17 +168,27 @@ public partial class ConvertWindow : FluentWindow try { var sources = _entries.Select(en => en.Path).ToList(); + DiagLog($" starting ConvertManyAsync, {sources.Count} files"); var results = await _engine.ConvertManyAsync(sources, options, reporter, _cts.Token); + DiagLog($" finished, {results.Count} results"); + foreach (var r in results) + DiagLog($" [{r.Status}] {Path.GetFileName(r.SourcePath)} msg={r.Message}"); ApplyResults(results); ProgressStatusText.Text = SummarizeResults(results); } catch (OperationCanceledException) { + DiagLog(" canceled"); ProgressStatusText.Text = "변환이 취소되었습니다."; } catch (Exception ex) { + DiagLog(" EXCEPTION: " + ex); ProgressStatusText.Text = "오류: " + ex.Message; + MessageBox.Show(this, + "변환 중 오류가 발생했습니다:\n\n" + ex.Message + "\n\n로그: " + DialogLogPath, + "EverythingToJpeg", + MessageBoxButton.OK, MessageBoxImage.Error); } finally { @@ -234,54 +275,6 @@ public partial class ConvertWindow : FluentWindow => MessageBox.Show(this, message, "EverythingToJpeg", MessageBoxButton.OK, MessageBoxImage.Information); - private object CreateFileEntryTemplate() - { - const string xaml = """ - - - - - - - - - - - - - - - - - - - - - - - - - """; - return System.Windows.Markup.XamlReader.Parse(xaml); - } } public sealed class FileEntry : System.ComponentModel.INotifyPropertyChanged