I have a class with the following code:
public delegate void EventHandler(HMIEventArgs e);
public event EventHandler OnEvent;
private void ReadReg(RegisterMap register)
{
if (!_hmiConnected) return;
var eventArgs = new HMIEventArgs();
var handler = OnEvent;
try
{
byte slaveId = 1;
ushort startAddress = register.Register;
eventArgs.Event = Events.GotData;
eventArgs.IPAddress = _hostname;
eventArgs.Name = register.FriendlyName;
ushort[] val = _master.ReadHoldingRegisters(slaveId, startAddress, 1);
eventArgs.Data = val[0];
handler?.Invoke(eventArgs);
// -------- THIS GETS PRINTED ------------
Debug.WriteLine("Got data from " + _hostname + ":" + register.Register + "(" + register.FriendlyName + ") : " + val[0]);
}
catch (Exception err)
{
Debug.WriteLine(err.ToString());
}
}
Several instances of this class are created in another class :
new Thread(() =>
{
tasks.Add(Task.Factory.StartNew(() =>
{
_masters.Add(new HMIMaster().Connect(ipAddress, port).SetRegisters(registers));
_masters.Last().OnEvent += HMIEvent;
Debug.WriteLine(_masters.Count + " masters");
}));
}).Start();
private static void HMIEvent(HMIEventArgs e)
{
// HOWEVER THIS SOMETIMES DOESN'T SHOW FOR
// ALL INSTANCES OF THE PREVIOUS CLASS
Debug.WriteLine(">> in logger (" + e.IPAddress + " " + e.Event + ") >> " + e.Name + " :: " + e.Data);
var handler = OnEvent;
handler?.Invoke(e);
}
Am I doing something wrong here?
I would use a static event to avoid registering every time on new instance and un-register on dispose (to void memory leaks). No locking is required in your case so I would simplify it like that:
(First class)
public static event EventHandler<HMIEventArgs> HMIEvent;
private void OnHMIEvent(HMIEventArgs e)
{
HMIEvent?.Invoke(this, e);
}
private void ReadReg(RegisterMap register)
{
...
OnHMIEvent(new HMIEventArgs()
{
Name = register.FriendlyName,
Event = Events.GotData,
IPAddress = _hostname,
eventArgs.Data = val[0]
});
...
}
(Second Class)
...
FirstClass.HMIEvent += FirstClass_HMIEvent; // Probably in your static constructor, register only once (unregister if required on disposal)
...
private void FirstClass_HMIEvent(object sender, HMIEventArgs e)
{
// (FirstClass)sender can be used here if required
}
By the way those two lines at your sample code should not be there (on your static HMIEvent method, you didn't provide us what is the OnEvent on your second class and you dont need to pass it on the handler var every time):
var handler = OnEvent;
handler?.Invoke(e);
Related
I am in need of a solution to trigger code when an external application is closing / closes.
I am unable to use System.Diagnostics Process.GetProcessByName to detect if the process is running since it might conflict with an anticheat system. I would need trigger the snippet of code only when the program closes and only then.
I made a good, event-based implementation.
class Monitor
{
public event EventHandler ProgramStarted;
public event EventHandler ProgramClosed;
public Monitor(string process)
{
string pol = "2";
if (!process.EndsWith(".exe")) process += ".exe";
var queryString =
"SELECT *" +
" FROM __InstanceOperationEvent " +
"WITHIN " + pol +
" WHERE TargetInstance ISA 'Win32_Process' " +
" AND TargetInstance.Name = '" + process + "'";
var s = #"\\.\root\CIMV2";
ManagementEventWatcher watcher = new ManagementEventWatcher(s, queryString);
watcher.EventArrived += new EventArrivedEventHandler(OnEventArrived);
watcher.Start();
}
private void OnEventArrived(object sender, EventArrivedEventArgs e)
{
if (e.NewEvent.ClassPath.ClassName.Contains("InstanceDeletionEvent"))
{
EventHandler handler = ProgramClosed;
handler?.Invoke(this, e);
}
else if (e.NewEvent.ClassPath.ClassName.Contains("InstanceCreationEvent"))
{
EventHandler handler = ProgramStarted;
handler?.Invoke(this, e);
}
}
}
To use it, you just create an instance of the class and set up the events. For example:
static void Main(string[] args)
{
var mon = new Monitor("chrome");
mon.ProgramClosed += Mon_ProgramClosed;
mon.ProgramStarted += Mon_ProgramStarted;
Console.ReadKey(true);
}
private static void Mon_ProgramStarted(object sender, EventArgs e)
{
MessageBox.Show("Program started.");
}
private static void Mon_ProgramClosed(object sender, EventArgs e)
{
MessageBox.Show("Program closed.");
}
Make sure to add reference to System.Drawing if you're using a console app, and ,for winforms, adjust the modifiers.
I have a pretty basic android application. Most of it is still from the template. I use an own class library for a XMPP client. Now what is happening, is that if I declare a global TextView for use in events or different methods(OnCreate, OnResume etc.) my events seem not to get raised.
My library is definetly not the reason since I tested it under different circumstances.
[Activity(Label = "#string/app_name", Theme = "#style/AppTheme.NoActionBar", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
public XMPPClient client;
TextView consoleText;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_main);
Android.Support.V7.Widget.Toolbar toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
FloatingActionButton fab = FindViewById<FloatingActionButton>(Resource.Id.fab);
fab.Click += FabOnClick;
//consoleText = FindViewById<TextView>(Resource.Id.consoleText);
client = new XMPPClient("10.0.2.2", 5222, "chris3", "Define")
{
AuthenticationInfo = true
};
client.OnPublishEvent += Client_OnPublishEvent;
client.OnConnection += Client_OnConnection;
client.OnMessageReceived += Client_OnMessageReceived;
}
private void Client_OnMessageReceived(object sender, MessageEventArgs e)
{
//consoleText.Text += "\nMessage: " + e.text + " from " + e.from;
Log.Debug("mytag", "Message!");
}
private void Client_OnPublishEvent(object sender, SubscriptionEventArgs e)
{
//consoleText.Text += "\nPublication: " + e.title + " with content " + e.content + " at " + e.published;
Log.Debug("mytag", "Publication");
}
private void Client_OnConnection(object sender, ConnectionEventArgs e)
{
//consoleText.Text += "\nConnection status changed: " + e.Status;
Log.Debug("mytag", "ConnectionChange");
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
MenuInflater.Inflate(Resource.Menu.menu_main, menu);
return true;
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
int id = item.ItemId;
if (id == Resource.Id.action_subscribe)
{
client.Subscribe("Node1");
Log.Debug("mytag", "Subscribed");
return true;
}
return base.OnOptionsItemSelected(item);
}
private void FabOnClick(object sender, EventArgs eventArgs)
{
View view = (View) sender;
client.Connect();
}
}
When the TextView consoleText object is there, my OnConnection-event gets fired in the beginning, and when I close the client.
However, if it isn't there, every event works perfectly fine.
For now as I am a beginner with Xamarin I'm really interested on the source of the problem.
You likely need to wrap the text assignments to be processed against the primary dispatcher (UI Thread).
So for example it would look something like this.
private void Client_OnMessageReceived(object sender, MessageEventArgs e)
{
Activity.RunOnUiThread(() => {
consoleText.Text += "\nMessage: " + e.text + " from " + e.from;
});
Log.Debug("mytag", "Message!");
}
Give that a try and let me know if it still errors, don't forget you'll need to wrap all of the consoleText.Text assignments inside of this.
Activity.RunOnUiThread(() => {
});
// You might not need the Activity part, I can't remember exactly which
// one it is for an AppCompatActivity.
RunOnUiThread(() => {
});
This is an app that uses WMI to fetch Local Administrator group members on a remote computer. In an attempt to make threadsafe calls to update my main UI from within Background Worker I am getting a StackOverflowException. I have copied the example from another thread on Stack. Could someone help me to identify the cause?
private void getLocalAdministrators(string serverName)
{
try
{
System.Management.ManagementScope scope = new System.Management.ManagementScope("\\\\" + serverName + "\\root\\cimv2");
scope.Connect();
StringBuilder qs = new StringBuilder();
qs.Append("SELECT PartComponent FROM Win32_GroupUser WHERE GroupComponent = \"Win32_Group.Domain='" + serverName + "',Name='" + "Administrators'\"");
System.Management.ObjectQuery query = new System.Management.ObjectQuery(qs.ToString());
System.Management.ManagementObjectSearcher searcher = new System.Management.ManagementObjectSearcher(scope, query);
foreach (System.Management.ManagementObject group in searcher.Get())
{
string groupDetails = serverName + tab + group["PartComponent"].ToString() + tab;
string domainPart = groupDetails.Split('=')[1];
domainPart = domainPart.Replace("\"","").Replace(",Name","");
string accountPart = groupDetails.Split('=')[2];
accountPart = accountPart.Replace("\"", "");
if (query != null)
{
updateUISafely(serverName + tab + domainPart + tab + accountPart);
}
else
{
updateUISafely("Error with: " + serverName);
}
}
}
catch (Exception ex)
{
updateUISafely("Error with: " + serverName + ". " + ex.Message);
}
}
public delegate void ProcessResultDelegate(string result);
void updateUISafely(string result)
{
if (textBox2.InvokeRequired)
{
var d = new ProcessResultDelegate(updateUISafely);
d.Invoke(result);
}
else
{
textBox2.AppendText(result + nl);
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string[] strArray;
strArray = textBox1.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
int objectCount = strArray.Count();
int count = 0;
foreach (string str in strArray)
{
getLocalAdministrators(str);
count++;
backgroundWorker1.ReportProgress((100 * count) / objectCount);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
foreach (string item in resultsList)
{
textBox2.AppendText(item + Environment.NewLine);
}
btnGet.Enabled = true;
}
You are invoking (aka calling) the delegate, causing it to repeatedly call updateUISafely without ever invoking on the UI thread.
You should call Invoke on the form/control instead providing the delegate as parameter.
Use:
this.Invoke(d);
this is my first C# post.
I have a question on event binding.
I have a FileWatcher which I'd like to bind to functions that are defined in a separate class called FileWatcherEvents.
I don't want the events to be declared in the Program class, how can this be done?
As you can see, I try to bind the events for Created and Deleted.
The problem is that these events are not called when I delete or create a file in the folder. But when I declare the event handlers in the Program class, it does work.
Any help or insights is appreciated.
Program
using System.IO;
class Program : ServiceBase
{
private FileSystemWatcher _watcher;
public Program()
{
FileWatcherEvents fwe = new FileWatcherEvents();
this._watcher = new FileSystemWatcher();
((System.ComponentModel.ISupportInitialize)(this._watcher)).BeginInit();
//
// _watcher
//
this._watcher.EnableRaisingEvents = true;
this._watcher.Filter = "*.txt";
this._watcher.NotifyFilter =
((NotifyFilters)(((((NotifyFilters.FileName
| NotifyFilters.DirectoryName)
| NotifyFilters.LastWrite)
| NotifyFilters.LastAccess)
| NotifyFilters.CreationTime)));
this._watcher.Path = "T:\\out\\";
this._watcher.Deleted += new FileSystemEventHandler(fwe.ShipmentFileCreated);
this._watcher.Created += new FileSystemEventHandler(fwe.FileDeleted);
((System.ComponentModel.ISupportInitialize)(this._watcher)).EndInit();
}
static void Main(string[] args)
{
Program prg = new Program();
Console.Write(FileManager.getNewestFile("T:\\out\\"));
while (Console.Read() != 'q') ;
}
}
FileWatcherEvents
class FileWatcherEvents
{
public void ShipmentFileCreated(object sender, FileSystemEventArgs e)
{
Console.WriteLine("CREATED: " + sender.ToString() + e.ToString());
}
public void FileDeleted(object sender, FileSystemEventArgs e)
{
Console.WriteLine("DELETED: " + sender.ToString() + e.ToString());
}
}
I believe you would need to declare fwe in a larger scope, like at the Program level instead of inside the Program constructor. Otherwise the object will go away, and possibly all the events that lead to it as well (never been entirely clear on what happens to the functions that handle events on an instance when the instance goes away, but the events could still occur, but it's very possible they will no longer run).
Edit:
I got your code to work with some minor adjustments. Mainly I had to move EnableRaisingEvents to the end of the block because .NET throws an exception if you do it before setting the path. How did you not see that exception?
class Program
{
private FileSystemWatcher _watcher;
public Program()
{
FileWatcherEvents fwe = new FileWatcherEvents();
this._watcher = new System.IO.FileSystemWatcher();
this._watcher.Filter = "*.txt";
this._watcher.NotifyFilter = ((System.IO.NotifyFilters)(((((
System.IO.NotifyFilters.FileName | System.IO.NotifyFilters.DirectoryName)
| System.IO.NotifyFilters.LastWrite) | System.IO.NotifyFilters.LastAccess)
| System.IO.NotifyFilters.CreationTime)));
this._watcher.Path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
this._watcher.Deleted += new System.IO.FileSystemEventHandler(fwe.ShipmentFileCreated);
this._watcher.Created += new System.IO.FileSystemEventHandler(fwe.FileDeleted);
this._watcher.EnableRaisingEvents = true;
Console.ReadLine();
}
public static void Main()
{
Program prg = new Program();
using (System.IO.StreamWriter w = new System.IO.StreamWriter(
System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "TestFile.txt"), false))
{
w.WriteLine(DateTime.Now.ToLongTimeString());
}
Console.ReadLine();
}
}
class FileWatcherEvents
{
public void ShipmentFileCreated(object sender, FileSystemEventArgs e)
{
Console.WriteLine("CREATED: " + sender.ToString() + e.ToString());
}
public void FileDeleted(object sender, FileSystemEventArgs e)
{
Console.WriteLine("DELETED: " + sender.ToString() + e.ToString());
}
}
Turns out that the code works fine, the events fired, but the functions weren't because of the *.txt filter in the private FileSystemWatcher object.
I have this example code that DOES work.
main form:
FileTransferManager fm = new FileTransferManager();
...
public FrmMain()
{
InitializeComponent();
...
fm.OnFile += fm_OnFile;
}
...
void fm_OnFile(object sender, FileTransferEventArgs e)
{
var recvFile = new FrmReceiveFile(fm, e);
recvFile.Show();
e.Accept = true;
}
and FrmReceiveFile:
public partial class FrmReceiveFile : Form
{
private FileTransferManager fm;
private FileTransferEventArgs ftea;
public FrmReceiveFile(FileTransferManager ftm, FileTransferEventArgs fea)
{
InitializeComponent();
fm = ftm;
ftea = fea;
Text = "File transfer: " + ftea.Jid;
lblSize.Text = Util.HumanReadableFileSize(ftea.FileSize);
lblFileName.Text = ftea.Filename;
lblDescription.Text = ftea.Description;
fm.OnError += fm_OnError;
fm.OnEnd += fm_OnEnd;
fm.OnStart += fm_OnStart;
fm.OnProgress += fm_OnProgress;
}
void fm_OnStart(object sender, FileTransferEventArgs e)
{
MessageBox.Show("file transfer started"); ///// THIS APPEARS & EVERYTHING WORKS!
if (e.Sid != ftea.Sid)
return;
}
...
And here is my code, all in one form, yet somehow it does not work.
public partial class Form1 : Form
{
private string sid = "";
FileTransferManager fmout = new FileTransferManager(); //// this FileTransferManager is for outgoing files
FileTransferManager fmin = new FileTransferManager(); //// this FileTransferManager is for incomeing files
FileTransferEventArgs fta = new FileTransferEventArgs();
Jid _jid = new Jid();
public Form1()
{
InitializeComponent();
fmout.OnError += fmout_OnError;
fmout.OnEnd += fmout_OnEnd;
fmout.OnStart += fmout_OnStart;
fmout.OnProgress += fmout_OnProgress;
fmout.XmppClient = xmppClient;
fmin.XmppClient = xmppClient;
fmin.OnFile += fmin_OnFile;
fmin.OnEnd += fmin_OnEnd;
fmin.OnStart += fmin_OnStart;
fmin.OnProgress += fmin_OnProgress;
}
////////////////////////////////////////////////////////////////////////////////////////////
void fmin_OnFile(object sender, FileTransferEventArgs e)
{
DisplayEvent("INCOMING FILE: " + e.Filename + " - " + e.FileSize); ///// THIS APPEARS CORRECTLY
e.Accept = true;
fta = e;
}
void fmin_OnStart(object sender, FileTransferEventArgs e) /// THIS WON'T START! :(
{
MessageBox.Show("Incoming file!"); /// THIS WON'T START! :(
if (e.Sid != fta.Sid)
return;
}
Looks like e.Accept = true; does not launch fmin_OnStart ... any ideas what might be the problem?
Thanks!
The difference (that can be made out from the code you have shared) in the two pieces of code is that in the first one you are registering the "fm.OnStart += fm_OnStart" event when your "OnFileHandler" is called, while in the other one (not working) you are doing that upfront, even when your OnFileHandler is not called.
Though, as an user of FileTransferManager, i dont think that should matter.
Still, you can try the same thing in the second code.. so do it as below.
void fmin_OnFile(object sender, FileTransferEventArgs e)
{ fmin.OnStart += fmin_OnStart;
DisplayEvent("INCOMING FILE: " + e.Filename + " - " + e.FileSize);
e.Accept = true; fta = e; }
If that works, i would rather question the programmer of FileTransferManager.