Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

SetWaitableTimer problem

478 views
Skip to first unread message

James Rutland

unread,
Dec 13, 2005, 9:26:41 AM12/13/05
to
Hi,

does anyone have any idea how to use the callback function in the following
winapi call:

function SetWaitableTimer(hTimer,lpDueTime, lPeriod, pfnCompletionRoutine,
lpArgToCompletionRoutine, fResume)

I can get the timer triggering ok, but if i try and set up a procedure for
pfnCompletionRoutine (so i can tell delphi to do some useful stuff once the
timer has been reached) nothing happens..
pfnCompletionRoutine is of type TFNTimerAPCRoutine but there is no
documentation anywhere on how to use it with delphi (all the examples use a
nil value)

probably missing something really obvious as delving into the winapi is
definitely not one of my strong points!

cheers

James


Martin James

unread,
Dec 13, 2005, 10:52:52 AM12/13/05
to

"James Rutland" <laneseng...@tiscali.co.uk> wrote in message
news:439eda1e$1...@newsgroups.borland.com...

> Hi,
>
> does anyone have any idea how to use the callback function in the
following
> winapi call:
>
> function SetWaitableTimer(hTimer,lpDueTime, lPeriod, pfnCompletionRoutine,
> lpArgToCompletionRoutine, fResume)
>
> I can get the timer triggering ok, but if i try and set up a procedure for
> pfnCompletionRoutine (so i can tell delphi to do some useful stuff once
the
> timer has been reached) nothing happens..

'nothing happens' - this is not very clear. Does your completion routine
get called? If not, how do you know that the timer is being triggered?
Could you post some code eg completion routine, SWT call.

The extra 'lpArgToCompletionRoutine' parameter that is passed around should
allow you to pass an object and then call a method of it in your completion
routine.

I suspect, though it does not explicitly say, that the thread that wants to
execute the callback must wait in an alertable state.

Rgds,
Martin

James Rutland

unread,
Dec 13, 2005, 12:28:02 PM12/13/05
to
Hi,

I've kindof answered my own question (but not entirely).. and Yes you are
absolutely correct about the alertable state .. the Timer is/was definitely
triggering as it is a routine to return the computer outof standby and then
perform an action. if the computer computer is already on, it will call the
procedure and then do nothing (other than create a new trigger point, and
then wait for the next trigger). If the computer is in standby it will bring
it out of standby, perform the operation, and then if there is no user
interaction, place itself in standby again. (this should be automatic
according to the scant documentation that i can find regarding delphi and
the APC stack)

the procedure for the time being is just showmessage('here'); so i can see
if it is being called.

the timer is firing, as the computer comes out of standby at the requested
time but does not call the procedure..

however more googling eventually kind of gave me an answer, in that I need
to put the timer on alertable status (shame i didn't check back on the news
group sooner as this is what you suggested.. )

I've done this by using sleepex(interval,TRUE) which now works SOME of the
time - which again is down to lack of knowledge rather than any problem with
the api call..

current code is this - which i gladly post as I can see this being useful
for a few people - anyone who wants to create a scheduler, or to emulate
certain functions of xpMCE.. it's not very tidy code at the moment as it was
6am this morning when i was doing it after a LONG night! so would welcome
anyone tidying it up or making it more efficient up for me :-)


procedure WaitTill(ADateTime: TDateTime);
var
WT: THandle;
WakeUpTime: LARGE_INTEGER;
SysTime : _SystemTime;
FTime : _FileTime;
begin
DateTimeToSystemTime(ADateTime, SysTime);
SystemTimeToFileTime(SysTime, FTime);
LocalFileTimeToFileTime(FTime, FTime);
WakeUpTime.LowPart:=FTime.dwLowDateTime;
WakeUpTime.HighPart:=FTime.dwHighDateTime;
WT := CreateWaitableTimer(nil, TRUE,'MatrixTimer');
CancelWaitableTimer(WT); // this was a bit random .. would have thought
it should go after the sleepEX
SetWaitableTimer(WT,WakeUpTime.quadpart,1000,@TimerAPCProc,nil,true);
SleepEx(50000 ,TRUE); // tried using INFINITE but this slowed the system
down (thought the opposite would happen!)
CloseHandle(WT);
end;

procedure
TimerAPCProc(lpArgToCompletionRoutine:Dword;dwTimerLowValue,dwTimerHighValue:DWORD
); stdcall;
begin
showmessage('here');
end;

cheers
James

"Martin James" <mjames...@dial.pipex.com> wrote in message
news:<439eed9c$1...@newsgroups.borland.com>...

James Rutland

unread,
Dec 14, 2005, 7:55:27 AM12/14/05
to
Another update to my problem..

would still appreciate a way of calling the completion procedure properly,
however for anyone who wants it here is another angle to a solution..

This works pefectly if you start a new application and add the code..
however, there is a problem with it somewhere, as on my main application
(300,000 lines of code) if the app is minimized to the system tray it still
works fine, however if the mainform is active (goes from system tray idle
state to covering the whole screen) I get the "Canvas Does Not Allow
Drawing" message and Delphi goes into a tail spin.. I have read elsewhere
that this could be due to the VCL not being thread safe and to try and use
Canvas.Lock on anything that is redrawing itself.. so far this hasn't
helped, so will have to do more investigation.. doh!

procedure WaitTill(ADateTime: TDateTime);
var
WakeUpTme: LARGE_INTEGER;


SysTime : _SystemTime;
FTime : _FileTime;
begin
DateTimeToSystemTime(ADateTime, SysTime);
SystemTimeToFileTime(SysTime, FTime);
LocalFileTimeToFileTime(FTime, FTime);

WakeUpTme.LowPart:=FTime.dwLowDateTime;
WakeUpTme.HighPart:=FTime.dwHighDateTime;
WT := CreateWaitableTimer(nil,TRUE,'MatrixTimer');
CancelWaitableTimer(WT);
SetWaitableTimer(WT,WakeUpTme.quadpart,1000,@TimerAPCProc,nil,true);
hThread:=CreateThread(nil, 0, @ThdProc, nil, CREATE_SUSPENDED, hThdID);
if hThread > 0 then
ResumeThread(hThread);
end;

var
hThread: THandle;
hThdID: Cardinal;
WT: THandle;
procedure ThdProc;
begin
WaitForSingleObjectEx(WT,INFINITE,TRUE);
showmessage('Timer Triggered!');
TerminateThread(hThread, 0);
CloseHandle(WT);
end;

Cheers
James

"James Rutland" <laneseng...@tiscali.co.uk> wrote in message

news:439f...@newsgroups.borland.com...

David J Taylor

unread,
Dec 14, 2005, 8:20:16 AM12/14/05
to
James Rutland wrote:
[]

> the "Canvas Does Not Allow Drawing" message

Can be due to trying to do things too early in the start-up of an
application. Check the forms' create methods.

David


James Rutland

unread,
Dec 14, 2005, 8:55:07 AM12/14/05
to
Phew.. disabled great swathes of my code before i had a brainwave.. the vcl
isn't threadsafe and what was i doing when the timer triggered? calling a
delphi messagebox.. solution call a native windows one.. problem solved!

I am still not entirely happy with this solution as it seems a bit "messy"
and still think a proper call to the completion routine would be the best
way of doing things (so if anyone can work that one out, please let me
know!).. as if the completion routine is called the APC would take care of
resuming the computer to standby - but luckily this is quite easy
programitcally.

Also if you tried compiling the code I posted last time and it did'nt work
it's because the thread variable (WT) should be declared before the main
WaitTill Procedure (sorry, just cut and pasted the code in the wrong order),
so anyway here is the solution I will be using until someone can think of a
way to call the completion Routine -

var
hThread: THandle;
hThdID: Cardinal;
WT: THandle;

procedure ThdProc;
begin
WaitForSingleObjectEx(WT,INFINITE,TRUE);
MessageBoxEx(0,'Timer Triggered.','Hooray!',MB_OK or MB_ICONQUESTION,0);
TerminateThread(hThread, 0);
CloseHandle(WT);
end;

procedure WaitTill(ADateTime: TDateTime);
var
WakeUpTme: LARGE_INTEGER;
SysTime : _SystemTime;
FTime : _FileTime;
begin
DateTimeToSystemTime(ADateTime, SysTime);
SystemTimeToFileTime(SysTime, FTime);
LocalFileTimeToFileTime(FTime, FTime);
WakeUpTme.LowPart:=FTime.dwLowDateTime;
WakeUpTme.HighPart:=FTime.dwHighDateTime;

WT := CreateWaitableTimer(nil,TRUE,'MatrixTimer'); // change the
'MatrixTimer' to something unique...


CancelWaitableTimer(WT);
SetWaitableTimer(WT,WakeUpTme.quadpart,1000,@TimerAPCProc,nil,true);
hThread:=CreateThread(nil, 0, @ThdProc, nil, CREATE_SUSPENDED, hThdID);
if hThread > 0 then
ResumeThread(hThread);
end;

This is one handy code snippet, with a multitude of applications.. feel free
to use it
Cheers,
James

"David J Taylor" <david...@writeme.com.not-this-bit> wrote in message
news:43a0...@newsgroups.borland.com...

nede...@gmail.com

unread,
May 28, 2019, 3:15:58 AM5/28/19
to
Thanks James, this example was very helpfull!! (old thread I know)
0 new messages