Issues with using and calling EventHandlers - c#

I'm using Kellerman .NET SFTP Library and I'm having some issues using event handlers
According to the documentation it has the following events:
I'm interested in two of them:
TransferCompleteEvent
FailureEvent
I would like to display a message when the transfer is complete and restart the upload if connection failed.
In my class I have the following:
public static void uploadToSFTP()
{
try
{
SFTP myConnection = new SFTP();
myConnection.EnableLogging();
myConnection.HostAddress = "servername";
myConnection.UserName = "username";
myConnection.Password = "password";
myConnection.CurrentDirectory = "directory";
myConnection.Connect();
//UPLOADING FILE TO SFTP SERVER
myConnection.UploadFileAsync(yesterdaysZipFile, localZipFileName);
while (myConnection.IsBusy == true)
{
//PRINT HOW LONG REMAINING FROM UPLOAD
Console.WriteLine(myConnection.EstimatedTimeRemaining);
}
//declaring an eventhandler
myConnection.TransferCompleteEvent += SFTPCompleted;
myConnection.Disconnect();
myConnection.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
And then I have
public static void SFTPCompleted(Object sender, TransferCompletedEventArgs e)
{
Console.WriteLine("Completed");
}
My problem is in this line:
myConnection.TransferCompleteEvent += SFTPCompleted;
When I use the debugger and get to this line it skips and goes to the next line it never goes to
public static void SFTPCompleted(Object sender, TransferCompletedEventArgs e)
{
Console.WriteLine("Completed");
}
What am I doing wrong here?
And in regards the FailureEvent I can't even get it to compile:
myConnection.FailureEvent += TransferFailed;
Here's the event:
public static void TransferFailed(Object sender, SFTP.FailureEventHandler e)
{
Console.WriteLine("failed");
}
I get this compiler error:
Error 1 No overload for 'TransferFailed' matches delegate 'KellermanSoftware.NetSFtpLibrary.SFTP.FailureEventHandler'
This is my first time using this library. Any suggestion would be helpful.

This line:
myConnection.TransferCompleteEvent += SFTPCompleted;
is attaching an event handler.
You are attaching the event handler after calling UploadFileAsync and waiting for it to stop being busy. At this point, the event would have already fired, so you're missing out on hearing about it.
You should attach the event handler as soon as you create the myConnection object.
SFTP myConnection = new SFTP();
myConnection.TransferCompleteEvent += SFTPCompleted;
Your second problem is that you cannot attach the TransferFailed event. That's because you have the wrong parameters in your handler function. My guess is that it should be:
public static void TransferFailed(Object sender, SFTP.FailurEventArgs e)
Have a look at the SFTP.FailureEventHandler declaration. It will tell you what the parameters need to be.

Related

NEW TITLE -- The application called an interface that was marshalled for a different thread

After making the project simpler, I believe I identified the problem is actually a result the async marshalling.
UPDATE: I made the code simpler to try to figure out what was going on. So here is an update... The Observable collection is being populated on a new thread (async method). I tried moving the assigning of the ItemsSource to after the ObservableCollection is loaded as seen below
async void LoadAllData(object sender, EventArgs e)
{
if (sender != null)
{
App.GeoLocationComplete -= LoadAllData;
}
await ViewModelObjects.NearbyLocations.LoadLocationData();
lvPlaces.ItemsSource = ViewModelObjects.NearbyLocations.GBSLocationDetails;
}
The definition for the data load method is a follows:
public async Task LoadLocationData()
{....}
When I run this code I get the following error:
The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))
I know what is causing the error (the data was loaded on a thread other than the UI thread) but I don't know how to fix it. Suggestions?
UPDATE UPDATE: So I believe I have identified the root cause of the problem but have not figured out how to fix it. I started by simplifying my code as follows and it worked.
public nearbyplaces()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
LoadAllData(null, null);
}
void LoadAllData(object sender, EventArgs e)
{
lobj_Places = new ObservableCollection<GBSLocationDetail>()
{
new GBSLocationDetail()
{
Title = "Location 1",
Distance = "20 Miles",
AddInfo = "Something Else",
AttributesTexts="Gay, Bar, Dance"
}
};
lvPlaces.ItemsSource = lobj_Places;
}
HOWEVER, what I need is for the LoadAllData method to be called once I have the GPS location from the device. So in my App.XAML.cs I have the following delegate event declared:
public static Plugin.Geolocator.Abstractions.IGeolocator gobj_RealGeoCoordinator;
public static event GeoLocationCompleteEventHandler GeoLocationComplete;
public static bool gb_WaitingForLocation = true;
Then I have the following code call the event once I get the location back from the device:
private async void ProcessStartupandResume()
{
if (gobj_RealGeoCoordinator == null)
{
gobj_RealGeoCoordinator = CrossGeolocator.Current;
ViewModelObjects.AppSettings.CanAccessLocation = App.gobj_RealGeoCoordinator.IsGeolocationEnabled;
if (!ViewModelObjects.AppSettings.CanAccessLocation)
{
await MainPage.DisplayAlert(ResourceStrings.GetValue("NoLocationServicesTitle"), ResourceStrings.GetValue("NoLocationServicesMessage"), ResourceStrings.GetValue("OKButtonText"));
}
//Only add the events if the object has to be created.
gobj_RealGeoCoordinator.PositionChanged += gobj_RealGeoCoordinator_PositionChanged;
gobj_RealGeoCoordinator.PositionError += (sender, e) =>
{
ProcessException(new Exception(e.Error.ToString()));
};
}
//Set this to null to trigger the first check
ib_GPSReenabled = null;
if (gobj_RealGeoCoordinator.IsListening)
await gobj_RealGeoCoordinator.StopListeningAsync();
gobj_RealGeoCoordinator.DesiredAccuracy = 50;
await gobj_RealGeoCoordinator.StartListeningAsync(10000, 20);
}
private static void gobj_RealGeoCoordinator_PositionChanged(object sender, PositionEventArgs e)
{
var pos = e.Position;
ViewModelObjects.AppSettings.Latitude = pos.Latitude;
ViewModelObjects.AppSettings.Longitude = pos.Longitude;
if (gb_WaitingForLocation)
{
gb_WaitingForLocation = false;
GeoLocationComplete?.Invoke(new object() , null);
}
}
Then in my page I subscribe to the GeoLocationComplete event using the LoadAllData method as seen below. Even when I use a local object and try to set the ItemsSource for the ListView in the code when executed as a result of the event being raised, I receive the error. See code below which subscribed to the event:
public nearbyplaces()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
if (App.gb_WaitingForLocation)
App.GeoLocationComplete += LoadAllData;
else
LoadAllData(null, null);
}
Any suggestions on how to fix this?
OK so I figured it out. I needed to invoke the event on the main thread and I did that with the following code:
Device.BeginInvokeOnMainThread(() =>
{
GeoLocationComplete?.Invoke(new object(), null);
});
After inserting this code, the error was gone. Changing the code back to simply
GeoLocationComplete?.Invoke(new object(), null);
cause the error to occur again. Thus I believe this resolved my problem. Hope this helps someone else. :)

Check if event (doubleClick) is running

I am writing a tool which switchs between a lot of states. For some events I need to be sure they wont get executed a second time while the called function (inside the event) is running. This is how I managed it before:
// Global variables //
public bool func1IsRunning = false;
public bool func2IsRunning = false;
...
public void listView_DoubleClick(object sender, EventArgs e)
{
if(!func1IsRunning)
{
func1();
func1IsRunning = false;
}
}
public void func1()
{
func1IsRunning = true;
// some code in here //
}
But with every extension of my tool the list of the global variables grows up. Also the events and functions getting less clear to read.
Isnt there a way like this(?):
public void listView_DoubleClick(object sender, EventArgs e)
{
if(DoubleClick.IsHandled)
{
func1();
}
}
public void func1()
{
// some code in here //
// ................. //
DoubleClick.IsHandled = true; // at the end of the function //
}
So what I am looking for is a way to determine if an event is still running or not. My code is working, im just unhappy with how it looks like.
Any ideas?
UPDATE 1
I decided to use Steve's answer as it solves my problem by the clearest way.
Anyway it is NOT running correctly for now.
Here is how my code looks like:
public void listView_DoubleClick(object sender, EventArgs e)
{
try
{
listView.DoubleClick -= new EventHandler(listView_DoubleClick);
itemEdit();
}
finally
{
listView.DoubleClick += new EventHandler(listView_DoubleClick);
}
}
The code above is NOT disabling the handler.
public void listView_DoubleClick(object sender, EventArgs e)
{
try
{
listView.DoubleClick -= listView_DoubleClick;
itemEdit();
}
finally
{
listView.DoubleClick += listView_DoubleClick;
}
}
This code is also not disabling the handler.
This is the line where the handler gets enabled (MainForm.Designer.cs):
this.listView.DoubleClick += new System.EventHandler(this.listView_DoubleClick);
There are no errors raised. The event just gets fired again and again. Where is the problem?
UPDATE 2:
As Sinatr asked in the comments below if my function is really waiting or just enabling user input he discovered where the mistake was made.
Steve's answer is correct according to my wrong written question. Thanks a lot to all of you guys.
Just disable the event handler
public void listView_DoubleClick(object sender, EventArgs e)
{
try
{
listView.DoubleClick -= listView_DoubleClick;
// Now, even if func1 causes a DoubleClick event,
// or user manages to trigger a DobuleClick
// there is no event registered and this code could
// reentered until you exit from func1.
func1();
}
finally
{
// Important part. the finally block is required
// because you should readd the event handler
// ALSO in case an exception occurs in func1
// and it is not handled there
listView.DoubleClick += listView_DoubleClick;
}
}
EDIT
Looking at your comment I suspect that this DoubleClick event is assigned to more than one control. If this is the case, using the global listView global instance of a listview doesn't disable the double click on other controls that are linked to the same code.
If this is the case then you need a more generic approach
public void listView_DoubleClick(object sender, EventArgs e)
{
Control c = sender as Control;
try
{
if(c != null)
{
c.DoubleClick -= listView_DoubleClick;
// Now, even if func1 causes a DoubleClick event,
// or user manages to trigger a DobuleClick
// there is no event registered and this code could
// reentered until you exit from func1.
func1();
}
}
finally
{
// Important part. the finally block is required
// because you should readd the event handler
// ALSO in case an exception occurs in func1
// and it is not handled there
if(c != null) c.DoubleClick += listView_DoubleClick;
}
}
Of course, this is just to enable/disable DoubleClicks events, it cannot works if you assign this event handler to other standard events like Click that have the same signature (object sender, EventArgs e)
How about something like the following using locks:
private object globalLock = new object();
private Dictionary<int, object> lockObjects = new Dictionary<int, object>();
public void listView_DoubleClick(object sender, EventArgs e)
{
object lockObject;
lock (globalLock) // to avoid two threads creating the object
{
if (!lockObjects.ContainsKey(1))
lockObjects.Add(1, new object());
lockObject = lockObjects[1];
}
if (Monitor.TryEnter(lockObject) // enter only if no thread has already entered
{
try { func1(); }
finally { Monitor.Exit(lockObject); }
}
}
This is different to Steve's logic in the matter that it is thread-safe.
A simple state-machine should solve your problem without requiring too many variables. Create an Enum named AppState like this:
enum AppState
{
Ready = 1,
InsideListView1Click = 2,
InsideListView1DoubleClick = 3
InsideListView2Click = 4,
InsideListView2DoubleClick = 5
}
This enum could grow as you add new controls and/or event-handlers to your application. Now use a single global variable that keeps track of the application state and modify it inside event-handlers appropriately:
private AppState m_State = AppState.Ready;
And in the event-handlers you would do:
private void ListView1_DoubleClick(object sender, EventArgs e)
{
lock
{
if(m_State != AppState.Ready)
return;
else
m_State = AppState.InsideListView1DoubleClick;
}
//Do your stuff
m_State = AppState.Ready;
}
This way newer calls will be ignored instead of being queued. If you expect to be in multiple states at the same time, you could apply [Flags] attribute on this enum as well. Also note that enums are thread-safe and evaluating them is atomic, so multi-threading shouldn't be a problem either.

Lost network connection causes Excel to crash

My Excel AddIn is written in NetOffice, ExcelDNA, C#
It calls web service to get data. It takes a while to fetch a large amount of data.
During the process of data fetch, if network connection is lost, then Excel will hung, shows like "not responding". Now if I try to close Excel, it will ask you to close or debug. I simply close it.
Then when I restart Excel, there is an annoying message box comes up saying
"Excel experienced a serious problem with the 'commodity add-in' add-in. If you have seen this message multiple times, you should disable this add-in and check to see if an update is available. Do you want to disable this add-in?."
I wonder how to handle the situation when connection is lost appropriately? Thanks
Make the web service call asynchronously, if possible. Most WS will provide async versions and non-async versions of the calls that you can make.
If this is not possible, consider executing the web service data fetch within a separate thread.
In both scenarios, you should put some plumbing code in place to kill the job after a certain period, and probably some means to notify the user that not all is well.
"Excel experienced a serious problem with the 'XXX add-in' add-in. If
you have seen this message multiple times, you should disable this
add-in and check to see if an update is available. Do you want to
disable this add-in?."
You get this problem when an unhandled exception occurs. Excel will prompt you to disable the Add-In next start up. This can lead users to posts like this to fix it.
The pain is worse when you have to support clients using Citrix in non-admin environments. To get around the problem of Excel wanting to diable the add-In you have to add a Global Exception handler so the exception isn't referred back to Excel to avoid prompting users to disable the Add-In.
public YouAddInCtrl()
{
InitializeComponent();
// Add the event handler for handling UI thread exceptions to the event.
System.Windows.Forms.Application.ThreadException += ApplicationThreadException;
// Add the event handler for handling non-UI thread exceptions to the event.
AppDomain.CurrentDomain.UnhandledException += ApplicationUnhandledException;
}
private void ApplicationThreadException(object sender, ThreadExceptionEventArgs e)
{
addInManager.TopLevelExceptionHandler(e.Exception);
}
private void ApplicationUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
addInManager.TopLevelExceptionHandler((Exception)e.ExceptionObject);
}
// Any exceptions returned to Excel will cause the Addin to be disabled
// So we must swallow them here.
internal void TopLevelExceptionHandler(Exception ex)
{
var e = new NotificationEventArgs(NotificationEventArgs.NotificationEnum.TopLevelException);
if (NotifyEventTopLevelException != null)
{
if (NotifyEventTopLevelException(ex,e))
{
System.Diagnostics.Process.Start("mailto:Support#XYZ.com%3e?subject=XYZ%202%20PROD%20Environment%20Problem&body=Hi,%0A%0AIssue:%0A%0ASteps%20to%20Reproduce:");
}
}
LogExceptions(ex);
}
I would also suggest that you run the WebService request on a different thread, eg:
BackgroundWorker1.WorkerReportsProgress = true;
BackgroundWorker1.WorkerSupportsCancellation = true;
BackgroundWorker1.DoWork += DoWorkExecuteQuery;
BackgroundWorker1.RunWorkerCompleted += RunWorkerCompletedExecuteQuery;
private bool QueryData()
{
var thinkProgBar = new ThinkingProgressBar();
thinkProgBar.ShowCancelLink(true);
thinkProgBar.SetThinkingBar(true);
BackgroundWorker1.RunWorkerAsync(thinkProgBar);
thinkProgBar.ShowDialog();
if (thinkProgBar.Tag != null && thinkProgBar.Tag.ToString() == "Cancelled")
{
CancelGetDataByFilters();
thinkProgBar.SetThinkingBar(false);
return false;
}
thinkProgBar.SetThinkingBar(false);
return true;
}
private void DoWorkExecuteQuery(object sender, DoWorkEventArgs e)
{
dtQueryData = null;
e.Result = e.Argument;
((ThinkingProgressBar)e.Result).SetThinkingBar(true);
dtQueryData = WEBSERVICE.GetData(); //CALL YOUR WEBSERVICE HERE
}
private void RunWorkerCompletedExecuteQuery(object sender, RunWorkerCompletedEventArgs e)
{
var dlg = e.Result as ThinkingProgressBar;
if (dlg != null) {
((ThinkingProgressBar)e.Result).SetThinkingBar(false);
dlg.Close();
}
}
Here is the ThinkingProgress bar:
public partial class ThinkingProgressBar : Form
{
private System.DateTime startTime = DateTime.Now;
public ThinkingProgressBar()
{
InitializeComponent();
}
private void lblClose_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
this.Tag = "Cancelled";
this.Hide();
}
public void ShowCancelLink(bool show)
{
lblClose.Visible = show;
}
public void SetThinkingBar(bool on)
{
if (on)
{
lblTime.Text = "0:00:00";
startTime = DateTime.Now;
timer1.Enabled = true;
timer1.Start();
}
else
{
timer1.Enabled = false;
timer1.Stop();
}
}
private void timer1_Tick(object sender, EventArgs e)
{
var diff = new TimeSpan();
diff = DateTime.Now.Subtract(startTime);
lblTime.Text = diff.Hours + ":" + diff.Minutes.ToString("00") + ":" + diff.Seconds.ToString("00");
lblTime.Invalidate();
}
}

Question to Auto logout C# desktop application

I need to implement an auto logout feature in C#. Previously i have asked a similiar question before and i managed to implement it using the System.Windows.Forms.Timer . But right now i have a additional requirement apart from resetting the timer when the user move the mouse or enters a key i also need to reset the timer when a new message is received via the serial port ( DataReceived event handler ).
serialPort.DataReceived += port_DataRecieved;
I need to include the reset function in a portion of the port_DataRecieved function. I cannot simply add another delegate method to the serialPort.DataReceived which will perform the reset as the serialPort.DataReceived will received a lot of other messages that i am not interested in. I want to perform a reset when the message that i am interested in arrives. And i know where to put the reset feature. The issue is that the timer does not reset in port_DataRecieved method. And i cannot achieve the desired result using the System.Threading.Timer. Anyone can guide me or provide some suggestion on this issue ? Any help provided will be greatly apperciated.
public partial class Form1: Form
{
private System.Windows.Forms.Timer sessionTimer = new System.Windows.Forms.Timer();
public Form1()
{
initialiseTimer();
}
private void port_DataRecieved(object sender, SerialDataReceivedEventArgs e)
{
try
{
serialPort= (SerialPort)sender;
str = serialPort.ReadExisting();
string[] split = str.Split(Convert.ToChar(10));
for (int i = 1; i < split.Length; i++)
{
str = split[i];
if (split[i].StartsWith("+CMTI:"))
{
sessionTimer.Stop();
sessionTimer.Start();
//Other codes
}
}
}
catch (Exception)
{
MessageBox.Show("Error processing received commands !", "CONNECTION ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
sendRecPort.Close();
}
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
sessionTimer.Stop();
sessionTimer.Start();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
sessionTimer.Stop();
sessionTimer.Start();
}
private void initialiseTimer()
{
sessionTimer.Interval = (5 * 60 * 1000);
sessionTimer.Tick += new EventHandler(logOutUser);
sessionTimer.Stop();
sessionTimer.Start();
}
private void logOutUser(object sender, EventArgs e)
{
// logout the user
this.Hide();
//Open up the login Form
login.Show();
}
}
Your problem is that the the DataReceived event is being executed on a thread other than the UI thread. You're trying to modify the timer (a UI object) from a non-UI thread. This typically throws an exception, but it's possible that the method that issues the DataReceived event is swallowing that exception.
From the documentation for the DataReceived event:
The DataReceived event is raised on a
secondary thread when data is received
from the SerialPort object. Because
this event is raised on a secondary
thread, and not the main thread,
attempting to modify some elements in
the main thread, such as UI elements,
could raise a threading exception. If
it is necessary to modify elements in
the main Form or Control, post change
requests back using Invoke, which will
do the work on the proper thread.
You need to synchronize with the UI thread to set the timer.
void ResetTimer()
{
sessionTimer.Stop();
sessionTimer.Start();
}
private void port_DataRecieved(object sender, SerialDataReceivedEventArgs e)
{
//Other codes
this.Invoke((MethodInvoker)delegate { ResetTimer(); });
//Other codes
}
I need to include the reset function in a portion of the port_DataReceived function.
Ok. Gotcha.
I cannot simply add another delegate method to the serialPort.DataReceived which will perform the reset as the serialPort.DataReceived will receive a lot of other messages that I am not interested in.
Ok, but I thought you said:
I want to perform a reset when the message that I am interested in arrives.
So you either have to listen to that DataReceived method, or you won't know when that message arrives.
I'm confused. What is it you want to do? Magic?
if (dataReceived == "someValue1")
{
//action if matches "someValue1"
}
else if (dataReceived.Contains("someValue2"))
{
// action if contains "someValue2"
}
else if (dataReceived.IndexOf("someValue3") != -1 )
{
// action if contains "someValue3"
}
else if (dataReceived == "someValue4")
{
// action if matches "someValue4"
}
else
{
// default action
}

Is there a way to tell which EventLog caused the EntryWritten event in C#?

I'm working on a web application that shows event logs data, similar to Event Viewer. The application also has to provide a way for users to subscribe to event logs and receive notifications when an entry is written to subscribed logs, using a web service.
I'm using this code to subscribe to an event log in the web service:
EventLog eventLog = new EventLog(observer.logName, observer.machineName);
eventLog.EnableRaisingEvents = true;
eventLog.EntryWritten += new EntryWrittenEventHandler(eventLog_EntryWritten);
eventList.Add(eventLog);
I'm trying to use observers as subscribers to event logs, and when an EventWritten event is handled, call the Update method of one observer. The problem is, I don't know how to differentiate event logs, since they all use the same event handler. I'm doing this because the number of event logs differs from one machine to another. Also, I want observers to handle only one type of EventLog, ie. one observer would send an e-mail when an event is written to the Application log.
I use this line of code to get all logs on the current computer:
remoteEventLogs = EventLog.GetEventLogs(machineName);
EventWritten event handler has this object sender parameter, but Visual Studio shows its type as EventLogInternal, which I can't use, and I can't cast the sender to EventLog to get EventLog.Log property. If I try to cast it, like this:
void eventLog_EntryWritten(object sender, EntryWrittenEventArgs e)
{
var log = (EventLog)sender;
}
I get an exception saying I can't cast an EventLogInternal to EventLog.
Is there a way to know which EventLog fires the event?
Thanks
I think the problem is, that the whole concept of the EventLog class is that it assumes it works upon a single Log - which it does. So neither the EventWrittenEventArgs nor the EventEntry class sport a member that contains the Log-name, as it is implicitly given by the associated EventLog-instance. Bad is of course, that you cannot get to it inside the EventWritten-handler.
You could create a wrapper around System.Diagnostics.EventLog, like so:
class MyEventLog : EventLog
{
public MyEventLog(string logName, string machineName)
: base(logName, machineName)
{
base.EnableRaisingEvents = true;
base.EntryWritten += MyEventLog_EntryWritten;
}
void MyEventLog_EntryWritten(object sender, EntryWrittenEventArgs e)
{
Console.WriteLine("Entry in {0} log.", base.Log);
// Your code
}
}
Then use MyEventLogin places where you normally would use EventLog. Probably give it a better name though.
You could also factor out the "Your Code" part by providing an Action<string, EntryWrittenEventArgs> property that is being called from inside MyEventLog_EntryWritten and can be set to your "external" handler function.
Another option would be to use reflection along these lines:
string log = (string)sender.GetType().GetProperty("Log").GetValue(sender, null);
since sender in this case actually has the Log property.
I think that what you are looking for can be found in the EntryWrittenEventArgs.
The MSDN shows there is a property called Entry that shows you all kinds of information about what just got logged. There are some properties that might help you in the EventLogEntry class, such as MachineName or UserName.
Here is a link to the Args class
http://msdn.microsoft.com/en-us/library/system.diagnostics.entrywritteneventargs.aspx
Here is a link to the Entry class
http://msdn.microsoft.com/en-us/library/system.diagnostics.eventlogentry.aspx
I don't see a direct link to the specific event log, but if you poke around in that class with the debugger the Entry object might give you enough information to look it up.
I hope this helps some.
I agree with the idea of wrapping the EventLog class within another class as suggested by Christian. I recently worked on such a requirement.
This is the class that I created
public class EventLogWatcher : EventLog
{
Action<string, EntryWrittenEventArgs> _changeHandler;
public EventLogWatcher(string logName, Action<string, EntryWrittenEventArgs> changeHandler)
: base(logName)
{
_changeHandler = changeHandler;
}
public void EnableCapture()
{
base.EnableRaisingEvents = true;
base.EntryWritten += EventLogChangeHandler;
}
public void DisableCapture()
{
base.EnableRaisingEvents = false;
base.EntryWritten -= EventLogChangeHandler;
}
private void EventLogChangeHandler(object sender, EntryWrittenEventArgs e)
{
_changeHandler(base.Log, e);
}
}
Here is a usage
foreach (string eventlogType in LogTypes)
logWatchers.Add(new EventLogWatcher(eventlogType, EventLogChangeHandler));
foreach (EventLogWatcher localLog in logWatchers)
{
try
{
localLog.EnableCapture();
}
catch(Exception ex)
{
EventManager.PublishExceptionLogMessage(ex);
}
}
EventManager.PublishInfoLogMessage($"Started EventLog listeners for {string.Join(",", LogTypes)} logs");
private void EventLogChangeHandler(string eventLogType, EntryWrittenEventArgs e)
{
try
{
if (UploadAllowed(eventLogType, e))
{
Dm.EventLog model = _eventLogEntryMapper.MapEntryToModel(e);
Task.Factory.StartNew(
() => _eventLogUploadService.UploadEventLog(model),
_cancellationTokenProvider.Token,
TaskCreationOptions.None,
TaskScheduler.Default);
}
}
catch(Exception ex)
{
EventManager.PublishExceptionLogMessage(ex);
}
}
Another option would be to change the event-registration like this:
eventLog.EntryWritten += (sender, e) => eventLog_EntryWritten(eventLog, e);

Categories