0
votes

I am enhancing a c# win forms application that interacts with SalesForce CRM via webservices.

I have the below code to make a 'thread safe' update to a label on my form:

 delegate void SetTextCallback(string text);

 private void SetText(string text)
    {
        // InvokeRequired required compares the thread ID of the
        // calling thread to the thread ID of the creating thread.
        // If these threads are different, it returns true.
        if (this.lblAccessStatus.InvokeRequired)
        {
            SetTextCallback d = new SetTextCallback(SetText);
            this.Invoke(d, new object[] { text });
        }
        else
        {
            this.lblAccessStatus.Text = text;
            this.lblAccessStatus.Refresh();
        }
    }

My form (called SFDetachifier.cs) has a button execute which takes the dates of two calendar controls then calls:

lblAccessStatus.Visible = true;
picProgress.Visible = true;

string fromDate = dtManualFromDate.Value.ToString("yyyy-MM-dd") + fromTime;
string toDate = dtManualToDate.Value.ToString("yyyy-MM-dd") + toTime;
string[] arg = new string[] { "S1", fromDate, toDate };
SFCtrl._SessionLog.Append("Manual detach requested on " + SFOrgName + "\n");
SFCtrl._SessionLog.Append("Date range requested: " + fromDate + " to " + toDate + "\n");

bgProcessing_Production.RunWorkerAsync(arg);

bgProcessing_Production has the below code for the Background worker which includes a call to setText

private void bgProcessing_Production_DoWork(object sender, DoWorkEventArgs e)
    {
        String[] args = (String[])e.Argument;
        e.Result = args[0];

        // clear the datagrid view
        gvTaskCases.DataSource = null;
        //gvTaskCases.Rows.Clear();

        //if (gvTaskCases.Rows.Count != 0)
        //    {
        //    gvTaskCases.Rows.Clear(); // .Update();
        //    }

        SetText("login to SalesForce (on " + SFOrgName + ") ...please wait");

Now, when I run my application I set some dates, then execute the above code by clicking the execute button, it does it's work (calling SalesForce XML web services) and puts the results in a grid.

that all works fine, the problem occurrs when I try to run the procedure again (for example with different dates)

Then I get an error 'Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.'

I don't understand, I have a delegate setup?

What am I doing wrong - I'm guessing that when the backround worker is run again it runs on a new thread, thus being thread unsafe.

How can I fix this error please?

2
Just checking - could the gvTaskCases.DataSource = null; be causing the exception? - C.Evenhuis
Interesting that the control name in the error message is '' - Luc Morin
both good points above, however, ` gvTaskCases.DataSource = null;` is not the problem (although I did get some thread safety issues with that control in the commented code above the call to setText - Our Man in Bananas
BeginInvoke would fix the problem? I don't see why but you may give it a try - Menelaos Vergis

2 Answers

1
votes

You can shorten things up a bit using anonymous delegates like this:

    private void removeGridDS()
    {
        this.gvTaskCases.Invoke((MethodInvoker)delegate
        {
            if (this.gvTaskCases.DataSource != null)
            {
                this.gvTaskCases.DataSource = null;
            }
        });
    }

    private void clear_gvTaskCases()
    {
        this.gvTaskCases.Invoke((MethodInvoker)delegate
        {
            if (this.gvTaskCases.Rows.Count != 0)
            {
                this.gvTaskCases.Rows.Clear();
            }
        });
    }

Now you don't need the hard-coded delegates. Also, you weren't using the passed in bools.

0
votes

here is what I ended up doing in order to get around this issue, as one of the comments above suggested, it looks likely that it's the calls to the control gvTaskCases which is throwing the error so I have created thread safe method calls on that control too:

delegate void SetTextCallback(string text);
delegate void clear_gvTaskCasesCallback(bool clearIt);
delegate void remove_gvTaskCasesDSCallback( bool removeDS);

private void removeGridDS(bool removeDS)
    {
        // InvokeRequired required compares the thread ID of the
        // calling thread to the thread ID of the creating thread.
        // If these threads are different, it returns true.
        if (this.gvTaskCases.InvokeRequired)
        {
        remove_gvTaskCasesDSCallback d = new remove_gvTaskCasesDSCallback(removeGridDS);
        this.Invoke(d, new object[] { removeDS });
        }
        else
        {
            if (this.gvTaskCases.DataSource !=null)
            {
            this.gvTaskCases.DataSource=null;
            }
        }
    }

    private void clear_gvTaskCases(bool clearIt)
    {
    // InvokeRequired required compares the thread ID of the
    // calling thread to the thread ID of the creating thread.
    // If these threads are different, it returns true.
    if (this.gvTaskCases.InvokeRequired)
    {
        clear_gvTaskCasesCallback d = new clear_gvTaskCasesCallback(clear_gvTaskCases);
        this.Invoke(d, new object[] { clearIt });
        }
        else
        {
        if (this.gvTaskCases.Rows.Count != 0)
        {
        this.gvTaskCases.Rows.Clear();
        }
    }
}