我在遗留应用程序中遇到了一个奇怪的错误(不是我自己编写的),当我在日历上更改日期时,我收到了StackOverflow异常。
简化版本如下。这是包含两个控件的Windows窗体的代码隐藏,一个名为 label2 的标签和一个名为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;
}
我看不到堆栈跟踪,我看到的只有:
{无法计算表达式,因为当前线程处于堆栈溢出状态。}
此外,我对这些评论感兴趣,因为它没有检查我的StackOverflow异常的堆栈跟踪,因为它看起来像this isn't possible至少没有第三方工具。
可能导致这种情况的原因是什么?感谢
答案 0 :(得分:6)
请记住,程序是基于堆栈的。当程序运行时,每次调用函数时,都会在堆栈上放置一个新条目。每次函数完成时,都会从堆栈弹出以查看返回的位置,因此您可以继续使用先前的方法。当函数完成且堆栈为空时,程序结束。
重要的是要记住这个堆栈是慷慨的,但有限。在空间不足之前,您只能在堆栈上放置这么多函数调用。当我们说堆栈溢出时会发生这种情况。
DoEvents()
只是另一个函数调用。您可以将它放在一个长期运行的任务中,以允许您的程序处理来自操作系统的有关用户活动的消息:点击,击键等。它还允许您的程序处理来自操作系统的程序所需的消息。重新画出它的窗户。通常,只有一个或两个(甚至零)消息等待DoEvents()调用。您的程序处理这些,DoEvents()
调用从堆栈弹出,原始代码继续。有时候,可能会有很多消息在等待。如果这些消息的任何也会导致代码再次调用DoEvents()
,那么我们现在处于调用堆栈的另一个深层。如果该代码反过来发现一条等待导致DoEvents()
运行的消息,那么我们将又是另一个层次。你可以看到它的发展方向。
DoEvents()
是这类问题的常见原因。 MouseMove事件可以很快堆积在你身上。当您有一个按下的键时,KeyPress事件也会发生这种情况。通常情况下,我不希望Calendar DateChanged事件出现这种问题,但如果您在其他地方有DoEvents,或者驱动另一个事件(可能在您的标签上)反过来更新您的日历,您可以轻松创建一个周期将强制您的程序螺旋式进入StackOverflow情况。
您要做的是探索 BackgroundWorder 组件。
您可能还想阅读DoEvents()
关于此问题的文章:
答案 1 :(得分:1)
通常,您的消息泵非常靠近堆栈顶部。添加大量消息并不会导致“深度”堆栈,因为它们都由顶级泵处理。使用DoEvents
正在堆栈中更深处创建一个新的消息泵。如果您正在调用的其中一条消息也调用DoEvents
,那么您现在在堆栈中的消息泵甚至更深。如果该消息泵有另一条消息调用DoEvents
...并且您明白了。
堆栈再次清除的唯一方法是将消息队列清空,此时您开始调用堆栈,直到到达顶级消息泵。
这里的问题是你的代码不容易。它在一个循环中调用DoEvents
一个 lot ,所以它需要有一段空闲队列很长一段时间才能真正退出该循环。最重要的是,如果您碰巧有一个“活动”应用程序将大量消息发送到消息队列,可能会发生大量monthCalendar1_DateChanged
个事件,甚至是循环中使用DoEvents
的其他事件,或者只是其他事件可以防止队列变空,不难相信你的堆栈会变得足够深,导致SOE。
理想的解决方案当然是不使用DoEvents
。改为编写异步代码,以便堆栈深度永远不会超过常量值。
答案 2 :(得分:-3)
DoEvents不应该在任何情况下使用,并且您不需要子字符串来存档TypeWriting效果
这是我目前最了解的方式:
using System.Threading;
private string text = "this is my test string";
private void button1_Click(object sender, EventArgs e)
{
new Thread(loop).Start();
}
private void loop()
{
for (int i = 0; i < text.Length; i++)
{
AddChar(text[i]);
Thread.Sleep(50);
}
}
private void AddChar(char c)
{
if (label1.InvokeRequired)
Invoke((MethodInvoker)delegate { AddChar(c); });
else
label1.Text += c;
}