-2

I need to invoke a C dll as a background process. The execution of the dll is time-taking and therefore I want to show a progress bar on the main GUI. In order to use ReportProgress, I need to be able pass two arguments by reference and update the progress bar whenever the values of the arguments change. However, RunWorkerAsync only seems to take values (not references).

How can I do it?

Thanks.

Here is the simplified code:

public void button1_Click(object sender, EventArgs e)
    {
       //Variable declarations and initializations

       List<object> arguments = new List<object>();
                arguments.Add(curgen);
                arguments.Add(dataindex);

        backgroundWorker1.RunWorkerAsync(arguments);
        backgroundWorker1.ReportProgress(curgen * 100 / ngen, "GEN");
        backgroundWorker1.ReportProgress(dataindex * 100 / (DIMENSION * FITNESSCASES), "DATA");

    }

 private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        List<object> genericlist = e.Argument as List<object>;
        // Getting variables from object
         calldll.gpinnovization(ref curgen, ref dataindex);
    }

I want to pass 'curgen' and 'dataindex' by reference.

Prometheus
  • 153
  • 3
  • 12
  • 2
    Can you post your code? – Dustin Kingen Apr 05 '13 at 16:13
  • This can of course not work, the code that called RunWorkerAsync() is long gone. The simple workaround is to update the variables in the RunWorkerCompleted event handler. – Hans Passant Apr 05 '13 at 16:31
  • @HansPassant As I understand, RunWorkerAsync() is a different thread. If 'curgen' and 'dataindex' are updated inside gpinnovization(), then ReportProgress should be able to update the progress bar. Please elaborate. – Prometheus Apr 05 '13 at 16:38

2 Answers2

2

You can use a lambda for the DoWork event handler to close over variables (note that closures close over variables, not values, so this will not just copy the value). From there you can either do the work right in the lambda, or pass the variable, by reference, to other methods.

int someValue = 5;

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, args) =>
{
    ProcessValue(ref someValue);
};
worker.RunWorkerAsync();
Servy
  • 202,030
  • 26
  • 332
  • 449
  • But you are not passing anything to RunWorkerAsync. Am I missing something? – Prometheus Apr 05 '13 at 16:31
  • @Sunith Correct, I'm not. I don't see the need to pass anything to `RunWorkerAsync`. Ever. Just close over the values when adding the `DoWork` event handler instead. It's both easier and more effective. – Servy Apr 05 '13 at 16:33
  • If I want to use `someValue` in `ReportProgress`, how should I do it? Where should I put `ProgrssChanged`? – Prometheus Apr 08 '13 at 08:25
  • @Sunith You can add the progress changed event handler just before or after the `DoWork` event handler. When you call `ReportProgress` you can pass a variable via it's parameter which will be accessible in the arguments of `ProgressChanged`. – Servy Apr 08 '13 at 13:45
-2

For the benefit of others, here's how I finally did it. Thanks to Servy for suggesting lambda expressions:

        BackgroundWorker backgroundWorker1 = new BackgroundWorker();
        backgroundWorker1.WorkerReportsProgress = true;
        backgroundWorker1.DoWork += (s, args) =>
            {
                Mydll.MyCfunction(ref curgen, ref dataindex);
            };
        backgroundWorker1.RunWorkerAsync();
        backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;


        while (backgroundWorker1.IsBusy)
        {
            backgroundWorker1.ReportProgress(curgen * 100 / ngen, "GEN");
            backgroundWorker1.ReportProgress(dataindex * 100 / (DIMENSION * FITNESSCASES), "DATA");
            Application.DoEvents();
        }

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e1)
    {
        switch (e1.UserState.ToString())
        {
            case "GEN":
                progressBar1.Value = e1.ProgressPercentage;
                break;
            case "DATA":
                progressBar2.Value = e1.ProgressPercentage;
                break;
        }

    }
Prometheus
  • 153
  • 3
  • 12
  • 1) You're attaching the `ProgressChanged` event handler many times. You should only do it once. 2) Don't use `DoEvents`, particularly not like this. You're not using it properly. – Servy Apr 08 '13 at 13:47
  • @Servy I changed my answer. Is this the correct way? Though it works, I don't know if its reliable. – Prometheus Apr 08 '13 at 14:54
  • Nope, it still has all of the problems I've already outlined. – Servy Apr 08 '13 at 14:55
  • The issue is that `curgen` and `dataindex` are updated inside `Mydll.MyCfunction`. How can I report the progress without using the while(isbusy) loop. – Prometheus Apr 08 '13 at 14:59
  • Start a timer and report progress in the tick event of the timer if it's not possible for you to report progress within the function doing the work. You can close over the same variables when attaching the tick event handler for the timer to ensure they're all accessing the same variables. – Servy Apr 08 '13 at 15:02
  • @Servy Ok, so in the above code, I moved the `ProgressChanged` event handler outside the while(isbusy) loop. This still works. But when I remove `Application.DoEvents()`, it throws a message box saying 'Managed Debugging Assistant DisconnectedContext has detected a problem in ...' – Prometheus Apr 08 '13 at 15:18
  • I will look into tick event (timer class). Though, I believe, I won't be able to show a "true" progress percentage using a timer event. – Prometheus Apr 08 '13 at 15:19
  • `Though, I believe, I won't be able to show a progress percentage using a timer event.` Why not? `timer.Tick += (s,args)=> backgroundWorker1.ReportProgress(dataindex * 100 / (DIMENSION * FITNESSCASES), "DATA");` Was that so hard? – Servy Apr 08 '13 at 15:22
  • I replaced the while loop in the above code with `Timer timer = new Timer(); timer.Tick += (s, args) => backgroundWorker1.ReportProgress(curgen * 100 / ngen, "GEN");`. And now the progress bar doesn't update. – Prometheus Apr 08 '13 at 15:41
  • Did you start the timer, set it's interval, etc.? – Servy Apr 08 '13 at 15:43
  • My bad. Yes, it works now. Can you explain, why using while(isbusy) is wrong. Thanks! – Prometheus Apr 08 '13 at 15:48
  • Because you're blocking the UI thread, preventing it from being able to process requests. You need to be performing non-blocking operations that allow the UI thread to continue processing events while your work is being done. [Using DoEvents is essentially a hack](http://stackoverflow.com/questions/5181777/use-of-application-doevents) that makes it appear to be working, but results in highly fragile an bug-ridden code that breaks in all but the simplest of use cases. – Servy Apr 08 '13 at 15:57
  • Got it. My takehome is not to use DoEvents in future. Thanks again for all the help! – Prometheus Apr 08 '13 at 16:06