C# Block code till processes release handle on files - c#

I have a foreach loop that starts a process within a try/catch. In the finally section of my try/catch/finally I am trying to ensure the the process does not have a handle on any files. I have to delete files that were being processed.
Nothing I have tried seems to be working. I continue to get System.IO exceptions. "The file is currently in use by another process."
You can see in the finally I am using the WaitForExit() before returning from this method. The very next method call is one to delete files. Why would the process still be open or have a handle on any of these file after this?
Thanks!
try
{
foreach (var fileInfo in jsFiles)
{
//removed for clarity
_process.StartInfo.FileName = "\"C:\\Program Files\\Java\\jre6\\bin\\java\"";
_process.StartInfo.Arguments = stringBuilder.ToString();
_process.StartInfo.UseShellExecute = false;
_process.StartInfo.RedirectStandardOutput = true;
_process.Start();
}
}
catch (Exception e)
{
BuildMessageEventArgs args = new BuildMessageEventArgs("Compression Error: " + e.Message,
string.Empty, "JSMin", MessageImportance.High);
BuildEngine.LogMessageEvent(args);
}
finally
{
_process.WaitForExit();
_process.Close();
}

There's something seriously wrong here. You're starting a bunch of processes, but only waiting for the last spawned one to exit.
Are you sure you don't want the foreach outside the try block?
If you tell us more about what exactly you're trying to do, we could provide better suggestions.

I think you need to restructure your code. As it stands a failure for any of the processes in the foreach will cause an exit from the loop. Even if everything does succeed then your WaitForExit and Close calls in the finally block will only address the last process from the loop above.
You need to deal with each process and its success and/or failure individually. Create a method that accepts a fileInfo parameter and spawns and waits on each process. Move your loop into the client code that will be calling the suggested method.

Is the process a Console application or a GUI application?
For a GUI application, you will have to do Process.CloseMainWindow.

foreach (var fileInfo in jsFiles)
{
using (Process process = new Process())
{
try
{
//Other stuff
process.Start();
}
catch (...)
{
//Exception Handling goes here...
}
finally
{
try
{
process.WaitForExit();
}
catch (...)
{
}
}
}
}
Process.WaitForExit() might throw an exception, so it needs a try/catch of it's own.
If you create the process in the using statement, you don't have to worry about closing it, .NET will dispose of it properly.
It's usually better to not precede local variables with an underscore character. Most people just use that for their fields.

Related

Dispose (or Close) killed process

Calling process.Dispose() or process.Close() (no matter which one, 'cos .Close is called inside .Dispose implementation) after process.Kill() sometimes hangs application.
I can't reproduce this bug stable, but sometimes when WaitForExit finishes by timeout passed to it, application hangs on process.Close() command.
Please suggest me, what the cause of this problem may be?
Note:
I've seen similar question. But there are not answers & upvoted comment says, that the cause of the problem perhaps at details, which are not provided at that question. So I added more details.
I've also seen a linked solution, but I can't use ProcessStartInfo.UseShellExecute = true; 'cos I need to redirect output.
Sorry for verbose code, but I pass it such way, 'cos at similar unanswered question commentators noticed, that not enough details provided (as I noted above)
private static async Task<int> RunMethod(string processArguments)
{
// 1. Prepare ProcessStartInfo
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.Arguments = processArguments;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
// 2. Create process inside using-block to be disposed
using (var proc = new Process())
{
proc.StartInfo = startInfo;
// 3. Subscribe output streams handlers
proc.OutputDataReceived += (sender, outputLine) => { HandleMessage(outputLine); };
proc.ErrorDataReceived += (sender, errorLine) => { HandleMessage(errorLine); };
// 4. Start process
if (!proc.Start())
{
proc.Close();
return -1;
}
// 5. Start the asynchronous read of the standard output stream.
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
// 6. Waiting process to exit with timeout on threadpool to not block UI thread
// Re#er warns me "captured variable `proc` is disposed in the outer scope". But I think it's Ok, 'cos we're awaiting this task inside using block (in next line)
var waitingProcessTask = Task.Run(() => proc.WaitForExit(TIMEOUT), _cancelToken);
bool hasExited = await waitingProcessTask;
// 7. Stop reading streams
// Not sure, these 2 CalncelXxxRead methods are needed. But hope it won't hurt at least
proc.CancelErrorRead();
proc.CancelOutputRead();
// 8. If !hasExited (i.e. TIMEOUT is reached) we kill the process
if (!hasExited)
{
Logger.Debug("0. Before Kill()");
proc.Kill();
proc.Refresh(); // not sure, it's needed
}
// If uncomment next 2 lines, then problem moves here from end of using block
//proc.Close();
//Logger.Debug("1. after .Close call"); // <------------------ This log we don't see sometimes
Logger.Debug("2. last inside using-block");
} // end of using block
Logger.Debug("3. after using-block"); // <------------------ This log we don't see sometimes (if `.Close` wasn't called before)
return 0;
}
I've figured out with my issue. The problem was: my process once in a while** spawned child daemon-process, which is ment to work forever. Child process (by design) always inherits redirected output streams if parent streams are redirected.
From the other side, .Kill() don't kill child processes - only for which it was kalled. (Until .Net 5.0, where .Kill(bool) solves this problem).
So .Dispose() or .Close() never finish, 'cos waits to release output streams, which are held by infinite child process.
It was very interesting adventure to figure out, what is happening =)
**) - that's why reproduce was very unstable
PS: Thank to #Blindy for directing me the way quite close to where the real cause of problem lie.

Parallel.ForEach exits unexpectedly without exceptions

I am running a .NET 4.7 C# console application in which I am iterating through a collection of files (list of strings with file paths.
I want to run an operation on each file in parallel.
private void LaunchComparators()
{
//1) Get Trade Files
var files = GetTradeFiles();
//2) Run comparisons
try
{
Parallel.ForEach(files, file => LaunchComparator(file));
}
catch (Exception ex)
{
Log.Error(ex.Message);
throw ex;
}
//2 Write Results
WriteResults();
}
private void LaunchComparator(string file)
{
var comparator = new TradeComparator();
var strategyComparisonOutput = comparator.ComparePerStrategy(file);
}
While running the comparison, the first comparison completes and then the program abruptly stops without any exceptions.
I am not sure what I should do differently here, so that the files are all processed individually.
I am new to parallel programming/threading. Any help appreciated.
Parallel For and ForEach operations do not re-throw exception that occur inside the loop. If you don't add exception handling inside the loop the exceptions are lost and the loop just terminates.
Here is a link to an article on how to handle exception inside of Parallel processing loops:
https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-handle-exceptions-in-parallel-loops

Reiterating a Try statement using GoTo in the Catch clause?

I am examining some code examples and have seen similar variation to the following examples, one of them made me very curious.
When a goto_tag is placed before a try statement. This makes complete sense and it just runs through the try again.
retry_tag: //the goto tag to re-attempt to copy a file
try {
fileInfo.CopyTo( toAbsolutePath + fileInfo.Name, true ); //THIS LINE MIGHT FAIL IF BUSY
} catch {
Thread.Sleep(500); //wait a little time for file to become available.
goto retry_tag; //go back and re-attempt to copy
}
However, as the following was presented to me I did not understand it. When a goto_tag is placed within the try statement, and is called from within the catch block.
try {
retry_tag: //the goto tag to re-attempt to copy a file
fileInfo.CopyTo( toAbsolutePath + fileInfo.Name, true ); //THIS LINE MIGHT FAIL IF BUSY
} catch {
Thread.Sleep(500); //wait a little time for file to become available.
goto retry_tag; //go back and re-attempt to copy
}
Is the try block resurrected? Or are the two examples functionally identical, or is this an completely illegal operation and won't even compile?
This is all purely out of curiosity, and of course I would prefer the first example, if either of them..
Thanks for any insight!!
To actually answer your question:
No, these are not the same.
The first example will compile; the second is illegal.
(The matter of whether you should write code like this is a different question... and off course, you should not if you can help it.)
You can implement a simple while instead of goto:
// loop until ...
while (true) {
try {
fileInfo.CopyTo(Path.Combine(toAbsolutePath, fileInfo.Name), true);
// ... file copied
break;
}
catch (IOException) {
Thread.Sleep(500); //wait a little time for file to become available.
}
}

Checking if Word has terminated correctly

I'm automating a Word document from a WPF application in C#.
In the application I create a Word object with
_applicationWD = new Microsoft.Office.Interop.Word.Application();
and I terminate it with
_applicationWD.Quit(Microsoft.Office.Interop.Word.WdSaveOptions.wdDoNotSaveChanges);
Everything is neatly put in try/catch statements. As Quit is the last thing that can go wrong, I need to check if the Quit statement succeeded and then try to terminate it in some other way, otherwise I will have a lot of WinWord.exe in the process list.
Is there any way to check if Word closed or maybe get the PID of the process and force it to terminate from code?
this should kill all the winword processes
try
{
System.Diagnostics.Process[] procs = System.Diagnostics.Process.GetProcessesByName("WinWord");
foreach (var myproc in procs)
myproc.Kill();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}

Is there any alternative other than Process to execute built-in shell commands in C#

Is there any alternative other than Process to execute built-in shell commands in C#? Currently I am using Process class to run those commands. But in the current scenario I want to run more than 200 such commands in parallel. So spawning more than 200 processes is not a good idea. Is there any other alternative?
"Running a dos command" is the equivalent to "Creating a process and running it" so even if there is an other api, there will still be 200 processes (which, by the way, is nothing to worry about unless you're on a really, really tiny system)
You could but, shouldn't do
using Microsoft.VisualBasic;
Interaction.Shell(...);
Note: You would have to add a reference to the the VisualBasic assembly.
This is a direct answer to your question but, not somthing you should do.
As Max Keller pointed out, System.Diagnostics.Process always starts a new system process.
If have to start processes/operations for more than just some seconds, I would prefer to save all commands in a temp file and execute this with System.Diagnostics.Process rather than ever single operation.
// Get a temp file
string tempFilepath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "MyBatchFile.bat");
// Ensure the file dont exists yet
if (System.IO.File.Exists(tempFilepath)) {
System.IO.File.Delete(tempFilepath);
}
// Create some operations
string[] batchOperations = new string[]{
"START netstat -a",
"START systeminfo"
};
// Write the temp file
System.IO.File.WriteAllLines(tempFilepath, batchOperations);
// Create process
Process myProcess = new Process();
try {
// Full filepath to the temp file
myProcess.StartInfo.FileName = tempFilepath;
// Execute it
myProcess.Start();
// This code assumes the process you are starting will terminate itself!
} catch (Exception ex) {
// Output any error to the console
Console.WriteLine(ex.Message);
}
// Remove the temp file
System.IO.File.Delete(tempFilepath);

Categories

Resources