Erweiterte Programmierthemen: Ordnerüberwachung
Die FileSystemWatcher-Klasse bietet uns die Möglichkeit, über Änderungen an Ordnern und Dateien benachrichtigt zu werden. Dabei „überwacht“ die Klasse einen bestimmten Ordner (und deren Dateien und evtl. Unterordner), welcher über den Konstruktor oder die Eigenschaft Path gesetzt werden kann. Mit Hilfe der Eigenschaft Filter können wir einen Filter für Datei- und Ordnernamen angeben. Mehrere Filter werden mit dem Senkrechtstrich (|) getrennt. Eine Angabe eines Teilnamens (z. B. *.txt oder CSharp_*.txt) ist mit Hilfe des Platzhalters (*) möglich. Wollen wir Unterordner bei der Überwachung mit einbeziehen, so muss die Eigenschaft IncludeSubdirectories auf true gesetzt werden. Der Standardwert der Eigenschaft ist false.
Um auf die verschiedenen Änderungen an den Dateien oder Ordnern zu reagieren, können wir die Ereignisse Changed (Datei geändert), Created (Datei oder Ordner erstellt), Deleted (Datei oder Ordner gelöscht) und Renamed (Datei oder Ordner umbenannt) registrieren. Den Events werden ein FileSystemEventArgs-Objekt als Event-Argument übergeben. Über die Eigenschaft ChangedType können wir die Art der Änderungen (also ändern, erstellen, löschen oder umbenennen) als Wert der Enumeration WatcherChangeTypes abrufen. Die Eigenschaft FullPath gibt den Pfad mit Datei- oder Ordnernamen der geänderten Datei oder des Ordners zurück. Durch die Eigenschaft Name können wir hingegen nur den Datei- oder Ordnernamen abrufen. Beim Renamed-Event empfiehlt es sich, das Event mit dem RenamedEventHandler zu registrieren, sodass wir als Event-Argument ein RenamedEventArgs-Objekt übergeben bekommen. Hierdurch können wir zusätzlich über die Eigenschaft OldFullPath und OldName den Namen oder Pfad vor der Umbenennung abrufen.
Standardmäßig ist die FileSystemWatcher-Klasse deaktiviert. Wollen wir diese aktivieren, sodass Ereignisse ausgelöst werden falls Dateiänderungen auftreten, so muss die Eigenschaft EnableRaisingEvents auf true gesetzt werden. Das Beispielprogramm überwacht alle Dateien und Ordner im ausgewählten Ordner. Wählen Sie doch einmal das komplette Laufwerk, auf welchem das Betriebssystem installiert ist. Sie werden erstaunt sein, wie viele Dateioperationen bei verschiedenen Programmaufrufen (z. B. bei Mozilla Firefox) oder auch im Leerlauf ausgeführt werden.
Im Beispiel verwenden wir die Funktion Select() der TextBox, in welcher wir Logausgaben zu den Dateioperationen ausführen, um den Textzeiger zu setzen (im Beispiel ans Ende). Die Funktion ScrollToCaret() scrollt bis zur Position, an welcher sich der Cursor befindet. Wie Ihnen vermutlich auffallen wird, befindet sich der komplette Code in einem Block mit dem Funktionsaufruf Invoke(). Dies ist zwingend notwendig, da die grafische Oberfläche auf einem anderen Task arbeitet wie die FileSystemWatcher-Klasse. Invoke() erlaubt den Zugriff auf die Elemente der grafischen Oberfläche von einem anderen Task heraus. Hierfür muss ein delegate-Block deklariert werden, welcher für die Invoke()-Funktion in MethodInvoker gecastet werden muss.
Form1.cs
using System; using System.IO; using System.Windows.Forms; namespace CSV20.Ordnerüberwachung { public partial class Form1 : Form { private FileSystemWatcher oFileWatch = new FileSystemWatcher(); public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { if (folderBrowserDialogOrdner.ShowDialog() == System.Windows.Forms.DialogResult.OK) { // Ordner-Pfad und Filter setzen oFileWatch.Path = folderBrowserDialogOrdner.SelectedPath; oFileWatch.Filter = "*.*"; // Events registrieren (wir könnten auch für jedes Event eine eigene Event-Funktion verwenden) oFileWatch.Created += new FileSystemEventHandler(oFileWatch_Action); oFileWatch.Changed += new FileSystemEventHandler(oFileWatch_Action); oFileWatch.Deleted += new FileSystemEventHandler(oFileWatch_Action); // Event für Umbennung registrieren (wir benötigen die RenamedEventArgs um den // alten und neuen Namen der Datei / des Ordners zu erhalten) oFileWatch.Renamed += new RenamedEventHandler(oFileWatch_ActionRename); // Unterverzeichnisse mit einbeziehen oFileWatch.IncludeSubdirectories = true; // Events aktivieren oFileWatch.EnableRaisingEvents = true; // Fenster minimieren und danach wieder in normale Fenstergröße zurückkehren, // dies ist ein Trick, sodass wir die Anwendung wieder in den Vordergrund bekommen this.WindowState = FormWindowState.Minimized; this.WindowState = FormWindowState.Normal; } else // Bei Abbruch, Fenster schließen Close(); } void oFileWatch_Action(object sender, FileSystemEventArgs e) { this.Invoke((MethodInvoker)delegate { textBoxLog.Text += DateTime.Now.ToString(); switch (e.ChangeType) { case WatcherChangeTypes.Created: textBoxLog.Text += " Erstellt "; break; case WatcherChangeTypes.Changed: textBoxLog.Text += " Geändert "; break; case WatcherChangeTypes.Deleted: textBoxLog.Text += " Gelöscht "; break; } textBoxLog.Text += e.FullPath + "\r\n"; textBoxLog.Select(textBoxLog.Text.Length, 0); textBoxLog.ScrollToCaret(); }); } void oFileWatch_ActionRename(object sender, RenamedEventArgs e) { this.Invoke((MethodInvoker)delegate { textBoxLog.Text += DateTime.Now.ToString(); textBoxLog.Text += " Umbenannt "; textBoxLog.Text += e.OldFullPath + " zu "; textBoxLog.Text += e.FullPath + "\r\n"; textBoxLog.Select(textBoxLog.Text.Length, 0); textBoxLog.ScrollToCaret(); }); } } }