Форум: "WinAPI";
Текущий архив: 2003.02.10;
Скачать: [xml.tar.bz2];
ВнизОчистка очереди сообщений Найти похожие ветки
← →
Igorek (2002-12-24 13:30) [0]Проблема такая:
Выставлен таймер. Когда приходит сообщение WM_TIMER - выполняются некоторые действия. Интервал таймера по видимому меньше времени, за которые эти действия выполняются. Так что получается, что сообщения WM_TIMER загромождают очередь. В некоторый момент времени юзер делает действие, по которому надо остановить таймер. Это действие передается как сообщение CM_MOUSELEAVE. Но оно оказывается в конце очереди. Получается, что после этого действия еще некоторое время обрабатываются WM_TIMER, пока дело не дойдет до CM_MOUSELEAVE. А надо сразу.
Варианты решения:
1) при каждом вызове обработчика таймера очищать очередь от таких же сообщений; пробую так:
while PeekMessage(Msg, SomeHandle, WM_TIMER, WM_TIMER, PM_REMOVE) do;
2) постоянно проверять очередь на наличие CM_MOUSELEAVE; если есть, то изымать и делать DispatchMessage; оно потом должно удалить таймер, но очередь все равно наверно надо почистить
К сожалению пока не работает. Кто что скажет?
← →
Woolen (2002-12-24 13:48) [1]Вариант 1.
Попробуйте не пользоваться TTimer. Этот класс действительно по таймеру ставит в очередь сообщения. Но в Windows предусмотрено 2 варианта срабатывания стандартного таймера: 1) постановка WM_TIMER в очередь 2)вызов процедуры пользователя. Класс TTimer знает только о первом. Функции работы с таймером SetTimer и KillTimer примитивны и просты в использовании. Прочтите список параметров и воспользуйтесь ими. Есть и другой вариант. Уж не знаю, подойдет ли хотябы один из них.
Вариант 2.
Пользуйтесь стандартным таймером. В обработчике события OnTimer первой строкой отключите таймер поставив свойство Enabled в False. Далее следует остальной код вашей "долгоиграющей" процедуры, во время работы которой отключенный таймер никаких сообщений никуда не посылает. А последней строкой поставьте свойство Enabled таймера снова в True, чтобы он начал отсчет для помещения сообщения в очередь.
← →
Igorek (2002-12-24 13:55) [2]
> TTimer знает только о первом. Функции работы с таймером
> SetTimer и KillTimer примитивны и просты в использовании.
> Прочтите список параметров и воспользуйтесь ими. Есть и
> другой вариант. Уж не знаю, подойдет ли хотябы один из них.
Ими я и пользуюсь. Хотя может и стоит попробовать TTimer.
← →
Woolen (2002-12-24 14:16) [3]Если пользуетесь, можете заставить (если вам подходит) операционную систему напрямую вызывать вашу функцию, а не ставить сообщение в очередь. Единственное, почему, я думаю, может не подойти: это то, что когда функция еще не исполнилась до конца, она будет вызвана еще раз.
← →
Igorek (2002-12-24 14:34) [4]
> Woolen © (24.12.02 14:16)
> Если пользуетесь, можете заставить (если вам подходит) операционную
> систему напрямую вызывать вашу функцию, а не ставить сообщение
> в очередь. Единственное, почему, я думаю,
А как?
Вот я выставил таймер:
SetTimer(FSelfHandle, IfThen(FUpDown, SCROLL_DOWN_ID, SCROLL_UP_ID),
ScrollDelay, nil);
FSelfHandle - дескриптор, предварительно полученный через AllocateHWnd.FSelfHandle := AllocateHWnd(MsgHandler);
MsgHandler - моя процедура обработки сообщений по этому хендлу.
Наверно оно действ. не ставит в очередь, а вызывает процедуру. Но где-то оно скапливается, так как процедура не успевает завершиться до момента следующего вызова. Может при вызове процедуры убивать таймер и выставлять снова? Счас попробую.
← →
Игорь Шевченко (2002-12-24 14:47) [5]Кстати, CM_MOUSELEAVE в очереди нет и быть не может.
RTFS: Forms.pas, function TApplication.DoMouseIdle: TControl;
← →
Igorek (2002-12-24 16:46) [6]
> Игорь Шевченко © (24.12.02 14:47)
> Кстати, CM_MOUSELEAVE в очереди нет и быть не может.
И то правда. VCL делает Perform.
← →
Igorek (2002-12-24 17:01) [7]Понял!
procedure TApplication.HandleMessage;
var
Msg: TMsg;
begin
if not ProcessMessage(Msg) then Idle(Msg);
end;
Т.е. пока в очереди есть сообщения - Idle не вызовется.
Жалко, что Idle в protected секции. Можно было бы напрямую вызвать. Может отнаследоваться от TAplication и повысить видимость? Но как тогда встроить это в проэкт?
Есть ли менее извращенные методы?
← →
Бурундук (2002-12-24 18:56) [8]Насколько я помню, у WM_TIMER самый низкий приоритет
(За исключением, быть может WM_PAINT - не помню).
Поэтому проблема врядли связана с очередью.
← →
Бурундук (2002-12-24 19:01) [9]Я был неправ.
Судя по исходникам, CM_MOUSELEAVE вызывается
только из TApplication.Idle
и, таким образом, имеет наинизший приоритет
← →
Бурундук (2002-12-24 19:18) [10]>Может отнаследоваться от TAplication и повысить видимость?
Есть стандартный трюк для вызова защищённых методов:
THackApplication = class(TApplication);
// экземпляр этого класса не создаётся.
// просто делается приведение
THackApplication(Application).Idle(Msg);
← →
Clickmaker (2002-12-24 19:47) [11]CM_MOUSELEAVE же связан с уходом курсора с контрола. Так может в самом обрабочике таймера проверять координаты и если курсор уже ушел, то тупо говорить Exit?
← →
Igorek (2002-12-24 20:11) [12]
> Бурундук (24.12.02 19:18)
> Есть стандартный трюк для вызова защищённых методов:
>
> THackApplication = class(TApplication);
> // экземпляр этого класса не создаётся.
> // просто делается приведение
>
> THackApplication(Application).Idle(Msg);
Этот трюк у меня вырубил комп нафиг. Только Reset помог. :-(
← →
Igorek (2002-12-24 20:13) [13]
> Clickmaker © (24.12.02 19:47)
> CM_MOUSELEAVE же связан с уходом курсора с контрола. Так
> может в самом обрабочике таймера проверять координаты и
> если курсор уже ушел, то тупо говорить Exit?
Можно конечно, но некрасиво как-то.
Кроме того оно у меня раскидано по разным классам. Обработчик не знает, какой контрол запустил таймер. Но все равно спасибо за вариант.
← →
Woolen (2002-12-25 00:35) [14]To Игорек:
{А как?
Вот я выставил таймер:
SetTimer(FSelfHandle, IfThen(FUpDown, SCROLL_DOWN_ID, SCROLL_UP_ID),
ScrollDelay, nil);
FSelfHandle - дескриптор, предварительно полученный через AllocateHWnd. FSelfHandle := AllocateHWnd(MsgHandler);
MsgHandler - моя процедура обработки сообщений по этому хендлу.
Наверно оно действ. не ставит в очередь, а вызывает процедуру. Но где-то оно скапливается, так как процедура не успевает завершиться до момента следующего вызова. Может при вызове процедуры убивать таймер и выставлять снова? Счас попробую.}
Ни фига, это не вызов процедуры. Адрес процедуры - последний параметр, а он - nil, что по дельфийский, в данном случае, означает нулевой адрес расположения процедуры. Для виндов это значит, что надо посылать сообщения WM_TIMER окну, что система и делает. Сообщения скапливаются в очереди. Я же говорил, о том, чтобы она вызывала процедуру, адрес которой нужнопередать последним параметром.
← →
Igorek (2002-12-25 11:17) [15]The SetTimer function creates a timer with the specified time-out value.
...
Remarks
An application can process WM_TIMER messages by including a WM_TIMER case statement in the window procedure or by specifying a TimerProc callback function when creating the timer. When you specify a TimerProc callback function, the DispatchMessage function simply calls the callback function instead of the window procedure. Therefore, you need to dispatch messages in the calling thread, even when you use TimerProc instead of processing WM_TIMER.
Все равно оно ставит в очередь, а DispatchMessage просто по разному обрабатывает (не оконную процедуру вызывает, а заданую).
← →
Igorek (2002-12-25 12:34) [16]Ладно, обошел проблему по другому. Всем спасибо.
Страницы: 1 вся ветка
Форум: "WinAPI";
Текущий архив: 2003.02.10;
Скачать: [xml.tar.bz2];
Память: 0.49 MB
Время: 0.011 c