filewatcher watch several files but handle them differently - c#

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);
}

Related

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}");
}
}

Search and Replace string in file in C#

I am trying to create a small form in C# to find one string in a TMX file (xml) and replace it for another. Then it would create an output file with all the modifications.
The form contains a search button to locate the file in the local disk and a REPLACE button which it would change "srclang="all"" for "srclang="en-US"".
So far I have the following:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void openFileDialog1_FileOk(object sender, CancelEventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
using (FileDialog fileDialog = new OpenFileDialog())
{
if (DialogResult.OK == fileDialog.ShowDialog())
{
string filename = fileDialog.FileName;
textBox1.Text = fileDialog.FileName;
}
}
}
private void label1_Click(object sender, EventArgs e)
{
}
private void button3_Click(object sender, EventArgs e)
{
this.Close();
}
private void button2_Click(object sender, EventArgs e)
{
StreamWriter writer = null;
Dictionary<string, string> replacements = new Dictionary<string, string>();
replacements.Add("*all*", "en-US");
// ... further replacement entries ...
using (writer = File.CreateText("output.txt"))
{
foreach (string line in File.ReadLines(textBox1.Text))
{
bool replacementMade = false;
foreach (var replacement in replacements)
{
if (line.StartsWith(replacement.Key))
{
writer.WriteLine(string.Format("{1}",
replacement.Key, replacement.Value));
replacementMade = true;
break;
}
}
if (!replacementMade)
{
writer.WriteLine(line);
}
}
}
File.Replace("output.txt", textBox1.Text, "ORIGINAL_TMX_FILE.bak");
}
}
}
This code is from Dave R. from this site, it really works with TXT files but I am not sure what I am doing wrong. I am totally a newbie here.
If anyone could help me write some lines to make it work, I would really appreciate it!
Probably the simplest thing that could work would be to use these methods
File.WriteAllLines()
File.ReadAllLines()
as in the example below:
File.WriteAllLines(
outputFileName,
File.ReadAllLines(inputFileName)
.Select(line => line.Replace(#"srclang=""en-US""", #"srclang=""all"""));
If there could be whitespace in the search text, you could replace the call to string.Replace with a Regex.Replace.
I think I understand what you're trying to do and the following segment is the essence of your code:
if (line.StartsWith(replacement.Key))
{
writer.WriteLine(string.Format("{1}",
replacement.Key, replacement.Value));
replacementMade = true;
break;
}
If your dictionary will always have one element, you only have to change the parameter of the WriteLine call. You're writing only the replacement value instead of the whole line with the replacements done on it. You might want to have something like
writer.WriteLine(line.Replace(replacement.Key, replacement.Value)
If you want to check for several replacements, store the replaced line in the inner loop and only write it at the end and remove the break command.
Another unrelated observation: the replacement.Key parameter in the WriteLine call is omitted since {1} refers to the second additional parameter (counting starts from 0).

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.

C# scan a folder and open files that have been created after a certain time

I am writing a little program in C# that scans a folder and opens the files that have been created after 5.30pm after a button has been pressed on the program. This will also have to search within sub-folders.
I need a couple of solutions to point me in the correct direction as I'm not sure how I would do this.
This is part of a folder watcher program. The problem is when the user goes home the PC is switched off and there are files being created to the directory after 17.30. So I need a way for when the program is restarted in the morning it detects anything created after 17.30 and open them.
private void button1_Click(object sender, EventArgs e)
{
folderBrowser.ShowDialog();
textBox1.Text = folderBrowser.SelectedPath;
filewatcher.Path = textBox1.Text;
Registry.SetValue("HKEY_CURRENT_USER\\SOFTWARE\\COMPANY\\FOLDERWATCHER", "FOLDERPATH", textBox1.Text);
}
private void Form1_Load(object sender, EventArgs e)
{
String WatchFolder = Registry.GetValue("HKEY_CURRENT_USER\\SOFTWARE\\COMPANY\\FOLDERWATCHER", "FOLDERPATH", "").ToString();
textBox1.Text = WatchFolder;
filewatcher.Path = WatchFolder;
}
private void Form1_Resize(object sender, EventArgs e)
{
if (WindowState == FormWindowState.Minimized)
{
ShowInTaskbar = true;
Hide();
}
}
private void fileSystemWatcher1_Changed(object sender, System.IO.FileSystemEventArgs e)
{
if(!e.FullPath.EndsWith("temp.temp"))
{
MessageBox.Show("You have a Collection Form: " + e.Name);
Process.Start("explorer.exe", e.FullPath);
}
}
private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
{
Show();
}
}
This is my complete code above. I would like to use a button to open or show the files created after 17.30.
Look at the System.IO namespace, it has everything you need.
the DirectoryInfo and File classes will do what you want.
Here is the recursive method you are looking for:
public static List<string> GetFilesCreatedAfter(string directoryName, DateTime dt)
{
var directory = new DirectoryInfo(directoryName);
if (!directory.Exists)
throw new InvalidOperationException("Directory does not exist : " + directoryName);
var files = new List<string>();
files.AddRange(directory.GetFiles().Where(n => n.CreationTime > dt).Select(n=>n.FullName));
foreach (var subDirectory in Directory.GetDirectories(directoryName))
{
files.AddRange(GetFilesCreatedAfter(subDirectory,dt));
}
return files;
}
Hope I helped.
You can use FileSystemWatcher (MSDN documentation) to detect files that have been created after pressing a button (while your application is running).
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = "C:\\YourDirectory";
watcher.Created += (sender, args) => {
// File was created
}
watcher.EnableRaisingEvents = true;
This allows you to track files as they are created (while your application is running).
If you just want to get a list of all directories that were created in a specified time range (before your application was started), then you can search the directory tree using Directory.GetDirectories and Directory.GetFiles.
In place of datetime place you date and time value.
void DirSearch(string dir)
{
try
{
foreach (string d in Directory.GetDirectories(dir))
{
foreach (string f in Directory.GetFiles(d, "*.*"))
{
if(DateTime.Compare(f.GetCreationTime, datetime))
{
//files found
}
}
DirSearch(d);
}
}
catch (System.Exception excpt)
{
Console.WriteLine(excpt.Message);
}
}

Grouping two methods together in C#

I currently have two different event handlers in C#, which perform two different functions. Although how could I combine the two methods together, so only 1 button could perform both actions? (Taking into account that button1_Click event must be performed first.)
private void button2_Click(object sender, EventArgs e)
{
var file = File.AppendText(#"c:\output2.txt");
foreach (string tmpLine in File.ReadAllLines(#"c:\output.txt"))
{
if (File.Exists(tmpLine))
{
file.WriteLine(tmpLine);
}
}
file.Close();
}
private void button1_Click(object sender, EventArgs e)
{
using (StreamWriter sw = File.AppendText(#"c:\output.txt"))
{
StreamReader sr = new StreamReader(#"c:\filename.txt");
string myString = "";
while (!sr.EndOfStream)
{
myString = sr.ReadLine();
int index = myString.LastIndexOf(":");
if (index > 0)
myString = myString.Substring(0, index);
sw.WriteLine(myString);
}
button2_Click(sender, e);
}
}
Instead of writing the code in the event handler, bring them out into two functions and then call those functions whichever way you want from the event handler.
You could, if I'm understanding you correctly, have one event handler call another. An event handler is "just" a method after all, so:
private void button1_Click(object sender, EventArgs e)
{
// All the code that's currently there
button2_Click(sender, e);
}
Or, you could extract the code from the event handlers into separate methods:
private void button1_Click(object sender, EventArgs e)
{
WriteToOutputDotTxt();
OtherMethodThatWritesToOutputDotTxt();
}
private void button2_Click(object sender, EventArgs e)
{
OtherMethodThatWritesToOutputDotTxt();
}
private void WriteToOutputDotTxt()
{
// Code that's currently in button1_Click
}
private void OtherMethodThatWritesToOutputDotTxt()
{
// Code that's currently in button2_Click
}
The code doesn't have to be contained within the event handlers, in fact you'll find it easier (if you're interested!) to test your code if you can separate it away from your UI. You could, for example, have a class called ProcessOutputFile and move the WriteToOutputDotTxt and OtherMethodThatWritesToOutputDotTxt methods onto that class. It's then a lot easier to write tests for that code as it's not "tied into" the UI code.
I try to keep most logic out of event handlers and create functions with logical names that i call from the event handlers. Then they can be called from wherever.
Just attach two event handlers to some button:
somebutton.Click += new EventHandler(button1_Click);
somebutton.Click += new EventHandler(button2_Click);
// given these two methods extracted from your events
void DoBar(object sender, EventArgs e)
{
var file = File.AppendText(#"c:\output.txt");
foreach (string tmpLine in File.ReadAllLines(#"c:\filename.txt"))
{
if (File.Exists(tmpLine))
{
file.WriteLine(tmpLine);
}
}
file.Close();
}
void DoFoo(object sender, EventArgs e)
{
using (StreamWriter sw = File.AppendText(#"c:\output.txt"))
{
StreamReader sr = new StreamReader(#"c:\filename.txt");
string myString = "";
while (!sr.EndOfStream)
{
myString = sr.ReadLine();
int index = myString.LastIndexOf(":");
if (index > 0)
myString = myString.Substring(0, index);
sw.WriteLine(myString);
}
}
}
// you can subscribe like this
button1.Click += DoFoo;
button1.Click += DoBar;
button2.Click += DoBar;
EDIT
forgot my sender and eventargs

Categories