C# Detect Closing Application - c#

I am trying to create a taskbar replacement, and I want a button for each running application.
public void AddBtn(string name) {
Button newButton = new Button();
this.Controls.Add(newButton);
newButton.Location = new Point(count * 75, Screen.PrimaryScreen.Bounds.Height - 25);
newButton.Text = name;
newButton.Name = count.ToString();
newButton.BringToFront();
count++;
}
private void GetRunning()
{
Process[] processes = Process.GetProcesses();
foreach (Process process in Process.GetProcesses().Where(p => !string.IsNullOrEmpty(p.MainWindowTitle)).ToList())
AddBtn(process.ProcessName);
}
This is what I have so far, it creates a button for each running application. I need to detect if one of the applications close, so I can remove the button that corresponds with it. If you think there is a better way to do it, let me know.

First off, it would be best to move the code for adding the buttons elsewhere, and only use GetRunning to get the currently running processes. Modify it like so:
private List<Process> GetRunning()
{
return Process.GetProcesses().Where(p => !string.IsNullOrEmpty(p.MainWindowTitle)).ToList();
}
private void AddButtons()
{
for (var process in this.GetRunning())
{
AddBtn(process.ProcessName);
}
}
Now, regarding your issue, there are two solutions:
Solution 1: Use events
Create a method, HandleClosed, which handles all the operations that need to be done when a process has exited:
private void HandleClosed(Process sender, EventArgs e)
{
// Stuff
}
Then, you can bind it to the processes' Exited event like so:
private void BindProcesses()
{
foreach (var process in this.GetRunning())
{
process.EnableRaisingEvents = true;
process.Exited += this.HandleClosed;
}
}
Now, whenever one of the processes exits, HandleClosed will be called, and the process that closed will be passed to it via sender.
Solution 2: Managing it yourself
Add a private field to your class, private List<Process> prevProcesses;.
Then create a new method, GetClosed:
private List<Process> GetClosed()
{
var current = GetRunning();
var result = this.prevProcesses.Except(current);
this.prevProcesses = current;
return result.ToList();
}
What this method does is it gets the current running processes, then compares that enumeration to prevProcesses. The processes that are in prevProcesses but aren't in current are the ones that have closed. Then, it updates prevProcesses with the current enumeration.
Finally, get the initial value for prevProcesses in your constructor, or any other method that you use for initialization:
this.prevProcesses = this.GetRunning();

Related

Passing data through threads to populate list box using C#

I am trying to update a Windows form listbox from a different thread in C#.
I have a button that creates a new thread. This thread calls a method in another class that starts an asynchronous function which populates a list data type.
The contents of the list needs to be set as the contents of the list box each time a new item is added to the list.
Updating the list box, in the same thread it was created, is a problem because of the need to update with each new item; using a loop will make the rest of the form controls unresponsive.
I have tried using
this.Invoke((MethodInvoker)(() => OutputBox.Items.Add(engineOutput)));
found
How to add item to listBox from other thread?,
but I haven't been able to get this to work. I am not sure if I am have it in the wrong place or it is not relevant to my solution. (The line above has been changed to fit the context of the solution and is shown below in the code snippet).
I have added a simplified version of code that shows where I have tried to implement the above solution and how the a visual explanation of the description above.
public partial class FrmPacketSniffer : Form
{
protected bool stop = false;
public FrmPacketSniffer()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
ThreadStart startSniff = new ThreadStart(StartSniffing);
Thread startThread = new Thread(startSniff);
startThread.Start();
}
private void StartSniffing()
{
PacketSniffng sniffer = new PacketSniffing;
sniffer.Connection(this);
}
}
class PacketSniffing
{
public static bool pause = false;
static List<string> headerList = new List<string>();
public static void Connection(FrmPacketSniffer frmPacketSniffer)
{
//Creates a socket called con...
Sniffing(con, frmPacketSniffer);
}
private void Sniffing(Socket con, FrmPacketSniffer frmPacketSniffer)
{
Action<IAsyncResult> collect = null;
collect = (ar)=>
{
headerList.Add("data");
//this.Invoke((MethodInvoker)(() => frmPacketSniffer.lstPackets.Items.Add(headerList)));
//Here I placed the code mentioned above.
frmPacketSniffer.lstPackets.DataSource = headerList; //List box 'lstPackets' is the target to update.
con.BeginReceive(packet, 0, 24, SocketFlags.None, new AsyncCallback(collect), null);
};
}
}
Can anyone provide any tips on how I can fix this please?
Thanks
You need to pass the instance of your form to your instance of PacketSniffing when you create it, and then use that instance and call Invoke on it.

Check multiple checkbox.checked state inside a do while cycle

I asked in a previous question how to "Threading 2 forms to use simultaneously C#".
I realize now that I was not explicit enough and was asking the wrong question.
Here is my scenario:
I have some data, that I receive from a local server, that I need to write to a file.
This data is being sent at a constant time rate that I cant control.
What I would like to do is to have one winform for the initial setup of the tcp stream and then click on a button to start reading the tcp stream and write it to a file, and at the same time launch another winform with multiple check-boxes that I need to check the checked state and add that info simultaneously to the same file.
This processing is to be stopped when a different button is pressed, closing the stream, the file and the second winform. (this button location is not specifically mandatory to any of the winforms).
Because of this cancel button (and before I tried to implement the 2nd form) I used a background worker to be able to asynchronously cancel the do while loop used to read the stream and write the file.
private void bRecord_Click(object sender, EventArgs e)
{
System.IO.StreamWriter file = new System.IO.StreamWriter(AppDomain.CurrentDomain.BaseDirectory + DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss") + ".xml", true);
data_feed = client.GetStream();
data_write = new StreamWriter(data_feed);
data_write.Write("<SEND_DATA/>\r\n");
data_write.Flush();
exit_state = false;
string behavior = null;
//code to launch form2 with the checkboxes
//...
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
int var = data_feed.ReadByte();
if (var != -1)
{
data_in += (char)var;
if (data_in.IndexOf("\r\n") != -1)
{
//code to check the checkboxes state in form2
//if (form2.checkBox1.Checked) behavior = form2.checkBox1.Text;
//if (form2.checkBoxn.Checked) behavior = form2.checkBoxn.Text;
file.WriteLine(data_in + behavior);
data_in = "";
}
}
}
while (exit_state == false);
});
worker.RunWorkerAsync();
}
private void bStop_Click(object sender, EventArgs e)
{
exit_state = true;
worker.CancelAsync();
}
I hope I've been clearer now.
I not experienced in event programming and just started in C# so please try to provide some simple examples in the answers if possible.
At first would it be enough to use one Winform? Disable all checkboxes, click a button which enables the checkboxes and start reading the tcpstream? If you need two Forms for other reasons let me know, but i think this isn't needed from what i can see in your question.
Then i would suggest you to use the Task Library from .Net. This is the "modern" way to handle multithreading. BackgroundWorker is kind of old school. If you just able to run on .Net 2.0 you have to use BackgroundWorker, but don't seem to be the case (example follows).
Further if you want to cancel a BackgroundWorker operation this isn't only call CancelAsync();. You also need to handle the e.Cancelled flag.
backgroundWorker.WorkerSupportsCancellation = true;
private void CancelBW()
{
backgroundWorker.CancelAsync();
}
private void backgroundWorker_DoWork += ((sender, args)
{
//Handle the cancellation (in your case do this in your loop for sure)
if (e.Cancelled) //Flag is true if someone call backgroundWorker.CancelAsync();
return;
//Do your stuff.
});
There is no common way to directly cancel the backgroundWorker
operation. You always need to handle this.
Now let's change your code to the modern TAP-Pattern and make some stuff you want to have.
private void MyForm : Form
{
private CancellationTokenSource ct;
public MyForm()
{
InitializeComponent();
checkbox1.Enable = false;
//Disable all checkboxes here.
ct = new CancellationTokenSource();
}
//Event if someone click your start button
private void buttonStart_Click(object sender, EventArgs e)
{
//Enable all checkboxes here
//This will be called if we get some progress from tcp
var progress = new Progress<string>(value =>
{
//check the behaviour of the checkboxes and write to file
file.WriteLine(value + behavior);
});
Task.Factory.StartNew(() => ListenToTcp(ct, progress as IProgress<string)); //starts the tcp listening async
}
//Event if someone click your stop button
private void buttonStop_Click(object sender, EventArgs e)
{
ct.Cancel();
//Disable all checkboxes (better make a method for this :D)
}
private void ListenToTcp(CancellationToken ct, IProgess<string> progress)
{
do
{
if (ct.IsCancellationRequested)
return;
int temp = data_feed.ReadByte(); //replaced var => temp because var is keyword
if (temp != -1)
{
data_in += (char)temp;
if (data_in.IndexOf("\r\n") != -1)
{
if (progress != null)
progress.Report(data_in); //Report the tcp-data to form thread
data_in = string.empty;
}
}
while (exit_state == false);
}
}
This snippet should do the trick. I don't test it so some syntax error maybe occur :P, but the principle will work.
The most important part is that you are not allowed to access gui
components in another thread then gui thread. You tried to access the
checkboxes within your BackgroundWorker DoWork which is no possible
and throw an exception.
So I use a Progress-Object to reuse the data we get in the Tcp-Stream, back to the Main-Thread. There we can access the checkboxes, build our string and write it to the file. More about BackgroundWorker vs. Task and the Progress behaviour you can find here.
Let me know if you have any further questions.

.NET SystemEvents method not fired when starting a new process from within app

Basically, this question also summarizes my issue:
SystemEvents.SessionEnding not fired until a Process (opened before) gets closed
But there is no answer to it yet. I have a Console app that starts another process from within itself. The app also listens for SystemEvents.SessionSwitch. If I comment out the code that starts the additional process, the event handler for SessionSwitch is hit. However, if I uncomment the code that starts the additional process, the handler is not hit. I'm 100% confident that the event handler not being hit is due to starting a new process from within my app... I just don't know why.
I tagged this as a possible multithreading issue because that's what some of the comments made in the question posted above seemed to indicate. However, I'm not sure at all what could be causing it.
Here's some of the code.
[STAThread]
static void Main(string[] args)
{
SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
_myFoo = new _myFoo();
_processManager = new ProcessManager();
// If I comment out this code block, the SessionSwitch event handler is hit
// ------------------------------------------------------
if (args.Length == 0)
{
// creates a duplicate process to monitor the current (main) process
_processManager.StartObserverProcess();
}
else
{
// start monitoring the main process
_processManager.ObserveMainProcess(int.Parse(args[0]));
}
// ----------------------------------------------------
_myFoo.Start();
}
// this method does not get hit if we start the 'duplicate'
// monitoring process from within ProcessManager
private static void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
if (e.Reason == SessionSwitchReason.SessionLock)
{
// Do something when session locked
}
if (e.Reason == SessionSwitchReason.SessionUnlock)
{
// Do something when session unlocked
}
}
The ProcessManager basically starts another 'duplicate' process that watches to see if the current process exits (I know the term 'duplicate' here is probably not accurate). Here's an excerpt:
public class ProcessManager
{
// create a new process to monitor the current process
// passing in the current process id as args
public void StartObserverProcess()
{
_mainProcess = Process.GetCurrentProcess();
_mainProcessId = _mainProcess.Id;
_observerProcess = new Process
{
StartInfo =
{
FileName = _mainProcess.MainModule.FileName,
Arguments = _mainProcessId.ToString()
},
EnableRaisingEvents = true
};
_observerProcess.Exited += OnObserverProcessExit;
_observerProcess.Start();
}
private void OnObserverProcessExit(object sender, EventArgs e)
{
// do something on main process exit
}
}

Executing a method simultaneously to program window and stop it anytime

I need a functionality that will allow to execute method in a background and leave window responsive, but I need to have a possibility to stop or suspend it anytime. I know that threads are partly answer to my question, but unfortunately there is no way to stop thread from executing a time-absorbing block of code just like that. I had thoughts about process communication, but is it a good idea? Or maybe there is a way to terminate a thread unconditionally?
The only option that you have, if it's important that you can always stop the code at any point in it's execution, and when you can't have cooperative cancellation on the part of the worker, then you need to have a separate process. It is the most reliable way of stopping the execution of code in the manor you've described. It cannot be reliably done using Threads.
It seems that you're looking for BackgroundWorker .
You'll have to check in the second thread if the main thread is asking it to stop, and do so if needed.
Simple (tested) example:
public partial class Form1 : Form
{
BackgroundWorker w = new BackgroundWorker();
public Form1()
{
InitializeComponent();
w.WorkerSupportsCancellation = true;
w.DoWork += new DoWorkEventHandler(w_DoWork);
w.RunWorkerAsync();
}
void w_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 10000000000; i++)
{
if (w.CancellationPending)
{
MessageBox.Show("Cancelled");
break;
}
//Do things...
}
}
private void button1_Click(object sender, EventArgs e)
{
w.CancelAsync();
}
}
EDIT
If you're speaking of an HTTP request, perhaps: HttpWebRequest.Abort? (Though see this answer.)
As stated in comments thread.Abort() is an option, but not advised. Rather you should be using thread.Interrupt() and the reasons for that are well detailed here.
The reason you should not kill a thread instantly is because it could cause a lock being set, but never unset due to the fact that the code was suddenly stopped with no path out. So if it locks code that you will need to reuse, there would be no way to unlock it from the previous call. Obviously, you could build around this, but I'm assuming you are using blocking code that isn't built from the ground up by you.
You could do it in a separate process and kill the process with much less risk, but then passing the data back and forth and the added mess becomes complicated.
Here is an example of using an external process to do this chuck and being able to kill the process will less risk.
public class Main
{
public Main()
{
//In use
DoCalculation calc = new DoCalculation();
calc.StartCalculation(123, 188, ReceivedResults);
//Cancel after 1sec
Thread.Sleep(1000);
calc.CancelProcess();
}
public void ReceivedResults(string result)
{
Console.WriteLine(result);
}
}
public class DoCalculation
{
private System.Diagnostics.Process process = new System.Diagnostics.Process();
private Action<string> callbackEvent;
public void StartCalculation(int var1, int var2, Action<string> CallbackMethod)
{
callbackEvent = CallbackMethod;
string argument = "-v1 " + var1 + " -v2 " + var2;
//this is going to run a separate process that you'll have to make and
//you'll need to pass in the argument via command line args.
RunProcess("calcProc.exe", argument);
}
public void RunProcess(string FileName, string Arguments)
{
SecurityPermission SP = new SecurityPermission(SecurityPermissionFlag.Execution);
SP.Assert();
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.FileName = FileName;
process.StartInfo.Arguments = Arguments;
process.StartInfo.WorkingDirectory = "";
process.OutputDataReceived += ProcessCompleted;
process.Start();
}
public void CancelProcess()
{
if (process != null)
process.Kill();
}
private void ProcessCompleted(object sender, DataReceivedEventArgs e)
{
string result = e.Data;
if (callbackEvent != null)
{
callbackEvent.Invoke(result);
}
}
}
Can you give us any more details on exactly what you are doing? Perhaps there are better alternatives to this problem.

Calling Invoke hangs program

Basically, this is what happens. I have a thread(endless loop) that runs as a background process while the form is showing. The thread checks if there is a need to add a new ToolStripMenuItem.
If the conditions are met, I'll need to use Invoke in order to create the UI object right? Problem with this is, when the this.Invoke or BeginInvoke is called, the form became unresponsive while the thread that does the checking is still running fine. Any ideas?
This is the first time i'm trying with this multithreading thingee. I'm sure i've missed out something.
public void ThreadSetCom()
{
while (true)
{
string[] tmpStrPort = System.IO.Ports.SerialPort.GetPortNames();
IEnumerable<string> diff = tmpStrPort.Except(strPort);
strPort = tmpStrPort;
System.Console.WriteLine(System.IO.Ports.SerialPort.GetPortNames().Length);
foreach (string p in diff)
{
var cpDropdown = (ToolStripMenuItem)msMenu.Items["connectToolStripMenuItem"];
cpDropdown = (ToolStripMenuItem)cpDropdown.DropDownItems["connectReaderToolStripMenuItem"];
ToolStripMenuItem tsmi = new ToolStripMenuItem();
tsmi.Text = p;
tsmi.Name = p;
tsmi.Click += new EventHandler(itm_Click);
if (this.msMenu.InvokeRequired)
{
GUIUpdate d = new GUIUpdate(ThreadSetCom);
this.Invoke(d);
}
else
{
cpDropdownList.DropDownItems.Add(tsmi);
}
}
}
}
Your ThreadSetCom method never exits:
while (true)
... with no return or break statements. That's going to hang the UI thread forever.
It's not clear what you're trying to achieve, but you definitely don't want to be looping like that in the UI thread. I'd argue that you don't want to be looping like that in a tight way in any thread, mind you...
I think a better approach for you would probably be to use a BackgroundWorker. I say that because what you're experiencing isn't that uncommon when doing multi-threading in a Windows Forms application. Further, the BackgroundWorker is able to manage the thread switching properly. Let me give you an example of that code with the BackgroundWorker.
Build a private class variable
private BackgroundWorker _worker;
Add to the CTOR
public {ctor}()
{
_worker = new BackgroundWorker();
_worker.WorkerSupportsCancellation = true;
_worker.WorkerReportsProgress = true;
_worker.DoWork += new DoWorkEventHandler(BackgroundThreadWork);
_worker.ProgressChanged += new ProgressChangedEventHandler(BackgroundThreadProgress);
}
DoWork handler
private void BackgroundThreadWork(object sender, DoWorkEventArgs e)
{
while (!_worker.CancellationPending)
{
string[] tmpStrPort = System.IO.Ports.SerialPort.GetPortNames();
IEnumerable<string> diff = tmpStrPort.Except(strPort);
strPort = tmpStrPort;
System.Console.WriteLine(System.IO.Ports.SerialPort.GetPortNames().Length);
foreach (string p in diff)
{
_worker.ReportProgress(1, p);
}
}
}
Report progress handler
private void BackgroundThreadProgress(object sender, ReportProgressEventArgs e)
{
var cpDropdown = (ToolStripMenuItem)msMenu.Items["connectToolStripMenuItem"];
cpDropdown = (ToolStripMenuItem)cpDropdown.DropDownItems["connectReaderToolStripMenuItem"];
ToolStripMenuItem tsmi = new ToolStripMenuItem();
tsmi.Text = e.UserState as string;
tsmi.Name = e.UserState as string;
tsmi.Click += new EventHandler(itm_Click);
cpDropdownList.DropDownItems.Add(tsmi);
}
The Loop
However, one thing you're going to have to do is figure out how to get out of this loop. When should it exit? Whatever that means, you need to add to the if statement that exists there in my example because this loop will never end otherwise.
What the effect of this code snippet:
GUIUpdate d = new GUIUpdate(ThreadSetCom);
this.Invoke(d);
is that the method 'ThreadSetCom' will be invoked in the UI thread. And there is an infinitive loop in that method. That is why your form becomes unresponsive.
I suggest you that you should move the foreach clause to a separate method and invoke this method in the UI thread when the condition is hit, for example the diff.Count>0.

Categories