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.
Related
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);
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(() => {
});
I was hoping someone could point me in the right direction. I want to make a simple WPF application that has a button and a textbox. I click the button, and it starts to loop downloading a bunch of files. I can't seem to figure out how to not let the downloading stop the UI from updating. From what I can gather I'm probably going to have to use some threading code; but so far all the examples I've found and tried don't work for me. Any help or direction on where I should look and learn would be great. I can't seem to figure out how I can output those textbox.text messages around each file download.
foreach (var ticker in tickers)
{
var url = string.Format(urlPrototype, ticker, startMonth, startDay, startYear, finishMonth, finishDay, finishYear, "d");
var csvfile = directory + "\\" + ticker.ToUpper() + ".csv";
tbOutput.Text += "Starting Download of : " + ticker + "\n";
webClient.DownloadFile(url, csvfile);
tbOutput.Text += "End Download of : " + ticker + "\n";
numStocks++;
}
tbOutput.Text += "Total stocks downloaded = " + numStocks + "\n";
If you mark your method as async, you can use the DownloadFileTaskAsync method
await webClient.DownloadFileTaskAsync(url, csvfile)
If you choose to use the BackgroundWorker, it allows you to output those messages into the TextBox around each file download. Here is a crude example adapted for your requirement.
1) At the class level, create an instance of the BackgroundWorker class and add event handlers to the BackgroundWorker instance's events:
BackgroundWorker workerDownload = new BackgroundWorker();
workerDownload.WorkerReportsProgress = true;
workerDownload.DoWork += workerDownload_DoWork;
workerDownload.ProgressChanged += workerDownload_ProgressChanged;
workerDownload.RunWorkerCompleted += workerDownload_RunWorkerCompleted;
2) Create an event handler for the background worker's DoWork event:
The DoWork event handler is where you run the time-consuming operation
on the background thread. Any values that are passed to the background
operation are passed in the Argument property of the DoWorkEventArgs
object that is passed to the event handler.
private void workerDownload_DoWork(object sender, DoWorkEventArgs e)
{
foreach (var ticker in tickers)
{
// you can pass the required info as argument:
string[] arrArg = (string[])e.Argument;
string theUrl = arrArg[0];
string directory = arrArg[1];
var url = string.Format(theUrl, ticker);
var csvfile = directory + "\\" + ticker.ToUpper() + ".csv";
// perform the download operation and report progress:
workerDownload.ReportProgress(0, "Starting Download of : " + ticker + "\n");
webClient.DownloadFile(url, csvfile);
workerDownload.ReportProgress(100, "End Download of : " + ticker + "\n");
numStocks++;
}
}
3) Create an event handler for the background worker's ProgressChanged event:
In the ProgressChanged event handler, add code to indicate the
progress, such as updating the user interface.
private void workerDownload_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
tbOutput.Text += e.UserState.ToString();
}
4) Create an event handler for the RunWorkerCompleted event:
The RunWorkerCompleted event is raised when the background worker has
completed.
private void workerDownload_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
tbOutput.Text += "Total stocks downloaded = " + numStocks + "\n";
}
5) Start running the background operation by calling the RunWorkerAsync method:
int numStocks = 0;
string strDirectory = "<a_directory>";
string strUrl = string.Format(urlPrototype, startMonth, startDay, startYear, finishMonth, finishDay, finishYear, "d");
string[] args = new string[2] { strUrl, strDirectory };
workerDownload.RunWorkerAsync(args);
There are a lot ways to implement it. For example:
1) Using async/await if you programming in .Net Framework 4.5. It is simpler than BackgroundWorker
https://msdn.microsoft.com/en-us/library/hh191443.aspx
private async void button_Click(object sender, RoutedEventArgs e)
{
Uri someUrl=new Uri(#"http://dotnetperls.com");
WebClient webClient=new WebClient();
await webClient.DownloadFileTaskAsync(someUrl, csvFile);
}
2) BackgroundWorker. This class is really intended to make asynchronous operations to avoid freezing UI.
See http://www.wpf-tutorial.com/misc/multi-threading-with-the-backgroundworker
public partial class MainWindow : Window
{
BackgroundWorker bw;
public MainWindow()
{
InitializeComponent();
bw = new BackgroundWorker();
bw.DoWork += bw_DoWok;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
}
}
void bw_RunWorkerComleted(object sender, RunWorkerCompletedEventAgs e)
{
MessageBox.Show("The result is " + e.Result.ToString());
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
foreach (var ticker in tickers)
{
var url = string.Format(urlPrototype, ticker, startMonth, startDay, startYear, finishMonth, finishDay, finishYear, "d");
var csvfile = directory + "\\" + ticker.ToUpper() + ".csv";
webClient.DownloadFile(url, csvfile);
numStocks++;
}
e.Result = "End Of Download ";
}
private void button_Click(object sender, RoutedEventArgs e)
{
bw.RunWorkerAsync();
tbOutput.Text += "Starting Download of : " + ticker + "\n";
}
3) Use Thread class and update using Dispatcher class:
ThreadStart job = new ThreadStart(() =>
{
foreach (var ticker in tickers)
{
var url = string.Format(urlPrototype, ticker, startMonth, startDay, startYear, finishMonth, finishDay, finishYear, "d");
var csvfile = directory + "\\" + ticker.ToUpper() + ".csv";
webClient.DownloadFile(url, csvfile);
numStocks++;
}
Dispatcher.BeginInvoke((Action)(()=> tbOutput.Text += "End Download of : " + ticker + "\n";}));
});
Thread thread = new Thread(job);
thread.Start();
http://www.beingdeveloper.com/use-dispatcher-in-wpf-to-build-responsive-applications
I must be doing something completely wrong but I cannot figure it out. I have a form and I add in VS a timer control. I also have a class that is watching for an application to start (notepad.exe). When the event arrives it is supposed to enable the timer, set the interval and on each tick do something (like firing a messagebox or changing a label). This seems not to be happening. A look in the code make help someone give me a clue. The code is below:
public partial class Monitor : Form
{
EventWatcher eventWatch = new EventWatcher();
ManagementEventWatcher startWatcher = new ManagementEventWatcher();
ManagementEventWatcher endWatcher = new ManagementEventWatcher();
public Monitor()
{
InitializeComponent();
startWatcher = eventWatch.WatchForProcessStart("notepad.exe");
endWatcher = eventWatch.WatchForProcessEnd("notepad.exe");
}
private void appTimer_Tick(object sender, EventArgs e)
{
label1.Text = "tick";
MessageBox.Show("Tick");
}
}
And the watcher class is
class EventWatcher
{
public ManagementEventWatcher WatchForProcessStart(string processName)
{
string queryString =
"SELECT TargetInstance" +
" FROM __InstanceCreationEvent " +
"WITHIN 2 " +
" WHERE TargetInstance ISA 'Win32_Process' " +
" AND TargetInstance.Name = '" + processName + "'";
// The dot in the scope means use the current machine
string scope = #"\\.\root\CIMV2";
// Create a watcher and listen for events
ManagementEventWatcher watcher = new ManagementEventWatcher(scope, queryString);
watcher.EventArrived += ProcessStarted;
watcher.Start();
return watcher;
}
public void ProcessStarted(object sender, EventArrivedEventArgs e)
{
Monitor monitor = new Monitor();
//set timer interval and start time for Monitor class. (form)
monitor.appTimer.Interval = 5000;
monitor.appTimer.Enabled = true;
MessageBox.Show("notepad started");
}
}
I noticed two things:
When notepad.exe starts and I have the MessageBox.Show("notpad started"); line commented out the messabox in the timer tick event won't fire. If it is as displayed above it will show me both messageboxes (notepad started and tick). However, the label1.Text will not change no matter what.
I am not sure what I am doing wrong. I am sure it has something to do with how the timer event is handled but I am not sure what I should be doing. Any ideas?
Your code creates a new instance of Monitor.
Thus you are not accessing the properties of the Monitor's instance you called eventWatch.WaitForProcessStart() in, so they're not changing.
One way to solve this would be an event that is fired as soon as ProcessStarted() is fired.
Your code could look like this:
class EventWatcher
{
public event EventHandler<EventArrivedEventArgs> ProcessStarted;
public ManagementEventWatcher WatchForProcessStart(string processName)
{
string queryString =
"SELECT TargetInstance" +
" FROM __InstanceCreationEvent " +
"WITHIN 2 " +
" WHERE TargetInstance ISA 'Win32_Process' " +
" AND TargetInstance.Name = '" + processName + "'";
// The dot in the scope means use the current machine
string scope = #"\\.\root\CIMV2";
// Create a watcher and listen for events
ManagementEventWatcher watcher = new ManagementEventWatcher(scope, queryString);
watcher.EventArrived += OnProcessStarted;
watcher.Start();
return watcher;
}
protected virtual void OnProcessStarted(object sender, EventArrivedEventArgs e)
{
EventHandler<EventArrivedEventArgs> copy = ProcessStarted;
if (copy != null)
copy(sender, e); // fire the event
}
}
public partial class Monitor : Form
{
EventWatcher eventWatch = new EventWatcher();
ManagementEventWatcher startWatcher = new ManagementEventWatcher();
ManagementEventWatcher endWatcher = new ManagementEventWatcher();
public Monitor()
{
InitializeComponent();
startWatcher = eventWatch.WatchForProcessStart("notepad.exe");
startWatcher.ProcessStarted += startWatcher_ProcessStarted; // subscribe to the event
endWatcher = eventWatch.WatchForProcessEnd("notepad.exe");
}
private void startWatcher_ProcessStarted(object sender, EventArrivedEventArgs e)
{
Monitor monitor = new Monitor();
//set timer interval and start time for Monitor class. (form)
monitor.appTimer.Interval = 5000;
monitor.appTimer.Enabled = true;
MessageBox.Show("notepad started");
}
private void appTimer_Tick(object sender, EventArgs e)
{
label1.Text = "tick";
MessageBox.Show("Tick");
}
}
It also looks like label1.Text will not change as you are running from a different thread. You would need to run an invoke on that label to update it from ManagementEventWatcher.
Use this class:
using System;
using System.Windows.Forms;
public static class ControlExtensions
{
/// <summary>
/// Executes the Action asynchronously on the UI thread, does not block execution on the calling thread.
/// </summary>
/// <param name="control"></param>
/// <param name="code"></param>
public static void UIThread(this Control #this, Action code)
{
if (#this.InvokeRequired)
{
#this.BeginInvoke(code);
}
else
{
code.Invoke();
}
}
}
and replace `label1.Text = "tick" ` with
this.UIThread(() => this.label1.Text = "tick"));
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.