How to ignore timer events in Delphis MessageDlg
Asked Answered
P

3

5

I have set up a global exception handler in Delphi. On some severe exceptions an error message is displayed (followed by Halt()). While the error message is shown, Delphi is processing the message queue, processing timer events, that lead to further errors.

What I want is to show an error dialog which does not process timer events. How is that possible in Delphi?

Edit: I use Dialogs.MessageDlg(...) to display the message.

Peninsula answered 22/11, 2013 at 11:31 Comment(3)
I think you should have some global flag "Final Error Message is displayed" and intercept App's OnMsg so that only messages addressed to that error dialog would pass through, other messages you would filter out docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/…Behl
If you use TTimer(s), you could recursively disable all TTimer(s) for the Application before you show the dialog. just a thought.Collier
@Collier Then my global exception handler would need to be aware of every TTimer. I prefer the other solutions.Peninsula
C
4

You can filter queued messages, such as WM_TIMER, with TApplication.OnMessage.

procedure TMainForm.ApplicationMessage(var Msg: TMsg; var Handled: Boolean);
begin
  if ShowingFatalErrorDialog then
    if Msg.Message = WM_TIMER then
      Handled := True;
end;

Either assign that event handler directly to Application.OnMessage or use a TApplicationEvents object.

Obviously you'll have to provide the implementation for ShowingFatalErrorDialog but I trust that it is obvious to you how to do so.

Claireclairobscure answered 22/11, 2013 at 12:1 Comment(8)
I was thinking of filtering the messages within the dialog. But with TApplicationEvents it will work this way without relying on global vars. Thanks.Peninsula
I think black list approach is a dead end here. One should implement white list policy insteadBehl
@Arioch'The You meant to post that comment to the question I think.Claireclairobscure
@AloisHeimer you don't have any warranty that your dialog would get all the messages. (Neither would TApplication in multithreaded apps, AFAICT). So that is why both me and David sugegst at intercepting at most global level of easily reachable - appliaction's main vcl thread levelBehl
@DavidHeffernan Well, i don't think that questions about "how should i shoot my leg" should be answered formally without hinting at better optionsBehl
@There queued timer messages will be pulled off by the modal dialogs message loop. 100%. Who else is pulling them?Claireclairobscure
I am about white/black listing. I mean there might be other deferred messages but WM_Timer that also can be harming and also better be filteredBehl
@Arioch'The But how to white list "good" messages? That would be all drawing messages (?) - which could theoretically do further harm. The approach to handle the timer messages is good enough for me.Peninsula
R
2

Try something like this:

    ...
  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;
Rhesus answered 22/11, 2013 at 11:57 Comment(3)
Thanks, I used your idea to swallow all exceptions in my final implementation. But my main focus was to not generate the fatal timer events in the first line.Peninsula
I think the FreeAndNil(E)-part of your code is wrong. According to the documentation, I think, you are not supposed to free the exception. This lead to an access violation for me. Have a look at this explanation.Peninsula
Thanks! Maybe ApplicationEvents.CancelDispatch could help to prevent the AV, but not destroying the exception surely is way better!Rhesus
P
1

Just for reference: I will use the following code, which is a mixture from both answers.

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

With TFatalErrorAppEvents as follows:

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;
Peninsula answered 22/11, 2013 at 13:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.