Multiple FileSystem watcher with multiple listbox control - c#

I'm trying to make a monitoring page to monitor various filesystem watcher running to do some job. What I need to know is how do you get multiple filesystem watcher's to access the list boxes in the UI thread.. Here is some code:
private void WatchFile(TextBox ctrlTB,ListBox ctrlLB,FileSystemWatcher _watcher)
{
// FileSystemWatcher _watcher = new FileSystemWatcher();
//var localTB = ctrlTB as TextBox;
//var localLB = ctrlLB as ListBox;
_watcher.Path = ctrlTB.Text;
_watcher.Path = ctrlTB.Text;
_watcher.NotifyFilter = NotifyFilters.LastWrite;
_watcher.Filter = "*.xml";
_watcher.Changed += new FileSystemEventHandler(convertXML);
// _watcher.Changed += (s, e) => convertXML(s,e);
// _watcher.Error += new ErrorEventHandler(WatcherError);
_watcher.EnableRaisingEvents = true;
_watcher.IncludeSubdirectories = false;
ctrlLB.Items.Add("Started Monitoring # " + ctrlTB.Text);
ctrlLB.SelectedIndex = ctrlLB.Items.Count - 1;
}
public void convertXML(object source, FileSystemEventArgs f)
{
/// some job
}
I need to post back the status for each filesystemwatcher back to it's respective listbox. I'm declaring the FSW's at the click of the start button. Every List box has a start button where it would be declared separately. An e.g.:
private void button9_Click(object sender, EventArgs e)
{
if (!Directory.Exists(this.textBox1.Text))
{
//Form2.ActiveForm.Text = "Please select Source Folder";
// popup.Show("Please Select Source Folder");
MessageBox.Show("Please Select Proper Source Folder");
return;
}
else
{
textBox1.Enabled = false;
button9.Enabled = false;
button1.Enabled = false;
// button4.Enabled = false;
FileSystemWatcher _watcher = new FileSystemWatcher();
_watcher.SynchronizingObject = this;
WatchFile(textBox1,listBox1 ,_watcher);
}
}
How does the thread know which contrl listbox to access.

Encapsulate your WatchFile and convertXml into its own class like so
public class MyFileWatcher {
private TextBox _textBox;
private ListBox _listBox;
FileSystemWatcher _watcher;
public MyFileWatcher(TextBox textBox, ListBox listBox, ISynchronizeInvoke syncObj) {
this._textBox = textBox;
this._listBox = listBox;
this._watcher = new FileSystemWatcher();
this._watcher.SynchronizingObject = syncObj;
this._watcher.Path = textBox.Text;
this._watcher.Changed += new FileSystemEventHandler(convertXML);
this._watcher.EnableRaisingEvents = true;
this._watcher.IncludeSubdirectories = false;
// add any other required initialization of the FileSystemWatcher here.
}
protected virtual void convertXML(object source, FileSystemEventArgs f) {
// interact with this._textBox and this._listBox here as required.
// e.g.
// this._listBox.Items.Add(f.FullPath);
}
}
This way you can tie the FileSystemWatcher instance to a particular TextBox and ListBox, or any other object you desire.
Then to replace your button9_Click method:
private void button9_Click(object sender, EventArgs e)
{
if (!Directory.Exists(this.textBox1.Text))
{
//Form2.ActiveForm.Text = "Please select Source Folder";
// popup.Show("Please Select Source Folder");
MessageBox.Show("Please Select Proper Source Folder");
return;
}
else
{
textBox1.Enabled = false;
button9.Enabled = false;
button1.Enabled = false;
// instantiate your new instance of your MyFileWatcher class.
MyFileWatcher myWatcher = new MyFileWatcher(textBox1,listBox1 ,this);
}
}
Note: I have not actually compiled or executed this code so there may be exceptions. But the overall pattern should solve what you're after.

Related

Combining Two buttons operation into single button in Visual Studio 2019 & C#

I have created a GUI in Visual Studio where I have two buttons. One button is used to show ip and second button is used to take that ip and run python script.
But I want to merge these both operations into only one button, that if clicked for the first time should show/take the ip, and when clicked a 2nd time, it should trigger the Python script and all that in single button click.
My code is:
private void button1_Click(object sender, EventArgs e)
{
var fileName = "C:\\test.txt";
var sr = new StreamReader(fileName);
string fileContent = sr.ReadToEnd();
var startBlock = "hello";
var endBlock = "exit";
var ip = ParseForIpRegex(fileContent, startBlock, endBlock);
myTextBox.Text = ip; //Change this to match your code
}
private readonly string IPV4_PATTERN = "[0-9.]";
private string ParseForIpRegex(string textToSearch, string startBlock, string endBlock)
{
var pattern = $#"{startBlock}\D*\s*({IPV4_PATTERN}+).*{endBlock}";
var ms = Regex.Match(textToSearch, pattern, RegexOptions.Singleline | RegexOptions.IgnoreCase);
if (ms.Groups.Count > 0)
{
return ms.Groups[1].Value;
}
return string.Empty;
}
private void button1_Click_1(object sender, EventArgs e)
{
var hostname = myTextBox.Text;
var username = textBox1.Text;
var password = textBox2.Text;
var psi = new ProcessStartInfo();
psi.FileName = #"C:\Python38\python.exe";
var script = #"C:pythonscript.py";
//var config_file = file_path;
psi.Arguments = $"{script} {hostname} {username} {password}";
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
var errors = "";
var results = "";
MessageBox.Show("script processing");
using (var process = Process.Start(psi))
{
errors = process.StandardError.ReadToEnd();
results = process.StandardOutput.ReadToEnd();
}
Console.WriteLine("ERRORS:");
Console.WriteLine(errors);
Console.WriteLine();
Console.WriteLine("Results:");
Console.WriteLine(results);
if (errors != "")
{
MessageBox.Show(errors);
}
else
{
MessageBox.Show(results);
}
}
Please let me know how I can merge this both operation into one. That in one click 1st button operation will perform and after that 2nd in single click.
I suppose you need to keep all buttons, so you dont use args, just call the method:
private void button1_Click(object sender, EventArgs e)
{
//DO job
// simulate the second click
button1_Click_1(sender, e)
}
private void button1_Click_1(object sender, EventArgs e)
{
//you could test args to check if its coming from first button
}
The answer of Frenchy, or you can add 2 handers on the event...
depending your preference.
this.button1.Click += new System.EventHandler(this.button1_Click);
this.button1.Click += new System.EventHandler(this.button1_Click_1);
take note that one of the two the designer will add it for you.
So add the second at form_load event (or where u see fit)
Cheers

Using file.move to rename new files in C#

I am very new to coding and I'm writing an application which will rename files
by appending milliseconds to the existing filename of files which have been scanned in from an MFD.
The folder is a shared folder and the renamed file should stay within it and not be copied elsewhere.
From doing a lot of searching, I know that File.Move is my way forward, however, I can't seem to get it to work.
Here's my code:
private void MonitorToggle_Click(object sender, EventArgs e)
{
// Create a new FileSystemWatcher object.
FileSystemWatcher fsWatcher = new FileSystemWatcher();
switch (MonitorToggle.Text)
{
// Start Monitoring…
case startMonitoring:
if (!FilePathField.Text.Equals(String.Empty))
{
//Set the watched folder path equal to the file path variable
fsWatcher.Path = FilePathField.Text;
// Set Filter.
fsWatcher.Filter = (FileTypeField.Text.Equals(String.Empty))? "*.*" : FileTypeField.Text;
// Monitor files and subdirectories.
fsWatcher.IncludeSubdirectories = true;
// Monitor all changes specified in the NotifyFilters.
fsWatcher.NotifyFilter = NotifyFilters.LastWrite;
fsWatcher.EnableRaisingEvents = true;
// Raise Event handlers.
fsWatcher.Changed += new FileSystemEventHandler(OnChanged);
fsWatcher.Created += new FileSystemEventHandler(OnCreated);
}
else
{
MessageBox.Show("Please select a folder to monitor.", "Warning",MessageBoxButtons.OK, MessageBoxIcon.Warning );
}
break;
// Stop Monitoring…
case stopMonitoring:
default:
fsWatcher.EnableRaisingEvents = false;
fsWatcher = null;
break;
}
}
public void OnChanged (object sender, FileSystemEventArgs e)
{
FileInfo file = new FileInfo(e.Name);
string dateStamp = DateTime.Now.ToString("fff");
string fName = file.Name;
string newFile = string.Concat(fName, dateStamp);
File.Move(fName,newFile);
}
public void OnCreated(object sender, FileSystemEventArgs e)
{
FileInfo file = new FileInfo(e.Name);
string dateStamp = DateTime.Now.ToString("fff");
string fName = file.Name;
string newFile = string.Concat(fName, dateStamp);
File.Move(fName, newFile);
}
private void BrowseButton_Click(object sender, EventArgs e)
{
// Create FolderBrowserDialog object.
FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
// Show a button to create a new folder.
folderBrowserDialog.ShowNewFolderButton = true;
DialogResult dialogResult = folderBrowserDialog.ShowDialog();
// Get selected path from FolderBrowserDialog control.
if (dialogResult == DialogResult.OK)
{
FilePathField.Text = folderBrowserDialog.SelectedPath;
Environment.SpecialFolder root = folderBrowserDialog.RootFolder;
}
}
Whenever I create a new file in the folder I'm watching, it does absolutely nothing. At first I thought it may be because I only had the "OnCreated" method, so I then copied it into the "OnChanged" method (I wasn't sure if copying an existing file into the folder counted as it being "created" but I've had no luck).
Out of interest, I'm also getting an exception thrown if I don't specify a type in the filter, but that's much less pressing at the minute.
If anyone can offer any pointers of where I may be going wrong, it'd be greatly appreciated.
There are a few problems with your code.
First, you should use e.FullPath instead of e.Name, otherwise the code will try to rename the file in the current directory, instead of watched directory.
Second, to receive Created event you should include NotifyFilters.FileName.
However, this will not help you much, because the file is locked in the created and changed events until the file is copied, so you'll get an exception. Also, you'll probably receive more than one Changed event (in my tests I always receive two, regardless of the file size, but it may be different on the different versions of Windows or .Net framework).
To fix this, you may use timers or threads to accomplish the task. Here's an example using ThreadPool thread. Whenever created is fired, you create a new thread. In the thread you check whether a file is locked (trying to open file), and when the file is unlocked, do the rename.
public class FileMon
{
public static void Run()
{
FileSystemWatcher fsWatcher = new FileSystemWatcher();
fsWatcher.Path = #"C:\Test\";
fsWatcher.Filter = "*.*" ;
fsWatcher.IncludeSubdirectories = true;
// Monitor all changes specified in the NotifyFilters.
fsWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName ;
fsWatcher.EnableRaisingEvents = true;
// Raise Event handlers.
fsWatcher.Changed += OnChanged;
fsWatcher.Created += OnCreated;
Console.WriteLine("[Enter] to end"); Console.ReadLine();
fsWatcher.EnableRaisingEvents = false;
}
static void Worker(object state)
{
FileSystemEventArgs fsArgs = state as FileSystemEventArgs;
bool done = false;
FileInfo fi = new FileInfo(fsArgs.FullPath);
do
{
try
{
using (File.Open(fsArgs.FullPath, FileMode.Open))
{
done = true;
}
}
catch
{
done = false;
}
Thread.Sleep(1000);
} while (!done);
Console.WriteLine("DOne");
string dateStamp = DateTime.Now.ToString("fff");
string fName = fi.FullName;
string newFile = fsArgs.FullPath + dateStamp;
File.Move(fsArgs.FullPath, newFile);
}
private static void OnCreated(object sender, FileSystemEventArgs e)
{
Console.WriteLine($"Created {e.ChangeType} : {e.Name}");
ThreadPool.QueueUserWorkItem(Worker, e);
}
static void OnChanged(object sender, FileSystemEventArgs e)
{
Console.WriteLine($"{e.ChangeType} : {e.FullPath}");
}
}

Read file when updated. Error The process cannot access the file because it is being used by another process

I'm want to read the last line of a CSV file when updated. When i save the updates the app brakes with this error "The process cannot access the file '' because it is being used by another process." My question is, the "another process" is the watcher? If it's the watcher how can i read the file when updated?
public partial class Auto_Window : Form
{
FileSystemWatcher watcher = new FileSystemWatcher();
private int i = 0;
public Auto_Window()
{
InitializeComponent();
watcher.Path = ConfigurationManager.AppSettings["CSVFolder"];
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Filter = ConfigurationManager.AppSettings["CSVFilter"];
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
}
private void OnChanged(object source, FileSystemEventArgs e)
{
i++;
var data = File.ReadAllLines(e.FullPath);
string last = data[data.Length - 1];
if (i == 1)
{
tb_art1.Invoke(new Action(() => tb_art1.Text = last));
}
if (i == 2)
{
tb_art2.Invoke(new Action(() => tb_art2.Text = "ART2"));
}
if (i == 3)
{
tb_art3.Invoke(new Action(() => tb_art3.Text = "ARTO3"));
MessageBox.Show("com?");
tb_art1.Invoke(new Action(() => tb_art1.Text = ""));
tb_art2.Invoke(new Action(() => tb_art2.Text = ""));
tb_art3.Invoke(new Action(() => tb_art3.Text = ""));
i = 0;
}
}
private void btn_auto_Click(object sender, EventArgs e)
{
this.Close();
}
private void btn_conf_Click(object sender, EventArgs e)
{
if ((Control.ModifierKeys & Keys.Shift) != 0 && (Control.ModifierKeys & Keys.Control) != 0)
{
Config cfgform = new Config();
cfgform.ShowDialog();
watcher.Path = ConfigurationManager.AppSettings["CSVFolder"];
watcher.Filter = ConfigurationManager.AppSettings["CSVFilter"];
}
}
private void btn_clear_Click(object sender, EventArgs e)
{
tb_art1.Text = "";
tb_art2.Text = "";
tb_art3.Text = "";
i = 0;
}
}
Update: Code updated
I solved the problem adding watcher.EnableRaisingEvents = false; at the start of the event and watcher.EnableRaisingEvents = true; at the end to prevent duplicated modifications ins seconds.
The other problem was the way i was reading the file. I solved in this way:
var fs = new FileStream(e.FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
var reader = new StreamReader(fs);
await Task.Delay(500);
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
searchList.Add(line);
}

How can I make that the user will be able to search for a multiple texts with textBox?

Today when I type something any text in textBox1 and then click the start button it will search inside files for the text i typed in textBox1.
Now I want to add something somehow that if the user type in the textBox1 for example: hello,hi it will search for hello and also for hi in the files. Not as one string/text but two separated. If I type: hello,hi,world now it will search in same time same files also for hello hi and world.
The textchanged event
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (textBox1.Text != "" && textBox3.Text != "" && Directory.Exists(textBox3.Text))
{
startButton.Enabled = true;
Properties.Settings.Default["Setting2"] = textBox1.Text;
Properties.Settings.Default.Save();
}
else
{
startButton.Enabled = false;
}
}
The start button click event
private void startButton_Click(object sender, EventArgs e)
{
label21.Visible = true;
startButton.Enabled = false;
stopButton.Enabled = true;
pauseresumeButton.Enabled = true;
timer1.Start();
if (!backgroundWorker1.IsBusy)
{
SetWorkerMode(true);
backgroundWorker1.RunWorkerAsync();
}
}
Dowork event
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
_stopwatch.Restart();
DirSearch(textBox3.Text, textBox2.Text, textBox1.Text, worker, e);
_stopwatch.Stop();
}
The DirSearch method where I search for the text in files.
void DirSearch(string rootDirectory, string filesExtension, string textToSearch, BackgroundWorker worker, DoWorkEventArgs e)
{
List<string> filePathList = new List<string>();
List<string> restrictedFiles = new List<string>();
int overallfiles = 0;
int numberoffiles = 0;
int numberofdirs = 0;
try
{
filePathList = SearchAccessibleFilesNoDistinct(rootDirectory, null).ToList();
}
catch (Exception err)
{
string ad = err.ToString();
}
foreach (string file in filePathList)
{
try
{
_busy.WaitOne();
if (worker.CancellationPending == true)
{
e.Cancel = true;
return;
}
List<MyProgress> prog = new List<MyProgress>();
int var = File.ReadAllText(file).Contains(textToSearch) ? 1 : 0;
overallfiles++;
if (var == 1)
{
numberoffiles++;
prog.Add(new MyProgress { Report1 = file, Report2 = numberoffiles.ToString() });
backgroundWorker1.ReportProgress(0, prog);
}
numberofdirs++;
label1.Invoke((MethodInvoker)delegate
{
label1.Text = numberofdirs.ToString();
label1.Visible = true;
});
}
catch (Exception)
{
restrictedFiles.Add(file);
continue;
}
}
}
In DirSearch the variable textToSearch contains the text I typed in textBox1.
If I typed in textBox1 only HI so like it is now it will search in each file for the existing of HI.
But if I type HI,HELLO,WORLD
Now I want it to search for existing in each file of HI HELLO WORLD not as one text string but each word on it's own existing.
If I type Hi HELLO WORLD then it will search it as one string/text but once the user put , between it should search each word/text.
You can split the input in the textbox based on space, comma's or any other separator and then pass these as individual inputs to your search method, hope this helps

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