マクロの記録/再生をする簡単なフォーム

「1」〜「5」「クリア」「記録」「再生」の8個のボタン、1個のラベルをフォームに配置する。

  • 数字ボタンを押すと、ラベル上にその数字が追加される。
  • 「クリア」を押すとラベルがまっさらになる。
  • 「記録」を押すと数字ボタンの押されたタイミング/履歴を保存する。
  • 「再生」を押すとタイミング/履歴通りに数字ボタンを押したイベントを再生する。

というのを VB.NET で作ってみた。
こーゆーのは GoF の Command パターンとか使うべきなんだろうけど、まだ VB.NET に慣れてないので…

Imports System.Threading
Public Structure CommandClass
    Public name As String
    Public arg As String
    Public Sub New(ByVal s1 As String, ByVal s2 As String)
        Me.name = s1
        Me.arg = s2
    End Sub
End Structure

Public Class Form1
    Private status As Integer = 0 '0:定常状態 1:記録中
    Private commands As Collection = New Collection()
    Private commandPreTime As Date
    Private timerDelegate As TimerCallback = New TimerCallback(AddressOf MyTimer)
    Private timer As Timer = New Timer(timerDelegate, Nothing, Timeout.Infinite, -1)
    Dim myCount As Integer = 0

    Private Function func(ByVal code)
        Label1.Text = Label1.Text & code
        If status = 1 Then '記録中
            If commands.Count > 0 Then
                Dim span As TimeSpan = Now() - commandPreTime
                commands.Add(New CommandClass("s", span.TotalSeconds))
            End If
            commandPreTime = Now()
            commands.Add(New CommandClass("r", code))
        End If
        func = code
    End Function

    Private Sub Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
            Handles Button1.Click, Button2.Click, Button3.Click, Button4.Click, Button5.Click
        func(sender.Text)
    End Sub

    Private Sub Button100_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button100.Click
        If status = 0 Then
            commands.Clear()
            status = 1
            Button100.Text = "保存する"
        Else
            status = 0
            Dim sfd As New SaveFileDialog()
            sfd.FileName = "events.txt"
            'sfd.InitialDirectory = "C:\"
            sfd.Filter = "TEXTファイル(*.txt)|*.txt|すべてのファイル(*.*)|*.*"
            sfd.FilterIndex = 1 '初期表示は*.txtだけ
            sfd.Title = "保存先のファイルを選択してください"
            If sfd.ShowDialog() = DialogResult.OK Then
                Dim stream As System.IO.Stream = sfd.OpenFile()
                If Not (stream Is Nothing) Then
                    Dim sw As New System.IO.StreamWriter(stream)
                    sw.WriteLine("# s:指定秒数SLEEPする / " & _
                        "p:指定されたキーのPressイベントを発行 /r:指定されたキーのReleaseイベントを発行")
                    For Each com In commands
                        sw.WriteLine(com.name & " " & com.arg.ToString)
                    Next
                    sw.Close()
                    stream.Close()
                End If
            End If
            Button100.Text = "記録開始"
        End If

    End Sub

    Private Sub Button101_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button101.Click
        Dim ofd As New OpenFileDialog()
        ofd.FileName = "events.txt"
        'ofd.InitialDirectory = "C:\"
        ofd.Filter = "TEXTファイル(*.txt)|*.txt|すべてのファイル(*.*)|*.*"
        ofd.FilterIndex = 1
        ofd.Title = "開くファイルを選択してください"
        ofd.RestoreDirectory = True
        If ofd.ShowDialog() = DialogResult.OK Then
            Dim stream As System.IO.Stream = ofd.OpenFile()
            If Not (stream Is Nothing) Then
                Dim sr As New System.IO.StreamReader(stream)
                Dim sarray As String()
                commands.Clear()
                While sr.Peek() > -1
                    Dim s As String = sr.ReadLine()
                    If s.Length < 3 Or s.StartsWith("#") Then
                        ' Do nothing
                        'Next While 'break; とか next に該当するのって何?
                    Else
                        sarray = s.Split(" ")
                        commands.Add(New CommandClass(sarray(0), sarray(1)))
                    End If
                End While
                sr.Close()
                stream.Close()
                myCount = 1
                timer.Change(10, Timeout.Infinite)
            End If
        End If
    End Sub

    Private Sub Button102_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button102.Click
        Label1.Text = ""
    End Sub

    Delegate Function FuncDelegate(ByVal arg)

    Public Sub MyTimer(ByVal o As Object)
        While myCount <= commands.Count
            Dim com As CommandClass = commands(myCount)
            myCount = myCount + 1
            If com.name = "r" Then
                Invoke(New FuncDelegate(AddressOf func), com.arg)
            Else
                timer.Change(Convert.ToInt32(com.arg * 1000), Timeout.Infinite)
                Exit Sub
            End If
        End While
        timer.Change(Timeout.Infinite, Timeout.Infinite)
        MessageBox.Show("再生を終了しました")
    End Sub
End Class