-6

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

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;
4

1 回答 1

2

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

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

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


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

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

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

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

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

如果您遇到Access Violation,通常是因为您正在尝试:

  • 使用尚未创建的东西。
  • 摧毁已经被摧毁的东西。
  • 使用已经被破坏的东西。

要进一步调查访问冲突:

  • 识别问题对象。
  • 在创建/销毁对象的代码中放置断点。
  • 运行你的代码,找到断点并弄清楚发生了什么。

其他问题

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

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

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

于 2013-09-01T20:51:01.677 回答