DoEvents を使うときは注意して!

イベント・タスク

はじめに

 処理に時間がかかるようなプログラムを組むとき、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回実行する事もできてしまいますので、二重起動させないような工夫が必要になります。

 今回は、フラグでプロシージャを二重起動させない方法と、ボタンの切替で二重起動を防止する方法を紹介しました。参考にしてみてください。

コメント

タイトルとURLをコピーしました