Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Для новичков > Исключение вылезает за try-except


Автор: kami 14.10.2013, 08:59
Доброго времени суток, уважаемые:
от слов к коду.
есть поток:
Код

procedure TMySuperThread.Execute;
var
  MSG: TMsg;
begin
  PeekMessage(MSG, 0, WM_USER, WM_USER, PM_NOREMOVE);
  CreateComponents; // тут создается таймер
  try
    while not Terminated and GetMessage(MSG, 0, 0, 0) do
      try
        if MSG.hwnd <> 0 then
          begin
            TranslateMessage(MSG);
            DispatchMessage(MSG);
          end;
      except
        on e: Exception do
          LogFileX.LogException(e);
      end;
  finally
    DestroyComponents;
  end;
end;


В обработчике таймера:
Код

    for i := FWSList.Count - 1 downto 0 do // FWSList = TObjectList<TWS>
        begin
          WS:=FWSList[i]; // вот тут - исключение EArgumentOutOfRangeException
          ...// тут производятся какие-то действия, в том числе, возможно, удаление объекта WS.


Периодически (пользуюсь MadExcept) "наружу" вылезает исключение EArgumentOutOfRangeException, т.е. на экране появляется окошко MadExcept-ов с кнопочками "продолжить, перезапустить..."

Вопрос:
почему оно, это окошко, вылезает? Ведь обработка WM_TIMER обернута в try-except, соответственно - исключение должно записаться в лог и уйти на следующий виток while.
Чего я не понимаю и что делаю не так?

Автор: Akella 14.10.2013, 12:28
Цитата(kami @  14.10.2013,  08:59 Найти цитируемый пост)
почему оно, это окошко, вылезает? Ведь обработка WM_TIMER обернута в try-except, 

Вылезает всегда или только во время работы из-под IDE?

Автор: kami 14.10.2013, 12:38
Цитата(Akella @  14.10.2013,  12:28 Найти цитируемый пост)
Вылезает всегда или только во время работы из-под IDE?

Вылезает не всегда (имеется ввиду - не на каждом вызове события таймера), но при работе из-под IDE, как ни старался, отловить не удалось.
P.S. Используется стандартный TTimer.
Причину возникновения EArgumentOutOfRangeException я уже отловил и устранил, но - с чего оно вылезло за except-блок?

UPD. Нашел, в чем дело. Исходный код оконной процедуры Ttimer:
Код

with Msg do
    if Msg = WM_TIMER then
      try
        Timer;
      except
        Application.HandleException(Self); // вот оно. Ну и как с этим бороться??? С какого перепугу тут образовался Application?
// у него же есть свой try-except в ProcessMessages.
      end
    else
      Result := DefWindowProc(FWindowHandle, Msg, wParam, lParam);

Автор: Akella 14.10.2013, 14:32
Цитата(kami @  14.10.2013,  12:38 Найти цитируемый пост)
но - с чего оно вылезло за except-блок?


По твоему коду непонятно, где именно.
Если окно с сообщением об исключении вываливается при работе аппликации из-под IDE (отладчика), то это нормально.

Автор: kami 14.10.2013, 14:47
Цитата(Akella @  14.10.2013,  14:32 Найти цитируемый пост)
Если окно с сообщением об исключении вываливается при работе аппликации из-под IDE (отладчика), то это нормально.

Если вываливается сообщение самого IDE (там, где есть чекбокс "игнорировать в будущем") - да, это нормально. Но оно останавливает работу потока в "боевом" режиме, не из-под IDE.


Цитата(Akella @  14.10.2013,  14:32 Найти цитируемый пост)
По твоему коду непонятно, где именно.

Я привел не полный код? Вроде, весь, относящийся к делу (пост №1):
в потоке создана очередь сообщений, которые вылавливаются в цикле. Каждая итерация цикла заключена в try-except. 
Приходит сообщение WM_TIMER, оно через DispatchMessage и оконную процедуру таймера попадает в обработчик OnTimer, там возбуждается исключение, но оно не "гасится" в except-блоке цикла обработки сообщений, хотя - должно.

В принципе, причина выяснена - оконная процедура TTimer самостоятельно вызывает Application.HandleException (кто б ее об этом еще просил).
Осталось выяснить - как с этим бороться. Единственное, что приходит в голову - каждый обработчик OnTimer нужно заключать в try-except, что мне совсем не улыбается.

Автор: kami 14.10.2013, 20:16
Цитата(kami @  14.10.2013,  14:47 Найти цитируемый пост)
каждый обработчик OnTimer нужно заключать в try-except, что мне совсем не улыбается.

Кстати, в этом случае получается, что я не могу передать возбужденное в OnTimer исключение "вверх" на уровень... Плохо...

Upd.
Либо - хватать Application.OnException и там... - re-raise что ли, но это вообще не нескафе...

Автор: Alexeis 17.10.2013, 09:18
  Делфя старается перехватывать все исключения в главном потоке. TTimer это компонент. Как известно, в делфях предполагается, что компоненты работают в главном потоке. В данном случае проще свой класс таймера написать. Тем более что там 3.5 строчки кода.

Этот ответ добавлен с нового Винграда - http://ru.vingrad.com/Исключение-вылезает-за-try-except-id525b8862ae2015960f000000#findElement_E7045_525f8132ae2015754700026d_0

Автор: Akella 17.10.2013, 09:51
Создай поток с событием, в котором будет срабатывать процедура через определённые интервалы времени.

Если что, у меня есть готовый пример.

Автор: kami 21.10.2013, 11:43
Цитата(Alexeis @  17.10.2013,  09:18 Найти цитируемый пост)
Делфя старается перехватывать все исключения в главном потоке. TTimer это компонент. Как известно, в делфях предполагается, что компоненты работают в главном потоке.

Это понятно. Однако, исключение и так будет перехвачено в Application.Run, в цикле обработки сообщений (да, в предыдущих сообщениях неправильно написал - ProcessMessages, по крайней мере в D2010, не ограждено try-except). На кой нужен прямой вызов HandleException из оконной процедуры таймера - так и не понял.
В общем и целом - вопрос решен:
Цитата(Alexeis @  17.10.2013,  09:18 Найти цитируемый пост)
 В данном случае проще свой класс таймера написать.



Цитата(Akella @  17.10.2013,  09:51 Найти цитируемый пост)
Если что, у меня есть готовый пример.

Было бы неплохо, т.к. не понимаю, как это - 
Цитата(Akella @  17.10.2013,  09:51 Найти цитируемый пост)
Создай поток с событием,


Автор: Akella 22.10.2013, 09:28
Код


type
  TMyThread = class(TThread)

  private
    FTerminateEvent: THandle;
   fInerval: integer;
...
...
  public
    property TerminateEventHandle: THandle read FTerminateEvent write FTerminateEvent;
    property Inerval: integer read fInerval write fInerval;
...
...

...
    procedure Execute; override;
  end;


implementation


procedure TMyThread.Execute;
begin

try
    FTerminateEvent := CreateEvent(Nil, False, False, Nil);
    SetEvent(FTerminateEvent);

    while not Terminated do
    begin
        if Terminated then Break;

        try
// fInerval - интервал срабатывания в миллисекундах, как у таймера, 1000 = 1 сек.

//выполняться начнёт только после того, как наступит время fInerval или будет вызвано где-нибудь SetEvent(Thread1.TerminateEventHandle);
          if WaitForSingleObject(FTerminateEvent, fInerval) <> WAIT_ABANDONED Then // вместо sleep
          begin
            if Terminated then exit;
             // выполняем в потоке нужную работу

          end;// if

        except
          on e:exception do
            raise;

        end;// try except

finlly
    if FTerminateEvent > 0 then
      CloseHandle(FTerminateEvent);

end;




где-нибудь в приложении, например,  в главной форме 

Код

Thread1: TMyThread;//объявляем
...
...

//по команде создаём запускаем
  Thread1 := TMyThread.Create(True);
  try
    Thread1.Priority := tpNormal;
    Thread1.FreeOnTerminate := true;
    Thread1.Inerval := VarToInt(spinInerval.EditValue, 0) * 1000 * 60;//время
  except
    if Assigned(Thread1) then
      FreeAndNil(Thread1);

    raise;
  end;

  Thread1.Start;// поток будет создан и запущен, но выполняться начнёт только после того, как наступит время


если нужно вызвать thread.execute принудительно, не дожидаясь, пока таймер сработает, можно вызвать в любой момент:
Код

SetEvent(Thread1.TerminateEventHandle);

Автор: Akella 22.10.2013, 09:49
https://www.google.com/search?q=thread+WaitForSingleObject&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:ru:official&client=firefox-a&channel=fflb#channel=fflb&lr=lang_ru&q=delphi+thread+WaitForSingleObject&rls=org.mozilla:ru%3Aofficial&tbs=lr:lang_1ru

Автор: kami 22.10.2013, 09:50
Цитата(Akella @  22.10.2013,  09:28 Найти цитируемый пост)
if WaitForSingleObject(FTerminateEvent, fInerval)

Понятно. Мне больше бы подошло RegisterWaitForSingleObject, с которым так и недопереразобрался. Тормозить очередь сообщений на WaitFor - не комильфо.
Спасибо.

Автор: Akella 22.10.2013, 14:22
Цитата(kami @  22.10.2013,  09:50 Найти цитируемый пост)
Тормозить очередь сообщений на WaitFor - не комильфо.

не понял

Добавлено через 23 секунды
А я думал, что это самый правильный подход.

Добавлено через 1 минуту и 13 секунд
вот ещё посмотри http://www.sql.ru/forum/1050286/shablon-klassa-dlya-raboty-s-potokom-wthread-thread

Автор: kami 22.10.2013, 14:40
Цитата(Akella @  22.10.2013,  14:22 Найти цитируемый пост)
не понял

Если (как, допустим, в примере) ставить в WaitFor dwMilliseconds = 1000, то получается, что ни одно окно (из числа созданных в этом потоке) целую секунду не сможет получить ни одного сообщения.

Автор: bems 22.10.2013, 15:36
так тебе говорят про отдельный поток, в котором нет гуя

Автор: kami 22.10.2013, 15:51
Цитата(bems @  22.10.2013,  15:36 Найти цитируемый пост)
в котором нет гуя

При чем здесь гуй?
У меня в сабжевом потоке крутятся:
- окно таймера
- окно, к которому привязан IWebBrowser (да и сам IWB кучу всего похоже создает)
- окно обслуживания синхронизации данных между потоками (что-то типа коллбаков хуков в Винде).
И ничего из этого не является GUI. Но всем этим окнам нужно обрабатывать сообщения (особенно - второму и третьему).

Добавлено через 1 минуту и 50 секунд
Цитата(kami @  22.10.2013,  09:50 Найти цитируемый пост)
Понятно. Мне больше бы подошло RegisterWaitForSingleObject, с которым так и недопереразобрался. Тормозить очередь сообщений на WaitFor - не комильфо.
Спасибо.

Только сейчас дошло: спасибо было сказано безо всякого подтекста - действительно за напоминание такой возможности.

Автор: bems 22.10.2013, 15:58
ну понятно, но речь идет об отдельном потоке-таймере. если не хочешь - RegisterWaitForSingleObject
в чем проблема я не пойму?

Цитата(kami @  22.10.2013,  15:51 Найти цитируемый пост)
окно, к которому привязан IWebBrowser (да и сам IWB кучу всего похоже создает)
ну для ком-тредов есть CoWaitForMultipleHandles

Автор: kami 22.10.2013, 16:08
Цитата(bems @  22.10.2013,  15:58 Найти цитируемый пост)
но речь идет об отдельном потоке-таймере. 

Изначально речь шла о не-джентльменском поведении стандартного TTimer в потоке smile 
А потом уже Akella привел код потока-таймера. Я же (наверное, некорректно) написал, что такой подход не совсем вписывается в задачу моего потока.


Цитата(bems @  22.10.2013,  15:58 Найти цитируемый пост)
в чем проблема я не пойму?

Да проблемы-то уже и нет, она решена
Цитата(kami @  21.10.2013,  11:43 Найти цитируемый пост)
В общем и целом - вопрос решен:Цитата(Alexeis @  17.10.2013,  09:18 ) В данном случае проще свой класс таймера написать.


Просто было интересен код, который предложил Akella 
Цитата(Akella @  17.10.2013,  09:51 Найти цитируемый пост)
Если что, у меня есть готовый пример.

Вот беседа немножко и подзатянулась.

Автор: Akella 22.10.2013, 22:11
Цитата(kami @ 22.10.2013,  14:40)
Цитата(Akella @  22.10.2013,  14:22 Найти цитируемый пост)
не понял

Если (как, допустим, в примере) ставить в WaitFor dwMilliseconds = 1000, то получается, что ни одно окно (из числа созданных в этом потоке) целую секунду не сможет получить ни одного сообщения.

Обычно в потоке выполняют работу безо всяких окон.
Это отдельный поток.
У меня в отдельном потоке таким образом выполняется процедура экспорта в XML и отправка данных на хостинг.
В другом приложении программа в отдельном потоке раз в минуту выполняет (образно говоря) idHttp.Get(...), при этом, пользователь спокойно работает с программой, даже не замечая, что выполняется что-то в отдельном потоке раз в минуту.

или я тебя не понял?

Автор: kami 23.10.2013, 12:42
Цитата(Akella @  22.10.2013,  22:11 Найти цитируемый пост)
или я тебя не понял?

Да вроде всё правильно.
Тем не более, я не согласен с утверждением 
Цитата(Akella @  22.10.2013,  22:11 Найти цитируемый пост)
Обычно в потоке выполняют работу безо всяких окон.
 У меня редко когда получается без них обойтись.
Под "окнами в потоках" я понимаю всё созданное через AllocateHWND и/или CreateWindow[Ex]...

Автор: bems 23.10.2013, 14:23
Цитата(kami @  23.10.2013,  12:42 Найти цитируемый пост)
У меня редко когда получается без них обойтись.

у меня чаще получается чем не получается, но у тебя же webbrowser control живёт в этом треде, а com-объекты требуют обработки сообщений в своём apartment-треде, тут ничего не сделаешь. смотри в сторону CoWaitForMultipleHandles или обрабатывай WM_TIMER руками

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)