【Excel VBA】ループ処理の中断ボタンを作る。マルチスレッドの1歩前に試すべき方法。

【Excel VBA】ループ処理の中断ボタンを作る。マルチスレッドの1歩前に試すべき方法。

 ExcelVBAで、ボタンを押したら処理が動くフォームを作る。そんな機会は結構多いと思います。そこで問題になるのが、処理を中断できないという点です。しかし、時間のかかる処理を行うアプリには、一時停止や中止ボタンがついているもの。そこで、今回は一工夫してExcelフォームに処理中止ボタンを付ける方法をご紹介します。

まえがき

 以下のようなフォームで、開始ボタンを押したらメイン処理を始め、中止ボタンを押したら処理を止められるようにします。環境はExcel2016です。
Office365は契約していないので、365での動作は未検証です。もし、検証された方はこのページの下のコメント欄にコメントして頂けますと幸いです。)

 さて、このフォームでそのまま、中止ボタンに停止処理を割り当てても、処理中断はできません。なぜなら、VBAの処理はシングルスレッドでしか動かないからです。(詳しくは以下のリンクを参照ください。)
https://docs.microsoft.com/ja-JP/troubleshoot/windows/win32/descriptions-workings-ole-threading-models
スレッドとはいわば作業場のことです。スレッドが一つしかないということは、言い換えれば、一度に一つの処理しかできないということです。開始ボタンでメイン処理を始めると、VBAはメイン処理が終わるまで、他の処理を始められません。従って、中止ボタンを押しても、割り当てられた処理を始めることができないのです。
 

やり方

 メイン処理のループに中止ボタンが押されたかどうかの判定を入れます。そして、押されていれば処理を終了するようにします。

Private F_Flag As Boolean
Private Sub 開始ボタン_Click()
    F_Flag = False
    Do

        ~やりたい処理~

        Application.Wait Now + TimeValue("00:00:01")
        If F_Flag = True Then
            Exit Do
            DoEvents
        End If
        DoEvents
    Loop 
end sub

Private Sub 終了ボタン_Click()
    F_Flag = True
End Sub

 注意したいのは、DoEventsを入れることです。DoEventsとはキューに残っている実行待ちの処理を一旦全て実行し終わるまで待機するためのものです。これを入れないと、何故か中止ボタンを押しても反応しません。(恐らく、中止ボタンを押したという処理がキューに入ったまま実行されずじまいになっている?)

まとめ

ExcelVBAで、ボタンを押したら処理が動くフォームを作りました。そのフォームに処理中止ボタンを付ける方法をご紹介しました。