tags:

views:

555

answers:

2

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#?

+2  A: 

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.

ShuggyCoUk
You generally don't have to pin delegates passed to API functions, as the InteropServices handle that stuff. You *do* have to make sure that your app maintains a reference to the delegate so that it isn't garbage-collected. Pinning is normally only necessary when passing chunks of memory from the managed world into the unmanaged world (which expects things to stay where they are).
MusiGenesis
@MusiGenesis, this is what I was referring to by "In .Net this is almost certain to happen to anything you pass in.", I probably should have called that out as being specific to PInvoke. Likewise I saw no reason to mention the need to keep a reference because P/Invoke does that for you if it's required: http://msdn.microsoft.com/en-us/23acw07k.aspx
ShuggyCoUk
PInvoke definitely doesn't maintain delegate references for you. The compiler will happily let you call CreateTimerQueueTimer and pass in `new TimerCallback(...)` as a parameter. This will even run successfully for awhile, until the delegate you created with `new` happens to be garbage collected.
MusiGenesis
I learned this the hard way, of course. :)
MusiGenesis
@Musi ah you mean if the delegate is referenced as a callback after the PInvoke function has returned, yes indeed I will make that clear in the answer.
ShuggyCoUk
@Shuggy: I have to reiterate that you don't need to pin the delegate with `GCHandle` in order to use `CreateTimerQueueTimer`. In fact, you *can't* pin a delegate with `GCHandle`.
MusiGenesis
@Musi ah yes, the PInvoke deals with it via an unmanaged stub. I wasn't aware of this. I will add links to why.
ShuggyCoUk
+2  A: 

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.

MusiGenesis