views:

64

answers:

1

In my code below, I am using Process objects to execute a series of DOS batch files. I'm shortening the list of scripts for example's sake.

The subsequent (1+) scripts execute via an event handler (instead of a for loop). That way, each subsequent script runs ONLY when the previous one finishes. Now for some reason as I execute the 2nd script I can't get the caught exception to populate the status bar with the error message.

I'm testing for when invalid script names are entered in an app-config file, and I suspect I am using delegates incorrectly. My anonymous delegate consists of a combination of "new code" and existing class methods. Thats what I think is wrong; If you all could shove me toward a why I'd appreciate it ;)

NOTE: this.copy_scripts[] is constructued from a Split of: "goodname.bat,nosuchscript.bat"

private void copybutton_Click(object sender, EventArgs e)
    {

        InitializeBar();

        this.nbr_of_copy_exits_ = 0;
        this.RunCopyScript(this.nbr_of_copy_exits_);
        return;
    }

private void RunCopyScript(Int32 CopyScriptIdx)
    {
        Process proc = null;
        try
        {
            proc = this.ObtainProcess(this.client_dest_dir_ + this.copy_scripts_[CopyScriptIdx]);
            proc.EnableRaisingEvents = true;
            proc.Exited += new EventHandler(CopyExited);
            proc.Start();
            this.progressBar.Value = ProgressInPercent(this.copy_scripts_.Count(), CopyScriptIdx);
        }
        catch (Exception ex)
        {
            this.UpdateControl(this.toolStripStatusLabel1, "Error involving " + this.copy_scripts_[CopyScriptIdx] + ": " + ex.Message);
            this.copybutton.BackColor = Color.Red;
        }
        return;
    }

void CopyExited(object sender, EventArgs e)
    {
        System.Diagnostics.Process senderProcess
            = sender as System.Diagnostics.Process;

        this.Invoke((MethodInvoker)delegate
        {
            if (++this.nbr_of_copy_exits_ == this.copy_scripts_.Count())
            {
                this.UpdateControl(this.toolStripStatusLabel1, "Copying COMPLETE.");
                this.progressBar.Value = 0;
            }
            else
            {
                this.RunCopyScript(this.nbr_of_copy_exits_);
            }
        });
    }

        private void UpdateControl(ToolStripStatusLabel tssl, String text)
    {
        tssl.Text = text;
        tssl.Refresh();
    }
+1  A: 

I think that rather than using an event to setup and continue the loop, I would create an asynchronous delegate with a callback method. When the process is complete you simply call back to the RunCopyScript(...) again. Take a look at delegates on MSDN, and asynchronous programming. I'm sure someone will have a way of doing this with Action, I just don't know it well enough to give you an example.

I also saw a neat snippet on how to get the output into a gui control element from a cross-threaded function. Unfortunately, I cannot find the elegant piece of coding that I saw earlier...if I do run across it, I'll post a link to it.

Okay, here is what I have - for what it's worth. I believe it is fairly accurate. I would not expect that it would compile out-of-the-box as I don't have all the files, paths, etc. to properly start processes. And setting up simulated batch files to Start() a Process would take quite a bit more work. I expect you should be able to work with this and have something closer to what you want if cross-referenced with the link I provided above.

I also commented out some lines of code that wouldn't be needed, I moved, or were not recognized. In addition, I did not do anything with your current try/catch block.

// same signature as the method to be called asynchronously
delegate void RunScript(Int32 scriptIdx);

// declare IAsyncResult
IAsyncResult result;

Process proc = null;

private void copybutton_Click(object sender , EventArgs e)
{
    InitializeBar();

    nbr_of_copy_exits_ = 0;
    //this.RunCopyScript(this.nbr_of_copy_exits_);
    RunScript start = new RunScript(RunCopyScript);

    result = start.BeginInvoke(nbr_of_copy_exits_ , new AsyncCallback(CopyExited) , proc);
    copybutton.Enabled = false  // you don't want the button to be clicked again.
}

private void RunCopyScript(Int32 CopyScriptIdx)
{
    try
    {
        proc = ObtainProcess(client_dest_dir_ + copy_scripts_[CopyScriptIdx]);
        proc.EnableRaisingEvents = true;
        //proc.Exited += new EventHandler(CopyExited);
        proc.Start();
        progressBar.Value = ProgressInPercent(copy_scripts_.Count() , CopyScriptIdx);
    }
    catch (Exception ex)
    {
        UpdateControl(this.toolStripStatusLabel1, "Error involving " + copy_scripts_[CopyScriptIdx] + ": " + 
            ex.Message);
        copybutton.BackColor = Color.Red;
    }
    //return;
}

void CopyExited(IAsyncResult iaRes)
{
    AsyncResult result = (AsyncResult)iaRes;
    RunScript caller = (RunScript)result.AsyncDelegate;

    Process senderProcess = (Process)iaRes.AsyncState;
    caller.EndInvoke(iaRes);

    if (++this.nbr_of_copy_exits_ == this.copy_scripts_.Count())
    {
        UpdateControl(toolStripStatusLabel1 , "Copying COMPLETE.");
        copybutton.Enabled = true;   // enable the button now that we're done
    }
    else
    {
        // start the process all over again
        iaRes = caller.BeginInvoke(this.nbr_of_copy_exits_ , new AsyncCallback(CopyExited) , proc);
    }
}

private void UpdateControl(ToolStripStatusLabel tssl , String text)
{
    Invoke((MethodInvoker)delegate
    {
        tssl.Text = text;
        progressBar.Value = 0;
    });

    //tssl.Refresh();  'System.Windows.Forms.ToolStripStatusLabel' does not contain a definition for 'Refresh'...
}

As I stated, there are more elegant implementations using Action delegates which I am sure can be asynchronous. I hope that someone provides an example.

dboarman
That was great, thanks for your work. I tried out your method and unfortunately got the same result. But your method uses things I haven't yet: Begin/EndInvoke, delegate definition, IAsync, etc. I'll use them shortly I'm sure. thanks.
Joe
@Joe: I wasn't sure how close I would be able to get to helping you but I thought it might be worth the effort of trying.
dboarman
It absolutely was. I'll trade a still unsolved little glitch (ie; the app config file is not likely to be changed to begin with, let alone have invalid entries in it) for a new way of doing things anytime. That was good info that I will absolutely use. We 'self-taughts' need to stick together. It hasn't been easy, has it ;)
Joe