時間がかかるループ処理を強制終了する方法は、Ctrl+Breakキーを押下する方法が一般的です。
ですが、ユーザーにCtrl+Breakキーを押させるというのスマートではありませんし、
強制的にループを抜けるのでシステムエラーが表示されたりもします。
そもそも強制的にループを抜けるという処理をユーザーにしてほしくはありませんが、どうしてもという要望もあります。
今回の要望はメールを送信するループ処理があり、実行した後に「しまった!ファイルを添付し忘れた!」という事態になった場合の「緊急停止」ボタンがほしいというものでした。
それでは実装していきましょう。
実装方法
今回は実装方法を簡単に表現するため、1秒ずつWaitしながらカウントアップ、値をカウンタフィールドにセットするループを作成します。
カウントアップボタンのクリックイベントのプログラムは以下の通りです。
あれこれ書いてますが、ポイントはwhileループの中に「Do Event」を記述する点です。
この命令によりwhileの最中でもイベントを受け取ることができます。
緊急停止ボタンクリック時に停止フラグ(flgStop)をTrueにすることで、ループの最中にイベント取得で停止フラグがTrueの場合に処理を抜けられるようにしています。
Do Eventを記述することのリスク
とはいえ、「Do Event」を記述することで、ループ中に何をされるか分からないというリスクが発生します。
例えば・・・
- ボタンをクリックしてループ処理を開始したのに、ループ処理中にまた同じボタンをクリックされて二重ループが発生する。
- ループ中に「☓」ボタンクリックでフォーム自体を閉じられる。
こういったことがないように、「DoEvent」で処理させたい以外の機能(今回は緊急停止ボタンクリック以外の機能)を全て動作しないように制御しておく必要があります。
ファンクションキーの割当があれば、ファンクションキーの制御も必要です。
先のサンプルソースでは以下の部分で、二重起動を防いでいます。
'実行フラグをONならすでに実行中なので処理中止 '実行フラグがOFFならONにして処理開始(ループ中にDoEventを発生させるためメール送信の二重起動を防ぐ) If flgCnt = True Then Exit Sub Else '処理を開始する際に実行フラグをON,中止フラグをOFFに初期化 flgCnt = True flgStop = False End If
上記のように実行中フラグがTrueなら動作しないようにしておきましょう。
尚、停止フラグと実行中フラグはprivate変数として先に定義済みです。
その他
今回はsleep()関数でカウントアップ時1秒sleepさせていますが、sleep関数はAPIなので事前にモジュール内で宣言が必要です。
‘sleep関数用
Private Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)