처리되지 않은 예외를 해결하는 응용 프로그램 오류 처리기를 설계하려고하지만 주변에서 벗어날 수없는 바람직하지 않은 동작이있는 경우가 있습니다.계단식 오류 메시지가 나타나지 않도록하는 방법
Application_DispatcherUnhandledException
은 UI 외부의 스레드가 문제가 발생할 때마다 호출됩니다. 그러면 App.HandleError
으로 전화 할 것입니다.이 방법은 문제를 기록하고 사용자에게 메시지를 표시하며 중요한 문제가있는 경우 응용 프로그램을 종료하는 정적 방법입니다.
내 주요 문제는 xaml의 일부가 Exception (예 : DataTemplate 또는 Routed Event의 예외) 생성을 시작할 때인 것처럼 보입니다. 대부분의 경우 WPF는 예외를 반복해서 던지는 컨트롤을 계속 생성하려고 시도합니다. 따라서 계단식 오류 메시지가 발생하고 응용 프로그램이 비정상적으로 충돌 할 때까지 모든 프로세서 전원을 소비합니다.
나는, 나 바로이 방법은 실행의 중간에 이미있는 경우 반환하여 내가 방법을 잠금으로써 오류 처리기에서이 문제를 해결했다고 생각하지만,이 두 가지 문제가있다 - 첫 번째이다 같은 예외가 계속 발생하면 사용자가 "OK"를 클릭하고 ErrorHandler의 실행이 잠금 해제되는 즉시 바로 다시 팝업됩니다. 계단식 오류 상태에 있는지 확인하여 응용 프로그램을 종료 할 수있는 방법이 필요합니다.
다른 문제는 두 개 이상의 개별 스레드가 동시에 서로 다른 오류를 생성하는 경우 계단식 오류에 대해이를 실수하는 솔루션을 원하지 않는다는 것입니다. 다른 하나가 먼저 도착 했으므로 오류는 무시됩니다.
아이디어가 있으십니까? 나는 Interlocked.Increment를 에러 카운트에 사용하는 것과 lock() 문을 사용하는 것, 그리고 마지막 몇 개의 에러를 타임 스탬프로 캐싱하는 것을 고려해 보았습니다.하지만 그것들에는 모두 단점이있는 것 같습니다.
다음은 최근 시도입니다. 얼마나 두꺼운 지에 대해 사과하지만 한 번에 아주 독특한 문제를 다루려고합니다.
private bool DispatchedErrorsLock = false;
private void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
//Prevent Recursion
e.Handled = true;
if(DispatchedErrorsLock || ExceptionHandlingTerminated) return;
DispatchedErrorsLock = true;
bool handleSilently = false;
//Ensures that minor xaml errors don't reset the application
if("PresentationFramework,PresentationCore,Xceed.Wpf.DataGrid.v4.3".Split(',').Any(s => e.Exception.Source.Contains(s)))
{
handleSilently = true;
}
HandleError(e.Exception, "Exception from external thread.", !handleSilently, !handleSilently);
DispatchedErrorsLock = false;
}
private static int SimultaneousErrors = 0;
private static bool ExceptionHandlingTerminated = false;
public static void HandleError(Exception ex, bool showMsgBox) { HandleError(ex, "", showMsgBox, true); }
public static void HandleError(Exception ex, string extraInfo, bool showMsgBox) { HandleError(ex, extraInfo, showMsgBox, true); }
public static void HandleError(Exception ex, string extraInfo = "", bool showMsgBox = true, bool resetApplication = true)
{
if(ExceptionHandlingTerminated || App.Current == null) return;
Interlocked.Increment(ref SimultaneousErrors); //Thread safe tracking of how many errors are being thrown
if(SimultaneousErrors > 3)
{
throw new Exception("Too many simultaneous errors have been thrown.");
}
try
{
if(Thread.CurrentThread != Dispatcher.CurrentDispatcher.Thread)
{
//We're not on the UI thread, we must dispatch this call.
((App)App.Current).Dispatcher.BeginInvoke((Action<Exception, string, bool, bool>)
delegate(Exception _ex, string _extraInfo, bool _showMsgBox, bool _resetApplication)
{
Interlocked.Decrement(ref SimultaneousErrors);
HandleError(_ex, _extraInfo, _showMsgBox, _resetApplication);
}, DispatcherPriority.Background, new object[] { ex, extraInfo, showMsgBox, resetApplication });
return;
}
if(!((App)App.Current).AppStartupComplete)
{ //We can't handle errors the normal way if the app hasn't started yet.
extraInfo = "An error occurred before the application could start." + extraInfo;
throw ex; //Hack: Using throw as a goto statement.
}
String ErrMessage = string.Empty;
if(string.IsNullOrEmpty(extraInfo) && showMsgBox)
ErrMessage += "An error occurred while processing your request. ";
else
ErrMessage += extraInfo;
if(!showMsgBox && !resetApplication)
ErrMessage += " This error was handled silently by the application.";
//Logs an error somewhere.
ErrorLog.CreateErrorLog(ex, ErrMessage);
if(showMsgBox)
{
ErrMessage += "\nTechnical Details: " + ex.Message;
Exception innerException = ex.InnerException;
while(innerException != null)
{ //Add what is likely the more informative information in the inner exception(s)
ErrMessage += " | " + ex.InnerException.Message;
innerException = innerException.InnerException;
}
}
if(resetApplication)
{
//Resets all object models to initial state (doesn't seem to help if the UI gets corrupted though)
((MUS.App)App.Current).ResetApplication();
}
if(showMsgBox)
{
//IF the UI is processing a visual tree event (such as IsVisibleChanged), it throws an exception when showing a MessageBox as described here: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/44962927-006e-4629-9aa3-100357861442
//The solution is to dispatch and queue the MessageBox. We must use BeginInvoke() because dispatcher processing is suspended in such cases, so Invoke() would fail..
Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate()
{
MessageBox.Show(ErrMessage, "MUS Application Error", MessageBoxButton.OK, MessageBoxImage.Error);
Interlocked.Decrement(ref SimultaneousErrors);
}, DispatcherPriority.Background);
}
else
{
Interlocked.Decrement(ref SimultaneousErrors);
}
}
catch(Exception e)
{
Interlocked.Decrement(ref SimultaneousErrors);
ExceptionHandlingTerminated = true;
//A very serious error has occurred, such as the application not loading or a cascading error message, and we must shut down.
String fatalMessage = String.Concat("An error occurred that the application cannot recover from. The application will have to shut down now.\n\nTechnical Details: ", extraInfo, "\n", e.Message);
//Try to log the error, but in extreme cases, there's no guarantee logging will work.
try { ErrorLog.CreateErrorLog(ex, fatalMessage); }
catch(Exception) { }
Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate()
{
MessageBox.Show(fatalMessage, "Fatal Error", MessageBoxButton.OK, MessageBoxImage.Stop);
if(App.Current != null) App.Current.Shutdown(1);
}, DispatcherPriority.Background);
}
}
적어도 카드 점처럼 경계에서 벗어날 수 있습니다. –
어디에서 증분 동시 오류입니까? – Paparazzi
단순히 예외를 Collection에 저장 한 다음 예외 목록에 바인딩 된'ItemsControl' 또는'ListBox'를 포함하는 단일 사용자 정의 팝업을 표시하는 것은 어떻습니까? – Rachel