Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use CreateTimerQueueTimer to create a high resolution timer in C#?

Tags:

c#

timer

I've used a Windows multimedia dll to created a high resolution timer with

timSetEvent()

But the timeSetEvent() page recommends the use of:

CreateTimerQueueTimer()

How can I use CreateTimerQueueTimer() to execute a method every 10 milliseconds in C#?

like image 493
Justin Tanner Avatar asked Jun 18 '09 23:06

Justin Tanner


3 Answers

Here is a link to a C# wrapper for CreateTimerQueueTimer:

http://social.msdn.microsoft.com/Forums/en-CA/csharpgeneral/thread/822aed2d-dca0-4a8e-8130-20fab69557d2

(scroll down to the last post by Hobz for the sample class)

I just tried this out myself and it works fine. One thing you'll need to add, though, is a call to timeBeginPeriod(1) before starting the timer in order to set your system to high-resolution. timeSetEvent calls timeBeginPeriod internally, which is why some people mistakenly assume that it creates a higher-resolution timer.

like image 54
MusiGenesis Avatar answered Sep 30 '22 12:09

MusiGenesis


The callback passed to CreateTimerQueueTimer is expected to be an unmanaged function which will exist for the lifetime of the callbacks. The managed delegate can move about in memory but the underlying stub created by the marshalling will not do this so there is no need to pin the delegate. There is however a need to keep the delegate from being garbage collected since the pointer from unmanaged code is not sufficient to keep it alive. You must therefore ensure that either the delegate is kept alive by some managed reference being maintained (Perhaps through the use of GCHandle

The PVOID Parameter which is passed to the callback function must be fixed in memory since again, the unmanaged function expects it not to move about after the function returns. In .Net this pinning happens (efficiently) automatically but only for the life of the called function. Thus if you are using a reference to some managed object (say by getting an IntPtr to it) the underlying object must be pinned (again the GCHandle can be used for that in a subtly different way). To see if this is the problem try using IntPtr.Zero as the parameter to test if it works.

If this solves things you will need to either allocate your parameter as raw bytes in the unmanaged heap (and marshal accordingly), use some blittable type that is safe to put into something of size PVOID (like an Int32) or use the GCHandle technique above to maintain a stable pointer to a managed instance, this will have significant performance implications if done wrongly.

like image 25
3 revs Avatar answered Sep 30 '22 14:09

3 revs


It's better to use timeSetEvent because its results are more consistent. On average modern hardware, for small intervals, the deviations in length of the intervals are about ten times smaller than when using CreateTimerQueueTimer. And that's assuming you didn't forget to increase the timer resolution before calling CreateTimerQueueTimer, otherwise the difference would be even bigger. So use timeSetEvent instead.

like image 39
Anonymous Avatar answered Sep 30 '22 12:09

Anonymous