1

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.

1
  • Looking at the stack should clarify the issue... If it does not provide enough information to you - post small repeated section of the stack to the question. Commented Oct 13, 2015 at 18:42

1 Answer 1

4

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Responding to "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." I disagree. If I replace a call to ShowDialog with something else that blocks (does not let TheCall to return) the stack is not piling up (as expected). So those 2 sentences at least are misleading. But the new Dispatcher loop makes sense so thank you for the answer. +1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.