由于事件处理中的循环,Delphi堆栈溢出

时间:2012-03-08 06:18:48

标签: delphi stack-overflow delphi-6

我正在处理具有两个列表框的应用程序。我使用值加载两个列表框,当我继续单击列表框中的项目时,我在调试时收到以下错误。

enter image description here

运行exe会导致应用程序关闭。有时我会收到“访问冲突”消息。

那么我应该怎样做才能从我的应用程序中消除这个错误?

修改

...

主窗体具有刷新所有控件的计时器 timer_RefreshCOntrol(intervali 1)。

修改editBox_one时的值(值) 这个函数叫做

Procedure TStringSetting.SetValue (const AValue : String);
  Begin
   ...
    If FValueControl <> Nil then
    Begin
     FValueControl.OnChange := VoidNotifyEvent;
     FValueControl.Text := NewValue;
     FValueControl.OnChange := EditChange;        //<--here the stackoverflow error comes....
    end;
  end;




 Procedure EditChange (Sender: TObject);
   Begin
       Value := FValueControl.Text;
       If Not EditIsValid then FValueControl.Font.Color := clRed
       else If Dirty  then FValueControl.Font.Color := clBlue
                  else FValueControl.Font.Color := clWindowText;

       If @OldCustomEditChange <> Nil then OldCustomEditChange(Sender);
    end;`


   the EditChange (Sender: TObject); <--keeps geting called and the stackoverflow error comes

EditChange被分配到FormCreate

上的编辑框

EDIT2

我不是原始的开发人员。我刚回来处理代码,无法进行重大重构。

编辑3 调用堆栈值但是什么是“???” enter image description here

编辑4

经过@Cosmin Prund和@david

之后

我得到了无限呼叫开始的地方

   Procedure TFloatSetting.EditChange (Sender: TObject);
  Begin
    SkipNextOnChange := True;
  Inherited EditChange(Sender);
  IfValidThenStore(FValueControl.Text);
  Inherited EditChange(Sender);  {<-------This is where it start}
 end;


 Procedure TStringSetting.EditChange (Sender: TObject);
  Begin
   Value := FValueControl.Text;
   If Not EditIsValid then FValueControl.Font.Color := clRed
     else If Dirty  then FValueControl.Font.Color := clBlue
                  else FValueControl.Font.Color := clWindowText;

   If @OldCustomEditChange <> Nil then OldCustomEditChange(Sender); {<---this keeps calling  Procedure TFloatSetting.EditChange (Sender: TObject);}
 end;

3 个答案:

答案 0 :(得分:9)

基于发布的调用堆栈,很明显错误发生的原因是:TStringSetting.EditChange触发TFloatSetting.EditChange,然后触发TStringSetting.EditChange。循环继续这样,直到所有堆栈空间都耗尽。

以下是有关可能发生这种情况的原因的一些提示,以及有关如何调试和修复它的提示:

  • OnChange以编程方式更改时,可能涉及的控件会触发Value事件处理程序。如果两个编辑器应该以两种格式显示相同的数据,并且您使用相应的OnChange事件处理程序来保持它们同步,则可能是原因。
  • 也许你是从另一个直接调用一个事件处理程序。

调试方法:

  • 您应首先尝试使用paulsm4建议的断点解决方案。如果每次调用其中一个OnChange处理程序时都发生堆栈溢出,则此解决方案很容易工作。
  • 注释掉其中一个事件处理程序的代码。运行程序,错误不应再出现。以微小(但逻辑)的量取消注释代码,测试并重复。当错误再次出现时,您知道您资助导致错误的行。如果您无法自己解决问题,请编辑问题,添加代码并标记刚刚发现的行,这会给您带来麻烦。

如果您正在使用的控件在以编程方式更改值时触发OnChange事件处理程序,则应使您的事件处理程序不可重入:这将确保无限递归循环。当代码更改属性时,我几乎总是假设控件触发OnChange或等效事件,并始终使用以下内容保护自己不被重新输入:

// Somewhere in the private section of your form's class:
FProcessingEventHandler: Boolean;

// This goes in your event handler
procedure TYourForm.EventHandler(Sender:TObject);
begin
  if FProcessingEventHandler then Exit; // makes code non-reentrant
  FProcessingEventHandler := True;
  try
    // old code goes here ...
  finally FProcessingEventHandler := False;
  end;
end;

答案 1 :(得分:3)

您向EditChange报告一个非终止递归调用序列。查看EditChange的代码,有两个递归调用的候选者:

  1. OldCustomEditChange等于EditChange,或调用的函数又会调用EditChange
  2. 通过调用FValueControl.Font来响应EditChange更改的事件处理程序。
  3. 这些是EditChange中代码调用自身的唯一机会。

    很容易看出这两种可能性如何导致非终止递归函数调用并最终导致堆栈溢出。在这两个候选人中,我的赌注是1号。我会仔细研究OldCustomEditChange被召唤时会发生什么。

    要调试这种性质的堆栈溢出,只需打开调用堆栈窗口并查看长序列调用。您通常会看到一个模式,其中一个函数可能通过一个或多个中间函数调用自身。

答案 2 :(得分:3)

建议:

  1. 在EditChange和OldCustomEditChange中设置断点,以查看谁在调用它们。每次调用。显然,只有 EditChange才能永远调用OldCustomEditChange。

  2. 查看.dfm以确保EditChange仅分配给一个事件(而不是多个事件),并且根本不分配OldCustomEditChange。