Форум: "Начинающим";
Текущий архив: 2006.03.12;
Скачать: [xml.tar.bz2];
ВнизTimerProc дважды Найти похожие ветки
← →
Дубинка (2006-02-24 15:18) [0]Глупый наверное вопрос. Я создаю таймер:
id := SetTimer(0, 0, 1000, @TimerProc);
Однако эта процедура:
procedure TimerProc(wnd: HWND; uMsg: Integer; idEvent: Integer; dwTime: DWORD);
begin
{.....}
end;
вызывается через каждую секунду два раза подряд. причем в первом случае
idEvent = 0, а во втором wnd = 0, мне интересно, как от этого избавиться...
Вроде бы надо Dispatch"ить сообщение, только не знаю как это сделать.
← →
umbra © (2006-02-24 15:24) [1]при вызове
SetTimer
в первом параметре стоит указывать окно, к которому таймер относитсяid := SetTimer(hwnd, 0, 1000, @TimerProc);
← →
Дубинка (2006-02-24 15:27) [2]бывает так что нет окна. К примеру, если функция вызывается из dll смысл в создании окла не то что нулевой, а вообще отрицательный. Да и в справке указано:
hWnd [in] Handle to the window to be associated with the timer. This window must be owned by the calling thread. If this parameter is NULL, no window is associated with the timer and the nIDEvent parameter is ignored.
То бишь может быть нулем.
← →
umbra © (2006-02-24 15:50) [3]так не надо ничего создавать. Из ДЛЛ функцию вызывает какое-то приложение, у которого есть окно. Вот его hwnd и надо передавать в SetTimer. Процесс диспетчеризации сообщения WM_TIMER и заключается в том, что указывается хендл окна, которому это сообщение нужно.
← →
Дубинка (2006-02-24 17:04) [4]ладно, указал я хэндл какого-то окна, все равно два раза вызывается.
← →
begin...end © (2006-02-24 17:06) [5]> Дубинка (24.02.06 15:18)
> вызывается через каждую секунду два раза подряд.
Полный код можно увидеть?
> в первом случае
> idEvent = 0, а во втором wnd = 0
На параметры можно пока не обращать внимание, т.к. у TimerProc не указано нужное соглашение о вызове (stdcall). Исправьте.
> Вроде бы надо Dispatch"ить сообщение, только не знаю как
> это сделать.
А в каком потоке таймер устанавливается? В главном потоке-то цикл выборки сообщений обычно уже есть :)
Вообще, если таймер создаётся без указания окна, то при "срабатывании" таймера происходит следующее:
1. В очередь потока, создавшего таймер, ставится сообщение WM_TIMER.
2. Если в потоке работает цикл выборки сообщений (GetMessage), и в этом цикле вызывается DispatchMessage, это сообщение выбирается из очереди и передаётся в DefWindowProc.
3. DefWindowProc вызывает указанную при создании таймера callback-функцию.
Т.е. разница между созданием таймера с указанием окна и без оного -- в том, что в первом случае WM_TIMER будет приходить в оконную процедуру указанного окна, а во втором случае -- в дефолтную оконную процедуру. И в обоих случаях, конечно, нужен цикл диспетчеризации сообщений.
← →
Дубинка (2006-02-24 17:23) [6]На параметры можно пока не обращать внимание, т.к. у TimerProc не указано нужное соглашение о вызове (stdcall). Исправьте.
Точно! А я даже и не обратил внимания. Не будете любезны объяснить мне? Я кажется отдаленно понимаю. Вроде по умолчанию стоит register, и параметры передаются слева направо, а должны наоборот. Только почему из-за этого функция дважды вызывается?
← →
Дубинка (2006-02-24 17:25) [7]Если оставить значимую часть в коде, остается это:
library gccore;
uses SysUtils, Windows;
var i: Integer;
procedure TimerProc(wnd: HWND; uMsg, idEvent: Integer; dwTime: DWORD);stdcall
begin
Inc(i);
TextOut(GetDC(0), 0,0, Pchar(inttostr(i)), 3);
end;
begin
SetTimer(0, 0, 1000, @TimerProc);
end.
И где здесь должен быть цикл обработки сообщений?
← →
umbra © (2006-02-24 17:51) [8]
> И где здесь должен быть цикл обработки сообщений?
>
в приведенном Вами коде - нигде. библиотека не работает сама по себе. ее использует какое-то приложение.
← →
begin...end © (2006-02-24 17:59) [9]> Дубинка (24.02.06 17:23) [6]
Да, по умолчанию стоит register. Это означает, что перед вызовом подпрограммы первые три параметра передаются через регистры процессора, а оставшиеся -- через стек (причём вначале в стек помещается самый "левый" из оставшихся параметров, затем -- следующий, и т.д.). Это соглашение означает также и то, что подпрограмма сама должна удалять из стека переданные ей параметры.
А если указано stdcall, то ВСЕ параметры передаются через стек (причём справа налево: сначала последний, потом предпоследий, и т.д.). И в этом случае тоже за очистку стека отвечает сама подпрограмма.
Система, рассчитывая на то, что функция объявлена с соглашением stdcall, перед вызовом TimerProc передаёт все четыре параметра через стек, и надеется, что он будет правильно очищен вызываемой процедурой. Но TimerProc была объявлена с соглашением register, и она удалит из стека только один параметр, т.к. её код сгенерирован в расчёте на то, что первые три параметра ей переданы через регистры. В результате стек балансируется неправильно, а это может повлечь за собой самые разнообразные чудеса.
> Дубинка (24.02.06 17:25) [7]
Здесь -- не нужен. Цикл уже есть (в том потоке, в контексте которого вызывается SetTimer), иначе вызова TimerProc не происходило бы.
← →
umbra © (2006-02-24 18:41) [10]по логике, если
TimerProc
вызывается дважды, значит на сообщениеWM_TIMER
реагируют дважды. Один раз реагирует сам самаTimerProc
, а второй - какая-то оконная процедура.
← →
begin...end © (2006-02-24 20:23) [11]Сорри, насчёт DefWindowProc в [5] -- эт я фигню какую-то сморозил. Меня чего-то MSDN с толку сбила: "When you specify a TimerProc callback function, the default window procedure calls the callback function when it processes WM_TIMER". DefWindowProc, вроде, ни при чём -- callback вызывает сама DispatchMessage.
Уж простите дурака :)
← →
Дубинка (2006-02-24 20:27) [12]Все. Мне все понятно. Благодарю!
Страницы: 1 вся ветка
Форум: "Начинающим";
Текущий архив: 2006.03.12;
Скачать: [xml.tar.bz2];
Память: 0.48 MB
Время: 0.013 c