Win 32 API Timers for .NET






3.29/5 (6 votes)
Dec 2, 2003
5 min read

101121

2217
This article describes how to work with timers provided by win32 API
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, // handle to window UINT_PTR nIDEvent, // timer identifier UINT uElapse, // time-out value TIMERPROC lpTimerFunc // timer procedure );
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, // handle to window
UINT_PTR uIDEvent // timer identifier
);
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, // handle to window
UINT uMsg, // WM_TIMER message
UINT_PTR idEvent, // timer identifier
DWORD dwTime // current system time
);
Parameters
hwnd
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;
// Constant to identify System.Windows.Forms.Message
// in WndProc is a timer Message
// Init Timer
[DllImport("user32")]
private static extern int SetTimer(IntPtr hwnd, int nIDEvent,
int uElapse, TimerProc CB);
// Kill Timer
[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; // Callback timer identifier
// Start Callback
// Save timer identifier
this.ID_CB_Timer = SetTimer(IntPtr.Zero, 0,
(int)this.numericUpDown2.Value, Cb);
// Send to form info about timer
this.statusBar1.Text = "CalBack " + this.ID_CB_Timer.ToString();
this.label7.Text = this.ID_CB_Timer.ToString();
// Stop timer
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.
// On Tick Timer
// Received Ticks via callback
private void OnTickTimer(IntPtr hWnd, uint nMsg, int nIDEvent,
int dwTime)
{
// Count ticks
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; // Timer's identifier
// Start Timer WndProc
// Save the timer id in ID_WN_Timer
this.ID_WN_Timer = SetTimer(this.Handle,5000,
(int)this.numericUpDown2.Value,null);
// Send the info about the new timer to the form
this.statusBar1.Text = "WndProc " + this.ID_WN_Timer.ToString();
this.label8.Text = this.ID_WN_Timer.ToString();
// Stop WndProc
this.Cnt_WN_Timer = 0;
this.label6.Text = "0";
int rs = 0;
rs = KillTimer(this.Handle,this.ID_WN_Timer);
if (rs == 0)
{
// Error killing timer
this.statusBar1.Text = "Error Kill Timer WndProc " +
rs.ToString() + " Timer id [" + this.ID_WN_Timer.ToString() + ']';
// Here we could search with GetLastError what is the
// cause the timer does not stop ticking
}
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.
// On Tick Timer
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
- 1.0 Initial Release