【问题标题】:freeing the popup twice crashes applcation [closed]释放弹出窗口两次崩溃应用程序[关闭]
【发布时间】:2013-09-01 06:35:20
【问题描述】:

在通过单击按钮调用的执行块中,我创建了一个弹出菜单以显示在单击按钮的位置。它当前显示正确,其中有几个项目,其中一个有几个子项目。当这个运行一次然后调用析构函数时,就可以了。但是如果我执行它两次(显示弹出窗口并单击一个项目两次)然后破坏,应用程序崩溃。我认为这是因为我没有正确释放弹出窗口(它被声明为私有属性)。

procedure TPlugIn.Execute(AParameters : WideString);
var
  i: Integer;
  pnt: TPoint;
begin
  GetCursorPos(pnt);

  FPopup := TPopupMenu.Create(nil);
  FPopup.OwnerDraw:=True;
  FPopup.AutoHotkeys := maManual;

  //SQL Upgrade
  Item := TMenuItem.Create(FPopup);
  Item.Caption := 'Database Install/Upgrade';
  Item.OnClick := ShowItemCaption;
  FPopup.Items.Add(Item);

  //Language Folder
  Item := TMenuItem.Create(FPopup);
  Item.Caption := 'Language Folder';
  Item.OnClick := ShowItemCaption;
  FPopup.Items.Add(Item);

  //Machines
  Item := TMenuItem.Create(FPopup);
  Item.Caption := 'Machines';

  MachineItem := TMenuItem.Create(FPopup);
  MachineItem.Caption := 'Sample Machine 1';
  MachineItem.OnClick := ShowItemCaption;
  Item.Add(MachineItem);

  MachineItem := TMenuItem.Create(FPopup);
  MachineItem.Caption := 'Sample Machine 2';
  MachineItem.OnClick := ShowItemCaption;
  Item.Add(MachineItem);


  FPopup.Items.Add(Item);

  Self.FPopup := FPopup;

  FPopup.Popup(pnt.X, pnt.Y);

end;

ShowItemCaption 过程中,我只显示该发送者对象的标题。我还没有编写具体的事件。如果它在执行过程中释放弹出窗口,则不再出现弹出窗口。

destructor TPlugIn.Destroy;
begin
  inherited;
  FPopup.Free;
  //ShowMessage('freed');
end;

【问题讨论】:

  • 您必须将nil 分配给您的FPopup 变量(因此在FPopup.Free 调用之后使用例如FreeAndNil(FPopup)FPopup := nil)。发生这种情况是因为当您第二次调用 FPopup.Free 时,FPopup 只是一个悬空指针,但仍然是 Assigned 是什么让 Free 方法认为后面有一个有效对象,因此它调用 Destroy 不是现有对象。
  • hmm 结果还是和以前一样。没关系,当我执行两次使用时,基本上我是FPopup 两次并调用Destroy?第二个执行调用是否替换旧的FPopup?我只是想知道它是否为两个实例之一调用Destroy,如果是这样的话。
  • 由于您没有在这里讲述整个故事,因此很难说出了什么问题。什么时候调用 TPlugin.Destroy?你在用线程吗?
  • 为什么你在按钮点击时创建它(可能发生很多次)但释放它一次?你应该在TPlugIn.Create 中创建你的TPopupMenu。否则,您将在这里发生一些重大的内存泄漏。更不用说,假设弹出菜单甚至从未打开过。所以它一开始就没有被创造出来。然后你尝试释放它,但它没有被创建,所以它会失败。
  • @Craig - 你怎么知道插件析构函数只运行一次?也许 TPlugin 是在每次执行时创建的,然后被销毁。

标签: delphi popup


【解决方案1】:

首先,您完全误诊了您的问题。因此,您没有提供我们需要的信息,以便为您提供明确的解决方案。

如果我采用您提供的代码,并按照您的描述进行测试:使用一个按钮调用 Execute 方法中的代码,另一个按钮调用FreeFPopup,我不会收到错误消息。实际上,您应该自己尝试一下;你也不应该得到一个错误;这意味着问题不在于您提供的代码。

不过,话虽如此:我可以帮助您更好地诊断问题,之后您可以自己解决问题 - 或者至少向我们提供更好的信息来帮助您。
此外,您在此代码中仍然存在许多需要修复的错误 - 即使这些错误不会导致您的应用程序崩溃。


让我们从诊断真正的问题开始。您的程序真的崩溃 还是您只是在调试器中遇到异常?我问,因为它通常需要更极端的东西才能真正崩溃 Delphi 应用程序。

如果您只是遇到异常,我怀疑调试器会将您带到FPopup.Free; 行(请注意,这不是问题所在的行 - 调试器通常会将您带到after; 这意味着问题出在继承的 Destroy 中)。 您还需要告诉我们您收到的异常类别和消息

无论哪种方式,即使您的应用程序真的崩溃了,它几乎总是会出现异常。您需要准确指出异常发生的位置。为此,您需要:

  • 通过调试器运行您的应用程序。
  • 确保调试器设置为在发生异常时停止。
  • 鉴于异常可能发生在 TPlugIn 类中,请确保该单元未禁用调试信息。
  • 您可能还需要将项目选项设置为“使用调试 DCU”。
  • 做你的测试。

当您遇到异常时,请记住调试器通常会显示导致异常的行之后的行。您现在需要结合错误消息来考虑问题行,以找出可能出现的问题。

如果您遇到访问冲突,这通常是因为您正在尝试:

  • 使用尚未创建的内容。
  • 销毁已经被销毁的东西。
  • 使用已经被销毁的东西。

进一步调查访问冲突:

  • 确定问题对象。
  • 在创建/销毁对象的代码中放置断点。
  • 运行您的代码,点击断点并找出发生了什么。

其他问题

  1. 您提到“如果您在执行过程中释放弹出窗口,它将不再出现”。 (大概这是您避免内存泄漏的尝试。)这是因为当您调用FPopup.Popup(pnt.X, pnt.Y); 时,它不会“暂停您的代码”并等待选择一个项目。您的代码继续运行,因为菜单使用事件驱动模型在单击项目时进行回调。因此,您的弹出菜单将被销毁,并在弹出后立即消失。
  2. Self.FPopup := FPopup; 行完全是多余的,什么也不做。你实际上是在说FPopup := FPopup - 你并没有以任何方式改变 FPopup 的价值。
  3. 应该很明显,标题“释放弹出窗口两次崩溃应用程序”是完全错误的。根据您的代码和描述:您创建两次弹出窗口并仅释放一次
  4. 这本身就是一个问题,因为正如 Jerry 所指出的 - 你有内存泄漏。基本上,您的代码会覆盖对您创建的第一个 TPopup 的引用,使其“孤立”并保留在内存中。然后你就只有Free/Destroy最后一个在 TPlugIn 析构函数中创建。
  5. 其中:在调用继承的 TPlugIn 析构函数之前释放弹出窗口。在这种情况下,这不是必需的,但通常按照创建的相反顺序进行清理是明智的。

每次调用 Execute 时都不需要(或至少不应该)重新创建弹出窗口。你应该做的就是让它再次弹出FPopup.Popup。这实际上是使 FPopup 成为私有类字段的原因的一部分。 IE。您只需设置一次并根据需要重复使用它。

您可以使用一种称为延迟初始化的技术;但实际上这通常是不必要的并发症。你最好简单地镜像你对 FPopup 的创建和销毁。 IE。如果您在销毁 TPlugIn 时销毁 FPopup - 您应该在创建 TPlugIn 时创建 FPopup

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 2020-12-23
    • 2019-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多