Wednesday, January 28, 2009

Getting Timers To Work on Non-UI Threads

One tricky issue with Windows API programming is getting a windows timer to work on a non-UI thread. These examples have been tested on Windows CE but are applicable to all other Win32 platforms. Here is some background and the most common issue with getting timers to work on non-UI threads.

Most threads spend their time waiting for certain events to occur. Once the event occurs the thread wakes up, performs the task and then starts waiting again. A common function that is used for waiting is WaitForMultipleObjects(). This is a great function for many threads. However, this does not work for Windows Timers. Windows Timers use WM_TIMER messages as their basis. This means that at least for WM_TIMER messages you will need to implement a message pump. It is easiest if you chose to have the timer call a function rather than handle the WM_TIMER message yourself so you can allow the default Window Proc to handle the message for you.

Here is a sample WaitForMultipleObjects() call:

dwReason = WaitForMultipleObjects(numHandles, handles, FALSE, timeOut);

The numHandles parameter is the number of handles you are waiting on. The handles parameter is an array of the handles you are waiting for. The third parameter tells windows if you would like to wait for them all to be signaled. The last parameter is the number of milliseconds to wait for these events to happen before breaking out of your wait.

The return value specifies which handle has been triggered. The first event would be equal to WAIT_OBJECT_0. The second event would be represented by WAIT_OBJECT_0+1 and so on to numHandles - 1. There are also WAIT_ABANDONED and WAIT_TIMEOUT values that are mostly self explanatory.

To change this thread to handle the WM_TIMER messages we will need to convert this to MsgWaitForMultipleObjects(). The main difference is that this API will also check for pending Windows Messages as well as wait on the handles in the array. Here is the above call tweaked to work with the new API:

dwWait = MsgWaitForMultipleObjects(numHandles, handles, FALSE, timeout, mask);

This looks like the same function with one additional argument, the wait mask. This parameter tells Windows that we would like to be woken up when we have a Windows message that is represented by the mask. For the WM_TIMER message the mask value can be QS_TIMER or WM_TIMER. After the mask value has been set the next step is to add a message pump for it in your handling code. In this case we assume you are using a case statement to handle the results of the call:

case WAIT_OBJECT_0 + numHandles: // Handle WM_TIMER Messages

{

MSG msg;

while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

{

DispatchMessage(&msg);

}
}
break;

Notice that the handle that fires is one more than the last event in the list. Now, if you have specified a TimerProc in your SetTimer() call the specified TimerProc will be called.

Now you can have a Windows Message based timer in any thread you desire!

No comments:

Post a Comment