Binding Events In another class - c#

this is my first C# post.
I have a question on event binding.
I have a FileWatcher which I'd like to bind to functions that are defined in a separate class called FileWatcherEvents.
I don't want the events to be declared in the Program class, how can this be done?
As you can see, I try to bind the events for Created and Deleted.
The problem is that these events are not called when I delete or create a file in the folder. But when I declare the event handlers in the Program class, it does work.
Any help or insights is appreciated.
Program
using System.IO;
class Program : ServiceBase
{
private FileSystemWatcher _watcher;
public Program()
{
FileWatcherEvents fwe = new FileWatcherEvents();
this._watcher = new FileSystemWatcher();
((System.ComponentModel.ISupportInitialize)(this._watcher)).BeginInit();
//
// _watcher
//
this._watcher.EnableRaisingEvents = true;
this._watcher.Filter = "*.txt";
this._watcher.NotifyFilter =
((NotifyFilters)(((((NotifyFilters.FileName
| NotifyFilters.DirectoryName)
| NotifyFilters.LastWrite)
| NotifyFilters.LastAccess)
| NotifyFilters.CreationTime)));
this._watcher.Path = "T:\\out\\";
this._watcher.Deleted += new FileSystemEventHandler(fwe.ShipmentFileCreated);
this._watcher.Created += new FileSystemEventHandler(fwe.FileDeleted);
((System.ComponentModel.ISupportInitialize)(this._watcher)).EndInit();
}
static void Main(string[] args)
{
Program prg = new Program();
Console.Write(FileManager.getNewestFile("T:\\out\\"));
while (Console.Read() != 'q') ;
}
}
FileWatcherEvents
class FileWatcherEvents
{
public void ShipmentFileCreated(object sender, FileSystemEventArgs e)
{
Console.WriteLine("CREATED: " + sender.ToString() + e.ToString());
}
public void FileDeleted(object sender, FileSystemEventArgs e)
{
Console.WriteLine("DELETED: " + sender.ToString() + e.ToString());
}
}

I believe you would need to declare fwe in a larger scope, like at the Program level instead of inside the Program constructor. Otherwise the object will go away, and possibly all the events that lead to it as well (never been entirely clear on what happens to the functions that handle events on an instance when the instance goes away, but the events could still occur, but it's very possible they will no longer run).
Edit:
I got your code to work with some minor adjustments. Mainly I had to move EnableRaisingEvents to the end of the block because .NET throws an exception if you do it before setting the path. How did you not see that exception?
class Program
{
private FileSystemWatcher _watcher;
public Program()
{
FileWatcherEvents fwe = new FileWatcherEvents();
this._watcher = new System.IO.FileSystemWatcher();
this._watcher.Filter = "*.txt";
this._watcher.NotifyFilter = ((System.IO.NotifyFilters)(((((
System.IO.NotifyFilters.FileName | System.IO.NotifyFilters.DirectoryName)
| System.IO.NotifyFilters.LastWrite) | System.IO.NotifyFilters.LastAccess)
| System.IO.NotifyFilters.CreationTime)));
this._watcher.Path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
this._watcher.Deleted += new System.IO.FileSystemEventHandler(fwe.ShipmentFileCreated);
this._watcher.Created += new System.IO.FileSystemEventHandler(fwe.FileDeleted);
this._watcher.EnableRaisingEvents = true;
Console.ReadLine();
}
public static void Main()
{
Program prg = new Program();
using (System.IO.StreamWriter w = new System.IO.StreamWriter(
System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "TestFile.txt"), false))
{
w.WriteLine(DateTime.Now.ToLongTimeString());
}
Console.ReadLine();
}
}
class FileWatcherEvents
{
public void ShipmentFileCreated(object sender, FileSystemEventArgs e)
{
Console.WriteLine("CREATED: " + sender.ToString() + e.ToString());
}
public void FileDeleted(object sender, FileSystemEventArgs e)
{
Console.WriteLine("DELETED: " + sender.ToString() + e.ToString());
}
}

Turns out that the code works fine, the events fired, but the functions weren't because of the *.txt filter in the private FileSystemWatcher object.

Related

Windows Forms - detecting an external application closing to trigger an event

I am in need of a solution to trigger code when an external application is closing / closes.
I am unable to use System.Diagnostics Process.GetProcessByName to detect if the process is running since it might conflict with an anticheat system. I would need trigger the snippet of code only when the program closes and only then.
I made a good, event-based implementation.
class Monitor
{
public event EventHandler ProgramStarted;
public event EventHandler ProgramClosed;
public Monitor(string process)
{
string pol = "2";
if (!process.EndsWith(".exe")) process += ".exe";
var queryString =
"SELECT *" +
" FROM __InstanceOperationEvent " +
"WITHIN " + pol +
" WHERE TargetInstance ISA 'Win32_Process' " +
" AND TargetInstance.Name = '" + process + "'";
var s = #"\\.\root\CIMV2";
ManagementEventWatcher watcher = new ManagementEventWatcher(s, queryString);
watcher.EventArrived += new EventArrivedEventHandler(OnEventArrived);
watcher.Start();
}
private void OnEventArrived(object sender, EventArrivedEventArgs e)
{
if (e.NewEvent.ClassPath.ClassName.Contains("InstanceDeletionEvent"))
{
EventHandler handler = ProgramClosed;
handler?.Invoke(this, e);
}
else if (e.NewEvent.ClassPath.ClassName.Contains("InstanceCreationEvent"))
{
EventHandler handler = ProgramStarted;
handler?.Invoke(this, e);
}
}
}
To use it, you just create an instance of the class and set up the events. For example:
static void Main(string[] args)
{
var mon = new Monitor("chrome");
mon.ProgramClosed += Mon_ProgramClosed;
mon.ProgramStarted += Mon_ProgramStarted;
Console.ReadKey(true);
}
private static void Mon_ProgramStarted(object sender, EventArgs e)
{
MessageBox.Show("Program started.");
}
private static void Mon_ProgramClosed(object sender, EventArgs e)
{
MessageBox.Show("Program closed.");
}
Make sure to add reference to System.Drawing if you're using a console app, and ,for winforms, adjust the modifiers.

c# FileSystemWatcher trigger delete event when i create a new New Microsoft Excel Worksheet.xlsx file

C# FileSystemWatcher trigger delete event when i create new "New Microsoft Excel Worksheet.xlsx" file.
Step 1: I create new file "New Microsoft Excel Worksheet.xlsx"
Step 2: The FileSystemWatcher trigger Create event
Step 3: The FileSystemWatcher trigger Delete event
I don't want to have any delete events when I create an excel file.
This is my code:
class Program
{
static void Main(string[] args)
{
var monitorSyncing = new MonitorSyncing(ConfigurationManager.AppSettings["MONITORING_FOLDER_PATH"]);
monitorSyncing.Monitoring();
Console.ReadLine();
}
}
public class MonitorSyncing
{
public MonitorSyncing(string monitorPath)
{
this._monitorPath = monitorPath;
}
private readonly string _monitorPath;
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public void Monitoring()
{
Console.WriteLine($"*** START MONITOR FOLDER: {this._monitorPath} ***");
var fileSystemWatcherSyncFolderUserSettings = new FileSystemSafeWatcher
{
Path = _monitorPath,
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.Security,
InternalBufferSize = 24576,
};
fileSystemWatcherSyncFolderUserSettings.Changed += OnChanged_SyncFolderUserSetting;
fileSystemWatcherSyncFolderUserSettings.Created += OnChanged_SyncFolderUserSetting;
fileSystemWatcherSyncFolderUserSettings.Deleted += OnChanged_SyncFolderUserSetting;
fileSystemWatcherSyncFolderUserSettings.Renamed += OnRenamed_SyncFolderUserSetting;
fileSystemWatcherSyncFolderUserSettings.Error += OnError;
fileSystemWatcherSyncFolderUserSettings.IncludeSubdirectories = true;
fileSystemWatcherSyncFolderUserSettings.EnableRaisingEvents = true;
}
private static void OnChanged_SyncFolderUserSetting(object source, FileSystemEventArgs e)
{
switch (e.ChangeType)
{
case WatcherChangeTypes.Created:
Console.WriteLine($"{DateTime.Now:dd-MM-yyyy HH:mm:ss.fff}. Created file: {e.FullPath}");
break;
case WatcherChangeTypes.Deleted:
Console.WriteLine($"{DateTime.Now:dd-MM-yyyy HH:mm:ss.fff}. Deleted file: {e.FullPath}");
break;
case WatcherChangeTypes.Changed:
Console.WriteLine($"{DateTime.Now:dd-MM-yyyy HH:mm:ss.fff}. Edit content file: {e.FullPath}");
break;
}
}
private static void OnRenamed_SyncFolderUserSetting(object source, RenamedEventArgs e)
{
// Show that a file has been renamed.
Console.WriteLine($"{DateTime.Now:dd-MM-yyyy HH:mm:ss.fff}. Renamed file '{e.OldFullPath}' to '{e.FullPath}'");
}
private static void OnError(object source, ErrorEventArgs e)
{
// Show that an error has been detected.
Console.WriteLine($"Error - The FileSystemWatcher has detected an error : {e.GetException().Message}");
// Give more information if the error is due to an internal buffer overflow.
if (e.GetException().GetType() == typeof(InternalBufferOverflowException))
{
Console.WriteLine(("Error - The file system watcher experienced an internal buffer overflow: " + e.GetException().Message));
}
}
}

FileSystemwatcher for a list of files

I have been following the guidance here FileSystemWatcher Changed event is raised twice.
However I have a list of files that I'm watching so if I delete 20 files together the event is called 20 times. This is expected and works fine.
How can I only have an event fired once for 20 "simultaneous" file changes (i.e How can I ignore all other file changes until the code in the first instance of Onchanged below has completed. Right now Onchanged is called 20 times.) ?
private void Main_Load(object sender, EventArgs e)
{
public static List<FileSystemWatcher> watchers = new List<FileSystemWatcher>();
UpdateWatcher();
}
public void OnChanged(object sender, FileSystemEventArgs e)
{
try
{
Logging.Write_To_Log_File("Item change detected " + e.ChangeType + " " + e.FullPath + " " + e.Name, MethodBase.GetCurrentMethod().Name, "", "", "", "", "", "", 2);
watchers.Clear();
foreach (FileSystemWatcher element in MyGlobals.watchers)
{
element.EnableRaisingEvents = false;
}
//Do some processing on my list of files here....
return;
}
catch (Exception ex)
{
// If exception happens, it will be returned here
}
finally
{
foreach (FileSystemWatcher element in MyGlobals.watchers)
{
element.EnableRaisingEvents = true;
}
}
}
public void UpdateWatcher() // Check Items
{
try
{
watchers.Clear();
for (int i = 0; i < MyGlobals.ListOfItemsToControl.Count; i++) // Loop through List with for
{
FileSystemWatcher w = new FileSystemWatcher();
w.Path = Path.GetDirectoryName(MyGlobals.ListOfItemsToControl[i].sItemName); // File path
w.Filter = Path.GetFileName(MyGlobals.ListOfItemsToControl[i].sItemName); // File name
w.Changed += new FileSystemEventHandler(OnChanged);
w.Deleted += new FileSystemEventHandler(OnChanged);
w.Created += new FileSystemEventHandler(OnChanged);
w.EnableRaisingEvents = true;
watchers.Add(w);
}
}
catch (Exception ex)
{
// If exception happens, it will be returned here
}
}
The key point here is what does "together" mean to you. after all the system does an independent delete operation for each, which would technically mean they are not all deleted at the exact same time, but if you just wanna be close, let's say if they are all deleted within 5 seconds of each other then we only want OnChange to fire once, you can do the following. Note that this doesn't handle the rename change notification. You weren't listening for it, so I assumed you don't need to.
you may wanna change the 5 seconds window to a small window depending on your use.
class SlowFileSystemWatcher : FileSystemWatcher
{
public delegate void SlowFileSystemWatcherEventHandler(object sender, FileSystemEventArgs args);
public event SlowFileSystemWatcherEventHandler SlowChanged;
public DateTime LastFired { get; private set; }
public SlowFileSystemWatcher(string path)
: base(path)
{
Changed += HandleChange;
Created += HandleChange;
Deleted += HandleChange;
LastFired = DateTime.MinValue;
}
private void SlowGeneralChange(object sender, FileSystemEventArgs args)
{
if (LastFired.Add(TimeSpan.FromSeconds(5)) < DateTime.UtcNow)
{
SlowChanged.Invoke(sender, args);
LastFired = DateTime.UtcNow;
}
}
private void HandleChange(object sender, FileSystemEventArgs args)
{
SlowGeneralChange(sender, args);
}
protected override void Dispose(bool disposing)
{
SlowChanged = null;
base.Dispose(disposing);
}
}

filewatcher watch several files but handle them differently

I need to watch several file at different time and sometimes at the same time.
I am using this as a test:
namespace FilewatcherTest
{
public partial class Form1 : Form
{
private System.IO.FileSystemWatcher FSWatcherTest;
public Form1()
{
InitializeComponent();
FSWatcherTest = new FileSystemWatcher();
EventHandling();
FSWatcherTest.Path = #"d:\tmp";
FSWatcherTest.Filter = "file.txt";
// Begin watching.
FSWatcherTest.EnableRaisingEvents = true;
}
protected void EventHandling()
{
FSWatcherTest.Changed += FSWatcherTest_Changed;
FSWatcherTest.Deleted += FSWatcherTest_Deleted;
FSWatcherTest.Renamed += FSWatcherTest_Renamed;
FSWatcherTest.Created += FSWatcherTest_Created;
}
private void FSWatcherTest_Changed(object sender, System.IO.FileSystemEventArgs e)
{
WriteToLog("File Changed");
}
private void FSWatcherTest_Created(object sender, System.IO.FileSystemEventArgs e)
{
WriteToLog("File Created");
}
private void FSWatcherTest_Deleted(object sender, System.IO.FileSystemEventArgs e)
{
WriteToLog("File Deleted");
}
private void FSWatcherTest_Renamed(object sender, System.IO.RenamedEventArgs e)
{
WriteToLog("File Renamed");
}
private void WriteToLog(string message)
{
using (var sw = new StreamWriter(#"d:\tmp\service.log", true))
{
sw.WriteLine(string.Format("{0} {1}", DateTime.Now,message));
}
}
}
}
Of course I'll change the hardcoded paths once I have something in place since this is going into a service I created.
My question is, can I use the same file watcher or should I use a unique one for each file?
If I use the same one, how do I know which file is raising the event?
Thanks!!
EDIT
Sorry I haven't used filesystemwatcher before and didn't know it mattered but the files will be in different directories and not of the same file type.
can I use the same file watcher or should I use a unique one for each file?
In your case, I don't think there is a reason to create a new instance of FileSystemWatcher for every file you're watching. Yes, you can use the same one. You can use a filter such as "*.txt" or whatever you need to watch a set of files...
If I use the same one, how do I know which file is raising the event?
The FileSystemEventArgs has a Name property which returns the name of the file that triggered the event.
So for example:
private void FSWatcherTest_Created(object sender, System.IO.FileSystemEventArgs e)
{
string fileName = e.Name;
WriteToLog("File Created: " + fileName);
}

cross-thread calls?

This is mt first time trying to write a not web based program, and my first time writing anything in C#.
I need a program that monitors folders, but I can't get it to work.
I have used the example from this post Using FileSystemWatcher with multiple files but is trying to make it a form.
My current problem comes in the ProcessQueue function where fileList apparently is defined in another thread.
Whenever a file is actually submitted to the watched folder I get an error that using fileList is a cross thread call
Can anyone explain this error to me, and how to fix it?
namespace matasWatch
{
public partial class Form1 : Form
{
private int n = 1;
private bool isWatching = false;
private List<string> filePaths;
private System.Timers.Timer processTimer;
private string watchedPath;
private FileSystemWatcher watcher;
public Form1()
{
filePaths = new List<string>();
watchedPath = "C:\\Users\\username\\Desktop\\test";
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (!isWatching)
{
button1.Text = "Stop";
isWatching = true;
watcher = new FileSystemWatcher();
watcher.Filter = "*.*";
watcher.Created += Watcher_FileCreated;
watcher.Error += Watcher_Error;
watcher.Path = watchedPath;
watcher.IncludeSubdirectories = true;
watcher.EnableRaisingEvents = true;
}
else {
button1.Text = "Watch";
isWatching = false;
watcher.EnableRaisingEvents = false;
watcher.Dispose();
watcher = null;
}
}
private void Watcher_Error(object sender, ErrorEventArgs e)
{
// Watcher crashed. Re-init.
isWatching = false;
button1_Click(sender, e);
}
private void Watcher_FileCreated(object sender, FileSystemEventArgs e)
{
filePaths.Add(e.FullPath);
if (processTimer == null)
{
// First file, start timer.
processTimer = new System.Timers.Timer(2000);
processTimer.Elapsed += ProcessQueue;
processTimer.Start();
}
else{
// Subsequent file, reset timer.
processTimer.Stop();
processTimer.Start();
}
}
private void ProcessQueue(object sender, ElapsedEventArgs args)
{
try
{
fileList.BeginUpdate();
foreach (string filePath in filePaths)
{
fileList.Items.Add("Blaa");
}
fileList.EndUpdate();
filePaths.Clear();
}
finally
{
if (processTimer != null)
{
processTimer.Stop();
processTimer.Dispose();
processTimer = null;
}
}
}
}
}
I assume that fileList is a windows forms control. The ProcessQueue method is called from a timer thread which is by default a background thread. The fileList control resides in the UI thread. You need to use the Invoke() method of the form passing it in a delegate the updates the fileList control.
Invoke(new Action(() =>
{
fileList.BeginUpdate();
foreach (string filePath in filePaths)
{
fileList.Items.Add("Blaa");
}
fileList.EndUpdate();
filePaths.Clear();
}));
Try using System.Windows.Forms.Timer instead of System.Timers.Timer so the timer tick event is executed on the UI thread.
See here for more details.

Categories