Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Функции SeTimer, KillTimer [D7, WinXP]


Страницы:   1 2

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?


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо