SetTimer Windows APIを利用したタイマーコンポーネントの作成 (コールバック関数呼び出し版)
タイマー処理をコンポーネント化して手軽に扱えるようにしたコードを紹介します。
「
SetTimer Windows APIを利用してタイマーを実装する」の記事では、SetTimer関数を呼び出してタイマーを実装するコードを紹介しましたが、この方法をそのまま用いると、コンポーネントでウィンドウメッセージを受け取る必要があり、コンポーネントをControlの派生として作成する必要があります。Controlの派生として作成するとフォーム内に配置されてしまうため、見栄えが良くありません。ここでは、コンポーネントをタイマーイベントをコールバック関数を受け取る方法で実装します。
コンポーネント部コード
以下のコードを記述します。
iPentecTimer.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
namespace iPentecTimer
{
public partial class iPentecTimer : Component
{
public int Interval { set; get; }
public bool Enabled {
set {
_enabled =value;
UpdateTimer();
}
get
{
return _enabled;
}
}
private bool _enabled=false;
[DllImport("user32.dll", ExactSpelling = true)]
static extern IntPtr SetTimer(IntPtr hWnd, IntPtr nIDEvent, uint uElapse, TimerProc lpTimerFunc);
delegate void TimerProc(IntPtr hWnd, uint uMsg, IntPtr nIDEvent, uint dwTime);
[DllImport("user32.dll", ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool KillTimer(IntPtr hWnd, IntPtr uIDEvent);
public delegate void OnTimerDelegate();
private OnTimerDelegate onTimer;
public iPentecTimer()
{
InitializeComponent();
Interval = 1000;
}
public iPentecTimer(IContainer container)
{
container.Add(this);
InitializeComponent();
Interval = 1000;
}
protected void UpdateTimer()
{
KillTimer(IntPtr.Zero, IntPtr.Zero);
if (Interval != 0) {
if (Enabled == true){
TimerProc tp = timerProc;
if (SetTimer(IntPtr.Zero, IntPtr.Zero, (uint)Interval, tp) == (IntPtr)0) {
}
}
}
}
private void timerProc(IntPtr hWnd, uint uMsg, IntPtr nIDEvent, uint dwTime)
{
if (onTimer != null) {
onTimer();
}
}
public event OnTimerDelegate OnTimer
{
add
{
onTimer += value;
}
remove
{
onTimer -= value;
}
}
}
}
解説
SetTimer関数の第四引数にコールバック関数のデリゲートを与えます。下記のコードでは、timerProcメソッドがコールバックにより、呼び出される動作になります。
protected void UpdateTimer()
{
KillTimer(IntPtr.Zero, IntPtr.Zero);
if (Interval != 0) {
if (Enabled == true){
TimerProc tp = timerProc;
if (SetTimer(IntPtr.Zero, IntPtr.Zero, (uint)Interval, tp) == (IntPtr)0) {
}
}
}
}
コンポーネント利用アプリ プログラム
UI
下図のUIを作成します。
Timerコンポーネントはタイマーコンポーネントのプロジェクトを参照するとツールパレットに表示されます。(下図参照)
コード
以下のコードを記述します。
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 iPentecTimerDemo
{
public partial class FormMain : Form
{
int count=0;
public FormMain()
{
InitializeComponent();
}
private void iPentecTimer1_OnTimer()
{
textBox1.Text += string.Format("+:{0:d}\r\n",count);
count++;
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text += "Start\r\n";
iPentecTimer1.Enabled = true;
}
private void button2_Click(object sender, EventArgs e)
{
textBox1.Text += "Stop\r\n";
iPentecTimer1.Enabled = false;
}
}
}
実行結果
プロジェクトを実行します。下図のウィンドウが表示されます。
[Start]ボタンをクリックします。タイマーが開始され、1秒ごとにTextBoxにメッセージが表示されます。
[Stop]ボタンをクリックするとタイマーの処理が停止します。
参考 / 補足
SetTimer関数を用いてウィンドウメッセージをキャプチャーする方式でコンポーネントを実装することもできます。ウィンドウメッセージをキャプチャーする方式のTimerコンポーネントは「
SetTimer Windows APIを利用したタイマーコンポーネントの作成 (ウィンドウメッセージ取得版)」の記事を参照してください。
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用