Как игнорировать события таймера в Delphis MessageDlg

Я установил глобальный обработчик исключений в Delphi. В некоторых серьезных исключениях отображается сообщение об ошибке (за которым следует Halt ()). Пока отображается сообщение об ошибке, Delphi обрабатывает очередь сообщений, обрабатывает события таймера, которые приводят к дальнейшим ошибкам.

Я хочу показать диалоговое окно с ошибкой, которое не обрабатывает события таймера. Как это возможно в Delphi?

Изменить: я использую Dialogs.MessageDlg (...) для отображения сообщения.


person Alois Heimer    schedule 22.11.2013    source источник
comment
Я думаю, у вас должен быть какой-то глобальный флаг. Отображается финальное сообщение об ошибке и перехватывается OnMsg приложения, чтобы проходили только сообщения, адресованные этому диалоговому окну ошибки, другие сообщения вы бы отфильтровали docs.embarcadero.com/products/rad_Studio/delph   -  person Arioch 'The    schedule 22.11.2013
comment
Если вы используете TTimer (s), вы можете рекурсивно отключить все TTimer (s) для Application перед отображением диалогового окна. просто мысль.   -  person kobik    schedule 22.11.2013
comment
@kobik Тогда мой глобальный обработчик исключений должен будет знать каждый TTimer. Я предпочитаю другие решения.   -  person Alois Heimer    schedule 22.11.2013


Ответы (3)


Вы можете фильтровать сообщения в очереди, например WM_TIMER, с помощью TApplication.OnMessage.

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, но я верю, что вам очевидно, как это сделать.

person David Heffernan    schedule 22.11.2013
comment
Я думал о фильтрации сообщений в диалоге. Но с TApplicationEvents это будет работать таким образом, не полагаясь на глобальные переменные. Спасибо. - person Alois Heimer; 22.11.2013
comment
Я думаю, что подход с использованием черного списка - это тупик. Вместо этого следует реализовать политику белого списка - person Arioch 'The; 22.11.2013
comment
@ Arioch'The Ты хотел опубликовать этот комментарий к вопросу, я думаю. - person David Heffernan; 22.11.2013
comment
@AloisHeimer, у вас нет гарантии, что ваш диалог получит все сообщения. (Как и TApplication в многопоточных приложениях, AFAICT). Вот почему и я, и Дэвид пытаемся перехватить на самом глобальном уровне легко достижимый - основной уровень потока vcl приложения. - person Arioch 'The; 22.11.2013
comment
@DavidHeffernan Ну, я не думаю, что на вопросы о том, как мне стрелять в ногу, нужно отвечать формально, не намекая на лучшие варианты - person Arioch 'The; 22.11.2013
comment
@ Там помещенные в очередь сообщения таймера будут извлечены циклом сообщений модального диалога. 100%. Кто еще их тянет? - person David Heffernan; 22.11.2013
comment
Я про белый / черный список. Я имею в виду, что могут быть и другие отложенные сообщения, кроме WM_Timer, которые также могут быть вредными, и их лучше фильтровать - person Arioch 'The; 22.11.2013
comment
@ Arioch'The Но как занести в белый список хорошие сообщения? Это все рисованные сообщения (?), Которые теоретически могут нанести дополнительный вред. Подход к обработке сообщений таймера мне подходит. - person Alois Heimer; 22.11.2013

Попробуйте что-то вроде этого:

    ...
  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;
person NGLN    schedule 22.11.2013
comment
Спасибо, я использовал вашу идею, чтобы проглотить все исключения в моей окончательной реализации. Но моя главная задача заключалась в том, чтобы не генерировать фатальные события таймера в первой строке. - person Alois Heimer; 22.11.2013
comment
Я думаю, что FreeAndNil(E)-часть вашего кода неверна. Согласно документации, я думаю, вы не должны освобождать исключение. Это привело к нарушению прав доступа для меня. Ознакомьтесь с этим объяснением. - person Alois Heimer; 26.03.2014
comment
Спасибо! Может быть, ApplicationEvents.CancelDispatch может помочь предотвратить антивирус, но лучше не уничтожать исключение! - person NGLN; 26.03.2014

Просто для справки: я буду использовать следующий код, который представляет собой смесь обоих ответов.

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;
person Alois Heimer    schedule 22.11.2013