Any way to simplify comparing lists of Processes? - c#

I was just curious if there was a simplified way to write this bit of code, or more efficient.
I want to wait for a new notepad.exe to exist, add that process to a list of processes or list of process ids only if it is not already in the list, also remove any processes in that list that do not exist anymore. Then repeat in an endless loop.
public static List<int> Clients = new List<int>();
while(true)
{
foreach (int r in Clients)
if (!ProcessExists(r))
{
Clients.Remove(r);
Console.WriteLine(r + " was removed.");
break;
}
bool check = false;
Process[] initial = Process.GetProcessesByName("notepad");
foreach (Process i in initial)
{
foreach(int r in Clients)
{
if (i.Id == r)
{
check = true;
}
}
if (check == false)
{
Clients.Add(i.Id);
Console.WriteLine(i.Id + " was added.");
}
check = false;
}
Thread.Sleep(250);
}
private static bool ProcessExists(int id)
{
return Process.GetProcessesByName("notepad").Any(x => x.Id == id);
}

The first loop that take care of deletion can be simpler using Linq:
Clients.RemoveAll(p => !ProcessExists(p));
Or this way, to keep the log:
Clients.RemoveAll(p => {
if (!ProcessExists(p)) {
Console.WriteLine(p + " was removed.");
return true;
}
return false;
});
For the second loop, you can use Contains() method of the List<> class:
Process[] initial = Process.GetProcessesByName("notepad");
foreach (Process i in initial)
{
if (!Clients.Contains(i.Id))
{
Clients.Add(i.Id);
Console.WriteLine(i.Id + " was added.");
}
}

Related

C# Dual Threading, Thread.IsAlive is false even when this thread didn't finish yet

I wrote a short Program which searches for empty directories and deletes them.
This process should run in background while a second process should write something to the Console every second so that the user knows the program is still running.
My problem is that the whole program stops after about 3 seconds while the processDirectory method didn't even finish.
My Main Method which calls a Method (processDirectory()) which runs in a second Thread:
static void Main(string[] args)
{
Thread delEmpty = new Thread(() => Thread2.processDirectory(#"C:\Users\Mani\Documents"));
delEmpty.Start();
printRunning(delEmpty);
File.WriteAllLines(#"C:\Users\Mani\Desktop\Unauthorized Folders.txt", Thread2.unauthorized);
File.WriteAllLines(#"C:\Users\Mani\Desktop\Empty Folders.txt", Thread2.emptyFolders);
Console.ReadKey();
}
My second Class which stores my processDirectory Method which should run in background:
public static List<string> unauthorized = new List<string>();
public static List<string> emptyFolders = new List<string>();
public static void processDirectory(string rootPath)
{
if (!Directory.Exists(rootPath)) return;
foreach (var dir in Directory.GetDirectories(rootPath))
{
try
{
processDirectory(dir);
if (Directory.GetFiles(dir).Length == 0 && Directory.GetDirectories(dir).Length == 0) Directory.Delete(dir, false);
}
catch (UnauthorizedAccessException uae) { unauthorized.Add(uae.Message); }
}
}
Code for printing something:
static async void printRunning(Thread delEmpty)
{
Console.CursorVisible = false;
for (int cnt = 1; delEmpty.IsAlive; cnt++)
{
switch (cnt)
{
case 1:
Console.Write("Running. ");
break;
case 2:
Console.Write("Running . ");
break;
case 3:
Console.Write("Running .");
cnt = 0;
break;
}
await Task.Delay(1000);
}
Console.Write("Finished!");
Console.CursorVisible = true;
}
I'm going to suggest that you avoid using threads and use an abstraction that deals with your threading issues for you. I suggest making use of Microsoft's Reactive Framework Team's Reactive Extensions (NuGet "System.Reactive") and Interactive Extensions (NuGet "System.Interactive").
Then you can do this:
static void Main(string[] args)
{
var rootPath = #"C:\Users\Mani\Documents";
using (Observable
.Interval(TimeSpan.FromSeconds(1.0))
.Subscribe(x => Console.WriteLine($"Running{"".PadLeft((int)x % 3)}.")))
{
Thread2.processDirectory(rootPath);
}
}
public static class Thread2
{
public static List<string> unauthorized = new List<string>();
public static List<string> emptyFolders = null;
public static void processDirectory(string rootPath)
{
if (!Directory.Exists(rootPath)) return;
emptyFolders =
EnumerableEx
.Expand(Directory.GetDirectories(rootPath), dir => Directory.GetDirectories(dir))
.Where(dir => Directory.GetFiles(dir).Length == 0 && Directory.GetDirectories(dir).Length == 0)
.ToList();
emptyFolders
.AsEnumerable()
.Reverse()
.ForEach(dir =>
{
try
{
Directory.Delete(dir, false);
}
catch (UnauthorizedAccessException uae) { unauthorized.Add(uae.Message); }
});
}
}
The key elements here are:
the Observable.Interval that sets up a timer to display the "Running" message every second.
the EnumerableEx.Expand which recursively builds the list of folders to be deleted.
the Reverse/ForEach which runs through the folders to be deleted (in reverse order) and deletes them.
It's important to not that the deleting happens on the main thread - it's just the "Running" message that comes out on the other thread. If needed, though, it would be fairly easy to push the deleting to another thread, but it isn't necessary.
To handle the case when GetDirectories throws an error, use this code:
Func<string, string[]> getDirectories = dir =>
{
try
{
return Directory.GetDirectories(dir);
}
catch (UnauthorizedAccessException uae)
{
unauthorized.Add(uae.Message);
return new string[] { };
}
};
emptyFolders =
EnumerableEx
.Expand(getDirectories(rootPath), dir => getDirectories(dir))
.Where(dir => Directory.GetFiles(dir).Length == 0 && getDirectories(dir).Length == 0)
.ToList();
You can solve the issue by on of two ways:
Make your method printRunning run synchronously
Add delEmpty.Join() so main thread will wait while delEmpty thread finishes
delEmpty.Start();
printRunning(delEmpty);
delEmpty.Join();
In case of 1st solution replace the printRunning method with the following one
static void printRunning(Thread delEmpty)
{
Console.CursorVisible = false;
for (int cnt = 0; delEmpty.IsAlive; cnt++)
{
switch (cnt % 3)
{
case 0:
Console.Write("Running.");
break;
case 1:
Console.Write("Running..");
break;
case 2:
Console.Write("Running...");
break;
}
Thread.Sleep(1000);
Console.SetCursorPosition(0, 0);
Console.Clear();
}
Console.Write("Finished!");
Console.CursorVisible = true;
}

Why im getting all the time InvalidOperationException?

I know what does it mean but i don't understand why i'm getting it.
In my class constructor i did:
namespace HM
{
class Core
{
public static Form1 form1;
Process[] pname;
private static List<float?> cpuSensorValues = new List<float?>();
private static List<float?> gpuSensorValues = new List<float?>();
Computer myComputer;
Computer computer;
public Core(Form1 f)
{
form1 = f;
myComputer = new Computer();
myComputer.CPUEnabled = true;
myComputer.FanControllerEnabled = true;
myComputer.MainboardEnabled = true;
myComputer.Open();
computer = new Computer();
computer.Open();
computer.GPUEnabled = true;
OpenHardwareMonitor.Hardware.ISensor isss;
Hardwares hwsd;
OpenHardwareMonitor.Hardware.ISensor ist;
pname = Process.GetProcessesByName("BFBC2Game");
}
I added this:
pname = Process.GetProcessesByName("BFBC2Game");
And when the game is running i see using a breakpoint that pname wontain one index that is the game process. And im sure to run first the game then the program.
Then i have a method in the class i call it with a timer in Form1 that get write to a log file the temeperature of the GPU Video Card.
This part was working good untill i added this pname process.
if (gpuSensorValues.Count == 30 && sensor.Value >= (float)numericupdown)
{
float a = ComputeStats(gpuSensorValues).Item1;
float b = ComputeStats(gpuSensorValues).Item2;
float c = ComputeStats(gpuSensorValues).Item3;
Logger.Write("********************************");
Logger.Write("GPU Minimum Temperature Is ===> " + a);
Logger.Write("GPU Maximum Temperature Is ===> " + b);
Logger.Write("GPU Average Temperature Is ===> " + c);
Logger.Write("********************************" + Environment.NewLine);
gpuSensorValues = new List<float?>();
pname.ToList().ForEach(p => p.Kill());
}
I added this :
pname.ToList().ForEach(p => p.Kill());
I wanted that if the temperature for example was 123c then kill/close/shut down the game at once ! Instead it's not writing to the logger file the temperature anymore and it's never kill the process but writing to the log file the exception message:
3/5/2014--4:10 AM ==> There was an exception: System.InvalidOperationException: No process is associated with this object.
at System.Diagnostics.Process.EnsureState(State state)
at System.Diagnostics.Process.EnsureState(State state)
at System.Diagnostics.Process.GetProcessHandle(Int32 access, Boolean throwIfExited)
at System.Diagnostics.Process.Kill()
at HardwareMonitoring.Core.gpuView(Boolean pause, List`1 myData, Nullable`1 myGpuTemp, Button b1, Decimal numericupdown) in d:\C-Sharp\HardwareMonitoring\HardwareMonitoring\Hardwaremonitoring\Core.cs:line 172
This is the complete method code if needed:
public float? gpuView(bool pause, List<string> myData, float? myGpuTemp, Button b1, decimal numericupdown)
{
try
{
if (pause == true)
{
}
else
{
foreach (var hardwareItem in computer.Hardware)
{
if (form1.videoCardType("ati", "nvidia") == true)
{
HardwareType htype = HardwareType.GpuNvidia;
if (hardwareItem.HardwareType == htype)
{
foreach (var sensor in hardwareItem.Sensors)
{
if (sensor.SensorType == SensorType.Temperature)
{
sensor.Hardware.Update();
if (sensor.Value.ToString().Length > 0)
{
/* else if (UpdatingLabel(sensor.Value.ToString(), label16.Text.Substring(0, label16.Text.Length - 1)))
{
// Label8 = GpuText;
}*/
//myData = new List<string>();
//this.Invoke(new Action(() => data = new List<string>()));
if (!form1.IsDisposed)
{
form1.Invoke(new Action(() => myData.Add("Gpu Temeprature --- " + sensor.Value.ToString())));
}
//this.Invoke(new Action(() => listBox1.DataSource = null));
//this.Invoke(new Action(() => listBox1.DataSource = data));
//form1.Invoke(new Action(() => lb1.DataSource = myData));
//sensor.Value.ToString() + "c";
myGpuTemp = sensor.Value;
//label8.Visible = true;
}
//if (sensor.Value > 60)
//{
gpuSensorValues.Add(sensor.Value);
if (gpuSensorValues.Count == 30 && sensor.Value >= (float)numericupdown)
{
float a = ComputeStats(gpuSensorValues).Item1;
float b = ComputeStats(gpuSensorValues).Item2;
float c = ComputeStats(gpuSensorValues).Item3;
Logger.Write("********************************");
Logger.Write("GPU Minimum Temperature Is ===> " + a);
Logger.Write("GPU Maximum Temperature Is ===> " + b);
Logger.Write("GPU Average Temperature Is ===> " + c);
Logger.Write("********************************" + Environment.NewLine);
gpuSensorValues = new List<float?>();
pname.ToList().ForEach(p => p.Kill());
}
b1.Enabled = true;
//}
//form1.Select();
}
}
}
}
else
{
HardwareType htype = HardwareType.GpuAti;
if (hardwareItem.HardwareType == htype)
{
foreach (var sensor in hardwareItem.Sensors)
{
if (sensor.SensorType == SensorType.Temperature)
{
sensor.Hardware.Update();
if (sensor.Value.ToString().Length > 0)
{
myGpuTemp = sensor.Value;
//label8.Visible = true;
}
if (sensor.Value > 60)
{
Logger.Write("The Current Ati GPU Temperature Is ===> " + sensor.Value);
b1.Enabled = true;
}
form1.Select();
}
}
}
}
}
}
}
catch (Exception err)
{
Logger.Write("There was an exception: " + err.ToString());
}
return myGpuTemp;
}
Line 172 is:
//form1.Select();
Just empty line nothing to do there.
The docs for Process.Kill() show that it can raise an InvalidOperationException if the process is not running.
Maybe try: pname.Where(p => !p.HasExited).ToList().ForEach(p => p.Kill());
The problem is that one or more of the instances in your pname list aren't in the state you expect. As pointed out in another answer this could be because the process isn't running. Also, one of the references could be null. To prevent the exception try adding a where clause like the following;
pname.ToList().Where(p != null && !p.HasExited).ForEach(p => p.Kill());
I'm not familiar with the Process class so I'm not entirely sure what you'll want to use for that second condition in the Where but that should give you the idea. This will just filter out instances in the list that would cause the error before calling Kill().
The only problem i can see in your code is you are not clearing the pname object. If the pname has reference to a process that is no longer running, you will get this exception. I recommend to get fresh list of process when you want to stop it. Replace the following line
pname.ToList().ForEach(p => p.Kill());
with
pname = Process.GetProcessesByName("BFBC2Game");
pname.ToList().ForEach(p => p.Kill());

List<T>Get Chunk Number being executed

I am breaking a list into chunks and processing it as below:
foreach (var partialist in breaklistinchunks(chunksize))
{
try
{
do something
}
catch
{
print error
}
}
public static class IEnumerableExtensions
{
public static IEnumerable<List<T>> BreakListinChunks<T>(this IEnumerable<T> sourceList, int chunkSize)
{
List<T> chunkReturn = new List<T>(chunkSize);
foreach (var item in sourceList)
{
chunkReturn.Add(item);
if (chunkReturn.Count == chunkSize)
{
yield return chunkReturn;
chunkReturn = new List<T>(chunkSize);
}
}
if (chunkReturn.Any())
{
yield return chunkReturn;
}
}
}
If there is an error, I wish to run the chunk again. Is it possible to find the particular chunk number where we received the error and run that again ?
The batches have to be executed in sequential order .So if batch#2 generates an error, then I need to be able to run 2 again, if it fails again. I just need to get out of the loop for good .
List<Chunk> failedChunks = new List<Chunk>();
foreach (var partialist in breaklistinchunks(chunksize))
{
try
{
//do something
}
catch
{
//print error
failedChunks.Add(partiallist);
}
}
// attempt to re-process failed chunks here
I propose this answer based on your comment to Aaron's answer.
The batches have to be executed in sequential order .So if 2 is a problem , then I need to be able to run 2 again, if it fails again. I just need to get out of the loop for good.
foreach (var partialist in breaklistinchunks(chunksize))
{
int fails = 0;
bool success = false;
do
{
try
{
// do your action
success = true; // should be on the last line before the 'catch'
}
catch
{
fails += 1;
// do something about error before running again
}
}while (!success && fails < 2);
// exit the iteration if not successful and fails is 2
if (!success && fails >= 2)
break;
}
I made a possible solution for you if you don't mind switching from Enumerable to Queue, which kind of fits given the requirements...
void Main()
{
var list = new Queue<int>();
list.Enqueue(1);
list.Enqueue(2);
list.Enqueue(3);
list.Enqueue(4);
list.Enqueue(5);
var random = new Random();
int chunksize = 2;
foreach (var chunk in list.BreakListinChunks(chunksize))
{
foreach (var item in chunk)
{
try
{
if(random.Next(0, 3) == 0) // 1 in 3 chance of error
throw new Exception(item + " is a problem");
else
Console.WriteLine (item + " is OK");
}
catch (Exception ex)
{
Console.WriteLine (ex.Message);
list.Enqueue(item);
}
}
}
}
public static class IEnumerableExtensions
{
public static IEnumerable<List<T>> BreakListinChunks<T>(this Queue<T> sourceList, int chunkSize)
{
List<T> chunkReturn = new List<T>(chunkSize);
while(sourceList.Count > 0)
{
chunkReturn.Add(sourceList.Dequeue());
if (chunkReturn.Count == chunkSize || sourceList.Count == 0)
{
yield return chunkReturn;
chunkReturn = new List<T>(chunkSize);
}
}
}
}
Outputs
1 is a problem
2 is OK
3 is a problem
4 is a problem
5 is a problem
1 is a problem
3 is OK
4 is OK
5 is OK
1 is a problem
1 is OK
One possibility would be to use a for loop instead of a foreach loop and use the counter as a means to determine where an error occurred. Then you could continue from where you left off.
You can use break to exit out of the loop as soon as a chunk fails twice:
foreach (var partialList in breaklistinchunks(chunksize))
{
if(!TryOperation(partialList) && !TryOperation(partialList))
{
break;
}
}
private bool TryOperation<T>(List<T> list)
{
try
{
// do something
}
catch
{
// print error
return false;
}
return true;
}
You could even make the loop into a one-liner with LINQ, but it is generally bad practice to combine LINQ with side-effects, and it's not very readable:
breaklistinchunks(chunksize).TakeWhile(x => TryOperation(x) || TryOperation(x));

Check If process is running every minute

I have this basic code that will check for notepad running every minute.
namespace Watcher
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; ; i--)
{
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Contains("notepad"))
{
Console.WriteLine("True");
}
Console.WriteLine("NFalse");
}
Thread.Sleep(10000);
}
}
}
}
The problem is that it returns "NFalse" for every running process (It will print 100 of them for example). How can I just make this print once to show that the process is not running?
Refactor your code.
You're doing too much in one method. Put your code that checks to see if notepad is running into a separate method:
static bool CheckIfProcessIsRunning(string nameSubstring)
{
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Contains(nameSubstring))
{
return true;
}
}
return false;
}
You could simplify this further using LINQ:
static bool CheckIfProcessIsRunning(string nameSubstring)
{
return Process.GetProcesses().Any(p => p.ProcessName.Contains(nameSubstring));
}
Once you have written this method, all that remains is to call it and print the right message depending on whether it returns true or false.
while (true)
{
string message = CheckIfProcessIsRunning("notepad") ? "True" : "NFalse";
Console.WriteLine(message);
Thread.Sleep(10000);
}
Now instead of one long method with complex logic, you have two very simple methods.
You just need to check the process you are interested in. Don't bother looping over all the running processes. Use Process.GetProcessByName().
for (int i = 0; ; i--)
{
Process[] processes = Process.GetProcessByName("notepad++"); // Without extension
if(processes.Length > 0){
Console.WriteLine("True");
}
else{
Console.WriteLine("False");
}
Thread.Sleep(10000);
}
Just change to this, so you only print out once.
var b = false;
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Contains("notepad"))
{
if (!b) b = true;
}
}
Console.WriteLine(b);
Well this seems to work well.
Dim x = Process.GetProcesses().ToList().FirstOrDefault(Function(p) p.ProcessName.Contains("Notepad"))
if x Is Nothing then
Console.WriteLine("false")
end if

C#: Collection was modified; enumeration operation may not execute [duplicate]

This question already has answers here:
How to remove elements from a generic list while iterating over it?
(28 answers)
Closed 9 years ago.
My goal is to delete a user from the user list in my application.But i cannot get to the bottom of this error. Some one plz bail me out.
if (txtEmailID.Text.Length > 0)
{
users = UserRespository.GetUserName(txtEmailID.Text);
bool isUserAvailable=false;
foreach (EduvisionUser aUser in users) // Exception thrown in this line
{
isUserAvailable = true;
if(!aUser.Activated)
{
users.Remove(aUser);
}
}
if (users.Count == 0 && isUserAvailable)
{
DeactivatedUserMessage();
return;
}
}
You can't modify a collection while you're iterating over it with a foreach loop. Typical options:
Use a for loop instead
Create a separate collection of the items you want to act on, then iterate over that.
Example of the second approach:
List<EduvisionUser> usersToRemove = new List<EduvisionUser>();
foreach (EduvisionUser aUser in users) --->***Exception thrown in this line***
{
isUserAvailable = true;
if(!aUser.Activated)
{
usersToRemove.Add(aUser);
}
}
foreach (EduvisionUser userToRemove in usersToRemove)
{
users.Remove(userToRemove);
}
Another alternative, if you're using List<T> is to use List<T>.RemoveAll:
isUserAvailable = users.Count > 0;
users.RemoveAll(user => !user.Activated);
You are trying to delete a user from the list you are looping trough.
this is impossible. Best is to create a new list and add the good ones in it instead of deleting the bad ones
if (txtEmailID.Text.Length > 0)
{
//#new list
List<EduvisionUser> listOfAcceptedUsers = new List<EduvisionUser>()**
users = UserRespository.GetUserName(txtEmailID.Text);
bool isUserAvailable=false;
foreach (EduvisionUser aUser in users) --->***Exception thrown in this line***
{
isUserAvailable = true;
//Add user to list instead of deleting
if(aUser.Activated)
{
ListOfAcceptedUsers.Add(aUser);
}
}
//check new list instead of old one
if (ListOfAcceptedUsers.Count == 0 && isUserAvailable)
{
DeactivatedUserMessage();
return;
}
}
you can do it like this. Use for instead foreach
for( int i =0; i< users.Count; i++ ) --->***Exception thrown in this line***
{
EduvisionUser aUser = users[i];
isUserAvailable = true;
if(!aUser.Activated)
{
users.Remove(aUser);
i--;
}
}
You cannot modify the collection while enumerating. Instead of removing select only what you need and leave the Garbage Collector take care of the rest:
users = users.Where(x => x.Activated);
Or even better, select only what you need from the repository:
users = UserRespository.GetUserName(txtEmailID.Text).Where(x => x.Activated);
My goal is to delete a WorkCalendar from the WorkCalendar but when select Wc that has WorkHour thrown an exception like this:" Collection was modified; enumeration operation may not execute." Any ideas? thanks for the help
Delete method:
try
{
if (!this.DataWorkspace.ApplicationData.WorkCalendars.CanDelete)
{
this.ShowMessageBox("", "", MessageBoxOption.Ok);
return;
}
if (this.WorkCalendars.SelectedItem != null)
{
if ((this.WorkCalendars.SelectedItem.FindCalendarWPs.Count() > 0) || (this.WorkCalendars.SelectedItem.FindCalendarWPs1.Count() > 0))
{
Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke
(() =>
{
RadWindow.Alert(" ");
});
return;
}
var y = DataWorkspace.ApplicationData.WorkCalendarDays.Where(w => w.WorkCalendar.Id == WorkCalendars.SelectedItem.Id).Execute().AsEnumerable();
foreach (var item in y)
{
if(item.WorkingHoursCollection != null && item.WorkingHoursCollection.Count() > 0)
foreach (var WH in item.WorkingHoursCollection)
{
WH.Delete();
}
item.Delete();
}
if (this.WorkCalendars.SelectedItem == this.DataWorkspace.ApplicationData.WorkCalendars.Where(U => U.Id == this.WorkCalendars.SelectedItem.Id).SingleOrDefault())
{
Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke
(() =>
{
RadWindow.Alert(" ");
});
return;
}
this.WorkCalendars.SelectedItem.Delete();
this.Save();
}
}
catch (Exception ex)
{
Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke
(() =>
{
var msg = new LightSwitchApplication.Presentation.GeneralViews.ExceptionMessage();
msg.DataContext = ex;
msg.ShowDialog();
});
}

Categories