【Excel VBA】開いている全ブックのセル変更履歴を記録(ログ)する方法

「誰がいつ、どのファイルの、どのセルの値を変更したのか」という操作履歴を自動で記録できれば、共同作業の信頼性やデータの監査性が大きく向上します。

Worksheet_Changeイベントは単一のシートしか監視できませんが、VBAのアプリケーションレベルイベントという高度な仕組みを使えば、Excelで開いているすべてのブック・すべてのシートに対する変更を検知し、一つのログシートに記録することが可能です。

この記事では、そのためのVBAコードの全体像と、実装のポイントを解説します。


目次

【重要】コードを記述する場所

今回のコードは、Excelアプリケーション全体の動作を監視する特殊なものです。そのため、すべてのコードを**ThisWorkbookモジュール**に記述する必要があります。標準モジュールやシートモジュールでは正しく動作しません。

  1. VBE(Alt+F11)を開きます。
  2. プロジェクトエクスプローラーから、**ThisWorkbook**をダブルクリックします。
  3. 表示されたコードウィンドウに、以下のコードをすべて貼り付けてください。

準備:ログを記録するシート

このマクロは、変更履歴を特定のシートに書き出します。マクロを記述するブックに、あらかじめ「LogSheet」という名前のシートを作成し、1行目に以下のような見出しを設定しておいてください。

A列B列C列D列E列
変更日時ブック名シート名セル番地変更後の値

完成したVBAコード(全体像)

'==================================================
' ThisWorkbookモジュールに、以下のコードをすべて記述します
'==================================================

'--- 1. イベントを検知するための変数を宣言 ---
' WithEventsキーワードが、この変数を特別なイベント変数にします
Private WithEvents AppEvents As Excel.Application

'--- 2. ブックを開いたときにイベント監視を開始 ---
Private Sub Workbook_Open()
    ' アプリケーションオブジェクトをイベント変数にセット
    Set AppEvents = Application
End Sub

'--- 3. ブックを閉じる前にイベント監視を終了 ---
Private Sub Workbook_BeforeClose(Cancel As Boolean)
    ' イベント変数への参照を解除
    Set AppEvents = Nothing
End Sub


'--- 4. セルが変更された瞬間に、このイベントが実行される ---
Private Sub AppEvents_SheetChange(ByVal Sh As Object, ByVal Target As Range)
    
    ' 変数を宣言
    Dim logSheet As Worksheet
    Dim nextRow As Long

    ' ログを記録するシートを指定
    On Error Resume Next ' ログシートがない等のエラーを回避
    Set logSheet = ThisWorkbook.Worksheets("LogSheet")
    If logSheet Is Nothing Then Exit Sub
    On Error GoTo 0

    '【無限ループ防止①】変更がログシート自身で起きた場合は記録しない
    If Sh.Name = logSheet.Name Then Exit Sub
    
    ' ログシートの最終行を取得
    nextRow = logSheet.Cells(logSheet.Rows.Count, "A").End(xlUp).Row + 1
    
    '【無限ループ防止②】ログ書き込み処理中は、一時的にイベントをオフにする
    Application.EnableEvents = False

    ' ログシートに変更履歴を書き込む
    With logSheet.Cells(nextRow, "A")
        .Value = Now() ' 日時
        .Offset(0, 1).Value = Sh.Parent.Name ' ブック名
        .Offset(0, 2).Value = Sh.Name ' シート名
        .Offset(0, 3).Value = Target.Address(False, False) ' セル番地
        .Offset(0, 4).Value = Target.Value2 ' 変更後の値
    End With
    
    ' イベントを再度有効化
    Application.EnableEvents = True
    
End Sub

コードのポイント解説

WithEvents での変数宣言

Private WithEvents AppEvents As Excel.Application WithEventsキーワードを付けて変数を宣言することで、その変数がExcelアプリケーションの様々なイベント(セルの変更、ブックを開くなど)を「傍受」できるようになります。

② イベント監視の開始と終了

Workbook_Openイベント内でSet AppEvents = Applicationを実行することで、このブックが開かれている間、AppEvents変数がExcel全体の動作の監視を開始します。逆にWorkbook_BeforeCloseSet AppEvents = Nothingとして監視を終了させます。

③ 変更イベントの捕捉 (AppEvents_SheetChange)

Private Sub AppEvents_SheetChange(...) これが、実際にセルが変更されたときに実行されるメインの処理です。プロシージャ名は**「WithEventsで宣言した変数名」 + _ + 「イベント名」**というルールで決まっています。

  • Sh As Object: 変更が加えられたシートのオブジェクトが渡されます。
  • Target As Range: 変更されたセル範囲のオブジェクトが渡されます。

④ 無限ループの防止

このコードで最も重要なのが、無限ループを防ぐ2つの仕組みです。

  1. ログシート自身の変更は記録しない: ログを書き込む行為自体も「セルの変更」イベントを発生させます。If Sh.Name = logSheet.Name Then Exit Subという一文で、ログシートへの書き込みが、さらなるログ記録を呼び出すのを防いでいます。
  2. イベントの一時的な無効化: より確実に連鎖を断ち切るため、ログを書き込む処理の前後をApplication.EnableEvents = False / Trueで囲んでいます。

まとめ

アプリケーションレベルのイベントを利用することで、個別のブックやシートにマクロを仕込むことなく、Excel全体の操作を監視する高度なツールを作成できます。

  1. ThisWorkbookモジュールWithEvents付きの変数を宣言する。
  2. Workbook_Openで、その変数にApplicationをセットして監視を開始する。
  3. 変数名_イベント名という名前のプロシージャ内に、実行したい処理を記述する。
  4. イベントの連鎖(無限ループ)が発生しないよう、細心の注意を払う。

このテクニックは高度ですが、ブックの監査ログの作成や、特定の操作を全ブックで共通して制御したい場合などに、非常に強力な解決策となります。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次