为什么将DoEvents放入循环会导致StackOverflow异常?

JMK*_*JMK 2 c# stack-overflow doevents winforms

我在遗留应用程序中遇到了一个奇怪的错误(不是我自己编写的),当我在日历上更改日期时,我收到了StackOverflow异常.

简化版本如下.这是包含两个控件的Windows窗体的代码隐藏,一个名为label2的Label 和一个名为MonthCalendar的日历,名为monthCalendar1.

我认为这里的想法是创造一个打字机效果.我在XP上,我在Windows 7上的同事能够运行这个:

private void monthCalendar1_DateChanged(object sender, DateRangeEventArgs e)
{
    const string sTextDisplay = "Press Generate button to build *** Reports ... ";

    for (var i = 0; i < 45; i++)
    {
        label2.Text = Mid(sTextDisplay, 1, i);
        System.Threading.Thread.Sleep(50);

        //Error on this line
        //An unhandled exception of type 'System.StackOverflowException' occurred in System.Windows.Forms.dll
        Application.DoEvents();
    }
}

public static string Mid(string s, int a, int b)
{
    var temp = s.Substring(a - 1, b);
    return temp;
}
Run Code Online (Sandbox Code Playgroud)

我看不到堆栈跟踪,我看到的是:

{无法计算表达式,因为当前线程处于堆栈溢出状态.}

另外,我对于为什么我没有检查StackOverflow异常的堆栈跟踪的评论感兴趣,因为看起来如果没有第三方工具,这是不可能的.

可能是什么导致了这个?谢谢

Joe*_*orn 8

请记住,程序是基于堆栈的.当程序运行时,每次调用函数时,都会在堆栈上放置一个新条目.每次函数完成时,都会从堆栈弹出以查看返回的位置,因此您可以继续使用先前的方法.当函数完成且堆栈为空时,程序结束.

重要的是要记住这个堆栈是慷慨的,但有限.在空间不足之前,您只能在堆栈上放置这么多函数调用.当我们说堆栈溢出时会发生这种情况.

DoEvents()只是另一个函数调用.您可以将它放在一个长期运行的任务中,以允许您的程序处理来自操作系统的有关用户活动的消息:点击,击键等.它还允许您的程序处理来自操作系统的程序所需的消息.重新画出它的窗户.通常,只有一个或两个(甚至零)消息等待DoEvents()调用.您的程序处理这些,DoEvents()调用从堆栈弹出,原始代码继续.有时候,可能会有很多消息在等待.如果这些消息中的任何一个也导致再次调用的代码运行DoEvents(),那么我们现在是调用堆栈中的另一个级别.如果该代码反过来发现一条等待导致DoEvents()运行的消息,那么我们将又是另一个层次.你可以看到它的发展方向.

DoEvents()与MouseMove事件结合使用是这类问题的常见原因.MouseMove事件可以很快堆积在你身上.当您有一个按下的键时,KeyPress事件也会发生这种情况.通常情况下,我不希望Calendar DateChanged事件出现这种问题,但是如果您在其他地方有DoEvents,或者驱动另一个事件(可能在您的标签上)反过来更新您的日历,您可以轻松创建一个周期将强制您的程序螺旋式进入StackOverflow情况.

你想要做的是探索BackgroundWorder组件.

您可能还想阅读我DoEvents()对这个问题的评论:

如何使用DoEvents()而不是"邪恶"?