I am implementing a winform application that is supposed to do the following:
OnLoad, open an XML file reading required information while watching for any new changes to the file (incoming new information)
A timer performing certain actions based on the nature of the provided information from the XML file. Please note that the nature of the action depends on the content of the XML file
What is the best way to achieve this? Two threads? Async? Some starting point would be much appreciated.
I think you need like this structure. Firstly you read XML file and configure your custom MyConfigurationClass object. After this point you can configure FileSystemWatcher object. And finally, you can configure your task schedular object with your desired time interval.
public partial class MainWindow : Window
{
MyConfigurationClass configuration;
string filePath = #"./some.xml";
FileSystemWatcher fileWatcher = new FileSystemWatcher();
System.Timers.Timer timer = new System.Timers.Timer();
public MainWindow()
{
// First read action action for
var task = Task.Run(() => ReadXML());
InitializeComponent();
FileWatherConfigure();
TimerConfigure(task.Result);
}
private void FileWatherConfigure()
{
fileWatcher.Path = System.IO.Path.GetDirectoryName(filePath);
fileWatcher.Filter = System.IO.Path.GetFileName(filePath);
fileWatcher.Changed += FileWatcher_Changed;
fileWatcher.EnableRaisingEvents = true;
}
private void TimerConfigure(SomeClass someClass)
{
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
timer.Interval = configuration.TimeInterval.TotalMilliseconds;
timer.Enabled = true;
}
private void FileWatcher_Changed(object sender, FileSystemEventArgs e)
{
Thread.Sleep(TimeSpan.FromSeconds(1));
SomeClass someClass = ReadXML();
// Do whatever you want file change
}
private void OnTimedEvent(object sender, ElapsedEventArgs e)
{
timer.Stop();
try
{
// Schedule Operation
}
catch (Exception ex)
{
}
timer.Start();
}
private SomeClass ReadXML()
{
// Read file and do what ever you want
}
}
public class SomeClass
{
// Data from XML
}
public class MyConfigurationClass
{
public TimeSpan TimeInterval { get; set; }
}
Related
I have a program that will check if .csv file is created. If created, do something, if not wait untill its created. Is it possible in C#?
Tried something like this but didn't work well...
var startTimeSpan = TimeSpan.Zero;
var periodTimeSpan = TimeSpan.FromMinutes(1);
var timer = new System.Threading.Timer((e) =>
{
if (!File.Exists(#"TestFileLocation"))
{
MethodName(); //Tried this to start method agian
}
else
{
HereIsRestOfTheCodeToDoSomeActions();
}
}, null, startTimeSpan, periodTimeSpan);
If my post isn't clear enough, sorry I'm not fluent in English, below are steps, how program will work:
Check if file is created in folder
If not, wait 1 minute and check again
If file is created do some things
Loop this steps from start
The perfect fit for your problem would be to use a FileSystemWatcher. Its job is to watch a directory for changes such as the creation of files as you need it in your problem.
That would also make the one-minute-threshold obsolete. Consider for example:
// necessary assembly using for using FileSystemWatcher classes.
using System.IO;
// ...
// instantiate your watcher.
using var watcher = new FileSystemWatcher(#"C:\path\to\folder");
// just filter for interesting attribtes.
watcher.NotifyFilter = NotifyFilters.FileName;
// attach an event handler to the event that is triggered when a file is created.
watcher.Created += OnCreated;
// ...
// implement the event handler.
private static void OnCreated(object sender, FileSystemEventArgs e)
{
// if the name is "your" name, you're ready to go ...
if (e.Name == "my_name")
// do something ...
}
However, if you still want to use a loop (due to constraints you didn't state in your post), then it might probably be something like the following less adorable approach:
var now = DateTime.Now;
var waitTime = TimeSpan.FromMinutes(1);
while (true)
{
if (!File.Exists(#"TestFileLocation"))
{
// do something ...
// exit the loop.
break;
}
Thread.Sleep(waitTime);
}
Note and take care that this endless-loop blocks the main thread until you found your file. A better approach would be to execute that task in a separate thread aside from the main thread.
Create FileSystemWatcher https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher?view=net-6.0 and add check in OnCreated
This code is in short the same as #markwellman other than wrapping code into a class. The code does not wait, it keeps monitoring and with that see no reason why to stop and start.
In the following example, pass the folder to watch and file file name to watch in the constructor of FileOperations
using System;
using System.IO;
using static System.IO.Path;
namespace YourNamespace
{
public class FileOperations : FileSystemWatcher
{
/// <summary>
/// Path to check for file
/// </summary>
public string MonitorPath { get; set; }
/// <summary>
/// File name to watch for
/// </summary>
public string FileName { get; set; }
public FileOperations(string monitorPath, string fileName)
{
if (!Directory.Exists(monitorPath))
{
throw new Exception($"Missing {monitorPath}");
}
MonitorPath = monitorPath;
Created += OnCreated;
Path = MonitorPath;
Filter = fileName;
FileName = fileName;
EnableRaisingEvents = true;
NotifyFilter = NotifyFilters.FileName;
}
private void OnCreated(object sender, FileSystemEventArgs e)
{
var fileName = Combine(MonitorPath, GetFileName(e.FullPath));
if (fileName.Equals(Combine(MonitorPath,FileName), StringComparison.OrdinalIgnoreCase))
{
// TODO
}
}
public void Start()
{
EnableRaisingEvents = true;
}
public void Stop()
{
EnableRaisingEvents = false;
}
}
}
Then in a form create a private instance of FileOperations. Invoke Stop method in form Closing event.
public partial class Form1 : Form
{
private readonly FileOperations _fileOperations =
new FileOperations("Path to watch", "file name to trigger on");
public Form1()
{
InitializeComponent();
Closing += OnClosing;
}
private void OnClosing(object sender, CancelEventArgs e)
{
_fileOperations.Stop();
}
}
I have 2 questions about backgroundWorker: one is cancellation and another is invoking.
My code briefly looks like this:
public partial class App : Form {
//Some codes omitted
public EditProcess Process = new EditProcess(ProcessTextBox);
private void ExecuteBtn_Click (object sender, EventArgs e) {
//DnldBgWorker is a backgroundWorker.
Download Dnld = new Download(dir, Process);
DnldBgWorker.DoWork += (obj, e) => GoDownload(Dnld, urllist, e);
DnldBgWorker.RunWorkerAsync();
DnldBgWorker.RunWorkerCompleted += (obj, e) => FinishExecution();
}
private void GoDownload(Download Dnld, string[] urllist, EventArgs e) {
foreach(string url in urllist) {
Dnld.Dnld(url);
}
for (int i = 0; i < 10; i++) {
System.Threading.Thread.Sleep(50);
if (DnldBgWorker.CancellationPending) {
e.Cancel = true;
return;
}
}
}
private void StopBtn_Click(object sender, EventArgs e) {
DnldBgWorker.CancelAsync();
}
}
public class Download {
// Some codes omitted
public WebClient client = new WebClient();
public EditProcess Process;
public Download(string dir, EditProcess Process) {
this.dir = dir;
this.Process = Process;
}
public void Dnld() {
client.DownloadFile(url, dir);
EditProcess.Text(String.Format("Downloaded: {0}\r\n"));
}
}
public class EditProcess {
public TextBox Box;
public EditProcess(TextBox Box) {
this.Box = Box;
}
public void Text(string textToAdd) {
Box.Text += textToAdd;
}
}
First, while DnldBgWorker is running, I clicked StopBtn to stop the DnldBgWorker and the asynchronous work would not stop. How should I stop DnldBgWorker?
Second, EditProcess.Text(String.Format("Downloaded: {0}\r\n")); would give me an error that cross-thread operation is not valid. I know that I should make a delegate to do this, but I don't know exactly how.
++) My code looks like it's doing very simple works in very complicated way, but I put really essential elements in this code so please understand
Let's address the issue before we get into the code
For some reason, you have a completely redundant loop waiting for cancel after your actual download is done. Hence BtnStop does not work for you
When you call EditProcess.Text from Dnld which is invoked in the BackgroundWorker context, you are accessing a GUI element from a thread which does not "own" it. You can read in detail about cross-thread operation here. In your case, you should do it via your ReportProgress call.
Now you can see how I have
Removed the redundant loop from GoDownload while moving the if (DnldBgWorker.CancellationPending) check to the download loop. This should make the StopBtn work now.
Added the ProgressChanged event handler to do the GUI change in the ExecuteBtn_Click. This is triggered by DnldBgWorker.ReportProgress call in the download loop of GoDownload method. Here we pass the custom formatted string as UserState
Also make sure that you have the enabled the ReportsProgress and SupportsCancellation properties like below, perhaps in your designer property box or in code lile DnldBgWorker.WorkerReportsProgress = true; DnldBgWorker.WorkerSupportsCancellation = true;
Hope everything else is clear with the code below.
public partial class App : Form {
//Some codes omitted
public EditProcess Process = new EditProcess(ProcessTextBox);
private void ExecuteBtn_Click (object sender, EventArgs e) {
//DnldBgWorker is a backgroundWorker.
Download Dnld = new Download(dir, Process);
DnldBgWorker.DoWork += (obj, e) => GoDownload(Dnld, urllist, e);
DnldBgWorker.RunWorkerAsync();
DnldBgWorker.RunWorkerCompleted += (obj, e) => FinishExecution();
DnldBgWorker.ProgressChanged += (s, e) => EditProcess.Text((string)e.UserState);;
}
private void GoDownload(Download Dnld, string[] urllist, EventArgs e) {
foreach(string url in urllist) {
Dnld.Dnld(url);
DnldBgWorker.ReportProgress(0, String.Format($"Downloaded: {url}\r\n"));
if (DnldBgWorker.CancellationPending) {
e.Cancel = true;
return;
}
}
}
private void StopBtn_Click(object sender, EventArgs e) {
DnldBgWorker.CancelAsync();
}
}
public class Download {
// Some codes omitted
public WebClient client = new WebClient();
public EditProcess Process;
public Download(string dir, EditProcess Process) {
this.dir = dir;
this.Process = Process;
}
public void Dnld() {
client.DownloadFile(url, dir);
}
}
public class EditProcess {
public TextBox Box;
public EditProcess(TextBox Box) {
this.Box = Box;
}
public void Text(string textToAdd) {
Box.Text += textToAdd;
}
}
There are 2 issues here:
Regarding cancellation - you need to check for cancellation status in the loop that does downloading (thus downloading only part of requested files), not in the later loop which I don't really understand.
As an additional side note you can avoid using BackgroundWorker by using WebClient.DownloadFileAsync and WebClient.CancelAsync combo.
As of reporting progress - make you BackgroundWorker report progress back to the UI thread via ReportProgress and update UI from there.
As for how to cancel a thread. Here is a basic example, for a console application, that I hope you can fit into your more complex code.
void Main()
{
var tokenSource = new CancellationTokenSource();
System.Threading.Tasks.Task.Run(() => BackgroundThread(tokenSource.Token));
Thread.Sleep(5000);
tokenSource.Cancel();
}
private void BackgroundThread(CancellationToken token)
{
while (token.IsCancellationRequested == false) {
Console.Write(".");
Thread.Sleep(1000);
}
Console.WriteLine("\nCancellation Requested Thread Exiting...");
}
The results would be the following.
.....
Cancellation Requested Thread Exiting...
Secondly, as far as how to invoke from your thread to interact with the user interface, hopefully this blog will help you. Updating Windows Form UI elements from another thread
Please let me know if you found this helpful.
To support cancellation you need to set the property
DnldBgWorker.WorkerSupportsCancellation = true;
It is not clear if you set it somewhere else, but you need it to cancel the background worker as you can read on MSDN
Set the WorkerSupportsCancellation property to true if you want the
BackgroundWorker to support cancellation. When this property is true,
you can call the CancelAsync method to interrupt a background
operation.
Also I would change the GoDownload method to
private void GoDownload(Download Dnld, string[] urllist, EventArgs e)
{
foreach(string url in urllist)
{
Dnld.Dnld(url);
// this is just to give more time to test the cancellation
System.Threading.Thread.Sleep(500);
// Check the cancellation after each download
if (DnldBgWorker.CancellationPending)
{
e.Cancel = true;
return;
}
}
}
For the second problem you need to call that method when your code is running on the UI thread and not in the background thread. You could easily achieve this moving the textbox update in the event handler for the ProgressChanged event. To set up the event handler you need another property set to true
DnldBgWorker.WorkerReportsProgress = true;
And set the event handler for the ProgressChanged event
DnldBgWorker.ProgressChanged += DnldBgWorker_ProgressChanged;
private void DnldBgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
EditProcess.Text(String.Format("Downloaded: {0}\r\n", e.ProgressPercentage));
}
and raise this event in the GoDownload with
DnldBgWorker.ReportProgress(i);
I have a service that processes files. Sometimes they process very quickly and sometimes they take a very long time. I can't control the process that sends the files to me. The are randomly dropped on me throughout the day and night. When I use the timer, it seems like the "ProcessFiles" method is abandoned whereever it is when the time has elapsed and ProcessFiles is called again. Since the files contain sensitive information, they can't sit on the server for a long time so I can't set the timer for any longer than 5 minutes and, still, at 5 minutes, the process sometimes interrupts itself. As a result, I have partially processed files. I would appreciate any thoughts and input on this quandary.
System.Timers.Timer _timer;
// As the files come in, massage them and encrypt them
public const string InPath = #"c:\input";
public const string OutPath = #"\c:\output";
public FileMassaging()
{
InitializeComponent();
}
public EventLog MyEventLog = new EventLog();
public string sSource = "FileMassaging";
public string sLog = "FileMassaging";
protected override void OnStart(string[] args)
{
// Create the source, if it does not already exist.
if (!EventLog.SourceExists(sSource))
EventLog.CreateEventSource(sSource, sLog);
// set up the service
ServiceStatus serviceStatus = new ServiceStatus();
serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING;
serviceStatus.dwWaitHint = 100000;
SetServiceStatus(this.ServiceHandle, ref serviceStatus);
// set up the service
_timer = new System.Timers.Timer();
_timer.Elapsed += new System.Timers.ElapsedEventHandler(this.OnTimer);
_timer.Interval = 5000;
_timer.Start();
// Update the service state to Running.
serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING;
SetServiceStatus(this.ServiceHandle, ref serviceStatus);
}
public void OnTimer(object sender, System.Timers.ElapsedEventArgs args)
{
ProcessFiles();
}
public void ProcessFiles()
{
string[] originalFiles = Directory.GetFiles(InPath + #"\", "*.txt");
foreach (string fileName in originalFiles)
{
// Check and process the file
CheckFile(InPath, OutPath, fileName);
}
}
public void CheckFile(string InPath,Outpath, fileName)
{
// for example only -- actual file processing is much longer
//
string infile = InPath+fileName;
string outfile= OutPath+fileName;
File.Move(infile,outfile,true);
}
For testing and extensiblity I would recommend a different overall structure.
First let's seperate out this work into classes where it makes sense. Lets start with a class called FolderWatcher:
public class FolderWatcher
{
private readonly string _inPath;
private readonly string _outPath;
public bool CurrentlyRunning { get; set; }
public FolderWatcher(string inPath, string outPath)
{
_inPath = inPath;
_outPath = outPath;
}
public void TryProcessFiles(object sender, ElapsedEventArgs e)
{
try
{
this.CurrentlyRunning = true;
ProcessFiles(sender, e);
}
catch (Exception)
{
throw;
}
finally
{
this.CurrentlyRunning = false;
}
}
public void ProcessFiles(object sender, ElapsedEventArgs e)
{
string[] originalFiles = GetFilesInDirectory();
foreach (var originalFile in originalFiles)
{
CheckFile(originalFile);
}
}
// Internal/Virtual so that this can mocked in unit testing.
internal virtual string[] GetFilesInDirectory()
{
return Directory.GetFiles(_inPath + #"\", "*.txt");
}
// Internal/Virtual so that this can mocked in unit testing.
internal virtual void CheckFile(string fileName)
{
string infile = $"{_inPath}{fileName}";
string outfile = $"{_outPath}{fileName}";
File.Move(infile, outfile);
}
}
This class has a single responsibility, to move files in response to an event.
Next let's build a class to wrap the FolderWatcher class that handles the timer functionality:
public class TimedFolderWatcher
{
private readonly FolderWatcher _folderWatcher;
private readonly Timer _timer;
public TimedFolderWatcher(FolderWatcher folderWatcher)
{
_folderWatcher = folderWatcher;
InitTimer();
}
private void InitTimer()
{
_timer.Elapsed += new System.Timers.ElapsedEventHandler(this.ProcessFiles);
_timer.Interval = 5000;
_timer.Start();
}
private void ProcessFiles(object sender, ElapsedEventArgs e)
{
_folderWatcher.TryProcessFiles(sender, e);
}
}
This class also has a single responsibility to call the ProcessFiles method every 5000 milliseconds.
Lastly we can init and call these classes this way:
var fileMassageService = new TimedFolderWatcher(new FolderWatcher(#"c:\input", #"c:\output"));
This approach lends itself to testing and follows the best practices of Dependency Injection which will allow you to use an IOC framework in the future if you need to.
From the MSDN Page on Timers try this (https://msdn.microsoft.com/en-us/library/system.timers.timer.interval(v=vs.110).aspx)
_timer = new System.Timers.Timer(5);
_timer.Elapsed += OnTimer;
_timer.AutoReset = true;
// _timer.Start();
_timer.Enable = true;
private static void OnTimer(object sender, System.Timers.ElapsedEventArgs args) { ProcessFiles(); }
in my WPF - C# application, I have a time consuming function, which I execute with a BackgroundWorker. The job of this function is to add given data from a file into a database. Now and then, I need some user feedback, for example the data is already in the store and I want to ask the user, whether he wants to merge the data or create a new object or skip the data completely. Much like the dialog windows shows, if I try to copy a file to a location, where a file with the same name already exists.
The problem is, that I cannot call a GUI-window from a non GUI-thread. How could I implement this behavior?
Thanks in advance,
Frank
You could work with EventWaitHandle ou AutoResetEvent, then whenever you want to prompt the user, you could the signal UI, and then wait for the responde. The information about the file could be stored on a variable.
If possible... my suggestion is to architect your long running task into atomic operations. Then you can create a queue of items accessible by both your background thread and UI thread.
public class WorkItem<T>
{
public T Data { get; set; }
public Func<bool> Validate { get; set; }
public Func<T, bool> Action { get; set; }
}
You can use something like this class. It uses a queue to manage the execution of your work items, and an observable collection to signal the UI:
public class TaskRunner<T>
{
private readonly Queue<WorkItem<T>> _queue;
public ObservableCollection<WorkItem<T>> NeedsAttention { get; private set; }
public bool WorkRemaining
{
get { return NeedsAttention.Count > 0 && _queue.Count > 0; }
}
public TaskRunner(IEnumerable<WorkItem<T>> items)
{
_queue = new Queue<WorkItem<T>>(items);
NeedsAttention = new ObservableCollection<WorkItem<T>>();
}
public event EventHandler WorkCompleted;
public void LongRunningTask()
{
while (WorkRemaining)
{
if (_queue.Any())
{
var workItem = _queue.Dequeue();
if (workItem.Validate())
{
workItem.Action(workItem.Data);
}
else
{
NeedsAttention.Add(workItem);
}
}
else
{
Thread.Sleep(500); // check if the queue has items every 500ms
}
}
var completedEvent = WorkCompleted;
if (completedEvent != null)
{
completedEvent(this, EventArgs.Empty);
}
}
public void Queue(WorkItem<T> item)
{
// TODO remove the item from the NeedsAttention collection
_queue.Enqueue(item);
}
}
Your UI codebehind could look something like
public class TaskRunnerPage : Page
{
private TaskRunner<XElement> _taskrunner;
public void DoWork()
{
var work = Enumerable.Empty<WorkItem<XElement>>(); // TODO create your workItems
_taskrunner = new TaskRunner<XElement>(work);
_taskrunner.NeedsAttention.CollectionChanged += OnItemNeedsAttention;
Task.Run(() => _taskrunner.LongRunningTask()); // run this on a non-UI thread
}
private void OnItemNeedsAttention(object sender, NotifyCollectionChangedEventArgs e)
{
// e.NewItems contains items that need attention.
foreach (var item in e.NewItems)
{
var workItem = (WorkItem<XElement>) item;
// do something with workItem
PromptUser();
}
}
/// <summary>
/// TODO Use this callback from your UI
/// </summary>
private void OnUserAction()
{
// TODO create a new workItem with your changed parameters
var workItem = new WorkItem<XElement>();
_taskrunner.Queue(workItem);
}
}
This code is untested! But the basic principle should work for you.
Specifically to your case
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(1000);
var a = Test1("a");
Thread.Sleep(1000);
var b = (string)Invoke(new Func<string>(() => Test2("b")));
MessageBox.Show(a + b);
}
private string Test1(string text)
{
if (this.InvokeRequired)
return (string)this.Invoke(new Func<string>(() => Test1(text)));
else
{
MessageBox.Show(text);
return "test1";
}
}
private string Test2(string text)
{
MessageBox.Show(text);
return "test2";
}
Test2 is a normal method which you have to invoke from background worker. Test1 can be called directly and uses safe pattern to invoke itself.
MessageBox.Show is similar to yourForm.ShowDialog (both are modal), you pass parameters to it (text) and you return value (can be a value of property of yourForm which is set when form is closed). I am using string, but it can be any data type obviously.
From the input of the answers here, I came to the following solution:
(Mis)Using the ReportProgress-method of the Backgroundworker in Combination with a EventWaitHandle. If I want to interact with the user, I call the ReportProgress-method and setting the background process on wait. In the Handler for the ReportProgress event I do the interaction and when finished, I release the EventWaitHandle.
BackgroundWorker bgw;
public MainWindow()
{
InitializeComponent();
bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
bgw.WorkerReportsProgress = true;
bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
}
// Starting the time consuming operation
private void Button_Click(object sender, RoutedEventArgs e)
{
bgw.RunWorkerAsync();
}
// using the ProgressChanged-Handler to execute the user interaction
void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
UserStateData usd = e.UserState as UserStateData;
// UserStateData.Message is used to see **who** called the method
if (usd.Message == "X")
{
// do the user interaction here
UserInteraction wnd = new UserInteraction();
wnd.ShowDialog();
// A global variable to carry the information and the EventWaitHandle
Controller.instance.TWS.Message = wnd.TextBox_Message.Text;
Controller.instance.TWS.Background.Set();
}
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show(e.Result.ToString());
}
// our time consuming operation
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(2000);
// need 4 userinteraction: raise the ReportProgress event and Wait
bgw.ReportProgress(0, new UserStateData() { Message = "X", Data = "Test" });
Controller.instance.TWS.Background.WaitOne();
// The WaitHandle was released, the needed information should be written to global variable
string first = Controller.instance.TWS.Message.ToString();
// ... and again
Thread.Sleep(2000);
bgw.ReportProgress(0, new UserStateData() { Message = "X", Data = "Test" });
Controller.instance.TWS.Background.WaitOne();
e.Result = first + Controller.instance.TWS.Message;
}
I hope I did not overlooked some critical issues. I'm not so familar with multithreading - maybe there should be some lock(object) somewhere?
I'm trying to create a simple app, what moves all files writed to some directory to other directory. That's my problem: if i write other than 10000 files at once in my directory(small .txt files over 1KB) - some of them not handling to move on output directory. I'm using FileSystemWatcher events handler to solve this problem. Here is my code example:
Class MyProgramm
{
void Process(Object o, FileSystemEventArgs e)
{
//do something with e.Name file
}
void main()
{
var FSW = New FileSystemWatcher{Path = "C:\\InputDir"};
FSW.Created += Process;
FSW.EnableRisingEvents = true;
Thread.Sleep(Timeout.Infinite);
}
}
Finally, we got some files processed, but some of written files stays unprocessed..
Any suggestions?
I had similar issues with the FileSystemWatcher. It seemed to "drop the ball" quite regularly. I went for an alternative solution by extending "ServiceBase", which has been working consistently since it went live as a windows service. Hope this helps:
public partial class FileMonitor : ServiceBase
{
private Timer timer;
private long interval;
private const string ACCEPTED_FILE_TYPE = "*.csv";
public FileMonitor()
{
this.ServiceName = "Service";
this.CanStop = true;
this.CanPauseAndContinue = true;
this.AutoLog = true;
this.interval = long.Parse(1000);
}
public static void Main()
{
ServiceBase.Run(new FileMonitor());
}
protected override void OnStop()
{
base.OnStop();
this.timer.Dispose();
}
protected override void OnStart(string[] args)
{
AutoResetEvent autoEvent = new AutoResetEvent(false);
this.timer = new Timer(new TimerCallback(ProcessNewFiles), autoEvent, interval, Timeout.Infinite);
}
private void ProcessNewFiles(Object stateInfo)
{
DateTime start = DateTime.Now;
DateTime complete = DateTime.Now;
try
{
string directoryToMonitor = "c:\mydirectory";
DirectoryInfo feedDir = new DirectoryInfo(directoryToMonitor);
FileInfo[] feeds = feedDir.GetFiles(ACCEPTED_FILE_TYPE, SearchOption.TopDirectoryOnly);
foreach (FileInfo feed in feeds.OrderBy(m => m.LastWriteTime))
{
// Do whatever you want to do with the file here
}
}
finally
{
TimeSpan t = complete.Subtract(start);
long calculatedInterval = interval - t.Milliseconds < 0 ? 0 : interval - t.Milliseconds;
this.timer.Change(calculatedInterval, Timeout.Infinite);
}
}
}