FileSystemWatcher et multiples events

Le
Delf
Bonjour.

J'ai des problèmes d'events levés plusieurs fois d'affilé.

Voici les classes :

=


using System;
using System.IO;

namespace Framework.IO.FileObjectMapper
{
public delegate void
FileObjectMapperHandler(FileObjectMapperEventArgs e);

public class FileObjectMapper
{
private readonly string m_File = null;
private readonly FileObjectMapperHandler m_Callback = null;
private FileSystemWatcher m_FileWatcher = null;

public FileObjectMapper(string pFile, FileObjectMapperHandler
pCallback, bool pLoadIfExists)
{
m_File = Path.GetFullPath(pFile);
m_Callback = pCallback;

if (pLoadIfExists && File.Exists(m_File))
{
this.OnFileChangedOrCreated(
this, new
FileSystemEventArgs(WatcherChangeTypes.All,
Path.GetDirectoryName(m_File), m_File));
}

m_FileWatcher = new
FileSystemWatcher(Path.GetDirectoryName(m_File),
Path.GetFileName(m_File));

m_FileWatcher.NotifyFilter = NotifyFilters.LastWrite;

m_FileWatcher.Created += new
FileSystemEventHandler(this.OnFileChangedOrCreated);
m_FileWatcher.Changed += new
FileSystemEventHandler(this.OnFileChangedOrCreated);

m_FileWatcher.EnableRaisingEvents = false;
m_FileWatcher.IncludeSubdirectories = false;
}

~FileObjectMapper()
{
if (m_FileWatcher != null)
{
m_FileWatcher.EnableRaisingEvents = false;
m_FileWatcher.Dispose();

m_FileWatcher = null;
}
}

public void StartListening()
{
m_FileWatcher.EnableRaisingEvents = true;
}

public void StopListening()
{
m_FileWatcher.EnableRaisingEvents = false;
}

private void OnFileChangedOrCreated(object sender,
FileSystemEventArgs e)
{
m_Callback(new FileObjectMapperEventArgs(e.FullPath));
}
}
}

=


using System;

namespace Framework.IO.FileObjectMapper
{
public class FileObjectMapperEventArgs : EventArgs
{
private string m_File = null;
private DateTime m_DateTime;

public FileObjectMapperEventArgs(string pFile)
{
m_File = pFile;
m_DateTime = DateTime.Now;
}

public string File
{
get
{
return m_File;
}
}

public DateTime DateTime
{
get
{
return m_DateTime;
}
}
}
}

=


Voici l'usage de l'objet :

=


FileObjectMapper fileObjectMapper =
new FileObjectMapper(@"C:TestConfig.xml",
this.LoadXmlFileInObject, false);

fileObjectMapper.StartListening();
Console.ReadLine();

fileObjectMapper.StopListening();
Console.ReadLine();



private void LoadXmlFileInObject(FileObjectMapperEventArgs e)
{
Console.WriteLine(string.Format("Loading {0} | {1}", e.File,
e.DateTime.ToString("HH:MM:ss.ffff")));
}

=


Résultat quand je modifie le fichier avec NotePad :

Loading C:TestConfig.xml | 16:08:05.0771
Loading C:TestConfig.xml | 16:08:05.0771

=> event OnChanged levé 2x

Résultat quand je modifie le fichier avec PSPad :

Loading C:TestConfig.xml | 16:08:05.0771
Loading C:TestConfig.xml | 16:08:05.0771
Loading C:TestConfig.xml | 16:08:05.0771

=> event OnChanged levé 3x

Si je créé le fichier, aucun event levé

Comment ce fait-il que le FileSystemWatcher pose autant de
problèmes ?!
Je ne suis pas le seul dans ce cas là

Merci pour toute aide.

--
Delf
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
soudep
Le #16552361
Hi Delf,

1). J'ai crée un variable global qui va stocker la chaîne
contenant le nom du fichier qui sera "déplacé".
Je l'appelle "strFileName"
2). Ensuite j'ajoute un évènement OnChanged qui fait la
vérification suivante avant d'afficher
dans la console :
Si (le type de changement = changement ET
la chaîne contenant le nom de fichier n'est pas le
même que celui d'avant
)
Alors
Afficher le message

private static void OnChanged(object source,
FileSystemEventArgs e)
{
if (e.ChangeType == WatcherChangeTypes.Changed &&
!(strFileName.Equals(e.FullPath))
)
{
Console.WriteLine("Fichier: " + e.FullPath + " " +
e.ChangeType);
strFileName = e.FullPath;
}
}
3). Il faudra peut-être remplacer la chaîne "strFileName" par
un HashTable contenant tous les noms de fichiers qui seront crées et
parcourir le HashTable pour faire les choses encore plus
correctement ?
Peut-être 1 HashTable pour les fichiers crées, 1 autre
pour les fichiers modifiés, 1 autre pour ceux effacés et 1 autre pour
ceux qui seront renommés ...

------------------------------------------------------- Copies-colles
tout ça et testes ---------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Security.Permissions;
using System.Collections;

namespace FileSystemWatcher1
{
public class Watcher
{

#region Declare Class Variables
public static string strFileNameChanged = string.Empty;
public static string strFileNameDeleted = string.Empty;
public static string strFileNameCreated = string.Empty;
#endregion

public static void Main() { Run(); }

#region Run Method
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public static void Run()
{
//Console.ReadLine();
string[] args = System.Environment.GetCommandLineArgs();
// If a directory is not specified, exit program.
if (args.Length != 2)
{
// Display the proper way to call the program.
Console.WriteLine("Usage: Watcher.exe (directory)");
Console.ReadLine();
}

// Create a new FileSystemWatcher and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();

watcher.Path = "C:\TEMP";//args[1];
/* Watch for changes in LastAccess and LastWrite times,
and
the renaming of files or directories. */
watcher.NotifyFilter = NotifyFilters.CreationTime |
NotifyFilters.LastAccess |
NotifyFilters.LastWrite |
NotifyFilters.FileName |
NotifyFilters.DirectoryName;
// Only watch text files.
watcher.Filter = "*.txt";

// Add event handlers.

watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.Deleted += new FileSystemEventHandler(OnChanged);
watcher.Renamed += new RenamedEventHandler(OnRenamed);

// Begin watching.
watcher.EnableRaisingEvents = true;

// Wait for the user to quit the program.
Console.WriteLine("Press 'q' to quit the sample.");
while (Console.Read() != 'q') ;
}
#endregion

#region Define the event handlers.
private static void OnChanged(object source,
FileSystemEventArgs e)
{
if (e.ChangeType == WatcherChangeTypes.Changed &&
!(strFileNameChanged.Equals(e.FullPath)))
{
// Specify what is done when a file is changed,
created, or deleted.
Console.WriteLine("File: " + e.FullPath + " " +
e.ChangeType);
strFileNameChanged = e.FullPath;

}
else if (e.ChangeType == WatcherChangeTypes.Deleted &&
!(strFileNameDeleted.Equals(e.FullPath)))
{
// Specify what is done when a file is changed,
created, or deleted.
Console.WriteLine("File: " + e.FullPath + " " +
e.ChangeType);
strFileNameDeleted = e.FullPath;

}
else if (e.ChangeType == WatcherChangeTypes.Created &&
!(strFileNameCreated.Equals(e.FullPath)))
{
// Specify what is done when a file is changed,
created, or deleted.
Console.WriteLine("File: " + e.FullPath + " " +
e.ChangeType);
strFileNameCreated = e.FullPath;
}
}

private static void OnRenamed(object source, RenamedEventArgs
e)
{
// Specify what is done when a file is renamed.
if (e.ChangeType == WatcherChangeTypes.Renamed)
Console.WriteLine("File: {0} renamed to {1}",
e.OldFullPath, e.FullPath);
}
#endregion

}//fin Class
}//fin NameSpace


- Anthony SEROU
Delf
Le #16576981
On 15 août, 13:20, soudep
        1). J'ai crée un variable global qui va stocker la cha îne
contenant le nom du fichier qui sera "déplacé".
             Je l'appelle "strFileName"
        2). Ensuite j'ajoute un évènement OnChanged qui fait la
vérification suivante avant d'afficher
             dans la console :
             Si (le type de changement = changement ET
                  la chaîne contenant le nom de fichi er n'est pas le
même que celui d'avant
                 )
             Alors
                 Afficher le message



En fait notepad fait 2 modifications et PSPad 3... j'ai donc rajouté
un membre de type DateTime.

Si j'intercepte 2 events "simultanés", je traite le 1° mais pas le 2°
car délai < N secs (ici fixé à 2 secs).
Le problème c'est que le traitement final (m_Callback), suite à
l'event peut prendre lui aussi du temps.

=> je détache le traitement synchrone lié à l'event via le ThreadPo ol
et je n'ai donc plus à gérer le délai du traitement.

using System;
using System.IO;
using System.Threading;

namespace Framework.IO.FileObjectMapper
{
public delegate void
FileObjectMapperHandler(FileObjectMapperEventArgs e);

public class FileObjectMapper : IDisposable
{
private readonly string m_File = null;

private readonly FileObjectMapperHandler m_Callback = null;
private readonly FileSystemWatcher m_FileWatcher = null;

private DateTime m_LastEvent;

public FileObjectMapper(string pFile, FileObjectMapperHandler
pCallback)
{
if (pFile == null || pFile.Trim().Length == 0)
{
throw new ArgumentNullException("File", "The 'File'
cannot be 'null' or empty'");
}

m_File = Path.GetFullPath(pFile);

if (pCallback == null)
{
throw new ArgumentNullException("Callback", "The
'Callback' cannot be 'null'");
}

m_Callback = pCallback;

string fileDirectory = Path.GetDirectoryName(m_File);

m_FileWatcher = new FileSystemWatcher(fileDirectory,
Path.GetFileName(m_File));

m_FileWatcher.NotifyFilter = NotifyFilters.LastWrite;

m_FileWatcher.Created += new
FileSystemEventHandler(this.OnFileChangedOrCreated);
m_FileWatcher.Changed += new
FileSystemEventHandler(this.OnFileChangedOrCreated);
}

~FileObjectMapper()
{
this.Dispose(false);
}

public void StartListening()
{
m_FileWatcher.EnableRaisingEvents = true;
}

public void StopListening()
{
m_FileWatcher.EnableRaisingEvents = false;
}

private void OnFileChangedOrCreated(object sender,
FileSystemEventArgs e)
{
ThreadPool.QueueUserWorkItem(new
WaitCallback(this.OnFileChangedOrCreated), e);
}

private void OnFileChangedOrCreated(object pState)
{
if (DateTime.Now - m_LastEvent >= TimeSpan.FromSeconds(2))
{
m_LastEvent = DateTime.Now;

m_Callback(new
FileObjectMapperEventArgs((FileSystemEventArgs)pState));
}
}

#region [ IDisposable Members ]

public void Dispose()
{
this.Dispose(true);
}

private bool m_IsDisposed = false;

private void Dispose(bool pDisposing)
{
if (!m_IsDisposed)
{
if (m_FileWatcher != null)
{
m_FileWatcher.EnableRaisingEvents = false;
m_FileWatcher.Dispose();
}

m_IsDisposed = true;

if (pDisposing)
{
GC.SuppressFinalize(this);
}
}
}

#endregion
}
}

--
Delf
Delf
Le #16584151
Bien, l'event sur la création du fichier ne fonctionne pas... j'ai
beau créer le fichier, le système ne bronche pas.

Terrible cette classe...

--
Delf
Delf
Le #16584581
On 20 août, 14:01, Delf
Bien, l'event sur la création du fichier ne fonctionne pas... j'ai



m_FileWatcher.NotifyFilter = NotifyFilters.CreationTime |
NotifyFilters.LastWrite;

--
Delf
Delf
Le #16588441
Ce qui donne au final :

using System;
using System.IO;
using System.Threading;

namespace Framework.IO.FileObjectMapper
{
public delegate void FileObjectMapperHandler(
FileObjectMapperEventArgs e);

public class FileObjectMapper : IDisposable
{
private readonly string m_File = null;

private readonly FileObjectMapperHandler m_Callback = null;
private readonly FileSystemWatcher m_FileWatcher = null;

private int m_DelayBetweenModification = 2;

private DateTime m_LastEvent;

public FileObjectMapper(
string pFile, FileObjectMapperHandler pCallback)
{
if (pFile == null || pFile.Trim().Length == 0)
{
throw new ArgumentNullException("File",
"The 'File' cannot be 'null' or empty'");
}

m_File = Path.GetFullPath(pFile);

if (pCallback == null)
{
throw new ArgumentNullException("Callback",
"The 'Callback' cannot be 'null'");
}

m_Callback = pCallback;

string fileDirectory = Path.GetDirectoryName(m_File);

m_FileWatcher = new FileSystemWatcher(
fileDirectory, Path.GetFileName(m_File));

m_FileWatcher.NotifyFilter NotifyFilters.CreationTime | NotifyFilters.LastWrite;

m_FileWatcher.Changed += new FileSystemEventHandler(
this.OnFileChangedOrCreated);
}

~FileObjectMapper()
{
this.Dispose(false);
}

public int DelayBetweenModification
{
get
{
return m_DelayBetweenModification;
}
set
{
if (value > 0)
{
m_DelayBetweenModification = value;
}
}
}

public void StartListening()
{
m_FileWatcher.EnableRaisingEvents = true;
}

public void StopListening()
{
m_FileWatcher.EnableRaisingEvents = false;
}

public bool ForceCall()
{
bool bCalled = File.Exists(m_File);

if (bCalled)
{
m_Callback(new FileObjectMapperEventArgs(
WatcherChangeTypes.All,
Path.GetDirectoryName(m_File),
Path.GetFileName(m_File)));
}

return bCalled;
}

private void OnFileChangedOrCreated(
object sender, FileSystemEventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(
this.OnFileChangedOrCreated), e);
}

private void OnFileChangedOrCreated(object pState)
{
bool bIsSafe = Monitor.TryEnter(this, 0);

if (bIsSafe && DateTime.Now - m_LastEvent > TimeSpan.FromSeconds(m_DelayBetweenModification))
{
FileSystemEventArgs e = pState as FileSystemEventArgs;

#region [ Waiting for the file accessibility ]

bool bAgain = true;

do
{
FileStream stream = null;

try
{
// Can we get access to the file?

stream = new FileStream(
e.FullPath,
FileMode.Open,
FileAccess.Write,
FileShare.None,
8192);

bAgain = false;
}
finally
{
if (stream != null)
{
stream.Close();
stream.Dispose();
}
}

if (bAgain)
{
Thread.Sleep(TimeSpan.FromMilliseconds(250));
}
}
while (bAgain);

#endregion

m_LastEvent = DateTime.Now;

m_Callback(new FileObjectMapperEventArgs(e));
}

}

#region [ IDisposable Members ]

/// <summary>
/// Releases all resources used by the
/// System.ComponentModel.Component.
/// </summary>

public void Dispose()
{
this.Dispose(true);
}

private bool m_IsDisposed = false;

private void Dispose(bool pDisposing)
{
if (!m_IsDisposed)
{
if (m_FileWatcher != null)
{
m_FileWatcher.EnableRaisingEvents = false;
m_FileWatcher.Dispose();
}

m_IsDisposed = true;

if (pDisposing)
{
GC.SuppressFinalize(this);
}
}
}

#endregion
}
}

Le do/while permet de tester si le fichier est accessible.
Par exemple, si onupload un fichier et qu'un FileSystemWatcher est en
écoute dessus, le OnCreated est levé alors que le fichier n'a pas été
fermé -> exception...

Si vous avez des remarques, n'hésitez pas.

--
Delf
Publicité
Poster une réponse
Anonyme