DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

フォルダ、ファイルの変更を監視する

フォルダやファイルが変更されたらすぐにそのことを知るための方法を紹介します。これは、System.IO.FileSystemWatcherクラスを使用すれば簡単にできます。ただしFileSystemWatcherクラスはWindows NT系のOSでしか使えませんので、Windows98/Meでは残念ながらできません。

フォルダまたはファイルが作成されたときにはCreatedイベントが、削除された時にはDeletedイベントが、名前が変更された時にはRenamedイベントが、サイズ、システム属性、最終書き込み時刻、最終アクセス時刻、またはセキュリティアクセス許可が変更されたときにはChangedイベントが発生します。

次の例では、フォルダ"C:\My Documents"内のTXTファイルの作成、削除、名前の変更を監視しています。WaitForChangedメソッドを使い同期的に監視しているため、監視中はフリーズしたようになります。

VB.NET
コードを隠すコードを選択
Dim watcher As New System.IO.FileSystemWatcher
'監視するディレクトリを指定
watcher.Path = "C:\My Documents"
'*.txtファイルを監視、すべて監視するときは""にする
watcher.Filter = "*.txt"
'ファイル名とディレクトリ名と最終書き込む日時の変更を監視
watcher.NotifyFilter = System.IO.NotifyFilters.FileName Or _
    System.IO.NotifyFilters.DirectoryName Or _
    System.IO.NotifyFilters.LastWrite
'サブディレクトリは監視しない
watcher.IncludeSubdirectories = False
'必要に応じてバッファサイズを変更
'watcher.InternalBufferSize = 4096
'同期的に監視を開始する
Dim changedResult As System.IO.WaitForChangedResult = _
    watcher.WaitForChanged(System.IO.WatcherChangeTypes.All)

If changedResult.TimedOut Then
    Console.WriteLine("タイムアウトしました。")
    Return
End If

'変更があったときに結果を表示する
Select Case changedResult.ChangeType
    Case System.IO.WatcherChangeTypes.Changed
        Console.WriteLine(("ファイル 「" + _
            changedResult.Name + "」が変更されました。"))
    Case System.IO.WatcherChangeTypes.Created
        Console.WriteLine(("ファイル 「" + _
            changedResult.Name + "」が作成されました。"))
    Case System.IO.WatcherChangeTypes.Deleted
        Console.WriteLine(("ファイル 「" + _
            changedResult.Name + "」が削除されました。"))
    Case System.IO.WatcherChangeTypes.Renamed
        Console.WriteLine(("ファイル 「" + _
            changedResult.OldName + "」の名前が「" + _
            changedResult.Name + "」に変更されました。"))
End Select
C#
コードを隠すコードを選択
System.IO.FileSystemWatcher watcher =
    new System.IO.FileSystemWatcher();
//監視するディレクトリを指定
watcher.Path = @"C:\My Documents";
//*.txtファイルを監視、すべて監視するときは""にする
watcher.Filter = "*.txt";
//ファイル名とディレクトリ名と最終書き込む日時の変更を監視
watcher.NotifyFilter = 
    System.IO.NotifyFilters.FileName
    | System.IO.NotifyFilters.DirectoryName
    | System.IO.NotifyFilters.LastWrite;
//サブディレクトリは監視しない
watcher.IncludeSubdirectories = false;
//必要に応じてバッファサイズを変更
//watcher.InternalBufferSize = 4096

//同期的に監視を開始する
System.IO.WaitForChangedResult changedResult =
    watcher.WaitForChanged(System.IO.WatcherChangeTypes.All);

if (changedResult.TimedOut)
{
    Console.WriteLine("タイムアウトしました。");
    return;
}

//変更があったときに結果を表示する
switch (changedResult.ChangeType)
{
    case System.IO.WatcherChangeTypes.Changed:
        Console.WriteLine(
            "ファイル 「" + changedResult.Name + "」が変更されました。");
        break;
    case System.IO.WatcherChangeTypes.Created:
        Console.WriteLine(
            "ファイル 「" + changedResult.Name + "」が作成されました。");
        break;
    case System.IO.WatcherChangeTypes.Deleted:
        Console.WriteLine(
            "ファイル 「" + changedResult.Name + "」が削除されました。");
        break;
    case System.IO.WatcherChangeTypes.Renamed:
        Console.WriteLine(
            "ファイル 「" + changedResult.OldName +
            "」の名前が「" + changedResult.Name +
            "」に変更されました。");
        break;
}

非同期的に監視する

次に非同期的に監視する例を紹介します。Button1をクリックすることにより監視を開始し、Button2をクリックすることにより監視を終了します。

VB.NET
コードを隠すコードを選択
Private watcher As System.IO.FileSystemWatcher = Nothing

'Button1のClickイベントハンドラ
Private Sub Button1_Click(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles Button1.Click
    If Not (watcher Is Nothing) Then
        Return
    End If

    watcher = New System.IO.FileSystemWatcher
    '監視するディレクトリを指定
    watcher.Path = "C:\My Documents"
    '最終アクセス日時、最終更新日時、ファイル、フォルダ名の変更を監視する
    watcher.NotifyFilter = System.IO.NotifyFilters.LastAccess Or _
        System.IO.NotifyFilters.LastWrite Or _
        System.IO.NotifyFilters.FileName Or _
        System.IO.NotifyFilters.DirectoryName
    'すべてのファイルを監視
    watcher.Filter = ""
    'UIのスレッドにマーシャリングする
    'コンソールアプリケーションでの使用では必要ない
    watcher.SynchronizingObject = Me

    'イベントハンドラの追加
    AddHandler watcher.Changed, AddressOf watcher_Changed
    AddHandler watcher.Created, AddressOf watcher_Changed
    AddHandler watcher.Deleted, AddressOf watcher_Changed
    AddHandler watcher.Renamed, AddressOf watcher_Renamed

    '監視を開始する
    watcher.EnableRaisingEvents = True
    Console.WriteLine("監視を開始しました。")
End Sub

'Button2のClickイベントハンドラ
Private Sub Button2_Click(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles Button2.Click
    '監視を終了
    watcher.EnableRaisingEvents = False
    watcher.Dispose()
    watcher = Nothing
    Console.WriteLine("監視を終了しました。")
End Sub

'イベントハンドラ
Private Sub watcher_Changed(ByVal source As System.Object, _
    ByVal e As System.IO.FileSystemEventArgs)
    Select Case e.ChangeType
        Case System.IO.WatcherChangeTypes.Changed
            Console.WriteLine(("ファイル 「" + e.FullPath + _
                "」が変更されました。"))
        Case System.IO.WatcherChangeTypes.Created
            Console.WriteLine(("ファイル 「" + e.FullPath + _
                "」が作成されました。"))
        Case System.IO.WatcherChangeTypes.Deleted
            Console.WriteLine(("ファイル 「" + e.FullPath + _
                "」が削除されました。"))
    End Select
End Sub

Private Sub watcher_Renamed(ByVal source As System.Object, _
    ByVal e As System.IO.RenamedEventArgs)
    Console.WriteLine(("ファイル 「" + e.FullPath + _
        "」の名前が変更されました。"))
End Sub
C#
コードを隠すコードを選択
private System.IO.FileSystemWatcher watcher = null;

//Button1のClickイベントハンドラ
private void Button1_Click(object sender, System.EventArgs e)
{
    if (watcher != null) return;

    watcher = new System.IO.FileSystemWatcher();
    //監視するディレクトリを指定
    watcher.Path = @"C:\My Documents";
    //最終アクセス日時、最終更新日時、ファイル、フォルダ名の変更を監視する
    watcher.NotifyFilter =
        (System.IO.NotifyFilters.LastAccess 
        | System.IO.NotifyFilters.LastWrite
        | System.IO.NotifyFilters.FileName 
        | System.IO.NotifyFilters.DirectoryName);
    //すべてのファイルを監視
    watcher.Filter = "";
    //UIのスレッドにマーシャリングする
    //コンソールアプリケーションでの使用では必要ない
    watcher.SynchronizingObject = this;

    //イベントハンドラの追加
    watcher.Changed += new System.IO.FileSystemEventHandler(watcher_Changed);
    watcher.Created += new System.IO.FileSystemEventHandler(watcher_Changed);
    watcher.Deleted += new System.IO.FileSystemEventHandler(watcher_Changed);
    watcher.Renamed += new System.IO.RenamedEventHandler(watcher_Renamed);

    //監視を開始する
    watcher.EnableRaisingEvents = true;
    Console.WriteLine("監視を開始しました。");
}

//Button2のClickイベントハンドラ
private void Button2_Click(object sender, System.EventArgs e)
{
    //監視を終了
    watcher.EnableRaisingEvents = false;
    watcher.Dispose();
    watcher = null;
    Console.WriteLine("監視を終了しました。");
}

//イベントハンドラ
private void watcher_Changed(System.Object source,
    System.IO.FileSystemEventArgs e)
{
    switch (e.ChangeType)
    {
        case System.IO.WatcherChangeTypes.Changed:
            Console.WriteLine(
                "ファイル 「" + e.FullPath + "」が変更されました。");
            break;
        case System.IO.WatcherChangeTypes.Created:
            Console.WriteLine(
                "ファイル 「" + e.FullPath + "」が作成されました。");
            break;
        case System.IO.WatcherChangeTypes.Deleted:
            Console.WriteLine(
                "ファイル 「" + e.FullPath + "」が削除されました。");
            break;
    }
}

private void watcher_Renamed(System.Object source, 
    System.IO.RenamedEventArgs e)
{
    Console.WriteLine(
        "ファイル 「" + e.FullPath + "」の名前が変更されました。");
}

上の例では、コードだけですべて行いましたが、Visual Studioのフォームデザイナを使用すれば、「ツールボックス」の「コンポーネント」タブにある「FileSystemWatcher」をフォームに配置するだけで簡単に使用できます。

注意:非同期で監視を行う場合、SynchronizingObjectの意味等のマルチスレッドプログラミングの知識がないと危険です。これについて詳しくは、こちらをご覧ください。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • このサイトで紹介されているコードの多くは、例外処理が省略されています。例外処理については、こちらをご覧ください。
  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • Windows Vista以降でUACが有効になっていると、ファイルへの書き込みに失敗する可能性があります。詳しくは、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。