Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > будильник на API


Автор: Dmitry_177 20.10.2006, 11:27
Хочу сделать будильник на API, используя функции CreateWaitableTimer и SystemTimeToFileTime. Т.к. в них нельзя задать определенное время, например в 18:00 чтобы прозвенел, нужно при установке таймера посчитать разницу между 18:00 и системным временем, и поставить этот интервал, чтобы через такой момент времени сработал будильник... Помогите пожалуйста с реализацией

Код

const
 HOUR = 18;

var
 hTimer: THandle;
 sysTime: TSystemTime;
...

hTimer := CreateWaitableTimer(nil, false, nil);
GetLocalTime(sysTime);   // получили системное время
HOUR - sysTime.wHour;   // как получить разницу? Подскажите пожалуйста...

SetWaitableTimer(hTimer, ..., 0, nil, nil, false);

Автор: Romikgy 20.10.2006, 13:38
Цитата(Dmitry_177 @  20.10.2006,  10:27 Найти цитируемый пост)
них нельзя задать определенное время

почему?
Код

BOOL SetWaitableTimer(

    HANDLE hTimer,    // handle to a timer object
    const LARGE_INTEGER *pDueTime,    // when timer will become signaled
    LONG lPeriod,    // periodic timer interval
    PTIMERAPCROUTINE pfnCompletionRoutine,    // pointer to the completion routine
    LPVOID lpArgToCompletionRoutine,    // data passed to the completion routine
    BOOL fResume    // flag for resume state
   );    
 

Parameters

hTimer

Identifies the timer object. The CreateWaitableTimer or OpenWaitableTimer function returns this handle.

pDueTime

Specifies when the state of the timer is to be set to signaled, in 100 nanosecond intervals. 
Use the format described by the FILETIME structure.[U] Positive values indicate absolute time. 
Negative values indicate relative time. [/U]The actual timer accuracy depends on the capability of 
your hardware.


Автор: Snowy 20.10.2006, 13:53
Цитата(Dmitry_177 @  20.10.2006,  11:27 Найти цитируемый пост)
Т.к. в них нельзя задать определенное время, например в 18:00
Не "нельзя", а нужно!
Там задаётся время срабатывания, а период - для повтора после этого времени.

Автор: Dmitry_177 20.10.2006, 15:10
а вот так нельзя сделать?
Код

var
 hTimer: THandle;
 sysTime: TSystemTime;
begin
hTimer := CreateWaitableTimer(nil, false, nil);
sysTime.wHour := 18;
sysTime.wMinute := 0;
sysTime.wSecond := 0;
SystemTimeToFileTime(sysTime, fTime);
SetWaitableTimer(hTimer, fTime, 0, nil, nil, false);
end;


только если честно не очень понятно как он должен сработать? толи callback функция должна быть, толи поток создать толи еще что?

Автор: Romikgy 20.10.2006, 16:07
Цитата(Romikgy @  20.10.2006,  12:38 Найти цитируемый пост)
Positive values indicate absolute time. 
Negative values indicate relative time.

имхо если положитнльно значит время абсалютно , если открицательно значит относительно
Цитата(Dmitry_177 @  20.10.2006,  14:10 Найти цитируемый пост)
толи callback функция должна быть

и имхо надо задать не только час срабатывания но и день месяц и т.д.

Автор: Dmitry_177 20.10.2006, 23:21
А как мне всетаки узнать в какой момент это сработает?

Цитата(Dmitry_177 @  20.10.2006,  15:10 Найти цитируемый пост)
только если честно не очень понятно как он должен сработать? толи callback функция должна быть, толи поток создать толи еще что?

Автор: maxim1000 20.10.2006, 23:55
Цитата(Dmitry_177 @  20.10.2006,  14:10 Найти цитируемый пост)
только если честно не очень понятно как он должен сработать? толи callback функция должна быть, толи поток создать толи еще что?

т.к. таймер waitable (т.е. ожидаемый), то его надо ожидать smile
советую посмотреть помощь по WaitForSingleObject и её родственникам

Автор: Snowy 21.10.2006, 11:58
Книжку Рихтера в руки 2 раза в день и все вопросы сами отвалятся.
Там расписано всё и очень подробно.

Автор: Dmitry_177 21.10.2006, 16:01
Посмотрите пожалуйста код, правильно ли он реализован? Вроде все работает, но правильно ли я закрываю хендл hTimer, а когда закрывать хендл hThread не знаю... Подскажите пожалуйста...

Код

var
 hTimer: THandle;
 hThread: THandle;
 sysTime: TSystemTime;
 fTime: TFileTime;
 idThread: DWORD;

procedure WaitThread; stdcall;
begin
 WaitForSingleObject(hTimer, INFINITE);
 ShowMessage('Time!!!!!!!');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 hTimer := CreateWaitableTimer(nil, false, nil);
   try
     GetLocalTime(sysTime);
     sysTime.wHour := 18;
     sysTime.wMinute := 0;
     sysTime.wSecond := 0;
     SystemTimeToFileTime(sysTime, fTime);
     LocalFileTimeToFileTime(fTime, fTime);
     SetWaitableTimer(hTimer, TLargeInteger(fTime), 0, nil, nil, false);

     hThread := CreateThread(nil, 0, @WaitThread, nil, 0, idThread);

     ShowMessage('Time installed!');
   finally
     CloseHandle(hTimer);
   end;
end;

Автор: maxim1000 21.10.2006, 17:02
когда основной поток доходит до CloseHandle, ожидающий поток всё ещё работает (точнее, ожидает)
HANDLE закрывать надо когда его уже никто не использует
в разных случаях могут получаться довольно сложные ситуации, но в данном всё просто: достаточно закрыть его в конце WaitThread

Автор: Dmitry_177 22.10.2006, 04:11
А вот так правильно будет? оба хендла (hTimer и hThread) закрываю в функции WaitThread после WaitForSingleObject. А если выключить программу (нажав на крестик) до срабатывания таймера, удалятся эти хендлы или их лучше в событии разрушения формы их удалять?

Код

var
 hTimer: THandle;
 hThread: THandle;
 idThread: DWORD;

function WaitThread(Param: Pointer): DWORD; stdcall;
begin
 WaitForSingleObject(hTimer, INFINITE);
 CloseHandle(hTimer);    -> Правильно ли тут будет удалять хендл hTimer?
 CloseHandle(hThread);  -> Правильно ли тут будет удалять хендл hThread?
 MessageBox(0, 'Time!!!!!!!', 'Time', MB_OK);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 sysTime: TSystemTime;
 fTime: TFileTime;
begin
 hTimer := CreateWaitableTimer(nil, false, nil);
 GetLocalTime(sysTime);
 sysTime.wHour := 18;
 sysTime.wMinute := 0;
 sysTime.wSecond := 0;
 SystemTimeToFileTime(sysTime, fTime);
 LocalFileTimeToFileTime(fTime, fTime);
 SetWaitableTimer(hTimer, TLargeInteger(fTime), 0, nil, nil, false);

 hThread := CreateThread(nil, 0, @WaitThread, nil, 0, idThread);

 MessageBox(0, 'Time installed!', 'Time', MB_OK);
end;

Автор: maxim1000 22.10.2006, 10:52
Цитата(MSDN)
The thread object remains in the system until the thread has terminated and all handles to it have been closed through a call to CloseHandle.

т.е. (ИМХО) дескриптор потока можно закрывать сразу после CreateThread, если он не нужен
да и вообще, дескриптор нигде больше не будет использоваться, его закрывают
в случае таких объектов, как таймер, событие, к которым доступ производится только по дескрипторам, после удаления всех дескрипторов они удаляются
а поток имеет ещё одного "пользователя" - собственно, сам поток, который выполняется
поэтому даже если все дескрипторы позакрывать, он будет выполняться, пока не закончится его основная функция

Автор: Dmitry_177 22.10.2006, 17:16
Спасибо, вы очень добры.. Если будет такая ситуация, ставим мы будильник на 18:00, а время будет уже больше 18:00, часов (19, 20, 21, 22, 23 например).. так вот день в sysTime, будет сегодняшний, а сработать таймер должен завтра, т.к. сегодня 18 часов уже было... Вот как прибавить к дате один день, с учетом того, что если день будет последним в текущем месяце, то 1-ый день будет следующего месяца, так же и с годом, если последний день года, т.е. 31 декабря...

Автор: Romikgy 22.10.2006, 17:32
Цитата(Dmitry_177 @  22.10.2006,  16:16 Найти цитируемый пост)
ставим мы будильник на 18:00, а время будет уже больше 18:00, часов (19, 20, 21, 22, 23 например).

для работы переодически есть параметр
Код

lPeriod
Specifies the period of the timer, in milliseconds. If lPeriod is zero, the timer is signaled once. 
If lPeriod is greater than zero, the timer is periodic. A periodic timer automatically reactivates 
each time the period elapses, until the timer is canceled using the CancelWaitableTimer 
function or reset using SetWaitableTimer. If lPeriod is less than zero, the function fails.


Цитата(Dmitry_177 @  22.10.2006,  16:16 Найти цитируемый пост)
Вот как прибавить к дате один день,

там время представленно в  in 100 nanosecond intervals т.е. добавь кол-во интервалов в сутках к дате сейчас и получишь слудующий день

Автор: Dmitry_177 22.10.2006, 17:55
А так правильно будет?

Код

GetLocalTime(sysTime);
if sysTime.wHour > 18 then
 sysTime.wDay := sysTime.wDay + 1;
sysTime.wHour := 18;
sysTime.wMinute := 0;
sysTime.wSecond := 0;

Автор: Romikgy 22.10.2006, 18:47
имхо не совсем, корректнее будет так 
Код

var
  sysTime: SYSTEMTIME;
  ft2: FILETIME;
begin
  { TODO -oUser -cConsole Main : Insert code here }
  GetLocalTime(sysTime);
  if sysTime.wHour >= 18 then
begin
 //sysTime.wDay := sysTime.wDay + 1;
 SystemTimeToFileTime(sysTime,ft2);
 ft2.dwHighDateTime:=ft2.dwHighDateTime+201;
 ft2.dwLowDateTime:=ft2.dwLowDateTime+ 711573504;
 FileTimeToSystemTime(ft2,sysTime);
end;
sysTime.wHour := 18;
sysTime.wMinute := 0;
sysTime.wSecond := 0;
end.


Автор: Dmitry_177 23.10.2006, 00:16
Работает ))) только если честно не пойму как это работает...(((

Автор: Romikgy 23.10.2006, 08:59
Цитата(Romikgy @  22.10.2006,  17:47 Найти цитируемый пост)
FILETIME

этот тип представляет собой 8 байтное целое число , в котором представленна дата в 0,1 мкс интервалах ,
т.е. 1 сутки это 24часа*60мин*60сек*1000мс*1000мкс*10( 10 это потому что 0,1)  
для перевода лучше юзать типа юнионов в си

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