
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