using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace TestProject
{
/// <summary>
/// 멀티미디어 타이머
/// </summary>
public class MultimediaTimer : IDisposable
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Event
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 틱 이벤트 - Tick
/// <summary>
/// 틱 이벤트
/// </summary>
public event EventHandler Tick;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Import
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region 타이머 이벤트 설정하기 - TimeSetEvent(delay, resolution, callback, userContext, eventType)
/// <summary>
/// 타이머 이벤트 설정하기
/// </summary>
/// <param name="delay">지연 시간 (단위 : ms)</param>
/// <param name="resolution">해상도 (단위 : ms)</param>
/// <param name="callback">콜백 함수</param>
/// <param name="userContext">사용자 컨텍스트</param>
/// <param name="eventType">이벤트 타입</param>
/// <returns>타이머 ID</returns>
/// <remarks>
/// eventType : 0(1회성 이벤트), 1(정기 이벤트)
/// </remarks>
[DllImport("winmm", SetLastError = true, EntryPoint = "timeSetEvent")]
private static extern uint TimeSetEvent(uint delay, uint resolution, MultimediaTimerDelegate callback, ref uint userContext, uint eventType);
#endregion
#region 타이머 이벤트 제거하기 - TimeKillEvent(timerID)
/// <summary>
/// 타이머 이벤트 제거하기
/// </summary>
/// <param name="timerID">타이머 ID</param>
[DllImport("winmm", SetLastError = true, EntryPoint = "timeKillEvent")]
private static extern void TimeKillEvent(uint timerID);
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 리소스 해제 여부
/// </summary>
private bool disposed = false;
/// <summary>
/// 주기
/// </summary>
private int interval;
/// <summary>
/// 해상도 (단위 : ms)
/// </summary>
private int resolution;
/// <summary>
/// 타이머 ID
/// </summary>
private uint timerID;
/// <summary>
/// 멀티미디어 타이머 대리자
/// </summary>
private readonly MultimediaTimerDelegate multimediaTimerDelegate;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 주기 - Interval
/// <summary>
/// 주기
/// </summary>
public int Interval
{
get
{
return this.interval;
}
set
{
if(this.disposed)
{
throw new ObjectDisposedException("MultimediaTimer");
}
if(value < 0)
{
throw new ArgumentOutOfRangeException("Interval");
}
this.interval = value;
if(Resolution > Interval)
{
Resolution = value;
}
}
}
#endregion
#region 해상도 - Resolution
/// <summary>
/// 해상도
/// </summary>
public int Resolution
{
get
{
return this.resolution;
}
set
{
if(this.disposed)
{
throw new ObjectDisposedException("MultimediaTimer");
}
if(value < 0)
{
throw new ArgumentOutOfRangeException("Resolution");
}
this.resolution = value;
}
}
#endregion
#region 실행 여부 - IsRunning
/// <summary>
/// 실행 여부
/// </summary>
public bool IsRunning
{
get
{
return this.timerID != 0;
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MultimediaTimer()
/// <summary>
/// 생성자
/// </summary>
public MultimediaTimer()
{
this.multimediaTimerDelegate = new MultimediaTimerDelegate(ProcessMultimediaTimer);
Resolution = 5;
Interval = 10;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Destructor
#region 소멸자 - ~MultimediaTimer()
/// <summary>
/// 소멸자
/// </summary>
~MultimediaTimer()
{
Dispose(false);
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 시작하기 - Start()
/// <summary>
/// 시작하기
/// </summary>
public void Start()
{
if(this.disposed)
{
throw new ObjectDisposedException("MultimediaTimer");
}
if(IsRunning)
{
return;
}
uint userContext = 0;
this.timerID = TimeSetEvent((uint)Interval, (uint)Resolution, multimediaTimerDelegate, ref userContext, 1);
if(this.timerID == 0)
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
}
#endregion
#region 중단하기 - Stop()
/// <summary>
/// 중단하기
/// </summary>
public void Stop()
{
if(this.disposed)
{
throw new ObjectDisposedException("MultimediaTimer");
}
if(!IsRunning)
{
return;
}
StopInternal();
}
#endregion
#region 리소스 해제하기 - Dispose()
/// <summary>
/// 리소스 해제하기
/// </summary>
public void Dispose()
{
Dispose(true);
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 멀티미디어 타이머 처리하기 - ProcessMultimediaTimer(id, message, userContext, reserved1, reserved2)
/// <summary>
/// 멀티미디어 타이머 처리하기
/// </summary>
/// <param name="id">ID</param>
/// <param name="message">메시지</param>
/// <param name="userContext">사용자 컨텍스트</param>
/// <param name="reserved1">예약 1</param>
/// <param name="reserved2">예약 2</param>
private void ProcessMultimediaTimer(uint id, uint message, ref uint userContext, uint reserved1, uint reserved2)
{
Tick?.Invoke(this, EventArgs.Empty);
}
#endregion
#region 중단하기 (내부용) - StopInternal()
/// <summary>
/// 중단하기 (내부용)
/// </summary>
private void StopInternal()
{
TimeKillEvent(this.timerID);
this.timerID = 0;
}
#endregion
#region 리소스 해제하기 - Dispose(disposing)
/// <summary>
/// 리소스 해제하기
/// </summary>
/// <param name="disposing">리소스 해제 여부</param>
private void Dispose(bool disposing)
{
if(this.disposed)
{
return;
}
this.disposed = true;
if(IsRunning)
{
StopInternal();
}
if(disposing)
{
Tick = null;
GC.SuppressFinalize(this);
}
}
#endregion
}
}