Click here to Skip to main content
15,887,175 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
Here is the relevant code:
public static ProcessStartInfo psi = new ProcessStartInfo(thecommand);
public static bool workerDone;
public frmMain()
{
   InitializeComponent();
   work1.DoWork += new DoWorkEventHandler(DoProcessing);
}
private void btnProcess_Click(object sender, EventArgs e)
{
   foreach (string loop in fileNamesToProcess)
   {
      Prepare(loop, out thePath, out tempStr, out thecommand, out thearguments);

      //               DoProcessing();
      psi.WindowStyle = ProcessWindowStyle.Minimized;
      psi.Arguments = thearguments;
      psi.FileName = thecommand;

      workerCompleted = false;
      BackgroundWorker work1 = new BackgroundWorker();
      work1.DoWork += new DoWorkEventHandler(DoProcessing);
      work1.RunWorkerCompleted += new RunWorkerCompletedEventHandler
                     (work1_RunWorkerCompleted);
      work1.WorkerReportsProgress = true;
      work1.RunWorkerAsync(psi);

      // Wait for thread to finish
      while (!workerCompleted)
      {
      }

      //  Delete the original
      File.Delete(workFile);
      //  Rename the processed file
      File.Move(outFile, workFile);

      MoveToDoneList();

      work1 = null;

   }
}
private static void DoProcessing(object sender, DoWorkEventArgs e)
{
   Process p = Process.Start(psi);
   //  Wait for it to finish processing before going on
   while (!p.HasExited)
   {
   }
   workerCompleted = true;
}
void work1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    workerCompleted = true;
}

Process p runs fine, workerDone is set to true, the foreach loop continues until work1.RunWorkerAsync() is encountered again, when I get the error that it is still busy, and cannot be run again.

Using a BackgroundWorker because my DoProcessing is a DOS command and I want to keep my window "alive" to be able to do a Stop (but not cancel the worker, just stop the foreach loop going on to the next item.)

I see a problem in that my process takes too long. Needs to send the occasional message, but I can't because by declaring the work1 where I do I can't access work1.ReprtProgress. Maybe I'll just have to forgo running the process in a thread and if I want to interrupt it, use task manager - is for my own use anyway.
Posted
Updated 28-Jun-11 22:52pm
v4
Comments
Sergey Alexandrovich Kryukov 29-Jun-11 3:26am    
You do not show some important detail, such as foreach... what? This is where you RunWorkerAsync multiple times (bad idea).
What is DOS command? there is no such thing anymore.
Why, can you run under debugger and put a break point to see what's going on?

Don't show fragment of code. Create a simple sample specially to show a problem and post it. Use "improve question".
--SA
Dylan Morley 29-Jun-11 4:43am    
Comment from OP:

I am running a command line app which processes audio. It only handles one file at a time (no batch facility) so my app provides batch facility - select a group of files and then call the command line app with each file in turn.

If I don't run the command line app through a background worker I lose control over my app and can't click the Stop button (or Cancel or whatever.)

Tried creating the bw at the beginning of the foreach and then nulling it at the end. Solved the problem of the bw running twice, but then I get a deadlock. Tried added a RunWorkerCompleted, bat that never gets executed.

BackgroundWorkers are designed to run once. For this task, you should use an old fashioned thread which manages the foreign process. You can use a Queue<ProcessStartInfo> to organise the information, and the thread can pull items off the queue, start the process, wait for it to end and then fire an event.
 
Share this answer
 
Comments
Nigel Mackay 29-Jun-11 5:00am    
Will give it a go. BackgroundWorker is just a lot easier to use.
You can do this using a background worker fine, but you're trying to do some of the work in the process button event handler and only some in the background worker.

Try this...

* Drop a BackgroundWorker component on your form. Name it 'audioWorker'
* Click on it and choose properties
* Set WorkerReportsProgress and WorkerSupportsCancellation to true
* Click on events and create event handlers for DoWork, ProgressChanged and RunWorkerCompleted

Add a new class to your project. Call it ProcessAudioStartArguments

C#
/// <summary>
/// A class to pass as an argument to the DoWork method of the background
/// worker
/// </summary>
public class ProcessAudioStartArguments
{
    /// <summary>
    /// ctor
    /// </summary>
    /// <param name="files">List of file names to process</param>>
    public ProcessAudioStartArguments(List<ProcessStartInfo> files)
    {
        this.FilesToProcess = files;
    }
    /// <summary>
    /// The list of files to delete
    /// </summary>
    public List<ProcessStartInfo> FilesToProcess { get; private set; }
}


Your actual code should then look something like this..

* Build up a list of arguments to send to the background worker
* Start the worker, pass in our custom arguments object

C#
private void btnProcess_Click(object sender, EventArgs e)
{
   List<ProcessStartInfo> files = new List<ProcessStartInfo>();

   // Prepare your arguments to send to the worker...
   foreach (string loop in fileNamesToProcess)
   {
      Prepare(loop, out thePath, out tempStr, out thecommand, out thearguments);

      ProcessStartInfo psi = new ProcessStartInfo();
      psi.WindowStyle = ProcessWindowStyle.Minimized;
      psi.Arguments = thearguments;
      psi.FileName = thecommand;

  files.Add(psi);
   }

   ProcessAudioStartArguments arguments = new ProcessAudioStartArguments(files);
   audioWorker.RunWorkerAsync(arguments);
}


* Do all processing in the worker thread. Check for cancellations, report on progress etc

C#
private void audioWorker_DoWork(object sender, DoWorkEventArgs e)
{
   ProcessAudioStartArguments arguments = (ProcessAudioStartArguments)e.Argument;
   foreach(ProcessStartInfo psi in arguments.FilesToProcess)
   {
     if (audioWorker.CancellationPending)
         break;

     // Some sort of progress reporting...
     var displayText = string.Format(Resources.ProcessingFile, Path.GetFileName(psi.FileName));
     audioWorker.ReportProgress(step, displayText);

     Process p = Process.Start(psi);
     p.WaitForExit();

     // Some sort of check for success? Return code of process?

     // Delete the original
     File.Delete(workFile);
     //  Rename the processed file
     File.Move(outFile, workFile);
   }
   e.Result = someObj // Report on the success of the processing...?
}


Handle progress reporting...

C#
void audioWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
   string currentFile = e.UserState.ToString();

   // Some sort of process window to display percentage done etc...
   progressWindow.SetText(currentFile);
   progressWindow.StepTo(e.ProgressPercentage);
}


So leave all the work to the background worker, you just start it once and pass in an arguments object that gives it all the information it requries to complete the job.

I do something similar in my article Secure Delete .NET[^].

HTH
 
Share this answer
 
Comments
Nigel Mackay 29-Jun-11 5:36am    
Mmmm, makes sense. Instead of my app waiting for the worker to finishe, the worker just gets on with the job until finished or cancelled. And modifying the existing code is mostly a question of just moving it around into the correct procedures. I can also make my app respond to the progress reports and move file names from the waiting-list to the done-list.
Much appreciated.
Nigel Mackay 29-Jun-11 8:28am    
Works fine, after adding UseShellExecute=false and changing ProcessWindowStyle.Minimized to CreateNoWindow=true. It ignored the windowminimized.
Dylan Morley 29-Jun-11 8:32am    
Cool, glad it helped
There are several issus that I can see with your code.

The first, most obvious one, is that you are allocating a new event handler for DoWork in the loop every time, but you don't deallocate it anywhere.

The second thing is that while you are looking to try and do something in a background thread, the while loop inside your foreach means that you've created a tight loop in there which chokes the primary thread. As a result of this, you've blocked the main thread.
 
Share this answer
 
Comments
Nigel Mackay 29-Jun-11 5:25am    
Yes, I am going to have to try an alternative approach. The problem is the the called process processes a file, with a different filename for output. So I add a prefix to the outfile, then when processing is finished I delete the source file and rename the outfile. Can't do this until the external process is finished. In fact, can't go on to the next file until it is finished the current one. So I have to sit there waiting for it to finishe. It is not a case of going on with other things in the meantime. I am going to have to implement some sort of queuing system for a separate thread.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900