UnhandledException в WinUI3, происходящее из фонового потока: как показать пользователю диалоговое окно с информацией о ⇐ C#
UnhandledException в WinUI3, происходящее из фонового потока: как показать пользователю диалоговое окно с информацией о
В настоящее время я пытаюсь показать пользователям моего приложения WinUI3 диалоговое окно в случае необработанного исключения. В этом случае я хочу предоставить пользователю диалоговое окно с краткой информацией о том, что произошла какая-то «техническая ошибка» (с дополнительной дополнительной информацией об ошибке), чтобы избежать ситуации, когда приложение просто аварийно завершает работу.
В моем конкретном сценарии исключение происходит из фонового потока. В части модели представления приложения я вызываю код, который периодически (каждые 10 секунд) обращается к базе данных для поиска некоторых данных. Если при поиске данных возникает исключение, оно всплывает до тех пор, пока не достигнет моего класса App.
В классе App я настроил код для подключения обработчиков событий для различных событий UnhandledException:
частный void RegisterUnhandledExceptionHandler() { UnhandledException += (_, args) => { Application_UnhandledException (это, аргументы); }; System.Threading.Tasks.TaskScheduler.UnobservedTaskException += (_, _) => { Application_UnhandledException (это, ноль); }; Current.UnhandledException += (_, args) => { Application_UnhandledException (это, аргументы); }; AppDomain.CurrentDomain.UnhandledException += (_, args) => { // --> этот обработчик вызывается в моем сценарии (исключение из фонового потока) // Свойство IsTermination в переменной args здесь истинно Application_UnhandledException (это, ноль); }; } Как видите, я не совсем уверен, о каких событиях мне следует говорить... В моем случае возникает последнее из четырех событий (AppDomain.CurrentDomain.UnhandledException), где я сделал комментарий "этот обработчик вызывается в моем сценарии (исключение из фонового потока)" .
Кроме того, я заметил, что свойство IsTermination экземпляра UnhandledExceptionEventHandler (переменная args) в этом случае имеет значение true. Поэтому я не уверен, можно ли вообще показать диалог.
Вот код класса App, который обрабатывает исключение:
частный async void Application_UnhandledException(отправитель объекта, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) { stringExceptionMessage = "Произошла техническая ошибка"; если (е != ноль) { e.Handled = правда; исключениеMessage = e.ToString(); } // Показать диалог с сообщением Диалог InformationDialog = новый InformationDialog(); // COMException --> Приложение вызвало интерфейс, который был настроен для другого потока. диалог.MessageTextBlock.Text = сообщение; дождитесь диалога.ShowAsync(); } Я немного сократил часть «Показать диалог с сообщением», но здесь я показываю диалоговое окно WinUI, производное от ContentDialog (мой класс InformationDialog наследуется от ContentDialog).
К сожалению, когда код достигает оператора InformationDialog Dialog = new InformationDialog(), возникает исключение COMException --> Приложение вызвало интерфейс, который был настроен для другого потока .
Я знаю, что невозможно получить доступ к элементам управления или отобразить диалоги потока пользовательского интерфейса из фонового потока. Поэтому я попытался отправить код, который показывает диалог, в поток пользовательского интерфейса, используя Task.Factory.StartNew и TaskScheduler.FromCurrentSynchronizationContext():
частный async void Application_UnhandledException(отправитель объекта, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) { stringExceptionMessage = "Произошла техническая ошибка"; если (е != ноль) { e.Handled = правда; исключениеMessage = e.ToString(); } ждут Task.Factory.StartNew( () => { // ждем и показываем здесь диалог }, CancellationToken.Нет, TaskCreationOptions.Нет, TaskScheduler.FromCurrentSynchronizationContext()); } Но теперь я получаю еще одно исключение: «System.InvalidOperationException: 'Текущий SynchronizationContext не может использоваться в качестве TaskScheduler.»
Что я делаю не так?
Для полноты картины это тот код на уровне моей модели представления, который инициирует поиск в базе данных. Он использует реактивные расширения для выполнения метода LookupDataFromDatabase каждые 10 секунд. Когда LookupDataFromDatabase вызывает исключение, создается сценарий, который я хочу рассмотреть.
public void Init() { // Используйте реактивное расширение для запуска таймера: https://stackoverflow.com/a/4452625/4424024 var timer = Observable.Interval(TimeSpan.FromMilli Seconds(10000)); timer.Subscribe(_ => LookupDataFromDatabase()); } Я знаю, что я мог бы также перехватить исключение более близко к этому коду, но я хочу иметь правильно работающий механизм, если вообще необработанное исключение (из фонового потока или нет) должно быть обработано в класс App моего приложения.
**РЕДАКТИРОВАНИЕ/ОБНОВЛЕНИЕ**
Я изменил способ отображения диалогового окна с помощью класса DispatcherQueue, как указано в этом сообщении.
Это избавило от исключения при попытке отобразить диалог без его правильной отправки в поток пользовательского интерфейса.
Однако кажется, что если исключение исходит из фонового потока и не перехватывается там, класс App не может или не должен препятствовать завершению работы приложения.
Я полагаю, именно поэтому приложение уже закрывается до того, как отображается какое-либо диалоговое окно.
См. эту статью «Исключения в управляемых потоках»: "Когда потоки могут завершать работу автоматически, без завершения работы приложения, серьезные проблемы программирования могут остаться незамеченными. Это особая проблема для служб и других приложений, которые работают в течение длительного времени. Когда потоки выходят из строя, состояние программы постепенно портится. Производительность приложения может ухудшиться или приложение может перестать отвечать на запросы."
Кроме того, свойство IsTermination моего экземпляра UnhandledExceptionEventArgs имеет значение true.
В настоящее время я пытаюсь показать пользователям моего приложения WinUI3 диалоговое окно в случае необработанного исключения. В этом случае я хочу предоставить пользователю диалоговое окно с краткой информацией о том, что произошла какая-то «техническая ошибка» (с дополнительной дополнительной информацией об ошибке), чтобы избежать ситуации, когда приложение просто аварийно завершает работу.
В моем конкретном сценарии исключение происходит из фонового потока. В части модели представления приложения я вызываю код, который периодически (каждые 10 секунд) обращается к базе данных для поиска некоторых данных. Если при поиске данных возникает исключение, оно всплывает до тех пор, пока не достигнет моего класса App.
В классе App я настроил код для подключения обработчиков событий для различных событий UnhandledException:
частный void RegisterUnhandledExceptionHandler() { UnhandledException += (_, args) => { Application_UnhandledException (это, аргументы); }; System.Threading.Tasks.TaskScheduler.UnobservedTaskException += (_, _) => { Application_UnhandledException (это, ноль); }; Current.UnhandledException += (_, args) => { Application_UnhandledException (это, аргументы); }; AppDomain.CurrentDomain.UnhandledException += (_, args) => { // --> этот обработчик вызывается в моем сценарии (исключение из фонового потока) // Свойство IsTermination в переменной args здесь истинно Application_UnhandledException (это, ноль); }; } Как видите, я не совсем уверен, о каких событиях мне следует говорить... В моем случае возникает последнее из четырех событий (AppDomain.CurrentDomain.UnhandledException), где я сделал комментарий "этот обработчик вызывается в моем сценарии (исключение из фонового потока)" .
Кроме того, я заметил, что свойство IsTermination экземпляра UnhandledExceptionEventHandler (переменная args) в этом случае имеет значение true. Поэтому я не уверен, можно ли вообще показать диалог.
Вот код класса App, который обрабатывает исключение:
частный async void Application_UnhandledException(отправитель объекта, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) { stringExceptionMessage = "Произошла техническая ошибка"; если (е != ноль) { e.Handled = правда; исключениеMessage = e.ToString(); } // Показать диалог с сообщением Диалог InformationDialog = новый InformationDialog(); // COMException --> Приложение вызвало интерфейс, который был настроен для другого потока. диалог.MessageTextBlock.Text = сообщение; дождитесь диалога.ShowAsync(); } Я немного сократил часть «Показать диалог с сообщением», но здесь я показываю диалоговое окно WinUI, производное от ContentDialog (мой класс InformationDialog наследуется от ContentDialog).
К сожалению, когда код достигает оператора InformationDialog Dialog = new InformationDialog(), возникает исключение COMException --> Приложение вызвало интерфейс, который был настроен для другого потока .
Я знаю, что невозможно получить доступ к элементам управления или отобразить диалоги потока пользовательского интерфейса из фонового потока. Поэтому я попытался отправить код, который показывает диалог, в поток пользовательского интерфейса, используя Task.Factory.StartNew и TaskScheduler.FromCurrentSynchronizationContext():
частный async void Application_UnhandledException(отправитель объекта, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) { stringExceptionMessage = "Произошла техническая ошибка"; если (е != ноль) { e.Handled = правда; исключениеMessage = e.ToString(); } ждут Task.Factory.StartNew( () => { // ждем и показываем здесь диалог }, CancellationToken.Нет, TaskCreationOptions.Нет, TaskScheduler.FromCurrentSynchronizationContext()); } Но теперь я получаю еще одно исключение: «System.InvalidOperationException: 'Текущий SynchronizationContext не может использоваться в качестве TaskScheduler.»
Что я делаю не так?
Для полноты картины это тот код на уровне моей модели представления, который инициирует поиск в базе данных. Он использует реактивные расширения для выполнения метода LookupDataFromDatabase каждые 10 секунд. Когда LookupDataFromDatabase вызывает исключение, создается сценарий, который я хочу рассмотреть.
public void Init() { // Используйте реактивное расширение для запуска таймера: https://stackoverflow.com/a/4452625/4424024 var timer = Observable.Interval(TimeSpan.FromMilli Seconds(10000)); timer.Subscribe(_ => LookupDataFromDatabase()); } Я знаю, что я мог бы также перехватить исключение более близко к этому коду, но я хочу иметь правильно работающий механизм, если вообще необработанное исключение (из фонового потока или нет) должно быть обработано в класс App моего приложения.
**РЕДАКТИРОВАНИЕ/ОБНОВЛЕНИЕ**
Я изменил способ отображения диалогового окна с помощью класса DispatcherQueue, как указано в этом сообщении.
Это избавило от исключения при попытке отобразить диалог без его правильной отправки в поток пользовательского интерфейса.
Однако кажется, что если исключение исходит из фонового потока и не перехватывается там, класс App не может или не должен препятствовать завершению работы приложения.
Я полагаю, именно поэтому приложение уже закрывается до того, как отображается какое-либо диалоговое окно.
См. эту статью «Исключения в управляемых потоках»: "Когда потоки могут завершать работу автоматически, без завершения работы приложения, серьезные проблемы программирования могут остаться незамеченными. Это особая проблема для служб и других приложений, которые работают в течение длительного времени. Когда потоки выходят из строя, состояние программы постепенно портится. Производительность приложения может ухудшиться или приложение может перестать отвечать на запросы."
Кроме того, свойство IsTermination моего экземпляра UnhandledExceptionEventArgs имеет значение true.
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение
-
-
Как предотвратить происходящее несколько входных этапов (запускается, выполнено, отменено)
Anonymous » » в форуме C# - 0 Ответы
- 10 Просмотры
-
Последнее сообщение Anonymous
-