I'm trying to create a precision timer. I found an example created with WinMM.dll. The sample works really fine. But it crashes with the first garbage collector.
How can I prevent the garbage collector from blocking the timer?
public class WinMMWrapper : IDisposable
{
[DllImport("WinMM.dll", SetLastError = true)]
public static extern uint timeSetEvent(int msDelay, int msResolution,
TimerEventHandler handler, ref int userCtx, int eventType);
[DllImport("Winmm.dll", CharSet = CharSet.Auto)] // <=== ADD THIS
static extern uint timeKillEvent(uint uTimerID); // <=== ADD THIS
public delegate void TimerEventHandler(uint id, uint msg, ref int userCtx,
int rsv1, int rsv2);
public enum TimerEventType
{
OneTime = 0,
Repeating = 1,
}
private readonly Action _elapsedAction;
private readonly int _elapsedMs;
private readonly int _resolutionMs;
private readonly TimerEventType _timerEventType;
private uint _timerId; // <=== ADD THIS
private bool _disposed; // <=== ADD THIS
public WinMMWrapper(int elapsedMs, int resolutionMs, TimerEventType timerEventType, Action elapsedAction)
{
_elapsedMs = elapsedMs;
_resolutionMs = resolutionMs;
_timerEventType = timerEventType;
_elapsedAction = elapsedAction;
}
public bool StartElapsedTimer() // <=== RETURN bool
{
StopTimer(); // Stop any started timer
int myData = 1;
// === SET _timerId
_timerId = timeSetEvent(_elapsedMs, _resolutionMs / 10, new TimerEventHandler(TickHandler), ref myData, (int)_timerEventType);
return _timerId != 0;
}
public void StopTimer() // <=== ADD THIS
{
if (_timerId != 0)
{
timeKillEvent(_timerId);
_timerId = 0;
}
}
private void TickHandler(uint id, uint msg, ref int userctx, int rsv1, int rsv2)
{
_elapsedAction();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!_disposed && disposing)
StopTimer();
_disposed = true;
}
~WinMMWrapper()
{
Dispose(false);
}
}
My Static Class
public static class Global
{
public static WinMMWrapper timer;
}
Create WinMMWrapper
private void TimerStart_Click(object sender, RoutedEventArgs e)
{
Global.timer = new WinMMWrapper(1, 1, WinMMWrapper.TimerEventType.Repeating, Tick);
Global.timer.StartElapsedTimer();
}
Tick Function
private static void Tick()
{
Console.WriteLine("Time : " + DateTime.Now.ToString("hh:mm:ss:ffff"));
}
Error Message
Managed Debugging Assistant 'CallbackOnCollectedDelegate' : A callback was made on the garbage-collected delegate of type 'CanBusRandomDataGenerator!CanBusRandomDataGenerator.WinMMWrapper+TimerEventHandler::Invoke'. This can cause app crashes, corruption, and data loss. When delegating to unmanaged code, it must be kept alive by the managed application until it is guaranteed that the delegates will never be called.'
The code is now exactly the same. It works for about 2 3 seconds, then it crashes to the following error. Error occurs within WinMMWrapper function without falling into Dispose.