ILIA BROUDNO
ILIA BROUDNO

Reputation: 1893

C# WPF Window.ShowDialog stack overflow exception

Surprisingly a stack overflow exception can be caused by repeatedly calling Window.ShowDialog asynchronously.

public MainWindow()
{
    InitializeComponent();
    TheCallDelegate = TheCall;
    _timer = new DispatcherTimer();
    _timer.Tick += _timer_Tick;
    _timer.Start();
}

DispatcherTimer _timer = null;

void _timer_Tick(object sender, EventArgs e)
{
    _timer.Dispatcher.BeginInvoke(TheCallDelegate);            
}

Action TheCallDelegate;

void TheCall()
{
    Window win = new Window();
    win.ShowDialog();
}

As you can see there is no actual recursion here (or there shouldn't have been) but once the exception happens you can see that the call stack is indeed full. Why? This can also be achieved without the use of a timer like so:

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        while (true)
        {
            this.Dispatcher.BeginInvoke(TheCallDelegate);
            await Task.Delay(1);
        }
    }

P.S. The code you see here is constructed specifically to illustrate the question so don't focus on why would anyone do this. The purpose of the question is to understand why does ShowDialog behave in this way.

Upvotes: 1

Views: 1015

Answers (1)

shf301
shf301

Reputation: 31394

As you should be able to see the stack trace each call to ShowDialog() pushes new frames onto the stack. Since you are calling ShowDialog() multiple times without closing, each call increases the stack depth, and the stack eventually overflows.

This happens because unlike the Show() method, ShowDialog() doesn't return until the window that it displays is closed. This works like any other method call, so it causes the stack to grow. Since ShowDialog() has to respond to user input, it starts a new Dispatcher loop. Since a Dispatcher is still running your timer keeps firing and opening new, nested dialogs.

So at a very high level your call stack will look like:

...Dispatcher Loop...
TheCall
ShowDialog
... Dialog Dispatcher Loop...
TheCall
ShowDialog
... Dialog Dispatcher Loop...
TheCall
ShowDialog
... Dialog Dispatcher Loop...

Which will eventually overflow as new dialogs are opened.

Upvotes: 4

Related Questions