【问题标题】:I don't understand stack overflow error with repeated DispatchMessageW in the call stack我不明白调用堆栈中重复 DispatchMessageW 的堆栈溢出错误
【发布时间】:2012-09-24 02:06:56
【问题描述】:

这是一个 Delphi 应用程序,但我想这是一个一般的 Windows 编程问题。

我让我的应用程序在周末运行(在 Delphi IDE 中),刚回来发现堆栈溢出。

堆栈是这样开始的......

:75c4417e kernel32.GetDriveTypeW + 0x23
:75c452ae kernel32.IsProcessorFeaturePresent + 0xa9
:75c45272 kernel32.IsProcessorFeaturePresent + 0x6d
:75c45248 kernel32.IsProcessorFeaturePresent + 0x43
:7678410b KERNELBASE.LoadStringBaseExW + 0xc7
:76678ed2 USER32.LoadStringW + 0x19
:0040c4ae LoadResString + $4A
uADStanDef.TADDefinition.Create(nil)
uADStanDef.TADDefinition.CreateTemporary
uADStanDef.TADConnectionDefTemporaryFactory.CreateObject
uADStanFactory.TADManager.CreateInterface((1050358107, 62550, 16757, (168, 100, 178, 87, 60, 74, 32, 21)),(no value),True)
uADStanFactory.ADCreateInterface((1050358107, 62550, 16757, (168, 100, 178, 87, 60, 74, 32, 21)),(no value),True)
uADCompClient.TADCustomConnection.Create($2DB7EB0)
fMainForm.TMainForm.ServerAliveTimerTimer($2E8DE38)    <========== my code
:004f1546 Winapi + $4F1546
:00461316 Winapi + $461316
:766762fa ; C:\Windows\syswow64\USER32.dll
:76676d3a USER32.GetThreadDesktop + 0xd7
:766777c4 ; C:\Windows\syswow64\USER32.dll
:7667788a USER32.DispatchMessageW + 0xf

所以,一个计时器即将到期,我正在创建一个新对象(AnyDac 组件的)并且堆栈溢出。代码肯定会释放对象。我已将其附加在下面以供那些想要检查的人使用,但我认为这不是我的问题。

然后堆栈继续

:7669cdfd ; C:\Windows\syswow64\USER32.dll
:7669cf5c ; C:\Windows\syswow64\USER32.dll
:766cf73c ; C:\Windows\syswow64\USER32.dll
:766cfa18 ; C:\Windows\syswow64\USER32.dll
:766cfb1f USER32.MessageBoxTimeoutW + 0x52
:766cfd15 USER32.MessageBoxExW + 0x1b
:766cfd57 USER32.MessageBoxW + 0x18
:00549986 Vcl + $549986
:00549aa2 Vcl + $549AA2
:00549873 Vcl + $549873
:00461316 Winapi + $461316
:766762fa ; C:\Windows\syswow64\USER32.dll
:76676d3a USER32.GetThreadDesktop + 0xd7
:766777c4 ; C:\Windows\syswow64\USER32.dll
:7667788a USER32.DispatchMessageW + 0xf

该块重复了三千行(!)而且我不知道它是什么或它在做什么。然后就结束了

StoreRoom.StoreRoom
:75c4339a kernel32.BaseThreadInitThunk + 0x12
:77eb9ef2 ntdll.RtlInitializeExceptionChain + 0x63
:77eb9ec5 ntdll.RtlInitializeExceptionChain + 0x36

我不理解所有这些重复的堆栈 - 任何人都可以建议吗?

(对于那些注意到我的异常处理正在显示一个对话框的聪明人来说,这是一个 TForm,它会在用户单击 OK 时关闭)

我的代码:

procedure TMainForm.ServerAliveTimerTimer(Sender: TObject);
begin
   try
      ADConnection := TADConnection.Create(Self);  <======= stack overflow here
      ADConnection.DriverName := 'mysql';
      ADConnection.Params.Add('Server=' + MAIN_STOREROOM_IP_ADDRESS);  
      // other params, such as password, removed for posting
      ADConnection.Connected := True;

   except
      on E : Exception do
      begin
         ADConnection.Free();
         theDialogForm := TDialogFormForm.Create(Nil);
         theDialogForm.ShowTheForm('Database problem'+#13+#10+''+#13+#10+
                                   E.ClassName+#13+#10+E.Message);    
         StopTheApplication();   <===== just calls ExitProcess(0);
         Exit;                     as I had problems with Halt elsewhere in the code
      end;
   end;

   if isMainStoreRoom then
   begin
      CheckIfStoreRoomIsAlive(SECONDARY_STOREROOM_IP_ADDRESS);
   end
   else
   begin
      CheckIfStoreRoomIsAlive(MAIN_STOREROOM_IP_ADDRESS);
   end;

   try    // Now, update our own timestamp
      timestamp  := GetCurrentUnixTimeStamp();
      ADConnection.ExecSQL('UPDATE server_status SET alive_timestamp="' + IntToStr(timestamp) + '" WHERE ip_address="' + ipAddress + '"');

   except
      on E : Exception do
      begin
         ADConnection.Free();
         Exit;
      end;
   end;

   ADConnection.Free();
end;     // ServerAliveTimerTimer()

【问题讨论】:

  • +1 表示有关堆栈溢出的堆栈溢出问题。
  • 为什么每次都需要创建一个连接类而不是使用ADConnection.Connected True/False?数据库连接设置会在运行时更改吗?
  • @pani +1 提问。 AnyDac 的人告诉我,我必须每次都创建一个新对象,因为这是一个异步的计时器处理程序,并且可能在我的代码的其他部分访问全局连接时执行
  • @Ashish 我恢复了您对标题的编辑,标题中的堆栈溢出是一种错误类型,而不是该站点的名称。
  • @TomBrunberg 好的。很抱歉编辑错误

标签: windows delphi stack-overflow


【解决方案1】:

您的堆栈溢出是由于MessageBox() 被一遍又一遍地调用以响应重复的窗口消息。在内部,MessageBox() 运行它自己的消息循环,显然它一遍又一遍地处理和分派相同的消息。这可能表明计时器误入歧途。我强烈建议您在首次进入 OnTimer 事件处理程序时禁用计时器,然后在退出前重新启用计时器。

另外,StopTheApplication() 不应该直接调用ExitProcess()(甚至Halt())。请改用Application.Terminate()

【讨论】:

  • +1 谢谢@Remy。我没有提到我在 IDE 中运行。对 MessageBox() 的调用可能来自那里吗?我根本不叫它。我确实调用了 MessageDlg(),但没有在任何计时器处理程序中调用 - 在我创建并显示自定义错误表单的那些处理程序中。我只有三个计时器,另外两个的处理程序代码比我发布的要短。我同意你关于禁用/启用计时器的观点,但最短的(显示的)是 8 秒。几次 d/b 访问不会花费 8 秒。
  • MessageBox() 可能会被Application.MessageBox() 调用,因为它正在被调用堆栈中的某些 VCL 代码调用。例如,Application.MessageBox() 有时会被异常处理程序调用。很难说,因为您的调用堆栈不显示 VCL 的函数名称。你是为 Release 而不是 Debug 编译?在任何情况下,调用MessageBox() 的任何内容显然都不是可重入的,而是陷入了递归循环,每次嵌套调用MessageBox() 时都会将越来越多的数据推入堆栈,直到堆栈溢出。
  • +1 谢谢,雷米。我正在使用 XE 2 启动器,这可能会解释堆栈,因为我没有 VCL 源代码。有没有什么方法可以在没有 VCL 源的情况下断点 MessaegeBox() ? (或该循环中的任何其他内容)
  • 定时器间隔是多少?如果这段代码的运行时间比定时器间隔长,并且有一些东西在调用 ProcessMessages,这将是可重入的。
  • @Mawg - 它在工作时似乎不是问题,但在失败时。至少在异常块中停止计时器,在显示对话框之前,应用程序无论如何都会退出。
猜你喜欢
  • 2012-05-21
  • 2022-11-01
  • 1970-01-01
  • 2019-05-18
  • 2014-01-26
  • 2019-02-16
  • 2011-09-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多