Start a fixed number of process using C# - c#

I want to run say 50 processes but since the machine will choke at 5, I want to run 5 processes at a time and and keep starting the next one as soon as one of the 5 running processes finish. Please advice what's a good way to do this?
I can wait on the last process I launch by Process.WaitForExit but that doesn't do the trick since what if one of the earlier 4 processes finishes first.
Thanks

I had to create a similar bottleneck while writing an app to handle hundreds of Powershell commands. I ended up creating a "queue" class that contained a list of objects. The objects were another custom class that contained the Process and a few events.
I'd loop through the processes, setting them up to be run, then add them to the Queue class. At the end I'd call a RunFirstScripts function that looped through the first 20 in the list and called a Start function, then removed it from the list.
I bound the Exit event to a handler that ran the next server and then removed it from the queue.
class ServerQueue
{
private List<Script> Servers = new List<Script>();
public void Add(Script Server)
{
Servers.Add(Server);
Server.Exited += cmd_Exited;
}
public void RunNextScript()
{
if (Servers.Count > 0)
{
Script ToRun = Servers[0];
Servers.Remove(ToRun);
ToRun.StartProcess();
}
}
public void StartFirstScripts()
{
byte Running = 0;
while (Servers.Count > 0 && Running <= 20)
{
RunNextScript();
Running++;
}
}
private void cmd_Exited(object sender, EventArgs e)
{
RunNextScript();
}
}
EDIT Here's some code for the Script:
class Script
{
public string Output
{
get
{
return pOutput;
}
}
public string Errors
{
get
{
return pErrors;
}
}
public bool IsRunning
{
get
{
return pIsRunning;
}
}
private string pOutput = "";
private string pErrors = "";
private bool pIsRunning = false;
public delegate void OutputEventHandler(Script sender, string Output, bool IsError);
public delegate void StatusEventHandler(Script sender);
public event OutputEventHandler OutputDataReceived;
public event OutputEventHandler ErrorDataReceived;
public event StatusEventHandler Started;
public event StatusEventHandler Exited;
private Process cmd;
public void StartProcess()
{
pIsRunning = true;
cmd.Start();
cmd.BeginOutputReadLine();
cmd.BeginErrorReadLine();
Started(this);
}
public void KillProcess()
{
if (IsRunning)
{
cmd.Kill();
}
}
public void SetupScript()
{
cmd = new Process();
//configure Process (but don't start it yet)
}
private void cmd_Exited(object sender, EventArgs e)
{
pIsRunning = false;
Exited(this);
//do other stuff
}
private void cmd_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
OutputDataReceived(this, e.Data, false);
//do stuff
}
private void cmd_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
ErrorDataReceived(this, e.Data, true);
//do stuff
}
}
There's a tiny race condition, if the planets align correctly you could attempt to start the same process twice but the second attempt should fail (as it's already running) and the way it's set up it's extremely unlikely to ever occur. You can add some extra error trapping if you like, there should probably be a try/catch in StartProcess (this is modified from my own solution and as such missing some context).
EDIT
Here is my Process to work with all these bits:
Process cmd = new Process();
cmd.StartInfo.FileName = "PowerShell.exe";
cmd.StartInfo.Arguments = "-executionpolicy unrestricted -file \"" + TempFile.FullName + "\" -pwd " + Pwd;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.RedirectStandardError = true;
cmd.StartInfo.UseShellExecute = false;
cmd.StartInfo.CreateNoWindow = true;
cmd.EnableRaisingEvents = true;
cmd.OutputDataReceived += cmd_UpdateDataReceived;
cmd.ErrorDataReceived += cmd_ErrorDataReceived;
cmd.Exited += cmd_Exited;

here is the edited code for completeness. Full credit to Deadly-Bagel. Thanks,
class ServerQueue
{
private List<Script> Servers = new List<Script>();
public void Add(Script Server)
{
Servers.Add(Server);
Server.Exited += cmd_Exited;
}
public void RunNextScript()
{
if (Servers.Count > 0)
{
Script ToRun = Servers[0];
Servers.Remove(ToRun);
ToRun.StartProcess();
}
}
public void StartFirstScripts()
{
byte Running = 0;
while (Servers.Count > 0 && Running <= 20)
{
RunNextScript();
Running++;
}
}
private void cmd_Exited(object sender, EventArgs e)
{
RunNextScript();
}
}
class Script
{
public string Output
{
get
{
return pOutput;
}
}
public string Errors
{
get
{
return pErrors;
}
}
public bool IsRunning
{
get
{
return pIsRunning;
}
}
private string pOutput = "";
private string pErrors = "";
private bool pIsRunning = false;
public delegate void OutputEventHandler(Script sender, string Output, bool IsError);
public delegate void StatusEventHandler(Script sender, EventArgs e);
public event OutputEventHandler OutputDataReceived;
public event OutputEventHandler ErrorDataReceived;
public event StatusEventHandler Started;
public event StatusEventHandler Exited;
private Process cmd;
public void StartProcess()
{
pIsRunning = true;
cmd.Start();
cmd.BeginOutputReadLine();
cmd.BeginErrorReadLine();
Started(this, null);
}
public void KillProcess()
{
if (IsRunning)
{
cmd.Kill();
}
}
public void SetupScript()
{
cmd = new Process();
cmd.EnableRaisingEvents = true;
cmd.Exited += new EventHandler(cmd_Exited);
//configure Process (but don't start it yet)
}
private void cmd_Exited(object sender, EventArgs e)
{
pIsRunning = false;
Exited(this, null);
//do other stuff
}
private void cmd_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
OutputDataReceived(this, e.Data, false);
//do stuff
}
private void cmd_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
ErrorDataReceived(this, e.Data, true);
//do stuff
}
}

Related

Event Handler Fires Twice When Plugging/Unplugging USB Serial Port

I am writing a program which is supposed to detect when a USB serial device is plugged in and then log on to the new com port. The code below works wonderfully, but I have noticed in debugging the code and stepping through it that the event handler "DetectChange" fires twice. I'm not sure that this is normal, or an action of the debugger.
In any case, the code works, but I am new at event handling and I would like to make sure that I am not going to cause any issues as I add more code to actually read and write from the serial port.
(I got some of this code from stackoverflow, but I have misplaced my paper with names for attribution. If you see your code below, my heartfelt thanks.)
using System;
using System.IO.Ports;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Management;
using System.Threading;
namespace SerialTest
{
public partial class Form1 : Form
{
SerialMethods serialMethods = new SerialMethods();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
loadCmdBox();
}
private void CmdBoxPort_SelectedIndexChanged(object sender, EventArgs e)
{
handleComPort();
}
private void handleComPort()
{
// Set the right port for the selected item.
// The portname is based on the "COMx" part of the string (SelectedItem)
string item = CmdBoxPort.SelectedItem.ToString();
// Search for the expression "(COM" in the "selectedItem" string
if (item.Contains("(COM"))
{
// Get the index number where "(COM" starts in the string
int indexOfCom = item.IndexOf("(COM");
// Set PortName to COMx based on the expression in the "selectedItem" string
// It automatically gets the correct length of the COMx expression to make sure
// that also a COM10, COM11 and so on is working properly.
string PortName = item.Substring(indexOfCom + 1, item.Length - indexOfCom - 2);
if (serialMethods._serialPort.IsOpen)
{
serialMethods._serialPort.Close();
serialMethods.Connect(PortName);
label5.Text = "Active Port: " + PortName;
}
else
{
serialMethods.Connect(PortName);
label5.Text = PortName;
}
}
else
return;
}
private void loadCmdBox()
{
// Get all serial (COM)-ports you can see in the devicemanager
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\cimv2",
"SELECT * FROM Win32_PnPEntity WHERE ClassGuid=\"{4d36e978-e325-11ce-bfc1-08002be10318}\"");
// Sort the items in the combobox
CmdBoxPort.Sorted = true;
// Add all available (COM)-ports to the combobox
foreach (System.Management.ManagementObject queryObj in searcher.Get().Cast<ManagementObject>())
{
CmdBoxPort.Items.Add(queryObj["Caption"]);
}
SerialPortService.PortsChanged += (sender1, changedArgs) => DetectChange(changedArgs.EventType);
label2.Text = "";
label3.Text = "";
label4.Text = "";
}
protected Task<Task> getSerPorts()
{
CmdBoxPort.Text = "";
CmdBoxPort.Update();
if (!String.IsNullOrEmpty(CmdBoxPort.Text))
{
handleComPort();
return Task.FromResult(Task.CompletedTask);
}
else
{
loadCmdBox();
return Task.FromResult(Task.CompletedTask);
}
}
private void ExitButton_Click(object sender, EventArgs e)
{
SerialPortService.CleanUp();
this.Close();
}
private void RefreshButton_Click(object sender, EventArgs e)
{
refresh();
}
protected Task<Task> refresh()
{
label2.Text = "";
label3.Text = "";
label4.Text = "";
CmdBoxPort.Items.Clear();
getSerPorts();
return Task.FromResult(Task.CompletedTask);
}
protected virtual void DetectChange(EventType changedArgs)
{
if (changedArgs == EventType.Insertion)
{
try
{
Task tr = (Task)Invoke(new Action( () => { getSerPorts(); }));
Task rr = (Task)Invoke(new Action(() => { refresh(); }));
}
catch (Exception ex) { MessageBox.Show("Exception at insertion invoke method " + ex, "Exception", MessageBoxButtons.OK); }
}
else if (changedArgs == EventType.Removal)
{
try
{
Task tr = (Task)Invoke(new Action( () => { getSerPorts(); }));
Task rr = (Task)Invoke(new Action(() => { refresh(); }));
}
catch (Exception ex) { MessageBox.Show("Exception at removal invoke method " + ex, "Exception", MessageBoxButtons.OK); }
}
return;
}
}
public static class SerialPortService
{
private static SerialPort _serialPort;
private static string[] _serialPorts;
private static ManagementEventWatcher arrival;
private static ManagementEventWatcher removal;
private static readonly SerialMethods SD = new SerialMethods();
static SerialPortService()
{
_serialPorts = SerialPort.GetPortNames();
MonitorDeviceChanges();
}
public static void CleanUp()
{
arrival.Stop();
removal.Stop();
}
public static event EventHandler<PortsChangedArgs> PortsChanged;
private static void MonitorDeviceChanges()
{
try
{
var deviceArrivalQuery = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
var deviceRemovalQuery = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
arrival = new ManagementEventWatcher(deviceArrivalQuery);
removal = new ManagementEventWatcher(deviceRemovalQuery);
arrival.EventArrived += (o, args) => RaisePortsChangedIfNecessary(EventType.Insertion);
removal.EventArrived += (sender, eventArgs) => RaisePortsChangedIfNecessary(EventType.Removal);
// Start listening for events
arrival.Start();
removal.Start();
}
catch (ManagementException err)
{
MessageBox.Show("Management exception = " + err, "Info", MessageBoxButtons.OK);
}
}
private static void RaisePortsChangedIfNecessary(EventType eventType)
{
lock (_serialPorts)
{
var availableSerialPorts = SerialPort.GetPortNames();
if (eventType == EventType.Insertion)
{
var added = availableSerialPorts.Except(_serialPorts).ToArray();
_serialPorts = availableSerialPorts;
PortsChanged.Raise(null, new PortsChangedArgs(eventType, added));
}
else if (eventType == EventType.Removal)
{
var removed = _serialPorts.Except(availableSerialPorts).ToArray();
_serialPorts = availableSerialPorts;
PortsChanged.Raise(null, new PortsChangedArgs(eventType, removed));
}
}
}
public static void Raise<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs
{
handler?.Invoke(sender, args);
}
}
public enum EventType
{
Insertion,
Removal,
}
public class PortsChangedArgs : EventArgs
{
private readonly EventType _eventType;
private readonly string[] _serialPorts;
public PortsChangedArgs(EventType eventType, string[] serialPorts)
{
_eventType = eventType;
_serialPorts = serialPorts;
}
public string[] SerialPorts => _serialPorts;
public EventType EventType => _eventType;
}
}
Just took a short look at this. It seems like getSerPorts() will always execute loadCmdBox() (CmdBoxPort.Text = ""; ... if (!String.IsNullOrEmpty(CmdBoxPort.Text))) that will attach a new event handler (previous attached event handlers will not be removed by attaching a new one).
You should either remove the existing event handler befor attaching a new one or only attach the event handler once.

Process.Exited not raised when the parent process is waiting

I want to launch a bunch of child processes and then wait for all the processes to be completed before proceeding. All works fine. But I want to introduce a delay between starting each consecutive process. The problem I face is that the Exited event from the child process can happen during the delay and I am not able to receive that event in the parent process while it is asleep.
please advice.
Here is some code:
public void SetupProcess(string program, string args)
{
cmd = new Process();
cmd.StartInfo.FileName = program;
cmd.StartInfo.Arguments = args;
cmd.EnableRaisingEvents = true;
//cmd.StartInfo.Verb = "Print";
cmd.StartInfo.CreateNoWindow = true;
cmd.Exited += new EventHandler(cmd_Exited);
}
public bool StartProcess(int delay)
{
bool retval = false;
if (cmd.Start())
{
Thread.Sleep(delay);
}
return retval;
}
private void cmd_Exited(object sender, EventArgs e)
{
pIsRunning = false;
if (Exited != null)
{
Console.WriteLine("Process exited. notfying queue...");
Exited(this, null); // signal the queue to run the next process
}
}
I took the time to implement GrawCube's suggestion. The code below will ensure:
1) No more than MaxConcurrent processes are started at a time (typical, for example no more than 4 processes on a Quad)
2) There is a certain Delay introduced between starting consecutive process (in my case the messaging service imposed this constraint)
Here is the code, hope that helps:
class ProcessExQueue
{
public int MaxConcurrent { get; set; }
public int Delay { get; set; }
public Action Done;
private List<Timer> _timers;
private List<ProcessEx> Servers = new List<ProcessEx>();
byte _running = 0;
private DateTime _lastStart;
public ProcessExQueue()
{
MaxConcurrent = 4;
Delay = 15000;
_timers = new List<Timer>();
}
public void Add(ProcessEx Server)
{
Servers.Add(Server);
Server.Exited += cmd_Exited;
Server.Started += cmd_Started;
}
public void RunProcessTimer(int delay)
{
System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += new System.Timers.ElapsedEventHandler(DoTask);
timer.Interval = delay;
timer.AutoReset = false; // only first time
timer.Start();
_timers.Add(timer);
Console.WriteLine("timer started with delay {0} # {1} ", delay, DateTime.Now);
}
void DoTask(object source, System.Timers.ElapsedEventArgs e)
{
Console.WriteLine("timer call # {0}", DateTime.Now);
lock (this)
{
if (Servers.Count > 0 && _running <= MaxConcurrent)
{
ProcessEx ToRun = Servers[0];
ToRun.Starter = (Timer)source;
Servers.Remove(ToRun);
bool success = ToRun.StartProcess();
if (success)
_running++;
}
else
{
Console.WriteLine("nothing started, queue size {0} running {1}", Servers.Count, _running);
}
}
}
public void StartFirstProcesses()
{
for (int i = 0; i < Servers.Count; i++)
{
RunProcessTimer(i == 0 ? 1000 : Delay*(i));
}
}
private void cmd_Exited(object sender, EventArgs e)
{
Console.WriteLine("Queue: process exited {0}.", ((ProcessEx)sender).GetPID());
// find the timer and get rid of it;
//TODO
Timer found = _timers.FirstOrDefault(t => t == ((ProcessEx)sender).Starter);
if (found != null)
{
found.Enabled = false;
}
lock (this)
{
_running--;
}
if (Servers.Count == 0) // no more processes to run
{
Console.WriteLine("No more processes to run. some threads may be asleep");
if (_running == 0)
{
Console.WriteLine("disarming all timers");
_timers.ForEach(t=> t.Enabled=false);
Done();
}
}
else
{
if (Servers.Count > 0 && _running <= MaxConcurrent)
{
TimeSpan tspan = (DateTime.Now - _lastStart);
RunProcessTimer(tspan.Milliseconds < Delay?Delay - tspan.Milliseconds:1);
}
}
}
private void cmd_Started(object sender, EventArgs e)
{
Console.WriteLine("Queue: process started {0}", ((ProcessEx)sender).GetPID());
_lastStart = DateTime.Now;
}
}
class ProcessEx
{
public Timer Starter { get; set; }
public bool IsRunning
{
get
{
return pIsRunning;
}
}
private bool pIsRunning = false;
public delegate void OutputEventHandler(ProcessEx sender, string Output, bool IsError);
public delegate void StatusEventHandler(ProcessEx sender, EventArgs e);
public event StatusEventHandler Started;
public event StatusEventHandler Exited;
private Process cmd;
public int GetPID()
{
return cmd.Id;
}
public bool StartProcess()
{
bool retval = false;
if (cmd.Start())
{
pIsRunning = true;
retval = true;
Console.WriteLine("Running {0} {1}", cmd.StartInfo.FileName, cmd.StartInfo.Arguments);
Console.WriteLine("Process Id {0}", cmd.Id);
if (Started != null)
Started(this, null);
}
return retval;
}
public void KillProcess()
{
if (IsRunning)
{
cmd.Kill();
}
}
public void SetupProcess(string program, string args)
{
cmd = new Process();
cmd.StartInfo.FileName = program;
cmd.StartInfo.Arguments = args;
cmd.EnableRaisingEvents = true;
//cmd.StartInfo.Verb = "Print";
cmd.StartInfo.CreateNoWindow = true;
cmd.Exited += new EventHandler(cmd_Exited);
}
private void cmd_Exited(object sender, EventArgs e)
{
pIsRunning = false;
if (Exited != null)
{
Console.WriteLine("thread {0} received exit signal", Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Process {0} exited. notfying queue...", ((Process)sender).Id);
Exited(this, null); // signal the queue to run the next process
}
}
}
// usage
class Program
{
static void Main(string[] args)
{
ProcessEx s = new ProcessEx();
s.SetupProcess(#"C:\MyP\MBN\Dev\Test\Hello\bin\Debug\Hello.exe", "process 0");
ProcessExQueue q = new ProcessExQueue();
q.Add(s);
s = new ProcessEx();
s.SetupProcess(#"C:\MyP\MBN\Dev\Test\Hello\bin\Debug\Hello.exe", "process 1");
q.Add(s);
s = new ProcessEx();
s.SetupProcess(#"C:\MyP\MBN\Dev\Test\Hello\bin\Debug\Hello.exe", "process 2");
q.Add(s);
q.Done = () => Console.WriteLine("Done!");
q.StartFirstProcesses();
/*
Thread t = new Thread(q.StartFirstProcesses);
t.Start();
*/
Console.ReadKey();
}
}
// sample process
class Program
{
static void Main(string[] args)
{
if (args.Count() > 0)
{
Console.WriteLine(Process.GetCurrentProcess().Id);
Thread.Sleep(5000);
Console.WriteLine("exiting... press any key");
Console.ReadKey();
}
}
}
I think you may be looking for the Thread.Join method?
https://msdn.microsoft.com/en-us/library/system.threading.thread.join%28v=vs.110%29.aspx
Which allows your parent thread to wait on a child thread.
I like the Timer solution suggested by GrawCube. I implemented this without the use of a timer, but the timer solution may be cleaner. Credit also goes to Deadly-Bagel for posting the original code which I modified to add delay and cpu threshold. Here is the code:
class Program
{
static void Main(string[] args)
{
ProcessEx s = new ProcessEx();
s.SetupProcess(#"C:\MyP\MBN\Dev\Test\Hello\bin\Debug\Hello.exe", "process 0");
ProcessExQueue q = new ProcessExQueue();
q.Add(s);
s = new ProcessEx();
s.SetupProcess(#"C:\MyP\MBN\Dev\Test\Hello\bin\Debug\Hello.exe", "process 1");
q.Add(s);
s = new ProcessEx();
s.SetupProcess(#"C:\MyP\MBN\Dev\Test\Hello\bin\Debug\Hello.exe", "process 2");
q.Add(s);
Thread t = new Thread(q.StartFirstProcesses);
t.Start();
Console.ReadKey();
}
}
class ProcessExQueue
{
private List<ProcessEx> Servers = new List<ProcessEx>();
public int MaxConcurrent { get; set; }
public int Delay { get; set; }
byte _running = 0;
public ProcessExQueue()
{
MaxConcurrent = 4;
Delay = 60000;
}
public void Add(ProcessEx Server)
{
Servers.Add(Server);
Server.Exited += cmd_Exited;
Server.Started += cmd_Started;
}
private bool RunNextProcess()
{
bool retval = false;
if (Servers.Count > 0)
{
ProcessEx ToRun = Servers[0];
Servers.Remove(ToRun);
retval = ToRun.StartProcess();
if (retval)
lock (this)
{
_running++;
}
}
Console.WriteLine("Thread Id {0} will sleep...", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(Delay);
Console.WriteLine("Thread Id {0} woke up", Thread.CurrentThread.ManagedThreadId);
return retval;
}
public void StartFirstProcesses()
{
while (Servers.Count > 0 && _running <= MaxConcurrent)
{
RunNextProcess();
}
}
private void cmd_Exited(object sender, EventArgs e)
{
lock (this)
{
_running--;
}
Console.WriteLine("Queue: process exited {0}.", ((ProcessEx)sender).GetPID());
if(Servers.Count > 0 && _running <= MaxConcurrent)
{
RunNextProcess();
}
}
private void cmd_Started(object sender, EventArgs e)
{
Console.WriteLine("Queue: process started {0}", ((ProcessEx)sender).GetPID());
}
}
class ProcessEx
{
public bool IsRunning
{
get
{
return pIsRunning;
}
}
private bool pIsRunning = false;
public delegate void OutputEventHandler(ProcessEx sender, string Output, bool IsError);
public delegate void StatusEventHandler(ProcessEx sender, EventArgs e);
public event StatusEventHandler Started;
public event StatusEventHandler Exited;
private Process cmd;
public int GetPID()
{
return cmd.Id;
}
public bool StartProcess()
{
bool retval = false;
if (cmd.Start())
{
pIsRunning = true;
retval = true;
Console.WriteLine("Running {0} {1}", cmd.StartInfo.FileName, cmd.StartInfo.Arguments);
Console.WriteLine("Process Id {0}", cmd.Id);
if (Started != null)
Started(this, null);
}
return retval;
}
public void KillProcess()
{
if (IsRunning)
{
cmd.Kill();
}
}
public void SetupProcess(string program, string args)
{
// https://msdn.microsoft.com/en-us/library/h6ak8zt5(v=vs.110).aspx
cmd = new Process();
cmd.StartInfo.FileName = program;
cmd.StartInfo.Arguments = args;
cmd.EnableRaisingEvents = true;
//cmd.StartInfo.Verb = "Print";
cmd.StartInfo.CreateNoWindow = true;
cmd.Exited += new EventHandler(cmd_Exited);
}
private void cmd_Exited(object sender, EventArgs e)
{
pIsRunning = false;
if (Exited != null)
{
Console.WriteLine("thread {0} received exit signal", Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Process {0} exited. notfying queue...", ((Process)sender).Id);
Exited(this, null); // signal the queue to run the next process
}
}
}
}

Should i pass a Backgroundworker to method

I have an app that has several methods that take a long time to complete. I am using a backgroundworker to run these methods and keep my UI responsive. My methods look something like
public void DoSomething()
{
while( HaveMoreWork )
{
// do work
}
}
Now i want the UI to be able to cancel this at any time so I have changed my methods to take a Backgroundworker like so
public void DoSomething(Backgroundworker worker)
{
while( HaveMoreWork && !worker.CancelationPending )
{
// do work
}
}
My question is, is there a better way to do this. Seems like passing a Backgroundwoker as an argument to all these methods is a bit messy. What is best practice for this?
I am using global variable
private BackgroundWorker _bwSearch = new BackgroundWorker();
private void InitializeBackgroundWorker()
{
_bwSearch = new BackgroundWorker();
_bwSearch.WorkerSupportsCancellation = true;
_bwSearch.DoWork += bwSearch_DoWork;
_bwSearch.RunWorkerCompleted += bwSearch_RunWorkerCompleted;
}
when clicked on stop button
private void btnCancel_Click(object sender, EventArgs e)
{
_bwSearch.Abort();
}
Updated:
Also I am using this simple helper class that is inherited from BackgroundWorker
public class AbortableBackgroundWorker : BackgroundWorker
{
private Thread _workerThread;
protected override void OnDoWork(DoWorkEventArgs e)
{
_workerThread = Thread.CurrentThread;
try
{
base.OnDoWork(e);
}
catch (ThreadAbortException)
{
e.Cancel = true;
Thread.ResetAbort();
}
}
public void Abort()
{
if (_workerThread != null)
{
_workerThread.Abort();
_workerThread = null;
}
}
}
public class DoSomethingService
{
private volatile bool _stopped = false;
public void Start(object socketQueueObject)
{
while (!_stopped)
{
...
}
}
public void Stop()
{
_stopped = true;
}
}
...
var doSomethingService = DoSomethingService();
doSomethingService.Start();
...
doSomethingService.Stop();

Static event handler in parent class is not triggered from Process.Exited in nested class

Each time CurlFile() is called it creates a new object from the ProcessObject class. The ProcessObject object starts the new Process. I want the Process.Exit event in each object to trigger the static event handler in the parent class, but it doesn't seem to be working for some reason.
class Curl
{
StringContainer oStrings = new StringContainer();
private static int _counter = 0;
private string _curl;
public Curl()
{
//oStartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
_curl = oStrings.Curl;
}
public void CurlFile(string _arg)
{
ProcessObject oProcessObject = new ProcessObject(_arg, _curl);
}
private static void oProcess_Exited(object sender, EventArgs e)
{
_counter++;
if (_counter == 1000)
{
MessageBox.Show("here");
}
}
private class ProcessObject
{
ProcessStartInfo oStartInfo = new ProcessStartInfo();
Process oProcess = new Process();
public ProcessObject(string _arg, string _curl)
{
oStartInfo.FileName = _curl;
oStartInfo.Arguments = _arg;
oProcess.EnableRaisingEvents = true;
oProcess.Exited += new EventHandler(oProcess_Exited);
oProcess = Process.Start(oStartInfo);
}
}
}
First off, as #Will mentions, you need to keep a reference to your Process objects so they don't get GC'd. Something like this (code untested):
class Curl
{
internal static List<ProcessObject> _processes = new List<ProcessObject>();
// ....
private static void oProcess_Exited(object sender, EventArgs e)
{
var p = sender as Process;
if (p != null && _processes.Contains(p))
_processes.Remove(p);
_counter++;
if (_counter == 1000)
{
MessageBox.Show("here");
}
}
public ProcessObject(string _arg, string _curl)
{
oStartInfo.FileName = _curl;
oStartInfo.Arguments = _arg;
oStartInfo.UseShellExecute = false;
oProcess.EnableRaisingEvents = true;
oProcess.Exited += new EventHandler(oProcess_Exited);
oProcess = Process.Start(oStartInfo);
Curl._processes.Add(oProcess);
}
}
Also, as some people have found, the Process class can be spotty about detecting the Exit. I don't know if this holds true for the Exited event also, but I'd watch out for it and explicitly set UseShellExecute = false; as I have above.

C# Sql Server "loading window"

This is my first post here, but I've using this site regularly to help me with my own app's, and I should say that this site has been a great help to me, so thanks to everyone.
Now my question:
I'm developing my first software app that exchanges data between a sql server and the app itself. It's beeing developed in C#. Saving or retreiving data from the sql server database is no problem.
What I want is a way to inform the user of the delay between the local machine (where the app is installed) and the server. I can make some animations or simply display some text messages. What I need help with is how to create the code that activates/fires/runs when that server communication time is running.
If you can't understand the idea, picture a video game. When it's loading (in some games) you can see the loading screen before the game starts. I need some code that displays that "loading window" when the the app is downloading or uploading data from/to the server.
I would appreciate any code example or web site recommendation.
PS: Sorry for the extensive text, but I want to make sure everyone understand so I don't have to repeat it again :P
How do I implement a progress bar in C#?
How to create a smooth progress bar in Visual C#
ProgressBar Class
I have developed a simple PleaseWait class 2 years ago, but I didn't update this class, It works very well, have look hope this will give you an idea to implement your logic.
public partial class frmWait : Form
{
public frmWait()
{
InitializeComponent();
}
bool _isMoving = false;
int _moveStart_x = 0;
int _moveStart_y = 0;
private void tmrProgress_Tick(object sender, EventArgs e)
{
if (barProgress.Value == barProgress.Maximum)
barProgress.Value = barProgress.Minimum;
else
barProgress.Value += 1;
}
private void btnCancel_Click(object sender, EventArgs e)
{
Close();
PleaseWait.Abort();
}
protected override CreateParams CreateParams
{
get
{
System.Windows.Forms.CreateParams p = base.CreateParams;
p.ClassStyle += 0x20000;
p.ExStyle += 0x8000000;
return p;
}
}
protected override void WndProc(ref Message m)
{
const int WM_NCHITTEST = 132;
base.WndProc(ref m);
switch (m.Msg)
{
case WM_NCHITTEST:
if (m.Result.ToInt32() == 1)
m.Result = new IntPtr(2);
break;
}
}
private void panelEx1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_isMoving = true;
_moveStart_x = e.X;
_moveStart_y = e.Y;
}
}
private void panelEx1_MouseUp(object sender, MouseEventArgs e)
{
_isMoving = false;
}
private void pnlContainer_MouseMove(object sender, MouseEventArgs e)
{
if (_isMoving)
this.Location = new Point(Location.X + e.X - _moveStart_x, Location.Y + e.Y - _moveStart_y);
}
}
public class PleaseWait
{
#region Static Operations
private static Boolean _isAborted = false;
private static Boolean _isVisible = false;
private static frmWait _waitForm;
private static String _waitingState = "";
private static Boolean _autoClose = false;
private static Boolean _cancelable = false;
private static System.Threading.Thread _waiterThred;
public delegate void CancelButtonPressed();
public static event CancelButtonPressed OnCancel;
public static Boolean AutoClose
{
get { return PleaseWait._autoClose; }
set { PleaseWait._autoClose = value; }
}
public static string WaitingState
{
get { return PleaseWait._waitingState; }
set { PleaseWait._waitingState = value; }
}
public static bool IsVisible
{
get { return _isVisible; }
internal set { _isVisible = value; }
}
public static void ShowPleaseWait()
{
ShowPleaseWait("", _autoClose, false);
}
public static void ShowPleaseWait(string waitingState)
{
ShowPleaseWait(waitingState, _autoClose, false);
}
public static void ShowPleaseWait(bool autoClose)
{
ShowPleaseWait("", autoClose, false);
}
public static void ShowPleaseWait(string waitingState, bool autoClose, bool cancelable)
{
if (_waiterThred != null)
{
if (_isVisible)
{
// the please wait it woking, just continue and apply the changes
_waitingState = waitingState;
_autoClose = autoClose;
_cancelable = cancelable;
return;
}
else
{
_waiterThred.Abort();
_waiterThred = null;
}
}
_waitingState = waitingState;
_autoClose = autoClose;
_cancelable = cancelable;
_isAborted = false;
_isVisible = false;
if (_autoClose)
Application.Idle += new EventHandler(Application_Idle);
_waiterThred = new System.Threading.Thread(DisplayWaitingForm);
_waiterThred.IsBackground = true;
_waiterThred.Name = "Please Wait....";
_waiterThred.Start();
Application.DoEvents();
}
public static void Abort()
{
_isAborted = true;
}
private static void Application_Idle(object sender, EventArgs e)
{
if (_autoClose)
_isAborted = true;
}
private static void DisplayWaitingForm()
{
if (_waitForm != null)
{
if (!_waitForm.IsDisposed)
_waitForm.Dispose();
_waitForm = null;
_isVisible = false;
}
try
{
if (_isAborted)
return;
_waitForm = new frmWait();
if (_cancelable)
{
_waitForm.btnCancel.Enabled = true;
_waitForm.btnCancel.Click += new EventHandler(btnCancel_Click);
}
try
{
_isVisible = true;
_waitForm.Show();
_waitForm.Focus();
while (!_isAborted)
{
System.Threading.Thread.Sleep(15);
_waitForm.lblMessage.Text = _waitingState;
Application.DoEvents();
_waitForm.lblMessage.Text = _waitingState;
}
_isVisible = false;
}
finally
{
FreeWaitingForm();
}
}
finally
{
_isVisible = false;
}
}
static void btnCancel_Click(object sender, EventArgs e)
{
if (_waitForm.InvokeRequired)
{
_waitForm.BeginInvoke(new EventHandler(btnCancel_Click), new object[] { e });
}
else
{
if (OnCancel != null)
OnCancel.Invoke();
}
}
private static void FreeWaitingForm()
{
_waitingState = "";
_isVisible = false;
if (_waitForm == null)
{
return;
}
_waitForm.Hide();
if (!_waitForm.IsDisposed)
_waitForm.Dispose();
_waitForm = null;
}
#endregion
}
use like following code :
PleaseWait.ShowPleaseWait("Please wait", true, false);
// If second param is true then it will close the form automatically.
// If third param is true the it will expose a cancel button, so you can cancel your Asynchronous operations.
I didn't insert design code, you can understand by looking at code.
hope this help.
First let me thank you for your replies.
Toby your answer got me thinking about thread monitoring my sql connections but it was a bit tricky and confusing since the app is still in develop and will use a lot more connections.
S.Amani answer it wasn't quite what I want, but thanks to that I found a easier way. I created a form (could be anything else), placed a label saying: Saving To Data Base, took out the top bar, defined location and defined it's parent to be disabled when shown and enabled when closed. The following code is what I put inside my DataBaseInteractionClass
private Wait myCustomWaitDialog = new Wait(); // My Waiting form
private void SaveToDatabase(myObjectToSave obj) // Method called to save data do DB
{
// Create the connections and queries
(...)
// This is what I did
// Show Waiting Form
myCustomWaitDialog.Show();
// Instanciate the command that will carry the query and to DB
SqlCommand command = new SqlCommand(Queries.GetData(code), conn);
// This is important
//Create event that will fire when the command completes
command.StatementCompleted += new StatementCompletedEventHandler(command_StatementCompleted);
// Execute the transaction
SqlDataReader reader = command.ExecuteReader();
// Rest of the code (validations, close connections, try/catch, etc
(...)
}
void command_StatementCompleted(object sender, StatementCompletedEventArgs e)
{
// This is the method that closes my Waiting Dialog
myCustomWaitDialog.CloseDialog();
myCustomWaitDialog.Dispose();
}
It's not quite what I want yet, but is the best solution that I found so far. For now it will do :)
Anyway, thanks for the replies and I hope this helps someone else.

Categories