【问题标题】:Stack Overflow in DelphiDelphi 中的堆栈溢出
【发布时间】:2011-02-05 20:38:49
【问题描述】:

我在 StackOverflow.com 上发布了我的 Stack Overflow 问题。最好的讽刺!

无论如何。我在我的 SkypeReply 事件处理程序上调用这个过程,它被触发了很多:

  Procedure OnCategoryRename;
  Var
    CategoryID : Integer;
    sCtgName : String;
  Begin
    if (AnsiContainsStr(pCommand.Reply,'GROUP')) and (AnsiContainsStr(pCommand.Reply,'DISPLAYNAME')) then
      begin
         sCtgName := pCommand.Reply;
         Delete(sCtgName,1,Pos('GROUP',sCtgName)+5);
         CategoryID := StrToInt(Trim(LeftStr(sCtgName,Pos(' ',sCtgName))));
         sCtgName := GetCategoryByID(CategoryID).DisplayName; // Removing THIS line does not produce a Stack Overflow!
         ShowMessage(sCtgName); 
      end;

这样做的目的是遍历我的 Skype 群组列表,以查看已重命名的群组。 AFAIK 这并不重要,因为我的 S.O 已被追踪到出现在 here

Function GetCategoryByID(ID : Integer):IGroup;
Var
  I : Integer;
  Category : IGroup;
Begin
  // Make the default result nil
  Result := nil;

  // Loop thru the CUSTOM CATEGORIES of the ONLY SKYPE CONTROL used in this project
  // (which 100% positive IS attached ;) )
  for I := 1 to frmMain.Skype.CustomGroups.Count do
    Begin
      // The Category Variable
      Category := frmMain.Skype.CustomGroups.Item[I];
      // If the current category ID returned by the loop matches the passed ID
      if Category.Id = ID then
        begin
          // Return the Category as Result (IGroup)
          Result := Category;
          // Exit the function.
          Exit;
        end;
    End;
End;

当我在 Result := Category; 处设置断点时;和单步通过,这两条线被一遍又一遍地执行,紧随其后!

当我在第一个代码 sn-p 中注释掉 sCtgName := GetCategoryByID(CategoryID).DisplayName; 时,没有溢出,消息被显示一次它应该。但是,GetCategoryByID 是我写的一个函数,我也写了一个类似的函数,它工作得很好(GetCategoryByName),所以我不明白为什么它决定重复

// Return the Category as Result (IGroup)
Result := Category;
// Exit the function.
Exit;

一遍又一遍。

编辑:这里是如何重现它:https://gist.github.com/813389

编辑:这是我的 CallStack,根据要求:

Edit2:更多信息:

【问题讨论】:

  • 如果您想自己调试,请使用 F7 进入而不是 F8 跳过
  • 我在您显示的代码中看不到 SO。你没有告诉我们什么?
  • 如果你正在递归地做一些没有结束的事情,你可能会得到堆栈溢出。 OnCategoryRename 对我来说听起来像是一个事件。如果GetCategoryByID 中有触发OnCategoryRename 的内容,您将得到一个永无止境的递归。您可以通过在单步执行代码时查看调用堆栈来查看是否发生这种情况。
  • 什么Delphi版本?您是否退出了 Delphi,删除了所有 .DCU 文件,然后启动 Delphi 并再次尝试您的项目?您使用的是哪个Exit(我有一个客户,他有一卡车的单位有procedure Exit;)。
  • @Jeff:frmMain.Skype.CustomGroups.Item[I] 是什么类型?调用它会导致SkypeReply 处理程序触发吗?或者可以将其转换为 Category 的类型导致 SkypeReply 触发?我可以看到您正在检查pCommand.Reply 是否包含'GROUP',但我没有看到后者从前者中删除(尽管它可能在处理程序的代码中完成,如果仍然需要的话)。如果由于某种原因,处理程序在 proc 完成之前被触发(或 因为 proc 导致处理程序触发),到那时 pCommand.Reply 是否可能仍然包含 'GROUP'

标签: delphi function stack-overflow skype


【解决方案1】:

确保在关闭“优化”、打开“堆栈帧”和“使用调试 .dcu”的情况下编译您的项目,以获得尽可能详细的调用堆栈。然后在此处发布您遇到堆栈溢出时获得的调用堆栈(如果您无法从中识别问题的性质)。

【讨论】:

  • +1,使用调用堆栈很容易识别堆栈溢出。
  • 我似乎无法复制调用堆栈?我该怎么做?
  • @David - 是的,我想。只是认为有一些特殊的方法可以做到这一点。
  • ... 或全选(堆栈窗口聚焦)(CTRL-A),然后复制(CTRL-C)
  • 您发布的调用堆栈似乎来自您在该行放置断点并显示它第一次停在那里的时间。如果您想查看导致问题的递归,您显然需要查看实际包含递归的调用堆栈(换句话说,发生堆栈溢出后得到的调用堆栈)。
【解决方案2】:

您的问题中没有显示的内容: 您在此处发布的“OnCategoryRename”函数是从“TForm.Skype1Reply”回调调用的子函数。

要看到这个,我不得不点击你的 github 链接——但我认为这是你问题的一个重点。

我的猜测:

  • 您的“GetCategoryById”函数实际上发送了一个查询,该查询会触发“Skype1Reply”。
  • 如果组名已更改,“Skype1Reply”将调用“OnCategoryRename”。
  • “OnCategoryRename”调用“GetCategoryById”
  • “GetCategoryById”触发“Skype1Reply”
  • 不知何故,“如果组名已更改”的测试仍然正确,因此“Skype1Reply”调用“OnCategoryRename”
  • “OnCategoryRename”调用“GetCategoryById”
  • 冲洗,重复

我认为快速而肮脏的解决方法是改变

sCtgName := GetCategoryByID(CategoryID).DisplayName; // Removing THIS line does not produce a Stack Overflow!

sCtgName := //find another way to get the new name, which you can probably get from your ICommand object
            pCommand.Reply.ReadDataFromReplyAndGetNewDisplayName;

以后,我建议您发布此类问题的完整代码示例。

【讨论】:

  • 我相信我确实提到过,但你是对的。但是,我在您的帖子之前已经进行了修复,但是由于这是正确的答案,因此它将被标记为。谢谢!
  • 此外,GetCategoryByID 也会触发 SkypeReply 事件,但我使用 AnsiContainsStr 检查回复的第一个字母是否为 #,如果是,则为递归调用 (?)。我不知道为什么它会这样做。但它现在可以工作了:)
【解决方案3】:

堆栈溢出可能是由无限递归引起的。

在编写包含事件处理程序的代码时必须非常小心。 正如 David 所说,您可以做的一件事来帮助您调试它,即步入而不是跳过此类调用。 F7 进入通话。

您可以做的另一件事是在函数 GetCategoryById 的顶部放置一个断点。现在看看你的调用堆栈。您看到堆栈中重复的名称了吗?这应该很清楚。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-18
    • 2010-12-26
    • 2016-06-12
    • 2014-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多