はじめに
処理に時間がかかるようなプログラムを組むとき、ExcelVBAではマルチスレッドが使えないので苦労します。
DoEventsを使えば、イベントを受け取れるようになります。
これはタスクを分けているだけなのですが、それでもループ処理の中に組み込めば処理を中断させるボタンからフラグを操作してループから抜け出すような事ができます。
ただ、注意しなければいけない事があります。
長い処理を抜けるプログラム
以下のプログラムは、時間がかかるFor文を途中で抜け出せるようにしたプログラムです。
中断用にフラグ(bStop)を設け、For文の途中で DoEvents を呼び出しているので、ボタンを押すとフラグをTrueにしてFor文を抜けてくれます。
Dim bStop As Boolean
'中断ボタンに割り当てる
Sub macroStop()
bStop = True
End Sub
’スタートボタンに割り当てる
Sub TestMacro()
bStop = False
Dim i As Long
For i = 1 To 3000
Range("A1") = i
If bStop Then Exit For
DoEvents
Next i
bStop = False
End Sub
一見、何も問題なさそうなプログラムですが、実は TestMacro() が2回呼び出されてしまう事にも配慮する必要があります。
1回目のスタートボタンの途中で、もう一度2回目のスタートボタンが押されると意図した通りに動作しないでしょう。
具体的には、1回目の処理が一旦中断して、2回目の処理が始まり、最後まで行くか中断ボタンで2回目の処理が終わると1回目に中断した所から再び1回目の処理が始まります。
このような事にならないようにするには、TestMacro()が二重起動しないようにする必要があります。
二重起動しないようにフラグを設けるか、ボタンを押した後でボタンを使用禁止にしてしまうのが現実的でしょう。
プロシージャが二重起動しないようにする
プロシージャが複数回実行された場合、冒頭で抜けるような仕組みを考えてみましょう。
カウンタ変数としてcnt を設けてその数で複数起動していないかを確かめます。
Dim bStop As Boolean
Dim cnt As Long
'中断ボタンに割り当てる
Sub macroStop()
bStop = True
cnt = 0
End Sub
Sub TestMacro()
'プロシージャの二重起動防止
cnt = cnt + 1
If cnt > 1 Then Exit Sub
bStop = False
Dim i As Long
For i = 1 To 3000
Range("A1") = i
If bStop Then Exit For
DoEvents
Next i
bStop = False
cnt = 0
End Sub
2回目のボタンプッシュを無効にする
シート上のボタン(フォームコントロールのボタン)をプッシュするたびにプログラムを切り替えれば、DoEventsによって同じプログラムが2回実行される事はありません。
ボタンのキャプションは切り替えることができるので、これをフラグ代わりにしてボタンをプッシュした時のプログラムを変更すれば良いでしょう。
Dim bStop As Boolean
Sub ボタン1_Click()
With ActiveSheet.Buttons("Button 1")
If .Caption = "Start" Then
.Caption = "Stop"
Call TestMacro
Else
.Caption = "Start"
Call macroStop
End If
End With
End Sub
'中断
Sub macroStop()
bStop = True
End Sub
'Start
Sub TestMacro()
bStop = False
Dim i As Long
For i = 1 To 3000
Range("A1") = i
If bStop Then Exit For
DoEvents
Next i
bStop = False
ActiveSheet.Buttons("Button 1").Caption = "Start"
End Sub
まとめ
DoEventsを使用すれば他のイベントを受け入れてくれるようになります。
ただ、その事で同じプロシージャを2回実行する事もできてしまいますので、二重起動させないような工夫が必要になります。
今回は、フラグでプロシージャを二重起動させない方法と、ボタンの切替で二重起動を防止する方法を紹介しました。参考にしてみてください。
コメント