
Introduction
Not long time ago, I was programming an application for Windows that every
certain time had to execute a task; in the development of the project everything
went with normality but arrived the day to prove the program in the definitive
machines where it had to remain working, and we observed that once in a while
timers stopped working. We did thousand verifications always reaching the same
result, sometimes, timers provided by the Framework don't tick, no events fired.
It was then when we began to look for a solution to replace these timers and the
solution that seemed better to me was the one to replace them by timers of the
API of Windows. We looked on the Internet in case somebody had had the same idea
that I had, without success, and therefore put hands to the work.
Background
The Win32 API has some system calls to make it possible, the first is
SetTimer
, used to init a timer and the second is
KillTimer
, used to kill or stop a timer. Here is a description of
these system calls.
SetTimer
The SetTimer
function creates a timer with the specified
time-out value.
UINT_PTR SetTimer(
HWND hWnd,
UINT_PTR nIDEvent,
UINT uElapse,
TIMERPROC lpTimerFunc
);
Parameters
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.
nIDEvent
[in] Specifies a nonzero timer identifier. If the hWnd
parameter
is NULL
, this parameter is ignored.
If the hWnd
parameter is not NULL
and the window
specified by hWnd
already has a timer with the value
nIDEvent
, then the existing timer is replaced by the new timer.
When SetTimer
replaces a timer, the timer is reset. Therefore, a
message will be sent after the current time-out value elapses, but the
previously set time-out value is ignored.
uElapse
[in] Specifies the time-out value, in milliseconds.
lpTimerFunc
[in] Pointer to the function to be notified when the time-out value elapses.
For more information about the function, see TimerProc
.
If lpTimerFunc
is NULL
, the system posts a
WM_TIMER
message to the application queue. The hwnd
member of the message's MSG
structure contains the value of the
hWnd
parameter.
Return Values
If the function succeeds and the hWnd
parameter is
NULL
, the return value is an integer identifying the new timer. An
application can pass this value to the KillTimer
function to
destroy the timer.
If the function succeeds and the hWnd
parameter is not
NULL
, then the return value is a nonzero integer. An application
can pass the value of the nIDTimer
parameter to the
KillTimer
function to destroy the timer.
If the function fails to create a timer, the return value is zero. To get
extended error information, call GetLastError
.
Remarks
An application can process WM_TIMER
messages by including a
WM_TIMER
case statement in the window procedure or by specifying a
TimerProc
callback function when creating the timer. When you
specify a TimerProc
callback function, the default window procedure
calls the callback function when it processes WM_TIMER
. Therefore,
you need to dispatch messages in the calling thread, even when you use
TimerProc
instead of processing WM_TIMER
.
The wParam
parameter of the WM_TIMER
message
contains the value of the nIDEvent
parameter.
The timer identifier, nIDEvent
, is specific to the associated
windows. Another window can have its own timer which has the same identifier as
a timer owned by another window. The timers are distinct.
KillTimer
The KillTimer
function destroys the specified timer.
BOOL KillTimer(
HWND hWnd,
UINT_PTR uIDEvent
);
Parameters
hWnd
[in] Handle to the window associated with the specified timer. This value
must be the same as the hWnd
value passed to the
SetTimer
function that created the timer.
uIDEvent
[in] Specifies the timer to be destroyed. If the window handle passed to
SetTimer
is valid, this parameter must be the same as the
uIDEvent
value passed to SetTimer
. If the application
calls SetTimer
with hWnd
set to NULL
,
this parameter must be the timer identifier returned by SetTimer
.
Return Values
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error
information, call GetLastError
.
Remarks
The KillTimer
function does not remove WM_TIMER
messages already posted to the message queue.
TimerProc
The TimerProc
function is an application-defined callback
function that processes WM_TIMER
messages. The
TIMERPROC
type defines a pointer to this callback function.
TimerProc
is a placeholder for the application-defined function
name.
VOID CALLBACK TimerProc(
HWND hwnd,
UINT uMsg,
UINT_PTR idEvent,
DWORD dwTime
);
Parameters
hwnd
[in] Handle to the window associated with the timer.
uMsg
[in] Specifies the WM_TIMER
message.
idEventnt
[in] Specifies the timer's identifier.
dwTime
[in] Specifies the number of milliseconds that have elapsed since the system
was started. This is the value returned by the GetTickCount
function.
Return Values
This function does not return a value.
Using the code
Common system calls :-
public const int WM_TIMER = 0x113;
[DllImport("user32")]
private static extern int SetTimer(IntPtr hwnd, int nIDEvent,
int uElapse, TimerProc CB);
[DllImport("user32")]
private static extern int KillTimer(IntPtr hwnd, int nIDEvent);
CallBack Timers
We needed an appropriate function callback to the call of API and a procedure
to which to do callback, also we must keep the identifier from timer for being
able to kill it in the future.
private delegate void TimerProc(IntPtr hWnd, uint nMsg,
int nIDEvent, int dwTime);
private TimerProc Cb = new TimerProc(this.OnTickTimer);
private int ID_CB_Timer = 0;
this.ID_CB_Timer = SetTimer(IntPtr.Zero, 0,
(int)this.numericUpDown2.Value, Cb);
this.statusBar1.Text = "CalBack " + this.ID_CB_Timer.ToString();
this.label7.Text = this.ID_CB_Timer.ToString();
Int rs = KillTimer(IntPtr.Zero,this.ID_CB_Timer);
If we obtain a Non-zero value when stop timer, an error occurred, we can
obtain info about error calling GetLastError()
system call.
When the tick occurs, she only add '1' to a counter of ticks and displays the
number of ticks in form.
private void OnTickTimer(IntPtr hWnd, uint nMsg, int nIDEvent,
int dwTime)
{
this.Cnt_CB_Timer+=1;
this.label5.Text = this.Cnt_CB_Timer.ToString();
}
TimerProc Timers
We don't need a callback, ticks are received on TimerProc
Form's
function.
private int ID_WN_Timer;
this.ID_WN_Timer = SetTimer(this.Handle,5000,
(int)this.numericUpDown2.Value,null);
this.statusBar1.Text = "WndProc " + this.ID_WN_Timer.ToString();
this.label8.Text = this.ID_WN_Timer.ToString();
this.Cnt_WN_Timer = 0;
this.label6.Text = "0";
int rs = 0;
rs = KillTimer(this.Handle,this.ID_WN_Timer);
if (rs == 0)
{
this.statusBar1.Text = "Error Kill Timer WndProc " +
rs.ToString() + " Timer id [" + this.ID_WN_Timer.ToString() + ']';
}
else
{
this.statusBar1.Text = "Kill Timer WndProc " + rs.ToString();
}
If we obtain a Non-zero value when stop timer, an error occurred, we can
obtain info about error calling GetLastError()
system call.
When the tick occurs, she only add '1' to a counter of ticks and displays the
number of ticks in form.
protected override void WndProc( ref System.Windows.Forms.Message m )
{
try
{
#region Switch system messages
switch (m.Msg)
{
case (int)WM_TIMER:
{
this.Cnt_WN_Timer+=1;
this.label6.Text = this.Cnt_WN_Timer.ToString();
break;
}
default:
{
base.WndProc(ref m);
break;
}
}
#endregion
}
catch (Exception ex)
{
this.statusBar1.Text = ex.Message;
}
}
Conclusion
Ok, that�s all, I expect on this, my first article submitted to Codeproject
it will help to someone. Thanks to all for the dozens of articles that I�ve read
here. I'll never edit again any HTML page with Visual Studio - there is
intelligent life on it!
Points of Interest
Refer Microsoft MSDN for related topics.
History