The Timer and Xojo.Core.Timer classes gives us a resolution that is more than enough for most of the cases where we need to use them. In fact, under macOS we don’t find any kind of limitation when using the classes provided by the Xojo Framework: we can achieve a maximum resolution of 1 ms on any of the most recent computers.
But when working with Windows OS this is not so straightforward . It doesn’t matter if we try to set the Timer Period property to a minimum value of 1 ms; Windows imposes a minimum resolution of 16 ms, insufficient when we demand the maximum precision for a critical task (period interval between Timer firings).
Of course Xojo has a solution for this, you can resort to a third-party plug-in that works extremely well … or you might find the following technique useful for all kinds of projects.
Let’s start with the foundation. The Timer period is determined by the hardware internal clock and, specifically, by the OS driver. Under Windows, all modern computers are capable of providing a resolution above the 16 ms set by Windows by default.
In fact, when we use the NtQueryTimerResolution
function (from the NTDLL.DLL library) we can get minimum, maximum and current timer values for the hardware it is running on, similar to these (based on my own computer running Windows 10 under a VMWare Fusion virtual machine):
- Minimum Resolution: 15.62 ms
- Maximum Resolution: 0.5 ms
- Current Resolution: 0.99 ms
As you can see, the first value matches the current Timer behavior under Windows, but the hardware tell us that we can reach even a higher resolution … even of 0.5 ms between timer firings.
In order to make this change, Windows offers several options (all of them available under Xojo via the use of Declares or, preferably, using External Methods).
The first of these options, the one that would be the optimal approach, uses the group of MultimediaTimer functions (or high resolution Timers); specially these:
CreateTimerQueueTimer
: Allows creating a timer with a delegate (or Callback) that will fire just one time, or regularly at the set period interval. This would be a behavior similar to the current Xojo Timer class.ChangeTimerQueueTimer
: Using this function, we can modify the firing period for an already created timer; like the one we got when using the previous function.
The main problem we will face when using these functions is that Windows creates these Timers on a different thread than the one used by the Xojo app. During my tests, this is a showstopper due to the fact that, currently, Xojo doesn’t support the use of pre-emptive threading. Thus, it can’t coordinate the execution of our Timer Callback with the tasks in place by the Xojo framework. As result, at some random point our app will be pointing to Nil objects and will silently stall.
In addition, these kind of Timers are reentrant. That means that the timer will execute the Callback again even if the previous call has not finished the job yet.
During my tests, I found a way to solve this kind of reentrant problem using one-time firing Timers. This way, the callback method would be the responsible for deleting the previously created Timer (this is needed to not overflow the maximum limit of 500 timers), and create a new one-time firing timer.
But this approach doesn’t solve the main problem: at a random point, the app unexpectly quits due to the fact of the non preemptive multithreading coordination between the one created by Windows for the Timer and the main, single thread, used by the Xojo apps.
Same Thread Timers
Once you have discarded the previous (optimal) approach, the technique you can use is based on the use of another kind of Timer provided by Windows: WaitableTimer
. This kind of timer is created and, most important, run under the same thread they were created on. Thus, there is no collision with the Xojo framework during the application execution. In adition, as it goes with the commented High Definition Timers, we can achieve the same maximum resolution of 1 ms.
To start creating and using this kind of Timers in your Xojo app, first add an External Method so we can set the function definition from the C or C++ Windows Library.
In a new Xojo project, add a Module and our first External Method in it using the following values:
- Method Name: CreateWaitableTimerW
- Parameters: att as ptr, resetMode as Boolean, timerName as CString
- Return Type: Uint32
- Lib: Kernel32.dll
- Soft: Enabled
As you can see, this function returns a UInt32 value that will be non zero if it has succeeded creating the Timer. In fact, the returned value is a Handler pointing to the Timer structure of data. Thus, it is recommended to store this value in a property you can access later when you need it for calling other functions to delete (i.e: cancel), and for deleting the handler in order to free the used memory.
From the Xojo code point of view, later we can use this external function in this way:
Dim name As CString = "name" handler = CreateWaitableTimerW(Nil, False, name)
In this example, the handler
variable is a property set in a class that works as a wrapper (see the example project).
Once we get the Handler, we will be able to set the parameters of the Timer functionality, using the SetWaitableTimer
function. As in the previous case, we need to add a new External Method using the following values:
- Method Name: SetWaitableTimer
- Parameters: hTimer as uint32, byref lpDueTime as LARGE_INTEGER, period as uint32, CompletionRoutine as ptr, parameter as uint32, resume as Boolean
- Return Type: Boolean
- Lib: Kernel32.dll
- Soft: Enabled
As you can see in the method signature, we are using a data type that is not available in the Xojo Framework: LARGE_INTEGER
. This is a very simple data structure needed when calling the function (in fact, it needs a pointer to this structure, so this is why we employ the ByRef
keyword here. This is the way to get a pointer to the structure).
So, we need to add a new Structure definition into our Xojo project, using these values:
- Structure Name: LARGE_INTEGER
And setting the following value in the Declaration editor for the Structure declaration:
quadPart As Int64
.
Setting the Timer Values
Now we can write the Xojo code to call the added External Method:
pLarge.quadPart = -5 b = SetWaitableTimer(handler, pLarge, TimerPeriod, AddressOf callback, 0, False)
The important arguments passed to the function are these:
- Handler: This is the (non zero) value we got calling the previous function.
- pLarge: This is a property added to the project, and whose data type matches the defined structure. As you can see, we have previously assigned to it the negative value -5. It is very important to assign here a value, or starting point from the past time, in this case, five milliseconds.
- TimerPeriod: Here we set the firing period for the Timer (for example, 1 ms.).
- AddressOf callback: Here we set the memory address of the Xojo method we want to execute when the Timer fires. It is very important to add this method into the project as a Shared Method. In this example, the Xojo Method name is
callback
.
The function call will return a Boolean value: True
if it had success, or False
otherwise.
Signaling the Timer Firing
However, this kind of timer need to be signaled for them to execute. That means that we need to add other External Method to our Module from the Windows Kernel32.DLL
library: SleepEx
:
- Method Name: SleepEx
- Parameters: miliseconds as uint32, bAlertable as Boolean
- Return Type: Uint32
- Lib: Kernel32.DLL
- Soft: Enabled
Once it has been set, we can create an endlees loop in our Xojo Code, so the Timer keeps signaling:
Do result = SleepEx(INFINITE, True) app.DoEvents Loop Until CancelTimer = True
I’m using here the Boolean CancelTimer
property in order to have a way to exit the loop (and interrupting the Timer execution) when required from the app.
Another important parameter is the one used by the call to the SleepEx
function: INFINITE. This is a Constant added to the project, whose value in hexadecimal is &hFFFFFFFF
.
Timer Cleaning
Unlike the Windows High Definition or Multimedia Timers, we can’t modify the firing period for the WaitableTimers once they have been set. The only way to do it is to delete the one previously created, and create a new one; something you need to do when quitting the app or when you just need the Timer to fire once.
For this, we need to use the CancelWaitableTimer
function. Add a new Exterrnal Method with this values for the method signature:
- Method Name: CancelWaitableTimer
- Parameters: thandler as uint32
- Return Type: Boolean
- Lib: Kernel32.DLL
- Soft: Enabled
In addition, and as part of the cleaning process, we need also to call the function in charge of freeing the used Handler. Add the last External Method with this signature:
- Method Name: CloseHandle
- Parameters: tHandler as Uint32
- Return Type: Boolean
- Lib: Kernel32.dll
- Soft: Enabled
Now, you can use the following Xojo code to cancel the timer and delete the handler:
If handler <> 0 Then Dim b As Boolean b = CancelWaitableTimer( handler ) b = CloseHandle( handler ) End If
To Summarize
As you can see, the main disadvantage of this technique is that you’ll be interrupting the usual workflow of the app: it will not return to the calling method or event once it enters into the method in charge of creating and firing the Timer (until the exit the Loop, of course). The way to make the app responsive and execute the code associated with the UI controls is through the App.DoEvents
line of code.
Additional Reading:
- About Multimedia or High Resolution Timers: CreateTimerQueueTimer function
- About setting the inner clock in Windows: timeBeginPeriod Function
- About setting Timers on the same Thread (WaitableTimers): SetWaitableTimer function
- About Windows Data Types: Data Types
Example Project
You can download the example project, available from this link, to see and test the following:
- Exposed technique in action (Timers with a maximum resolution of 1 ms.)
- How to modify the period value with the cancelation of a previously created timer
- How to modify on the fly the Callback code executed by the Timer
- How to get the Current, Minimum and Maximum clock resolutions from the Windows computer you’re using to run the app
Javier Rodriguez has been the Xojo Spanish Evangelist since 2008, he’s also a Developer, Consultant and Trainer who has be using Xojo since 1998. He manages AprendeXojo.com and is the developer behind the GuancheMOS plug-in for Xojo Developers, Markdown Parser for Xojo, HTMLColorizer for Xojo and the Snippery app, among others.