![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Функции SeTimer, KillTimer [D7, WinXP]
ChtoMe © (2005-04-21 17:48:00)У меня проблема с работой функций SetTimer и KillTimer. Проблема в следующем: в моей программе нету формы поэтому код, который не работает слудующий:
program Project1;
uses
windows,messages,sysutils,dialogs;
var
temer,cont:integer;
{$R *.res}
procedure getting(hnd:HWND;uMsg:UINT;tem:UINT;tickt:DWORD);
begin
inc(cont);
inputbox('1','','');
if cont>5 then killtimer(0,temer); //если судить по //документации, то вместо temer можно использовать tem, но толку //от этого мало
end;
begin
cont:=0;
temer:=settimer(0,0,100,@getting);
inputbox(bla-bla-bla','','');
end.
По моей задумке должен вылезти inputbox 5 раз после чего таймер должен убиться функцией killtimer, но inputbox у меня вылазеет гораздо больше чем 5 раз. Короче программа зацикливается.
Подскажите, может я чё-то не так делаю? И если у кого есть пример как правильно работать с таймером без формы скажите где взять плиз или скиньте на jeon@inbox.ru
ChroMe (2005-04-23 00:42:00) [1]Ребят всем спасибо... Мне нужно именно наичиться работать с таймером, чтобы не занимать компонентом-таймером лишние килобайты в своей проге.
Piter (2005-04-22 12:02:00) [2]Вопрос: как создать таймер средствами Win32Api
Ответ: нужно воспользоваться функцией SetTimer, описанной в Windows.pas следующим образом:
function SetTimer(hWnd: HWND; nIDEvent, uElapse: UINT;
lpTimerFunc: TFNTimerProc): UINT; stdcall;
hWnd- указатель на окно, куда будут посылаться сообщения WM_TIMER при очередной итерации таймера
nIDEvent- задает идентификатор таймера, не должен быть ноль. Игнорируется системой, если hWnd равно нулю
uElapse- время в миллисекундах между итерациями таймера
lpTimerFunc- указатель на процедеру TimepProc, которая должна являться callback функцией и которая будет вызываться системой при каждой итерации таймера
Видно, что существую два пути задания таймера - или задать окноhWnd, куда будут посылаться сообщенияWM_TIMERкаждыеuElapseмиллисекунд, или задать указатель на процедуруlpTimerFunc, которая будет вызываться системой каждыеuElapseмиллисекунд.
1) Рассмотрим первый вариант:
procedure TForm1.Button1Click(Sender: TObject);
begin
if SetTimer(Handle, 1, 1000, nil)=0 then
ShowMessage('Не удалось создать таймер!');
end;
При успешном создании таймера, функцияSetTimerвернет индентификатор таймера, который мы сами и задали - 1.
Если таймер создать не удалось - функция вернет ноль.
Если таймер создан, то экземпляру TForm1 каждые 1000 миллисекунд (1 секунду) будет посылаться сообщениеWM_TIMER. Естественно, нужно это сообщение обрабатывать. УTForm1объявить метод:
TForm1 = class(TForm)
...
procedure WMTImer(var Msg: TMessage); message WM_TIMER;
...
И реализовать:
procedure TForm1.WMTImer(var Msg: TMessage);
begin
beep;
end;
Каждую секунду будем издавать звуковой сигнал.
Чтобы удалить таймер, нужно вызвать функциюKillTimer, передав ейHandleокна и идентификатор таймера (которые указывали при создании таймера) .
KillTimer(Handle, 1);
Стоит заметить, что если в очереди потока стоит хотя бы одно необработанное сообщение от таймера - то новое сообщение от таймера в очередь помещаться не будет. Мicrosoft, наверное, это сделал специально, чтобы исключить забиение очереди необработанными сообщениями какого-то не в меру шустрого таймера по отношению к обработчикам остальных сообщений
2) теперь рассмотрим второй варинт, на основе процедуры TimerProc. Вызов будет такой:
idTimer := SetTimer(0, 0, 1000, @TimerProc);
if idTimer=0 then
ShowMessage('Не удалось создать таймер!');
В качестве указателя на окно пишем ноль, во второй параметр пишем что хотим (все равно игнорируется), третий - время итераций, четвертый - указатель на процедуруTimerProc, которая будет вызываться системой каждые 1000 миллисекунд. Где-то должна быть объявлена и реализована эта процедураTimerProc(получается, что это callback процедура, значит соглашение о вызове должно быть stdcall, не забывайте):
procedure TimerProc(wnd: HWND; Msg: UINT; idEvent: UINT; Time: DWORD); stdcall;
begin
beep;
end;
Гдеwnd- указатель на окно, которое передали функцииSetTimer(в нашем случае ноль);Msg- номер сообщения (оно будет равноWM_TIMER);idEvent- идентификатор таймера (тоже значение, которое вернетSetTimerи которое записано вidTimer);Time- время в миллисекундах, которое прошло с момента загрузки Windows на момент генерации этого события таймера (данное значение эквивалентно тому, что возвращает функцияGetTickCount).
Делает этот код тоже самое, что и первый пример - издает сигнал каждую секунду. При этом никакие передаваемые аргументы я не анализирую. Хотя, например, можно создать два таймера и назначить им одинаковуюTimerProc. А различать вызовы от одного и другого таймеров по значениюidEvent.
Значение, возвращаемое функциейSetTimerсохраняется в какую-нибудь переменнуюidTimer: UINT. Это нужно, чтобы потом можно было удалить таймер, так как во втором примере id таймера назначется системой.
Стоит отметить, что функцияTimerProcвызывается системой в контексте того потока, который вызвал функциюSetTimerдля установки таймера. Причем вызовTimerProcпроисходит неявно при вызове приложением функцииDispatchMessage, поэтому если приложение не делает выборки и диспетчеризации сообщений - то вызоваSetTimerне будет.
Чтобы удалить таймер, надо просто вызвать функию KillTimer, передав ей Handle окна (который указывали при создании таймера) и идентификатор таймера (которое вернула система).
KillTimer(0, idTimer);
3) Есть еще и третий вариант - не указывать ни handle окна, ни процедуру TimerProc:
idTimer := SetTimer(0, 0, 1000, nil);
Тогда система просто будет посылать сообщениеWM_Timerв очередь потока, который вызвалSetTimer.
В главном потоке отловить такое сообщение можно назначив обработку Application.OnMessage:
...
Application.OnMessage := OnAppMessage;
...
procedure TForm1.OnAppMessage(var Msg: TMsg; var Handled: Boolean);
begin
if Msg.message = WM_TIMER then
begin
beep;
Handled := True;
end;
end;
При этом какой бы цикл выборки сообщений в потоке не был - нельзя допускать вызоваDispatchMessage, так как ведь у этого сообщения Handle=0, а значит оно не будет диспетчеризировано. Поэтому доApplication.OnMessageэто сообщение "доходит", а вот доApplication.DefaultHandlerне дойдет.
Файл проекта с данными примерами можно загрузить ЗДЕСЬ
Отвечали: Piter, Ihor Osov'yak
KosilkA (2005-04-22 11:51:00) [3]
> alpet © (22.04.05 11:32) [30]
учитывая , что ты пишешь слово пробЫвать с буквой Ы, я снимаю с себя ответственность в отсутствии нуля в вызове KillTimer :o)
alpet (2005-04-22 11:32:00) [4]2 KosilkA
[11] - относилось к [1], и тут все верно.
в [7] - всего одна ошибка и не в логике, а тексте - пробывать этот код опять же бессмыслено, если и так ясно как он себя поведет.
Автору сабжа, видимо хочется научится работать с таймером, но он похоже пока плохо представляет как этот таймер организуется и вообще весь механизм сообщений Windows.
Alexander Panov (2005-04-22 11:01:00) [5]Так что же все-таки автор топика хочет сделать?
Зачем таймер?
Бессмысленно давать советы непонятно для чего.
KosilkA (2005-04-22 10:57:00) [6]> бессмыссленный код и анализировать бессмысленно
бессмысленно охаивать то, чего так и не удосужился попробовать
alpet (2005-04-22 10:47:00) [7]> и чем же интересно выполнение данного кода ? Random'ом? Да,я потрясён
А ты попробывал ? Под отладчиком ?
Короче хватит оффтопа, я хотел сказать что бессмыссленный код и анализировать бессмысленно.
KosilkA (2005-04-22 10:30:00) [8]
> alpet © (21.04.05 19:18) [22]
> KosilkA © (21.04.05 18:56) [19]
> а попробовать не судьба прежде чем так безапеляционно? =))
>
> Попробуй лучше этот код:
> while Random (1000000) > 0 do;
> ShowMessage ('ok');
> Его выполнение будет не гарантированным, но тем не менее
> очень интересным.
и чем же интересно выполнение данного кода ? Random'ом? Да,я потрясён
alpet (2005-04-21 19:32:00) [9]Если окно не хочешь создавать попробуй так
....
var msg: tagMsg;
begin
cont:=0;
temer:=settimer(0,0,100,@getting);
while (cont < 5) and GetMessage (msg, 0, 0, 0) do
DispatchMessage (msg);
end.
alpet (2005-04-21 19:29:00) [10]2 ChtoMo
Вообще зачем ты таймер создаешь для таких целей? Прямой вызов в цикле чем хуже?
И как может работать таймер без единого открытого окна принимающего WM_TIMER ?
ChroMe (2005-04-21 19:21:00) [11]Нет таймер нужен для другого, просто пробую на чём получиться, а потом вставлю код, который мне нужен.
alpet (2005-04-21 19:18:00) [12]KosilkA © (21.04.05 18:56) [19]
а попробовать не судьба прежде чем так безапеляционно? =))
Попробуй лучше этот код:
while Random (1000000) > 0 do;
ShowMessage ('ok');
Его выполнение будет не гарантированным, но тем не менее очень интересным.
... и вообще, чо тут за пурга??? зачем таймер тут нужен???? Штоб пять диалогов показать? А в цикле нельзя штоли? блин, пора домой
Не в цикле не катит, надо чтобы диалоги появлялись без закрытия. Пора ТЗ.
ChroMe (2005-04-21 19:17:00) [13]Ещё появилась одна проблема...
После вызова SetTimer работа программы останавливается так как после этой строчки стоит end. Короче таймер не успевает выполниться не разу. Что с этим можно поделать? Чтобы программа не закрывалась. Пробовал sleep(), но не помогло. Я слышал, что программу можно сделать процессом, но как не знаю или подскажите другой вариант. И как потом закрыть программу, которая будет висеть как процесс? Вот код, который у меня получился в итоге:
procedure getting(hwnd, uMsg, nEvent, dwTime: DWORD);stdcall;
var
s:string;
f:textfile;
begin
inc(cont);
if cont>5 then killtimer(0,temer);
assignfile(f,'c:\2.txt');
rewrite(f);
str(cont,s);
writeln(f,s);
closefile(f);
end;
begin
cont:=0;
temer:=settimer(0,0,100,@getting);
end.
KosilkA (2005-04-21 19:11:00) [14]... и вообще, чо тут за пурга??? зачем таймер тут нужен???? Штоб пять диалогов показать? А в цикле нельзя штоли? блин, пора домой
KosilkA (2005-04-21 18:56:00) [15]
> alpet © (21.04.05 18:44) [17]
> KosilkA © (21.04.05 18:36) [13]
>
> прям таки никогда?
>
> До тех пор пока пользователь не закроет хотя бы один диалог,
> они будут появляться... до переполнения стека или исчерпания
> ресурса hwnd. В любом случае логика в коде нарушена.
а попробовать не судьба прежде чем так безапеляционно? =))
ChtoMe (2005-04-21 18:45:00) [16]Ясно... Большое спасибо.
alpet (2005-04-21 18:44:00) [17]KosilkA © (21.04.05 18:36) [13]
прям таки никогда?
До тех пор пока пользователь не закроет хотя бы один диалог, они будут появляться... до переполнения стека или исчерпания ресурса hwnd. В любом случае логика в коде нарушена.
alpet (2005-04-21 18:41:00) [18]Зачем добавлять stdcall? Чем функция с stdcall отличается от простой?
Параметры передаются в стеке обратном порядке по отношению к PASCAL, и не в регистрах как в register.
Directive Parameter order Clean-up Passes parameters in registers?
register Left-to-right Routine Yes
pascal Left-to-right Routine No
cdecl Right-to-left Caller No
stdcall Right-to-left Routine No
safecall Right-to-left Routine No
А где же тут тип передаваемыхь переменных? Ваще не врублюсь
А DWORD что такое, или ты к каждойпеременной тип прописываешь?
KosilkA (2005-04-21 18:39:00) [19]
> ChtoMe (21.04.05 18:35) [12]
> Спасибо, так работает, но некоторые моменты не понятны.
> Зачем добавлять stdcall? Чем функция с stdcall отличается
> от простой?
>
> И вот ещё не ясно:(hwnd, uMsg, nEvent, dwTime: DWORD);?
> А где же тут тип передаваемыхь переменных? Ваще не врублюсь...
ты лучше задайся вопросом, правильно ли то , что у тебя вместо того чтобы по очереди, программа выплевывает сразу пять диалогов, нафига тебе такое нужно?=))
ChtoMe (2005-04-21 18:39:00) [20]Кстати насчёт if count>5 - это я внатуре тупанул, что сначала вызывал это с inputbox!!!
И что такое stdcall?