【VBA】処理の進捗をステータスバーに表示する方法(コピペOK)

VBA
スポンサーリンク
スポンサーリンク

この記事でできること

  • VBAマクロ実行中にステータスバーへ「○○件 / ○○件 処理中…」と進捗を表示できる
  • ScreenUpdating=False で処理速度を2〜5倍に高速化しながら、進捗表示も維持できる
  • DoEvents で「応答なし」表示を防ぎ、経過時間もリアルタイムで確認できる

対象: Excel 2016以降 / Microsoft 365、Windows 10/11


完成イメージ(Before / After)

Before(進捗表示なし):

  • マクロを実行する
  • 画面が固まる
  • タスクバーに「応答なし」と表示される
  • 動いてるのか止まってるのか分からず不安
  • 怖くなって強制終了→中途半端な状態に

After(進捗表示あり):

  • マクロを実行する
  • ステータスバーに「150 / 1000 件処理中… 経過: 00:01:23」と表示される
  • 画面はフリーズせず、進捗が目に見える
  • 安心して待てる。完了時に「処理完了」と表示される

500件のファイル処理マクロを実行したことがある。画面が固まって、ステータスバーには何も表示されない。1分経っても2分経っても画面は真っ白のまま。動いてるのか止まってるのか分からず、10分待った末に怖くなって強制終了した。結果、半分だけ処理された中途半端な状態になり、どこまで終わったのかを手作業で確認する羽目になった。

あの時「150 / 500 件処理中…」とステータスバーに表示されていれば、安心して待てたはずだ。実際にStatusBarを1行追加しただけで、大量ファイル統合(複数Excelファイルを1つに統合)の処理中も「ちゃんと動いてる」と分かるようになった。

この記事では、マクロ実行中に進捗を表示する方法を最小版と実務版の2パターンで紹介する。まずは最小版をコピペして、ステータスバーの手軽さを体験してほしい。

ステータスバーに進捗を出すだけで、マクロ実行中の「動いてるのか分からない」不安がなくなる。


実行前の準備

バックアップを取る

マクロ実行前に、Excelファイルのコピーを別フォルダに保存しておく。進捗表示の追加自体はデータを壊す操作ではないが、大量データを処理するマクロに組み込む以上、バックアップは習慣にしておく。

Excelをマクロ有効ブック(.xlsm)で保存する

拡張子が .xlsx のままだとマクロは保存できない。

  • 「ファイル」→「名前を付けて保存」
  • ファイルの種類を「Excelマクロ有効ブック (*.xlsm)」に変更
  • 保存

手順(コピペ → 実行まで約5分)

VBE(コードを書く画面)を開く

  • Excelで Alt + F11 を押す
  • VBE(Visual Basic Editor)が開く

標準モジュールを挿入する

  • VBEのメニュー →「挿入」→「標準モジュール」
  • 白い画面(コードウィンドウ)が表示される

コードを貼り付けて実行する

  • コードウィンドウに、下のコードをそのままコピペする
  • Alt + F8 → マクロ名を選んで「実行」

コード(最小版)– ステータスバーに処理件数を表示


'============================================================
' ■ ステータスバーに処理件数を表示(最小版)
'   → ループ処理の進捗をステータスバーで確認できる
'   → 処理完了後にステータスバーをリセットする
'============================================================
Sub ShowProgressMinimal()

    '--- ★書き換えポイント ---
    Dim wsName As String
    wsName = "Sheet1"          '← 処理対象のシート名

    Dim startRow As Long
    startRow = 2               '← データの開始行(1行目がヘッダーなら2)

    Dim dataCol As String
    dataCol = "A"              '← データが入っている列
    '--- ★ここまで ---

    Dim ws As Worksheet
    Set ws = ThisWorkbook.Worksheets(wsName)

    '--- 最終行を取得
    Dim lastRow As Long
    lastRow = ws.Cells(ws.Rows.Count, dataCol).End(xlUp).Row

    Dim totalCount As Long
    totalCount = lastRow - startRow + 1

    '--- ループ処理(ここに実際の処理を書く)
    Dim i As Long
    For i = startRow To lastRow

        '=== ここに1行ごとの処理を書く ===
        '(例)セルの値を読み取って何かする
        ' Dim val As String
        ' val = ws.Cells(i, dataCol).Value
        '=================================

        '--- ステータスバーに進捗を表示
        Application.StatusBar = (i - startRow + 1) & " / " & totalCount & " 件処理中..."

    Next i

    '--- 処理完了:ステータスバーをリセット
    Application.StatusBar = False

    MsgBox totalCount & " 件の処理が完了しました。", vbInformation

End Sub

書き換えポイント

変数 説明 初期値
`wsName` 処理対象のシート名 `”Sheet1″`
`startRow` データの開始行 `2`(1行目ヘッダー想定)
`dataCol` データが入っている列 `”A”`

コードの流れ

  • シートと最終行の取得: 処理対象のシートとデータの最終行を特定する
  • ループ処理: 各行に対して処理を実行する
  • StatusBar更新: ループの中で「○件 / ○件 処理中…」とステータスバーに表示する
  • リセット: 処理完了後に Application.StatusBar = False でステータスバーを元に戻す

テスト方法: A列に適当なデータを100行ほど入れて実行する。ステータスバー(Excel下部)に件数が表示されれば成功。フォルダ内のファイル一覧を取得する処理(フォルダ内ファイル一覧を自動取得)と組み合わせて、ファイル処理の進捗を表示することもできる。


コード(実務版)– 高速化+応答維持+経過時間表示

実務では1000行を超えるデータを処理することが珍しくない。最小版のまま大量データを流すと、処理は遅いし画面はチカチカするし、タスクバーに「応答なし」が出る。

以前、DoEventsを入れずに1000行の生産データを処理したら、タスクバーに「応答なし」と表示されて上司に「パソコン壊れた?」と聞かれた。DoEventsを1行追加するだけで応答なし表示が消えて、ステータスバーの進捗もスムーズに更新されるようになった。

実務版では以下の3つを追加する:

  • ScreenUpdating=False: 画面更新を止めて処理速度を2〜5倍に高速化
  • DoEvents: OSに制御を返して「応答なし」表示を防ぎ、StatusBarの表示も更新する
  • 経過時間表示: Timer関数で処理開始からの経過時間を「hh:mm:ss」形式で表示

複数シート処理(複数シートを一括処理する方法)のように対象が多い場合は、この実務版を使うことを推奨する。


'============================================================
' ■ 進捗表示+高速化+応答維持(実務版)
'   → ScreenUpdating=False で高速化
'   → DoEvents で「応答なし」防止+StatusBar更新
'   → Timer で経過時間を表示
'   → エラー時も ScreenUpdating=True に復帰する
'============================================================
Sub ShowProgressAdvanced()

    '--- ★書き換えポイント ---
    Dim wsName As String
    wsName = "Sheet1"          '← 処理対象のシート名

    Dim startRow As Long
    startRow = 2               '← データの開始行

    Dim dataCol As String
    dataCol = "A"              '← データが入っている列

    Dim updateInterval As Long
    updateInterval = 10        '← 何件ごとにステータスバーを更新するか
    '--- ★ここまで ---

    '--- 画面更新を停止(高速化)
    Application.ScreenUpdating = False

    '--- エラー時にScreenUpdating=Trueに復帰するためのエラー処理
    On Error GoTo ErrHandler

    Dim ws As Worksheet
    Set ws = ThisWorkbook.Worksheets(wsName)

    '--- 最終行を取得
    Dim lastRow As Long
    lastRow = ws.Cells(ws.Rows.Count, dataCol).End(xlUp).Row

    Dim totalCount As Long
    totalCount = lastRow - startRow + 1

    '--- 開始時刻を記録
    Dim startTime As Double
    startTime = Timer

    '--- ループ処理
    Dim i As Long
    For i = startRow To lastRow

        '=== ここに1行ごとの処理を書く ===
        '(例)セルの値を読み取って何かする
        ' Dim val As String
        ' val = ws.Cells(i, dataCol).Value
        '=================================

        '--- N件ごとにステータスバーを更新(毎回更新すると遅くなる)
        If (i - startRow + 1) Mod updateInterval = 0 Or i = lastRow Then

            '--- 経過時間を計算
            Dim elapsed As Double
            elapsed = Timer - startTime

            '--- 経過時間を hh:mm:ss 形式に変換
            Dim elapsedStr As String
            elapsedStr = Format(elapsed / 86400, "hh:nn:ss")

            '--- ステータスバーに進捗+経過時間を表示
            Application.StatusBar = _
                (i - startRow + 1) & " / " & totalCount & " 件処理中... " & _
                "経過: " & elapsedStr

            '--- OSに制御を返す(応答なし防止+StatusBar更新)
            DoEvents

        End If

    Next i

    '--- 処理完了:経過時間を含む完了メッセージ
    Dim totalElapsed As String
    totalElapsed = Format((Timer - startTime) / 86400, "hh:nn:ss")

    Application.StatusBar = "処理完了(" & totalCount & "件 / 経過: " & totalElapsed & ")"

    '--- 画面更新を再開
    Application.ScreenUpdating = True

    MsgBox totalCount & " 件の処理が完了しました。" & vbCrLf & _
           "経過時間: " & totalElapsed, vbInformation

    '--- 3秒後にステータスバーをリセット(完了メッセージを少し残す)
    Application.StatusBar = False

    Exit Sub

ErrHandler:
    '--- エラー時もScreenUpdatingを必ず復帰させる
    Application.ScreenUpdating = True
    Application.StatusBar = False
    MsgBox "エラーが発生しました。" & vbCrLf & _
           "エラー番号: " & Err.Number & vbCrLf & _
           "内容: " & Err.Description, vbCritical

End Sub

書き換えポイント

変数 説明 初期値
`wsName` 処理対象のシート名 `”Sheet1″`
`startRow` データの開始行 `2`
`dataCol` データが入っている列 `”A”`
`updateInterval` 何件ごとにステータスバーを更新するか `10`

コードの流れ

  • ScreenUpdating=False: 画面更新を停止して高速化する
  • On Error GoTo ErrHandler: エラー発生時に必ず ScreenUpdating=True に復帰するための安全策(エラー処理の詳細は VBAのエラー処理入門 を参照)
  • 最終行の取得: データの範囲を自動検出する
  • Timer で開始時刻を記録: 処理時間の計測を開始する
  • N件ごとにStatusBar更新: 毎ループではなく updateInterval 件ごとに更新(パフォーマンス維持)
  • DoEvents: OSに制御を返し、「応答なし」を防ぎつつStatusBarの表示を更新する
  • 完了処理: 経過時間を含む完了メッセージをMsgBoxで表示し、StatusBarをリセットする
  • エラーハンドラ: エラー時もScreenUpdating=TrueとStatusBar=Falseを実行して安全に復帰する

ScreenUpdating=False にした処理は、必ずエラー処理で True に戻す仕組みを入れておく。これを忘れると画面が真っ白になる。詳しくは VBAのエラー処理入門 を参照。


よくある落とし穴5選

1. 処理完了後もステータスバーにメッセージが残ったまま

原因: Application.StatusBar = False を書き忘れている。

対策: ループの後に必ず Application.StatusBar = False を入れる。エラーハンドラの中にも入れておく(実務版コード参照)。

2. ScreenUpdating=False のまま止まって画面が真っ白に

原因: エラーで処理が中断し、ScreenUpdating = True に戻る前にマクロが終了した。

自分もこれをやった。ScreenUpdating=Falseにしたら処理が3倍速くなって喜んでいたが、途中でエラーが出て止まった時にTrueに戻し忘れてExcelの画面が真っ白になった。セルをクリックしても何も表示されない。VBEを開いてイミディエイトウィンドウに Application.ScreenUpdating = True と入力して実行したらようやく復帰した。それ以来、On Error GoTo でScreenUpdatingの復帰を必ず書くようにしている。

対策: 実務版コードのように On Error GoTo ErrHandler で、エラー時も ScreenUpdating = True に戻す処理を入れる。

3. StatusBarの表示が更新されない

原因: ScreenUpdating = False の状態では、StatusBarの表示更新も止まることがある。

対策: StatusBarを更新した直後に DoEvents を呼ぶ。実務版コードではN件ごとにDoEventsを呼んでいる。

4. DoEventsを入れたら処理が遅くなった

原因: ループの中で毎回 DoEvents を呼んでいる。DoEvents は OS に制御を返す処理で、毎回呼ぶとオーバーヘッドが大きい。

対策: updateInterval(初期値10)件ごとに1回だけ呼ぶ。1000件のデータなら100回の DoEvents で済む。データ量が多い場合は updateInterval を50〜100に増やす。

5. Timer の値がマイナスになる

原因: Timer は午前0時からの経過秒数を返す。日付をまたぐ処理では0時にリセットされ、Timer - startTime がマイナスになる。

対策: 日付をまたぐ可能性がある処理では、Timer の代わりに Now を使う。通常の業務時間内の処理であれば Timer で問題ない。


FAQ

Q1: ステータスバーはどこに表示される?

Excelウィンドウの一番下にある細い帯がステータスバー。通常は「準備完了」や選択範囲の合計値が表示されている場所。Application.StatusBar に文字列を代入すると、ここに任意のテキストが表示される。

Q2: UserForm のプログレスバーとどちらがいい?

手軽さならStatusBar。見た目のインパクトならUserFormのプログレスバー。StatusBarはコード1行で表示できるので、既存のマクロに進捗表示を追加するならStatusBarが圧倒的に楽。UserFormは見栄えは良いが、フォーム作成+表示制御のコードが必要で手間がかかる。

Q3: ボタンクリックで進捗付きマクロを実行したい

ボタン設置(マクロをボタン1つで実行する方法)と組み合わせれば、ワンクリックで進捗表示付きの処理が走る。ボタンの OnAction に進捗表示付きマクロ名を指定するだけ。

Q4: ScreenUpdating=False にしなくても進捗は表示される?

される。最小版コードは ScreenUpdating を操作していないが、ステータスバーは更新される。ただし画面が毎行更新されるため処理が遅く、画面がチカチカする。大量データ(100行以上)を処理するなら、実務版の ScreenUpdating=False を推奨する。

Q5: 経過時間ではなく残り時間を表示したい

現在の処理速度から概算できる:


Dim progress As Double
progress = (i - startRow + 1) / totalCount
Dim remaining As Double
remaining = elapsed / progress - elapsed
Application.StatusBar = "残り約 " & Format(remaining / 86400, "hh:nn:ss")

ただし処理速度が一定でないと誤差が大きくなるため、あくまで目安として表示する。


まとめ

  • Application.StatusBar = "テキスト" でステータスバーに進捗を表示できる
  • 処理完了後は Application.StatusBar = False で必ずリセットする
  • ScreenUpdating = False で処理を高速化し、DoEvents でStatusBarの更新と「応答なし」防止を両立する
  • エラー時に ScreenUpdating = True に戻す処理を必ず入れる(On Error GoTo)
  • 進捗表示があるだけで、大量データ処理の「動いてるのか分からない」不安がなくなる

関連記事


次にやりたくなること


進捗表示があるだけで、マクロ実行中の不安がゼロになる。まずは最小版でStatusBarの手軽さを体験してみてほしい。

あわせて読みたい

コメント

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