LoginSignup
2
7

More than 3 years have passed since last update.

読んで字の如くDoEvents

Last updated at Posted at 2021-02-20

イマイチ理解できないDoEvents

ExcelVBAをそこそこ使いこなしている人でもイマイチ理解していないトピック。その一つがDoEventsだろう。
ネット上の数あるメジャーな解説サイトを調べてみたが、何だか良く理解できない。
その中で以下のような説明を見た。

発生したイベントがオペレーティングシステムによって処理されるように制御を戻します。

この説明でもわかる人にはわかるのだろうが、自分の場合はこれだけではハッキリと理解できなかった。:sweat_smile:
それなら自分で説明を書いてみようと思い立ったのがこの記事である。

無限ループから抜け出せ!

以下のマクロを実行したらどうなるかは、Doループを知っていればわかるだろう。

ExcelVBA
Sub Hoge_1()
    Do
        ' なんかの処理
    Loop
End Sub

これは無限ループである。ループからは一生抜けられない。これを実行するとマウスもキーボードも利かなくなり制御不能、終いにはExcel画面が真っ白になる。無限ループを一番簡単に書いたものである。

ExcelVBA
Sub Hoge_2()
    Do Until Range("A1").Value = 1
        ' なんかの処理
    Loop
End Sub

では上記はどうなるだろうか。
セルA11を入力すればループから抜けられるだろうと期待しだが、Hoge_1同様に画面が固まりセルに文字を入力することはできない。やはり無限ループである。

そこで、DoEventsを使ってみるとどうか。

ExcelVBA
Sub Hoge_3()
    Do Until Range("A1").Value = 1
        ' なんかの処理
        DoEvents
    Loop
    MsgBox "ループを抜けました。"
End Sub

ループ内にDoEventsをたった一行加えるだけで画面が固まらずマウスクリックやキー入力が普通にできるようになる。そしてセルA11を入力するとMsgBoxが現れて処理が終了する。ループを抜けられたということである。
これは一体どういうことなのか?DoEventsは何をしているのだろう。:astonished::question:

読んで字の如く - DoEvents

ExcelVBA
Sub Hoge_4()
    Application.Wait [Now()] + 10 / 86400
    ' 60秒 × 60分 × 24時間 = 86,400秒 (1日の秒数)
    DoEvents
    MsgBox "マクロ処理中のイベントを実行"
End Sub

上記ではApplication.Waitで10秒待機してからDoEventsを実行している。このマクロを実行してExcelワークシート内で作業をしてみよう。
マウスクリックやキー入力などの作業をしても画面が固まってしまい作業内容は無視されたかのように見える。
ここで構わず作業をしてみよう。
例えば、

イベント① ---> セルB3をクリック
イベント② ---> セルB39を入力
イベント③ ---> セルB5をクリック
イベント④ ---> セルB5を黄色で塗りつぶす

を10秒間でやってみる。画面は固まって無反応だが、とにかくやってみよう。10秒後にDoEventsが実行されると①〜④の溜まっていたイベントが処理され、MsgBoxが現れる。
09CCCFE9-B736-406F-B3CD-E86428FC2F41.png
実はDoEventsが無くてもマクロ終了後にイベント処理は行われる。キモはMsgBoxが出現する前に一連のイベント処理が行われるということだ。これは本来後回しにされるイベント処理をマクロ実行中に行ったということである。
つまりDoEventsがやっていることは、読んで字の如く「イベントを行う」と言うことである。

マクロ開始からDoEventsが呼ばれる時点までに溜まったイベント、あるいはDoEvents実行終了後に次のDoEventsを実行するまでに溜まった「イベントを行う」のである。もちろんイベントが溜まっていなければ何もせずに終了するだけなのでループ内で何回実行しても負荷は掛からない。:laughing:

なんちゃって非同期処理

ExcelVBAは非同期処理ができないらしい。
例えば、処理が重たくて終了までに時間がかかって画面が固まってしまい、正常な処理中にもかかわらずハングアップしてしまったのかのように見える場合がある。
画面が固まることを防ぐため、重たい処理はC#(あるいはVB.net)などで書いたexeにさせるという方法がある。
exeを実行したら即終了であれば単にexeを呼び出すだけで良いが、exeの結果によって後続処理を変える必要がある場合、DoEventsが役に立つ。
以下はユーザーフォームに配置したボタンのクリックイベントプロシージャである。
WshShellクラスでexeを実行し、exe処理終了までテキストボックスに簡単なアニメーションを表示して処理中であることをユーザーに知らせている。

ExcelVBA

Private eventFLg As Boolean

Private Sub CommandButton1_Click()
    If eventFLg Then
        MsgBox "処理中です。", vbInformation
        Exit Sub
    End If
    eventFLg = True

    Dim wsh As New WshShell
    Dim rt As WshExec
    Set rt = wsh.Exec("C:\hoge\hoge.exe")

    Dim logStr As String
    logStr = "-- START --" & vbCr
    TextBox1.Value = logStr

    Dim pos As Integer, anime As String
    pos = 1
    Do While rt.Status = 0
        Application.Wait [Now()] + 2 / 864000
        Select Case pos
            Case 1
                anime = ">>"
                pos = pos + 1
            Case 2 To 9
                anime = "  " & anime
                pos = pos + 1
            Case 10
                anime = Replace(anime, ">>", "<<")
                pos = pos + 1
            Case 11 To 17
                anime = Mid(anime, 3)
                pos = pos + 1
            Case 18
                anime = "<<"
                pos = 1
        End Select
        TextBox1.Value = logStr & vbCr & anime
        DoEvents
    Loop
    TextBox1.Value = logStr & vbCr & "-- END --"
    eventFLg = False

    If rt = -1 Then
        ' exeでエラーがあったときの処理
        ' ...
    Else
        ' exeが正常に終了したときの処理
        ' ...
    End If
End Sub
hoge.exe
static int Main(string[] args)
{
    try
    {
        int result = 0;
        // 重たい処理
        // ...
        return result;
    }
    catch (Exception ex)
    {
        // エラー処理
        // ...
        return -1;
    }
}

Doループを抜ける条件であるrt.Statusはexeが実行中は0である。ループの中でDoEventsが繰り返し呼ばれる為、ユーザーフォーム上で起こったイベントは即座に反映され、画面が固まることはない。exeの終了とともにrt.Status0でなくなり、ループを抜け、後続処理が行われる。
ユーザーフォーム上のボタンはDoループ中はクリックができてしまうのでexe処理中はフラグを立てて二重起動を回避している。
他にもタイトルバー右上の :x: がクリックされたときのことや、その他のコントロールが配置されていた場合のときのことも考慮が必要になるが、それは省略。:stuck_out_tongue_winking_eye:
また、rt変数でexeのintの戻り値を得られ、exeが正常終了したかどうか確認できる。

exeを実行する為のWshShellクラスを使うには参照設定で『Windows Script Host Object Model』へのチェックが必要だ。

Excel内部の非同期ではないので微妙だが、なんちゃって非同期処理はDoEventsが役立つ一例である。

:laughing::v::laughing::v::laughing::v: -- END --

2
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
7