timeSetEvent Windows APIを利用した高精度タイマーコンポーネントの作成 - C#
高精度のタイマー処理をコンポーネント化して手軽に扱えるようにしたコードを紹介します。
コンポーネント部コード
以下のコードを記述します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace iPentecEventTimer
{
public partial class iPentecEventTimer : Component
{
public delegate void OnTimerDelegate();
private OnTimerDelegate onTimer;
private CRITICAL_SECTION CriticalSection;
private uint TimerID = 0;
TimerEventHandler teHandler;
bool _enabled = false;
public uint Resolution { set; get; }
public uint Interval { set; get; }
public bool Enabled
{
set
{
_enabled = value;
UpdateTimer();
}
get
{
return _enabled;
}
}
public iPentecEventTimer()
{
InitializeComponent();
InitalizeProperty();
}
public iPentecEventTimer(IContainer container)
{
container.Add(this);
InitializeComponent();
InitalizeProperty();
}
private void InitalizeProperty(){
Resolution = 100;
Interval = 1000;
Enabled = false;
teHandler += TimerProc;
}
private void UpdateTimer()
{
if (DesignMode == false) {
if (Enabled == true) {
if (TimerID != 0) {
timeKillEvent(TimerID);
}
if (timeBeginPeriod(Resolution) == TIMERR_NOERROR) {
uint userctx = 0;
TimerID = timeSetEvent(Interval, Resolution, teHandler, ref userctx, TIME_PERIODIC);
}
}
else {
if (TimerID != 0) {
timeKillEvent(TimerID);
timeEndPeriod(Resolution);
}
}
}
}
private void TimerProc(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2)
{
InitializeCriticalSection(out CriticalSection);
EnterCriticalSection(ref CriticalSection);
try {
if (onTimer != null) {
onTimer();
}
}
finally {
LeaveCriticalSection(ref CriticalSection);
}
}
//OnTimer Event
public event OnTimerDelegate OnTimer
{
add
{
onTimer += value;
}
remove
{
onTimer -= value;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace iPentecEventTimer
{
public partial class iPentecEventTimer : Component
{
[DllImport("winmm.dll", SetLastError = true)]
static extern UInt32 timeSetEvent(UInt32 msDelay, UInt32 msResolution, TimerEventHandler handler, ref UInt32 userCtx, UInt32 eventType);
[DllImport("winmm.dll", SetLastError = true)]
static extern UInt32 timeKillEvent(UInt32 timerEventId);
[DllImport("winmm.dll")]
public static extern uint timeBeginPeriod(uint uMilliseconds);
[DllImport("winmm.dll")]
public static extern uint timeEndPeriod(uint uMilliseconds);
[DllImport("kernel32.dll")]
static extern void InitializeCriticalSection(out CRITICAL_SECTION lpCriticalSection);
//[DllImport("kernel32.dll")]
//static extern void InitializeCriticalSection(IntPtr lpCriticalSection);
[DllImport("kernel32.dll")]
static extern void EnterCriticalSection(ref CRITICAL_SECTION lpCriticalSection);
//[DllImport("kernel32.dll")]
//static extern void EnterCriticalSection(IntPtr lpCriticalSection);
[DllImport("kernel32.dll")]
static extern void LeaveCriticalSection(ref CRITICAL_SECTION lpCriticalSection);
//[DllImport("kernel32.dll")]
//static extern void LeaveCriticalSection(IntPtr lpCriticalSection);
[StructLayout(LayoutKind.Sequential)]
public struct CRITICAL_SECTION{
public IntPtr DebugInfo;
public long LockCount;
public long RecursionCount;
public uint OwningThread;
public uint LockSemaphore;
public int Reserved;
}
const int TIMERR_BASE = 96;
const int TIMERR_NOERROR = 0;
const int TIMERR_NOCANDO = TIMERR_BASE + 1;
const int TIMERR_STRUCT = TIMERR_BASE + 33;
const uint TIME_ONESHOT = 0;
const uint TIME_PERIODIC = 1;
const uint TIME_CALLBACK_FUNCTION = 0x0000;
const uint TIME_CALLBACK_EVENT_SET = 0x0010;
const uint TIME_CALLBACK_EVENT_PULSE = 0x0020;
private delegate void TimerEventHandler(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2);
}
}
動作
動作の概要は
こちらの記事を参照してください。
コンポーネント利用アプリ プログラム
UI
下図のUIを作成します。
タイマーコンポーネントはツールパレットに表示されるので、フォームに配置します。(下図参照)
また、タイマーコンポーネントのOnTimerイベントにコードを記述します。
コード
下記のコードを記述します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace iPentecEventTimerDemo
{
public partial class FormMain : Form
{
int cnt = 0;
delegate void Proc();
Proc dp;
public FormMain()
{
InitializeComponent();
dp = showMessge;
}
private void button1_Click(object sender, EventArgs e)
{
iPentecEventTimer1.Enabled = true;
}
private void iPentecEventTimer1_OnTimer()
{
Invoke(dp);
cnt++;
}
private void showMessge(){
textBox1.Text += string.Format("+ : {0:d}\r\n", cnt);
}
private void button2_Click(object sender, EventArgs e)
{
iPentecEventTimer1.Enabled = false;
}
}
}
実行結果
プロジェクトを実行します。下図のウィンドウが表示されます。
[Start]ボタンをクリックします。タイマーが開始され、1秒に1行ずつメッセージがテキストボックスに表示されます。
[Stop]ボタンをクリックします。タイマーが停止し、メッセージの表示が停止します。
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用