最近,我遇到了一个问题,但仍然无法解决。在一个应用程序中,我注册了一个调度程序异常处理程序。在同一应用程序中,第三方组件(DevExpress Grid Control
)在Control.LayoutUpdated
的事件处理程序中导致异常。我希望调度程序异常处理程序被触发一次。但是,相反,我得到了堆栈溢出。我制作了一个没有第三方组件的示例,并发现该示例在每个WPF应用程序中都会发生。
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
namespace MyApplication
{
/* App.xaml
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyApplication.App"
Startup="OnStartup"
/>
*/
public partial class App
{
private void OnStartup(object sender, StartupEventArgs e)
{
DispatcherUnhandledException += OnDispatcherUnhandledException;
MainWindow = new MainWindow();
MainWindow.Show();
}
private static void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
MessageBox.Show(e.Exception.Message);
e.Handled = true;
}
}
public class MainWindow : Window
{
private readonly Control mControl;
public MainWindow()
{
var grid = new Grid();
var button = new Button();
button.Content = "Crash!";
button.HorizontalAlignment = HorizontalAlignment.Center;
button.VerticalAlignment = VerticalAlignment.Center;
button.Click += OnButtonClick;
mControl = new Control();
grid.Children.Add(mControl);
grid.Children.Add(button);
Content = grid;
}
private void OnButtonClick(object sender, RoutedEventArgs e)
{
mControl.LayoutUpdated += ThrowException;
mControl.UpdateLayout();
mControl.LayoutUpdated -= ThrowException;
}
private void ThrowException(object sender, EventArgs e)
{
throw new NotSupportedException();
}
}
}
有什么办法可以防止这种行为? .NET Framework 3.0、3.5、4.0和4.5会发生这种情况。我不能只是将
try-catch
包裹在LayoutUpdated
事件处理程序中,因为它位于第三方组件中,而且我不认为应该发生堆栈溢出。 最佳答案
我认为Florian GI关于消息框是正确的,但是如果您在Handled
方法中执行了其他操作(或者没有做任何事情,或者只是将true
设置为OnDispatcherUnhandledException
而不是消息框),它仍然会永远循环并且不会到达mControl.LayoutUpdated -= ThrowException;
行。
所以我想我会通过dotPeek窥探一下代码...
当您在控件上调用UpdateLayout
时,最终它会到达ContextLayoutManager.UpdateLayout
方法,此方法的代码段如下所示:
// ... some code I snipped
bool flag2 = true;
UIElement element = (UIElement) null;
try
{
this.invalidateTreeIfRecovering();
while (this.hasDirtiness || this._firePostLayoutEvents)
{
//... Loads of code that I think will make sure
// hasDirtiness is false (since there is no reason
// for anything remaining dirty - also the event is
// raised so I think this is a safe assumption
if (!this.hasDirtiness)
{
this.fireLayoutUpdateEvent();
if (!this.hasDirtiness)
{
this.fireAutomationEvents();
if (!this.hasDirtiness)
this.fireSizeChangedEvents();
}
}
//... a bit more
flag2 = false;
}
}
finally
{
this._isUpdating = false;
this._layoutRequestPosted = false;
//... some more code
if (flag2)
{
//... some code that I can't be bothered to grok
this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (Delegate) ContextLayoutManager._updateLayoutBackground, (object) this);
}
}
// ... and for good measure a smidge more code
我将平底船,并建议_
firePostLayoutEvents
标志在您的情况下为true。_firePostLayoutEvents
设置为false的唯一地方是fireAutomationEvents
方法,因此我们假设在fireAutomationEvents
方法结束之前的某个地方引发了您的异常(我想是fireLayoutUpdateEvent
方法),因此此标志不会设置为false。但是,当然,finally在循环之外,因此它不会永远循环(如果这样做,则不会获得StackOverflowException)。
对,向前,因此我们正在调用
UpdateLayoutBackground
函数,该函数实际上只是调用NeedsRecalc
,因此让我们看一下...private void NeedsRecalc()
{
if (this._layoutRequestPosted || this._isUpdating)
return;
MediaContext.From(this.Dispatcher).BeginInvokeOnRender(ContextLayoutManager._updateCallback, (object) this);
this._layoutRequestPosted = true;
}
Rightyho,正在调用UpdateLayoutCallback,所以在那儿着眼睛...
private static object UpdateLayoutCallback(object arg)
{
ContextLayoutManager contextLayoutManager = arg as ContextLayoutManager;
if (contextLayoutManager != null)
contextLayoutManager.UpdateLayout();
return (object) null;
}
噢,这很有趣-它再次调用
UpdateLayout
-因此,我会怀疑一个稍微受过教育的猜测,这是问题的根本原因。因此,恐怕我对此无能为力。
关于c# - 如果已注册DispatcherUnhandledException,则Control.LayoutUpdated中的异常之后的堆栈溢出,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14211306/