To begin, I'm working on a pretty high level file system where I need to be able to (with very pin point accuracy) identify and process files in a timely manner. With that said, I am working on a system that will be using the FileSystemWatcher. The whole problem with the watcher though is the fact that it tends to have issues when throwing events when there are large files involved.
To remedy this I'm working on an abstract class that can handle the files individualy once they are created by the file system watcher.
The current road block that I am running into is that my out of process validation on the file is throwing an event, I'm just having problems catching it.
public abstract class absFile
{
public delegate void FileAvailable(object sender, FileEventArgs e);
public event FileAvailable OnFileAvailable;
public void isAvailable()
{
// Create a new threaded instance of the AvailableCheck private void
// This method will be run out of process to allow other operations to continue.
Thread toAvailableCheck = new Thread(new ThreadStart(AvailableCheck));
// Once the threaded object is created, start it.
toAvailableCheck.Start();
}
private void AvailableCheck()
{
// Declaring the file stream toThisFile to be used later with the File.Open
FileStream toThisFile;
// Declaring and instantiating the attempt counter for the loop
int tiAttemptNumber = 0;
// Declaring the event args for returning the events that are
// used by this object.
FileEventArgs toFileEventArgs = new FileEventArgs();
do {
try
{
// Attempt to open the file. If this fails the try
// will interrupt the processing and it will be caught.
toThisFile = File.Open(this.FilePath, FileMode.Open);
// If the file.open method does not fail, the isFileAvailable
// property will be set to true by updating the mbIsAvailable
// private boolean.
mbIsAvailable = true;
// populate the file event args to send back
// the number of attempts made at the file and the pause lenght
toFileEventArgs.Attempts = tiAttemptNumber;
toFileEventArgs.Pause = this.AttemptPause / 1000;
// This event is called when the file is complete.
// The client application will be able to handle this event.
OnFileAvailable(this, toFileEventArgs);
// close and dispose of the filestream.
toThisFile.Close();
toThisFile.Dispose();
}
catch (Exception toException)
{
// Since the open failed, add 1 to the counter so that
// it will eventually time out.
tiAttemptNumber++;
// Set the isFileAvailable property to false. This property
// will default as false, but as a part of standard, make sure that
// if the open fails that the flag IS set to false by updating the
// mbIsAvailable private boolean.
mbIsAvailable = false;
// Put the thread to sleep for the ammount of time specified
// by the AttemptPause. This will give the file time to finish
// whatever process it is involved in.
Thread.Sleep(this.AttemptPause);
}
// Continue to make attempts until either the file is marked as available
// or the number of current attempts is the same as or greater than the
// AccessAttempts property.
} while (!this.isFileAvailable && this.AccessAttempts > tiAttemptNumber);
}
this is the code that I am running as you can see in the private void AvailableCheck, OnfileAvailable is the delegate called passing back this and the file event args.
now i have inherited this abstract class, and i need to be able to catch that event.
toWatcher.Created += new FileSystemEventHandler(OnCreated);
is called in the main and farther down the code is the following method
private void OnCreated(object source, FileSystemEventArgs e)
{
lstStatus.Invoke(new MethodInvoker(delegate {
lstStatus.Items.Add(DateTime.Now.ToString("g") + " - " + e.Name + " - File Created Event Detected for: " + e.FullPath);
lstStatus.TopIndex = lstStatus.Items.Count - 1;
tgtFile ThisFile = new tgtFile(e.FullPath);
lstStatus.Items.Add(DateTime.Now.ToString("g") + " - " + e.Name + " - Creating tgtFile Object");
}));
}
The instatiation of the tgtFile object is passed the path which makes its way down the pike to the is available method.
as you can see the chance exists for the OnFileAvailable event to be fired from the tgtFile object.
Also as you can see, the the possibility for multiple tgtFile objects to exist in memory at the same time is there as well based on the threading design of the filesystemwatcher.
in my main application then i want to be able to do something like:
public tgtFile ThisFile;
ThisFile.OnFileAvailable += new EventHandler(OnFileAvailable);
but the EventHandler errors out, and that is where I am stuck.
If it is giving you a compiler error, it's probably because your "OnFileAvailable" method referenced in this line (from the bottom of your post):
ThisFile.OnFileAvailable += new EventHandler(OnFileAvailable);
is not expecting an EventHandler - it's expecting a FileAvailable delegate. Change it to:
ThisFile.OnFileAvailable += new absFile.FileAvailable(OnFileAvailable);
//note that this can also just be ThisFile.OnFileAvailable += OnFileAvailable;
and make sure OnFileAvailable looks like this:
public void OnFileAvailable(object sender, FileEventArgs e)
{
//...
}
First things first... Ensure the event is subscribed, before calling it. Wrap the event call in an IF statement:
if (OnFileAvailable != null)
OnFileAvailable(this, toFileEventArgs);
Related
I know you don't understand the question title. Let me tell you the whole scenario.
I have a class named as Processor. Processor can get notifiable steps or send notification to other application API depending on some condition. Like as below:
Method: SendOrStartProcess(Operation operation)
Method Implementation:
If(operation.steps.any(o=>o.canProcess)){
_service.notify(operations); //This is fine
}
else{
var totalSteps = await _service.getAdjustableSteps(); //It returns the adjustable steps and will invoke event which is subscribed by another class named as GetAdjustableStepsEnd. It can be invoked immediately or after some time.
//Set totalSteps in operationsc.steps and save in database and that's it.
}
Now, I have another class named as "OperationHandler" which subscirbed the GetAdjustableStepsEnd event.
public OperationHandler(IUnityContainer container)
{
_agent.GetAdjustableStepsEnd += GetAdjustableStepsEnd ;
}
public async void GetAdjustableStepsEnd (object sender, GetAdjustableStepsEndEventArgs e)
{
// Here i will call again the above method _processor.SendOrStartProcess(e.Operations);
}
//Now the problem is if event invokes after some time then it is fine because meanwhile i set the status in database. But if it invoked just after the getAdjustableSteps then i call SendOrStartProcess again and it sends getAdjustableSteps again because record is not set in the database. How to overcome this situation. I can not put lock on it because this is used by many clients.
For a game I'm developing I'm keeping track of a GameState to determine which systems should be active. To enable systems to register themselves to State changes, I've written the following code:
public static Action<State> OnDefaultStateChange;
public static Action<State> OnConstructionStateChange;
private static Dictionary<GameState, Action<State>> _stateChangeActions =
new Dictionary<GameState, Action<State>>()
{
{GameState.Default, OnDefaultStateChange},
{GameState.Construction, OnConstructionStateChange}
};
When a state is changed, it invokes the relevant action by looking up the GameState key in the _stateChangeActions dictionary.
Here's the strange behaviour that I can't understand.
If I subscribe to the action by using _stateChangeActions[key] += ListenerMethod;, it invokes correctly. But if I subscribe on the public static field, e.g OnDefaultStateChange += ListenerMethod;, and I invoke the action through the dictionary, it's as if there are no listeners.
I haven't been able to find out why this happens. Note: I'm using Unity Engine, and this issue isn't blocking me, I'm just curious.
Answer to your question
OnDefaultStateChange and _stateChangeActions have no relation to each other, other than the fact you use OnDefaultStateChange to initialize _stateChangeActions.
Your line with {GameState.Default, OnDefaultStateChange}, adds the object inside OnDefaultStateChange to the dictionary and not the reference, which means that _stateChangeActions[GameState.Default] is not the same as OnDefaultStateChange.
An example to show what is actually going on in your setup:
var state = new { LivesLeft = 2, ShirtColor = "brown" };
// Corresponds to 'OnDefaultStateChange'
Action<State> someAction = (s) =>
{
Console.WriteLine("Lives: " + s.LivesLeft);
};
// Corresponds to '_stateChangeActions'
Action<State> copyOfSomeAction = someAction;
// Subscribe to "OnDefaultStateChange"
someAction += (s) =>
{
Console.WriteLine("Shirt color: " + s.ShirtColor);
};
// 'someAction' is longer equal to 'copyOfSomeAction' since 'someAction'
// has been replaced with a new Action which produces the result from two other
// Actions.
someAction(state);
// Output:
// Lives: 2
// Shirt color: brown
copyOfSomeAction(state);
// Output:
// Lives: 2
As you can see OnDefaultStateChange and _stateChangeActions works as two independent objects, so "subscribing" to OnDefaultStateChange doesn't make that new subscriber available to _stateChangeActions.
How to solve your issue
I would suggest you make use of the event features in C#. I'm guessing a little on how you actually check the type of event to fire, but your event handling class could look something like this:
// MyEventHandlerClass.cs
public delegate void StateChangedEventHandler(object sender, State state);
public static event StateChangedEventHandler DefaultStateChanged;
public static event StateChangedEventHandler ConstructionStateChanged;
private static FireNewStateChangeEvent(State state) {
switch (state.StateChangeType)
{
case GameState.Default:
DefaultStateChanged.Invoke(this, state);
case GameState.Construction:
ConstructionStateChanged.Invoke(this, state);
}
}
To subscribe to events you simply do pretty much like you already do:
MyEventHandlerClass.DefaultStateChanged += ListenerMethod;
With this setup you can subscribe or unsubscribe (-=) to events from wherever.
I've a text file and the value in the file is being read into the application (console application). I want to update the value in the application when the value in the text file is changed. I've referred to this link and done some modification. The result is when I change the value in the text file and try to save it, the value in the application is not updated, because the file cannot be saved.
How can I update the value in the application if I change the value in the text file?
class Program
{
static void Main(string[] args)
{
TestClass sample = new TestClass();
sample.PropertyChanged += new PropertyChangedEventHandler(sample_PropertyChanged);
while (true)
{
using (StreamReader sr = new StreamReader("Testing.txt"))
{
// Read the stream to a string, and write the string to the console.
string str = sr.ReadToEnd();
sample.TestValue = str;
}
}
}
static void sample_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
TestClass sample = (TestClass)sender;
/*
* Use expression behind if you have more the one property instead sample.TestValue
* typeof(TestClass).GetProperty(e.PropertyName).GetValue(sample, null)*/
Console.WriteLine("Value of property {0} was changed! New value is {1}", e.PropertyName, sample.TestValue);
}
}
public class TestClass : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
string testValue = string.Empty;
public string TestValue
{
get { return testValue; }
set
{
testValue = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("TestValue"));
}
}
}
There at least three serious problems in your code:
You are polling incessantly when reading the file, which will leave very little time (essentially none) for any other process to write to the file.
You raise the PropertyChanged event any time the property setter is called, whether or not the property value actually did change.
You are raising the PropertyChanged event before you close the file. This unnecessarily extends the period of time the file is left open (indeed, it leaves open the risk that the event handler can force the file to be left open for an arbitrarily long time).
The simplest fixes for the above would look something like this:
while (true)
{
string str = File.ReadAllText("Testing.txt");
sample.TestValue = str;
Thread.Sleep(1000); // sleep for 1 second
}
and this:
public string TestValue
{
get { return testValue; }
set
{
if (testValue != value)
{
testValue = value;
// BUGBUG: Warning! This code is not thread-safe; it is possible for
// the current thread to check `PropertyChanged` just before some other
// thread changes its value to null, and then to try to invoke the handler
// just _after_ that other thread changes its value to null. This is fine
// if you are sure that the event and property are both only ever accessed
// in one single thread. But otherwise, you need to fix this bug, by
// following the normal C# idiom for raising events, i.e. store the field
// value in a local variable, and then if it's non-null, raise the event
// using the local variable's value instead of the event field itself.
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("TestValue"));
}
}
}
Please note the comment in the code above regarding your possible bug in the handling of the event.
Besides that, there are a couple of other refinements you could make to the code:
Before actually opening the file, use File.GetLastWriteTimeUtc() to check the file modification timestamp for your file, and only open the file to read it if the timestamp is newer than the last time you checked. This will avoid opening the file unnecessarily, reducing the chances of a file lock conflict.
Even better, instead of the above, don't poll at all. Let FileSystemWatcher do your work for you, and only read the file when the Changed event is raised. The main drawback to this is that FileSystemWatcher is not always (in fact, not usually) notified on a timely basis of changes to files. It will eventually raise the appropriate event, but it's tracking directory information which is itself not always updated by Windows on a timely basis, causing delays (up to a few dozen seconds, in my experience) in being notified of changes.
If you can accept up to e.g. a minute delay before being notified, then I'd recommend FileSystemWatcher. Otherwise, just polling every second (or less frequently), or the #1 option where you at least check the modification timestamp, would be fine.
New to C#. Like the title, I'm having difficulty trying to raise an event. It will eventually then be consumed on another form.
What I'm trying to do is have many instances of a custom user control (my event raising form(s)) that creates a tcp client, connects, and then closes. When this tcp client has an "error", be it a catch exception, I want an event to be raised. I'm forcing the error right now by having my internet turned off to test. My first problem is I can't even get the event to be raised at all. I'll show the event code I'm working with on my custom user control:
public delegate void TaskCompleteEventHandler(object sender, TaskCompleteEventArgs e);
public event TaskCompleteEventHandler TaskComplete;
public class TaskCompleteEventArgs : System.EventArgs
{
// add local member variables to hold text
private string errorString;
// class constructor
public TaskCompleteEventArgs(string ErrorString)
{
this.errorString = ErrorString;
}
// Property
public string ErrorString
{
get
{
return errorString;
}
set
{
errorString = value;
}
}
}
This is my method that processes the exception and ideally would raise the event and allow the host form to print the string and exception accordingly.
private void ErrorLogging(string ex)
{
errorString = String.Format(/*...errorString formatting...*/);
// instance the event args and pass it the errorString value
TaskCompleteEventArgs args = new TaskCompleteEventArgs(errorString);
// raise the event with the updated arguments
TaskComplete(this, args); //----> THIS IS WHERE I GET AN ERROR!! <----
this.Dispose();
}
The error is Object reference not set to an instance of an object.
Here's the Watch screen of my TaskComplete(this, args)
I can't seem to debug this... I'm just not strong enough yet to know what I've done wrong. How is it causing side effects?
I'm sure I'm going to have more issues on my main form when I get this going... Does anyone have a clue what's causing this? Thanks in advance.
EDIT: On my main form:
public Form1()
{
InitializeComponent();
// Start control disabled and subscribe each control the event
foreach (var control in controlList)
{
control.Enabled = false;
control.TaskComplete += new dev_emu_project.dev_emu_widget.TaskCompleteEventHandler(OnTaskComplete);
}
}
List<dev_emu_project.dev_emu_widget> controlList = new List<dev_emu_project.dev_emu_widget>();
public void OnTaskComplete(object sender, dev_emu_project.TaskCompleteEventArgs e)
{
//.... work for processing
}
}
You are getting a NullReferenceException because you're invoking an empty event, meaning no delegate has been registered to it. You need to make sure TaskComplete isn't null before invoking it.
Add a null check before invoking to make sure someone did register to your event:
if (TaskComplete != null)
{
TaskComplete(this, args);
}
From MSDN Event Tutorial:
Invoking an event
Once a class has declared an event, it can treat that event just like a field of the indicated delegate type. The field will either be null, if no client has hooked up a delegate to the event, or else it refers to a delegate that should be called when the event is invoked. Thus, invoking an event is generally done by first checking for null and then calling the event
Problem is that application closes without any error, VS stays opened.
I have multiple dynamically created FileSystemWatchers, all of them have eventhandler on "Created" event. So this eventhandler method looks like this :
void watcher_FileCreated(object sender, FileSystemEventArgs e)
{
FileInfo f1 = new FileInfo(e.FullPath);
filesDataGrid.Rows.Add(f1.Name);
foreach (TLPclass table in parameterForm.getParameters)
{
//uses some funcion form another class
}
}
Line which causes program to close is the one where I'm adding File name to DataGridView - filesDataGrid.Rows.Add(f1.Name);
Also runs OK without that line.
Weird thing is that application runs normally, when launched from .exe file in projects folder. I can't see error in my code, but I guess theres something awfully wrong with it, if it doesn't even show error message.
And - what are the most common reasons why program could just shut down with no warnings?
The FileSystemWatcher will trigger the events in a separate thread. The logic inside the event handlers will need to take that fact in consideration and perform any synchronization needed. So you'll need something like this:
private void watcher_FileCreated(object sender, FileSystemEventArgs e)
{
if (filesDataGrid.InvokeRequired)
{
filesDataGrid.Invoke((MethodInvoker)delegate { watcher_FileCreated(sender, e); });
}
else
{
FileInfo f1 = new FileInfo(e.FullPath);
filesDataGrid.Rows.Add(f1.Name);
foreach (TLPclass table in parameterForm.getParameters)
{
//uses some funcion form another class
}
}
}
Wrap your function with a Try{}catch (Exception ex) { } block.
i Would think it is because DataGridRow needs to be created with the variables from the FileSystemWatcher first.
e.g
DataGridViewRow row = filesDataGrid.NewRow();
row["columnname"] = f1.name;
filesDataGrid.Rows.Add(row);