如何在Delphi的MessageDlg中忽略计时器事件

5

我在Delphi中设置了一个全局异常处理程序。在某些严重的异常情况下会显示错误消息(随后是Halt())。当显示错误消息时,Delphi正在处理消息队列和处理定时器事件,这可能导致进一步的错误。

我的要求是显示一个错误对话框,该对话框不处理定时器事件。在Delphi中如何实现呢?

编辑:我使用Dialogs.MessageDlg(...)来显示消息。


我认为你应该有一个全局标志“最终错误消息已显示”,并拦截应用程序的OnMsg,以便只有针对该错误对话框的消息才能通过,其他消息则被过滤掉。http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/Forms_TApplication_OnMessage.html - Arioch 'The
2
如果您使用 TTimer,在显示对话框之前,您可以递归禁用 Application 中的所有 TTimer。这只是一个想法。 - kobik
@kobik 那么我的全局异常处理程序需要知道每个TTimer。我更喜欢其他解决方案。 - Alois Heimer
3个回答

4
你可以使用TApplication.OnMessage过滤排队的消息,例如WM_TIMER
procedure TMainForm.ApplicationMessage(var Msg: TMsg; var Handled: Boolean);
begin
  if ShowingFatalErrorDialog then
    if Msg.Message = WM_TIMER then
      Handled := True;
end;

可以直接将事件处理程序分配给Application.OnMessage,也可以使用TApplicationEvents对象。

显然,您需要提供ShowingFatalErrorDialog的实现,但我相信您知道如何做到这一点。


我在考虑过滤对话框中的消息。但是使用TApplicationEvents,它可以在不依赖全局变量的情况下工作。谢谢。 - Alois Heimer
我认为在这里采用黑名单方法是走不通的。应该改为实施白名单策略。 - Arioch 'The
@Arioch'The 你应该是想把那个评论发到问题下面吧。 - David Heffernan
@AloisHeimer,您无法保证您的对话框可以获得所有消息的保修(在多线程应用程序中,TApplication 似乎也不会如此)。因此,这就是为什么我和David建议拦截最容易到达的全局级别 - 应用程序主要vcl线程级别。 - Arioch 'The
我正在讨论白名单/黑名单的问题。我的意思是除了WM_Timer之外可能还有其他延迟消息,但它们也可能会造成危害,因此最好进行过滤处理。 - Arioch 'The
显示剩余3条评论

2
尝试像这样做:

尝试像这样做:

    ...
  private
    FAboutToTerminate: Boolean;
  end;

...

type
  ESevereError = class(Exception);

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Tag := Tag + 1;
  if Tag > 2 then
    raise ESevereError.Create('Error');
end;

procedure TForm1.ApplicationEvents1Exception(Sender: TObject;
  E: Exception);
begin
  if (E is ESevereError) and (not FAboutToTerminate) then
  begin
    FAboutToTerminate := True;
    Application.ShowException(E);
    Application.Terminate;
  end;
end;

谢谢,我使用了你的想法,在我的最终实现中吞掉了所有异常。但是我的主要重点是不在第一行生成致命的定时器事件。 - Alois Heimer
1
我认为你代码中的 FreeAndNil(E) 部分是错误的。根据文档,我认为你不应该释放异常。这会导致我的程序出现了访问冲突。请看看这个解释。 - Alois Heimer
谢谢!也许ApplicationEvents.CancelDispatch可以帮助防止 AV,但是不破坏异常肯定是更好的选择! - NGLN

1

仅供参考:我将使用以下代码,它是两个答案的混合。

procedure SaveShowErrorMessage(...)
begin
    with TFatalErrorAppEvents.Create(nil) do  //avoid timer and further exceptions
    try
      Dialogs.MessageDlg(...);
    finally
      Free;
    end;
end;

使用如下TFatalErrorAppEvents:
type
    TFatalErrorAppEvents = class(TApplicationEvents)
    protected
        procedure KillTimerMessages(var Msg: tagMSG; var Handled: Boolean);
        procedure IgnoreAllExceptions(Sender: TObject; E: Exception);
    public
        constructor Create(AOwner: TComponent); override;
    end;


constructor TFatalErrorAppEvents.Create(AOwner: TComponent);
begin
    inherited;
    OnMessage := KillTimerMessages;
    OnException := IgnoreAllExceptions;
end;

procedure TFatalErrorAppEvents.IgnoreAllExceptions(Sender: TObject; E: Exception);
begin
    //in case of an Exception do nothing here to ignore the exception 
end;

procedure TFatalErrorAppEvents.KillTimerMessages(var Msg: tagMSG; var Handled: Boolean);
begin
    if (Msg.message = WM_TIMER) then
      Handled := True;
end;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接