none
Cross-thread operation not valid

    Question

  • Please tell me that you are going to fix this before the official release!!

    I am using VB not C++.  I really shouldn't have to care that my code is not running in the same thread as the one that created the form that contains the control that I am trying to update.

    Please fix this or at least have some real examples that show how to update a control on a form.  The examples that I found were not helpful at all.
    Friday, May 13, 2005 6:42 AM

Answers

  • Hi,

    Unfortunately, enabling thread synchronization for controls is a very hard problem to solve--and even the most elegant solutions have considerable drawbacks.  So the bad news is that this behavior is here to stay; the good news is that there is a new control in Whidbey that can be very helpful in these scenarios--the BackgroundWorker.

    If you're not familiar with it, here's a quick walkthrough to aquaint you...
    Create a new Windows Forms Application
    Add a ListBox to the Form
    Add a Button to the Form
    Add a BackgroundWorker to the Form (it's in the Toolbox under "Components")
    Double-click Button1 and add the following code to Form1:


    ''' <summary>
    ''' Tell the worker to fire progress changed events, then start it
    ''' </summary>
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

       With Me.BackgroundWorker1
          .WorkerReportsProgress =
    True
          
    .RunWorkerAsync("C:\temp\bigfile.txt")
       
    End With
    End Sub

    ''' <summary>
    ''' This method is where we do the background work. I'm just reading a file as an example.
    ''' Each time a line is read, we fire a ProgressChanged event and pass the line that was
    ''' read as an argument.
    '''
    ''' Note that we can't update the UI in the DoWork method, but we can fire events ''' and update the UI in the handler(s) for that event.
    ''' </summary>
    Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

       Dim filename As String = e.Argument

       Using reader As New System.IO.StreamReader(filename)
          
    While Not reader.EndOfStream
             
    Me.BackgroundWorker1.ReportProgress(0.0, reader.ReadLine())
          
    End While
       
    End Using
    End Sub

    ''' <summary>
    ''' You can change UI in the ProgressChanged event
    ''' </summary>
    Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
       
    Me.ListBox1.Items.Add(e.UserState)
    End Sub

    ''' <summary>
    ''' You can also update UI in the RunWorkCompletedHandler
    ''' </summary>
    Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
       MsgBox(
    "Done reading the file!")
    End Sub

     



    If you run this code with and pass a large text file you'll see the UI update incrementally (while remaining responsive).

    I hope this helps!

    Joe
    The VB Team

    Friday, May 13, 2005 9:48 PM

All replies

  • Well you really should care what thread is accessing a control. You are not using VB6 now.

    There are hundreds of examples on how to do this correctly.

    Some including:

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnforms/html/winforms06112002.asp

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnforms/html/winforms08162002.asp?frame=true

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnforms/html/winforms01232003.asp?frame=true

    Or have a look at the new BackgroundWorker component.
    Friday, May 13, 2005 10:17 AM
  • Hi,

    Unfortunately, enabling thread synchronization for controls is a very hard problem to solve--and even the most elegant solutions have considerable drawbacks.  So the bad news is that this behavior is here to stay; the good news is that there is a new control in Whidbey that can be very helpful in these scenarios--the BackgroundWorker.

    If you're not familiar with it, here's a quick walkthrough to aquaint you...
    Create a new Windows Forms Application
    Add a ListBox to the Form
    Add a Button to the Form
    Add a BackgroundWorker to the Form (it's in the Toolbox under "Components")
    Double-click Button1 and add the following code to Form1:


    ''' <summary>
    ''' Tell the worker to fire progress changed events, then start it
    ''' </summary>
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

       With Me.BackgroundWorker1
          .WorkerReportsProgress =
    True
          
    .RunWorkerAsync("C:\temp\bigfile.txt")
       
    End With
    End Sub

    ''' <summary>
    ''' This method is where we do the background work. I'm just reading a file as an example.
    ''' Each time a line is read, we fire a ProgressChanged event and pass the line that was
    ''' read as an argument.
    '''
    ''' Note that we can't update the UI in the DoWork method, but we can fire events ''' and update the UI in the handler(s) for that event.
    ''' </summary>
    Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

       Dim filename As String = e.Argument

       Using reader As New System.IO.StreamReader(filename)
          
    While Not reader.EndOfStream
             
    Me.BackgroundWorker1.ReportProgress(0.0, reader.ReadLine())
          
    End While
       
    End Using
    End Sub

    ''' <summary>
    ''' You can change UI in the ProgressChanged event
    ''' </summary>
    Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
       
    Me.ListBox1.Items.Add(e.UserState)
    End Sub

    ''' <summary>
    ''' You can also update UI in the RunWorkCompletedHandler
    ''' </summary>
    Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
       MsgBox(
    "Done reading the file!")
    End Sub

     



    If you run this code with and pass a large text file you'll see the UI update incrementally (while remaining responsive).

    I hope this helps!

    Joe
    The VB Team

    Friday, May 13, 2005 9:48 PM
  • I'm lookin around a bit and and I think I'm just not gettin' it.  I see this reporting progress methods that get handled by events that perhaps run on seperate thread as the thread that issued the progresschange/report method and theres a finish event that probably happens automatically... But if this is a "background" worker, then should other events in the main UI still happen and the rest of the app continue running like nothing's goin on, because I havent acheived that in a clean manner so far, as far as I know.  I expected that the dowork event would run async, and the main app would continue like the dowork isnt processing, and if main app was gonna try to issue the worker to runansync again for another job, then it should probably check the busy state first... instead, the dowork seems to happen and lock everything down until it finishes, and unless I throw a doevents() method in there, the rest of the app's events seem frozen waiting for the dowork event to finish... I figured the whole point was to avoid DoEvents()... the reporting and event complete stuff seems more trivial to me, nice but not the "heart" of the matter... like I said, maybe Im just not gettin it... any help with a reply would be appreciated... thx
    Tuesday, June 21, 2005 3:11 PM
  • Been experimentin more with it, and with the advent of the doevents() in the dowork event, I do seem to get what I want and the completed event is nice... But I started to get the idea that perhaps this object was never meant for what I was trying to do and began to look at other threading code out there... I still was havin a prob, starting a thread (thread object) and gettin it to run like I wanted, here I was coding for framework 2 and my guess is currentthread.sleep method disappeared which may have been what I wanted... so I was not gettin async processing with that object either... I ran across an asynccallback object with a begininvoke method - I was a little lost with it, created a delagate, issued a begininvoke() method and I'd get a routine to run but not asnyc either... I am thinking something has to have changed, because I was sure I did this once a long time ago with the thread object and the start() method, but perhaps then I used a combination of sleep() and doevents() methods to get the job done then too... maybe what Ive been tryin to do is bad practice... I do know in old vb6 programs, the advent of just one single doevents() could mess alot of things up... and since with vb.net, I've tried to code threadsafe but have had to resort to alot of synclock blocks, seems still to be a delicate practice and one that requires alot of careful consideration...  any opinions on this are glady accepted, thx again

    Tuesday, June 21, 2005 4:30 PM
  • OK - this will probably be my last post for whomever might read this, no matter what else I might figure out... After some cross referencing the team system/framework 2 coding with a new sample I started in 2003/1.1 with thread object... I see that thread when started DID run async immediately and easily, using a synclock concept on a queue var for the state, and actually in the 2003 example on a listbox I was filling too (which may be good stuff put in 2.0 that objects are more protected at compile time, when they're bein referenced and shouldnt be where I earlier could not synclock a listbox from team sys/2.0 - anyways,) I was able to achieve after not too long threadsafe code, the state queue var was my way of knowing when end of thread happened, maybe theres better techniques... in my 2.0 example I, at thread-start after recreating the thread using the new thread(addressof dowork) init call to the thread, synclocked the state, cleared it, and enqueued an integer 1... then at thread worker-rtn-end, I synclocked the state queue and enqueued a 2... then in a timer event always goin in main UI, I synclocked the state queue, checked the count was > 0 and if so, peeked the end to see if was 2, if so, I made changes to the UI that were desired knowing now the thread must have finished...  the 1.1 code was simpler, I synlocked the lstbox and never worried about when ended... however the thread logic in both the 1.1 and 2.0 examples were about the same, but ran async for 1.1 environment, not for 2.0 (beta) Ive got... so maybe is a bug that will be fixed by 2.0 production release... or theres somethin new changed or  introduced in 2.0 I'm unfamiliar with, but I havent had successful multithreading in 2.0 without doevents() usage so far... the backgroundworker component seems the easiest way to implement all of these ideas... but again didnt run async for me, and that problem may be related to the 2.0 thread example I mentioned above... Happy coding!
    Tuesday, June 21, 2005 6:18 PM
  • Since you haven't posted any code, it's impossible to tell you where the problems lie, but see if this helps


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim wk As New Worker("The String")
        Dim WkThread As New System.Threading.Thread(AddressOf wk.DoSomeWork)
        AddHandler wk.DataChanged, AddressOf UpdateUI
        WkThread.Start()
      End Sub
      Private Delegate Sub UpdateUIDelegate(ByVal data As String)
      Private Sub UpdateUI(ByVal sender As Object, ByVal args As Worker.WorkerArgs)
        If Me.InvokeRequired Then
          Dim Parms() As Object = {args.Data}
          Me.Invoke(New UpdateUIDelegate(AddressOf ChangeLabel), Parms)
        Else
          ChangeLabel(args.Data)
        End If
      End Sub
      Private Sub ChangeLabel(ByVal s As String)
        Label1.Text = s
      End Sub

    Worker Class:

    Public Class Worker
      Private Data As String
      Public Event DataChanged(ByVal sender As Object, ByVal Args As WorkerArgs)
      Public Sub New(ByVal SomeData As String)
        Data = SomeData
      End Sub
      Public Sub DoSomeWork()
        'simulate some work
        System.Threading.Thread.CurrentThread.Sleep(5000)
        Data &= " with changes made!!"
        RaiseEvent DataChanged(Me, New WorkerArgs(Data))
      End Sub
      Public Class WorkerArgs
        Private TheData As String
        Public Sub New(ByVal Data As String)
          TheData = Data
        End Sub
        Public ReadOnly Property Data() As String
          Get
            Return TheData
          End Get
        End Property
      End Class
    End Class

    Tuesday, June 21, 2005 7:03 PM
  • >Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
       
    Me.ListBox1.Items.Add(e.UserState)
    End Sub<

    Are you sure you don't need to use Invoke here?  In 1.1 the event handler still runs on the thread that raised the event...
    Tuesday, June 21, 2005 7:05 PM
  • Thx for the reply... you did help me get on the right track... and helped me get more familiar with the delagate/invoke technique... I liked that...

    I first dropped the code in 2003, created a project with your code, ran like a charm...

    I then dropped the code in to a 2005 sample project, it worked good there too... So I was wondering what was different except for the invoke part... I wasnt usin a class for my threadwork... So I moved the code into the same main line code and out of the seperate class (Worker)... modified the surrounding code a lil... and it worked that way too...

    I then considered maybe the fact that the simulated work was only doing a sleep, I considered that was giving back cpu, and then doing a quick command later to change the string and all, so I made it add a string to a queue 450000 times in a loop instead of sleep... then I noticed that just that alone would cause some lil pauses in the responsiveness by the main app BUT it would still work, running in the background...

    I discovered soon later this morning after testing that code, figuring theres gotta be somethin else goin on in my code, and sure enough there was, didnt debug it long/good enough... I kept thinking the problem had to be in the thread runnin along with the main line timer tick event goin off every 100 ms...

    The timer tick event was the culprit.  The 2.0 thread was runnin' so fast that it would fill the queue way up or finish entirely within the 100ms... the timer event would see 'em and try to process all of what was in the queue at the start of the tick event, assuming the numbers were smaller and the queue was still growing, it would capture the total size even if the queue had 90000 items in it, start a loop for 1 to 90000 and then try to add each to the main UI listbox 1 at a time, the rest of the app would freeze up during this processing - not the thread's processing...

    So just to verify the concepts, I changed the timer event to run every 10ms instead and only do 1 add at a time to the listbox... Then all was well... But the listbox takes alot longer to fill that way... but... atleast my threads were good.  In a real-world need like this, perhaps theres an addrange method for the listbox that could be utilized but thats another coding issue...

    I'm kinda new to this multithreadin... but I think Im startin' to get it down...

    Thx again for the reply
    Wednesday, June 22, 2005 5:12 PM
  • I wondered about that too, I considered maybe some magic was happening there when using the background component, perhaps it is needed there, but atleast the component is givin us events easy to tap into things, but then invoke/or careful synclock's need to be applied perhaps...

    I have noticed a couple of other differences though...  in 2005/2.0, I could not (if my memory from yesterday serves me correctly,) synclock a boolean,int (something about reference types for those,) or a listbox saying somethin about wrong type or object was created from different thread or somethin... I only tried those mentioned and a queue variable, the queue variable was working for me... in 2003/1.1; however, a listbox I was able to synclock, and then thread code or base main-line code could access it... Im not sure why these things are this way...

    And lastly, another interesting thing I mentioned somewhere above, the sleep method dissappeared... well, its not in the code completion, but when I used the above last code example, it worked with a IDE warning...

    System.Threading.Thread.CurrentThread.Sleep(5000)

    Warning 1 Access of shared member or nested type through an instance; qualifying expression will not be evaluated. ...
    Wednesday, June 22, 2005 5:22 PM
  • I'm kind of new to Windows UI programming, so maybe there's a good reason for this that I'm not aware of this... However.

    Is there a reason why allowing any thread to communicate to the UI, forcing the programmer to use critical sections (mutexes) to do this properly is a bad idea? While the delagate interface is great, it just seems like quite a bit of extra work compared to simply using a critical section to solve the race condition.

    Friday, December 09, 2005 5:38 PM
  • I guess I'm missing something as well... If you have the time I'll pose this problem.

    I'm developing a wrapper library which provides socket data, in Byte() and Text form,
    which is returned via  events raised by my class module. This library is used as part of
    our (US Dept of Commerce) SMTP to ZipFile Message Transfer Agent.
    This event handler is used to capture socket data which is then displayed on a Windows Form to provide monitoing of the MTA processes.

    When using Visual Studio 2003 this approach worked fine. The object (form) which received the event data could simply append the socket text, from the event, to a windows forms textboxes with no problems.  The code is sown below:
    I've left the exception handleing out.. since its not required to see what is being done.

     Private Sub MTAConnection_DataReceived(ByVal StringData As String) Handles MTAConnection.DataReceived

            Dim Pos As Integer
            Dim MSGHeader As String

            Pos = StringData.IndexOf(vbCrLf)

            If Pos > 0 Then
                'not an empty line
                LastDataReceivedAt = Now.UtcNow

                If SERV.Text <> "Service Responding" Then
                    SERV.Text = "Service Responding"
                End If



                Pos = StringData.IndexOf(":")
                Try
                MSGHeader = StringData.Substring(0, Pos)


                Select Case MSGHeader

                      Case "LSOS" 'SMTP Out Queue Start
                        LSOS.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))


                        Case "LSOE" 'SMTP Out Queue End
                            LSOE.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "LSON" 'SMTP MSG Out MSGID
                           LSON.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "LSMT" 'SMTP MSG Out Time
                           LSMT.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "LSMI" 'SMTP MSG In MSGID
                            LSMI.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "LSIT" 'SMTP MSG In Time
                            LSIT.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "LRST" 'Router Queue Started

                            LRST.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "LRET" 'Router Queue Ended
                            LRET.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "LMRT" 'Message Routed Time
                           LMRT.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "LMRO" ' Message Routed MSGID
                          LMRO.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "LZPS" 'Zip Queue Start

                            LZPS.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "LZPE" 'Zip Queue End
                            LZPE.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "LZPN" 'Last MSG Zip
                            LZPN.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "LZPT" 'Last Zip Time
                            LZPT.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "LUNS" 'UnZip Queue Start
                           LUNS.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "LUNE" 'UnZip Queue End
                           LUNE.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "LUNN" 'Last Unzip MSGID
                           LUNN.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))


                        Case "LUNT" 'Last UnZip TIme

                           LUNT.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))

                        Case "DIAG"
                            Dim DiagString As String
                            DiagString = StringData.Substring(0, StringData.Length - 2)
                            DiagString = DiagString.Substring(4, DiagString.Length - 4)

                            If DiagnosticBox.Text.Length > 20000 Then
                                DiagnosticBox.Text = ""
                            End If
                            DiagnosticBox.Text += vbCrLf + DiagString

                        Case "SERV"
                            SERV.Text = StringData.Substring(Pos + 1, StringData.Length() - (Pos + 3))
                            If SERV.Text = "Service Running" Then
                                SERV.ForeColor = Color.Chartreuse

                            Else
                                SERV.ForeColor = Color.Red

                            End If


                    End Select


                Catch ex As Exception



                End Try

            End If



        End Sub

    I'm just starting with Visual Studio 2005 and have had no other problems geting the code to work.

    The events work fine and provide the data and text which can be written to the output window with debug.writeline( ). However, when I try to set the text on the windows form textbox,  an exception is thrown with the following message:

    InvalidOperationException: Cross-thread operation not valid: Control 'TextBox1' accessed from a thread other than the thread it was created on.

    Following your example from your post I added the code below to the Windows Form class along with a backgroundworker from the controlbox. I don't understand why the backgroundworker is unable to update the textbox. I am assuming that the task of the backgroundworker is to accept data from a seperate thread (eventsource) and provide
    a means of updating the UI with the data returned by the events. It looks there is a major
    departure in the way that textboxes and other wondows controls will accept data.
    I'll consult the documentation for 2005 and MSDN knowedge base for an answer.
    But if you have the time any assistance would be greatly appreciated.

    The Exception thown with the backgroundworker is shown here.

    Backgroundworker1  System.InvalidOperationException: Cross-thread operation not valid: Control 'TextBox1' accessed from a thread other than the thread it was created on.
       at System.Windows.Forms.Control.get_Handle()
       at System.Windows.Forms.Control.set_WindowText(String value)
       at System.Windows.Forms.TextBoxBase.set_WindowText(String value)
       at System.Windows.Forms.Control.set_Text(String value)
       at System.Windows.Forms.TextBoxBase.set_Text(String value)
       at System.Windows.Forms.TextBox.set_Text(String value)
       at SocketClientTest.Form1.BackgroundWorker1_ProgressChanged(Object sender, ProgressChangedEventArgs e) in G:\Visual Studio Projects\Visual Basic\SocketClient\SocketClientTest\SocketClientTest\Form1.vb:line 88


    'This is the event handler that accepts the socket data
    Private Sub SSLClient_ClearTextReceived(ByVal ClearTextIn As String) Handles SSLClient.ClearTextReceived
            Debug.WriteLine("TextIn :" + ClearTextIn)

            Newtext += ClearTextIn
            If Me.BackgroundWorker1.IsBusy = True Then

                Exit Sub
            Else
                With Me.BackgroundWorker1
                    .WorkerReportsProgress = True
                    .RunWorkerAsync(Newtext)
                End With



            End If

        End Sub

    Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
           
            BackgroundWorker1.ReportProgress(100, e.Argument)

            Newtext = ""

        End Sub

    'This is where I am trying to set the textbox text.
        Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged

            Try
                Debug.WriteLine("UserState " + e.UserState) 'Text is OK Here in Outout Window
                Dim TextString As String = e.UserState.ToString()
                TextBox1.Text += TextString 'This is where exception occurs

            Catch ex As Exception
                Debug.WriteLine( ex.ToString())


            End Try

        End Sub



    Thanks much,

    Bruce Stone


    Sunday, February 19, 2006 11:10 PM
  • Bruce,

    Hey man, thanks a million for being a trooper and trying your best to work your way through this.  I know that working through threading issues is a hard concept to grasp -- particularly if you come from a vb6 background where threads just weren't an issue -- but also not an option.  That's right, in VB6, you didn't have the option to run code asynchronously. Even calling the Windows API to create a thread wouldn't produce a truely asynchronous execution from VB6.  So, giving Threading to VB users was a challenge -- not only because of the complexity involved in providing the feature, but also in providing support to those who aren't used to dealing with the additional need.

    So here goes.  First off, while your attempt to use the background worker process is a valiant one, it unfortunately isn't what you want to use in your instance.  You see, the backgroundworker component is meant to kick off asynchronous processes and then update the UI from events received from the thread. Since your socket code is already running on another thread (know it or not), there is no need to set up the background worker every time you want to update the UI.  In fact, your work is nearly finished!

    Here's what I recommend.  First, create a delegate on your form called UpdateTextHandler. If you aren't used to delegates, don't panic!  I'll get you through this.  To add the delegate, add the following code under your Form declaration:

    Delegate Sub UpdateTextHandler( ByVal message as String )

    Second, add  a method to your form called UpdateText as follows:

    Public Sub UpdateText( ByVal message as String )
         Me.LSOS.Text += message
    End Sub

    Lastly, update the  your MTAConnection_DataReceived method. Change any calls to update your LSOE textbox as follows:

    Case "LSOE" 'SMTP Out Queue End
     Me.Invoke( New UpdateTextHandler( _
     
    AddressOf UpdateText ), new Object() _
     { StringData.Substring( Pos + 1, _
     StringData.Length() - (Pos + 3)) } )

    You can do updates to different textboxes a million ways but let's move on for now.  Does this seems complicated?  OK. Let me explain this madness.  For ease of explanation, think of a delegate as a placeholder for a method.  A delegate allows you to connect to any method that matches the "signature" the delegate has. So, the UpdateTextHandler and UpdateLsoeText has the same signature.  I can create a new UpdateTextHandler delegate instance by passing in the addressof the method I want to execute.  Since the method has parameters, I have to pass those in as well.  That's why I have the object array after the handler I can use the object array to pass in the value to the delegate as though I was passing it to the method itself.

    So how do you update different text boxes?  Here goes.  Take ALL of the code from your DataReceived event handler, and put it into your UpdateText method.  Inside your DataReceived event handler add the following code:

    Me.Invoke( New UpdateTextHandler( _
       AddressOf UpdateText ), _
       New Object() { StringData } )

    That should be it!  If you still don't understand what is going on, drop me a line and I'll be glad to help.

    HTH,

    Tobin

    Wednesday, April 19, 2006 6:19 AM
  •  

    Hi,

     

    I’m facing some problems related to data binding. I have added a binding to a textbox.

    (txtValue.DataBindings.Add ("Text", variable, "Value") ;)

     

    The variable object implements INotifyPropertyChanged. The variable value is changed in the background by a timer (later this is a real engine value). The variable change fires a PropertyChanged event.

     

    This event causes an exception “Cross-thread operation not valid: Control 'txtValue' accessed from a thread other than the thread it was created on.”

     

    How can I invoke the event on the thread the control was created?

     

    I tried to troubleshoot using the following MSDN link,

    http://msdn2.microsoft.com/en-us/library/ms171728(VS.80).aspx

    but it doen’t not solve my problem.

     

    More Details:

    I have bound the value property of a variable object using data binding as follows

     

    txtValue.DataBindings.Add("Text", variable, "Value");

     

    The PropertyChanged event is raised when the value in the engine has changed.

     

    /// <summary>

    /// Raises the <see cref="PropertyChanged"/> event.

    /// </summary>       

    protected void OnPropertyChanged(object sender, PropertyChangedEventArgs args)

            {

                _hasModifications = true;

     

                if (this.PropertyChanged != null)

                {

    foreach (PropertyChangedEventHandler del in this.PropertyChanged.GetInvocationList())

                    {

                        try

                        {

                            del.Invoke(sender, args);

                        }

                        catch

                        {

                            this.PropertyChanged -= del;

                        }

                    }

                }

            }

     

    How can I invoke the delegate on the txtValue control? In detail how can I get the control reference when I only have the delegate?

     

    Thanks in advance,

    Vaishu

     

     

    Tuesday, May 02, 2006 11:16 AM
  • or, you could just set

    ystem.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false

    and all your cross threading problems go away.

     

    Friday, June 09, 2006 8:48 AM
  • I certainly wouldn't deduce that all of your cross threading problems go away. 

    When a thread accesses a control on another thread, you can end up with highly undesired results.  That's the whole purpose of throwing the exception in the first place.  Setting this property will prevent the exception from being thrown, but your unpredictible results are still going to exist.  Unless you ABSOLUTELY know the context in which your control is being updated by other threads and can guarantee your results otherwise, I would highly recommend against using this property -- particularly in commercial applications where the hardware architecture is unknown.

    Friday, June 09, 2006 6:46 PM
  • Code like the following is throwing the Cross-thread exception even though I'm not changing the value of the the control:

    uint drive = (uint)Enum.Parse(typeof(drive_letter), (string)cbDrives.SelectedItem);

     

    Monday, July 24, 2006 4:06 PM
  • Have you tried invoking that code rather than using a direct call?

    Monday, July 24, 2006 4:13 PM
  • I'm not sure what you mean by "invoking" versus a "direct call".

    Monday, July 24, 2006 6:05 PM
  • Invoking means that you force execution of the desired call onto the UI thread.  When you (or one of the components you are using) spins up another thread, you cannot directly access the UI from that secondary (worker) thread.  You have to access force that access through the Invoke() method of the control class.  Since the Form object that houses your controls is a control itself, you can force execution of your SelectedItem code onto the UI thread by using "this.Invoke".  Using the line of code you provided, I wrote up a quick sample to demonstrate this for you:

    using System;
    using System.Windows.Forms;
    using System.Threading;
    
    namespace CrossThreadExample
    {
        public enum drive_letter : uint {
            A,B,C,D,E,F,G
        }
    
        delegate uint GetDriveHandler();
    
        public partial class Form1 : Form
        {
            public Form1() {
                InitializeComponent();
            }
    
            private void btnGo_Click(object sender, EventArgs e)  {
                Thread t = new Thread(new ThreadStart(Execute));
                t.Start();
            }
    
            private void Execute() {
                uint drive = (uint)this.Invoke(new GetDriveHandler(GetDriveNum));
                MessageBox.Show(drive.ToString());
            }
    
            private uint GetDriveNum() {
                return (uint)Enum.Parse(typeof(drive_letter), (string)cbDrives.SelectedItem);
            }
        }
    }
    

    So, I just created a form with a ComboBox named cbDrives that contained the values A,B,C,D,E,F, and G. I added a button named btnGo that will spin up a new thread and execute my desired code in that new thread.  I then created a method called "GetDriveNum". That method contains the code that get's the UInt value of the selected item.  Lastly, I created a method called "Execute" that will execute the code in a new thread.

    Notice in the Execute method that I call this.Invoke and pass it in an instance of the delegate I defined above the form called GetDriveHandler.  By using this.Invoke, I'm telling the runtime to execute the delegate on the thread of the form instead of the thread I'm working in.

    Obviously your sample will be a bit more involved, but this should get you started.  Let me know if you have any other questions.

    Tobin Titus

    Monday, July 24, 2006 8:40 PM
  • Thank you Tobin for demonstrating how to use the Me.Invoke() method to avoid the "cross-thread operation not valid" exception.  In particularl, I found your 19 Apr 2006, 6:19 AM UTC post very helpful.  I had a VS.NET 2003 application that starts a thread on a form using the following code:

    Private Const THREAD_WAIT_MSEC As Integer = 150
    Private
    mThread As Threading.Thread

    Private Sub ExecuteSqlScriptFile()
        mThread = New Threading.Thread(AddressOf
    ExecuteSqlScriptFileThread)
        mThread.Start()

        Do While mThread.ThreadState = Threading.ThreadState.Running OrElse _
                
    mThread.ThreadState = Threading.ThreadState.AbortRequested OrElse _
                 mThread.ThreadState = Threading.ThreadState.WaitSleepJoin OrElse _
                 mThread.ThreadState = Threading.ThreadState.Suspended OrElse
    _
                 
    mThread.ThreadState = Threading.ThreadState.SuspendRequested
            Try
                If mThread.Join(THREAD_WAIT_MSEC)
    Then
                    ' The Join succeeded, meaning the thread has finished running
                    Exit
    Do
                End
    If
            Catch ex As
    Exception
                ' Error joining thread; this can happen if the thread is trying to abort, so I believe we can ignore the error
                Exit
    Do
            End
    Try
        Loop
    End
    Sub

    Private Sub ExecuteSqlScriptFileThread()
        Try
            mMTSAutomationSuccess = mMTSAutomation.ExecuteScriptStart(mSqlServerLoginOptions, mScriptExecutionOptions)
        Catch ex As
    Exception
            System.Windows.Forms.MessageBox.Show("Error in ExecuteSqlScriptFileThread: " & ex.Message, "Error"
    , MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
        End
    Try
    End Sub

    The mMTSAutomation class executes a script across several Sql Server databases.  The class raises events while updating the databases and my form catches the events and updates progress bars and status labels as needed.  When I ported the code to VS.NET 2005 using the Visual Studio Conversion Wizard, it seemed to run fine until the form tried to update the progress bars when the events fired, at which point I received the "cross-thread" error.  Switching to use Me.Invoke() in each of the event handler subs on the form worked perfectly.  For example:

    Private Delegate Sub ProgressUpdateHandler(ByVal taskDescription As String, ByVal percentComplete As Single)

    Private Sub ProgressUpdate(ByVal taskDescription As String, ByVal percentComplete As Single)
        lblProgress.Text = taskDescription
        pbarProgress.Value = percentComplete
    End Sub

    Private Sub mMTSAutomation_ProgressChanged(ByVal taskDescription As String, ByVal percentComplete As Single) Handles mMTSAutomation.ProgressChanged
        Me.Invoke(New ProgressUpdateHandler(AddressOf ProgressUpdate), New Object
    () {taskDescription, percentComplete})
    End Sub

    Hopefully this will also be more stable when debugging.  In particular, I found that while debugging in VS 2003 the IDE would sometimes hang for about 30 seconds, at which point I'd get an error saying something like "The thread was stopping" even though the thread wasn't supposed to be ending at that time.  The other mitigating factor that probably didn't help is that the class running in the thread is calling SQL-DMO, which is a COM object.  I'm switching to SQL SMO, which is why I had to port the class to VS 2005 (SQL Management Objects isn't supported in VS 2003).

    One question: any advice on what the appropriate method is for waiting for the thread to end (or an alternate method of starting the thread and waiting for it to end)?  As shown above, I'm calling mThread.Start() then using a Do loop that monitors mThread.ThreadState and calls mThread.Join every 150 milliseconds.

    Thanks again,
    Matt

    Tuesday, July 25, 2006 2:58 AM
  • Whomever posted this last post.  Thanks.

    System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false does exactly what I need it to do and I don't have to create a bunch of delegates and other glue code to update a text box!

     

    THANKS!!!!

     

     

    Monday, November 20, 2006 1:33 AM
  • thats bad practice. you REALLY should go through the proper approach. Otherwise you will have some undeseriable effects. The whole invoking and enforcing invoking was made for a reason.
    Monday, November 20, 2006 1:37 AM
  • Setting this property may indeed appear to give you exactly what you want, but I would caution you against using it and against recommending this to others.  Threading issues can be a lot more complex than some would try to make them. Setting this property to false can cause some very strange behavior, particularly when called from VB.NET code.  For example, by nature when you create another thread and allow cross-thread updates to the UI, you allow both the user and the thread access to the controls at the same time.  This isn't a problem for some applications, but it is for many.  Additionally, VB.NET has some interesting functionality dealing with auto-generating forms at run time if one doesn't exist.  I've seen instances where a user tried to execute code on the worker thread, but instead of updating the primary thread's UI, it created a secondary form instance, updated it, then destroyed the form in the background.  Other problems could occur when destroying the form. Lets say your application starts on a primary thread then launches a form.  From that form, you kick off a worker thread that will return and update the UI.  What happens if the user closes the form that launched the thread?  The thread doesn't die and it tries to update it.  This will thrown an exception.

    Once again, this doesn't apply to everyone -- and that's the reason for the boolean value that allows you to turn off cross thread update checking.  However, this property should be looked at as the very minor exception to a very major rule that you should "invoke" your updates to the UI. 

    Monday, November 20, 2006 4:18 PM
  • Thanks a bunch, blomm.

    Frankly, sometimes you just don't have the time to "do what you really should do" in code. There are hot deadlines, families to feed, and a whole list of other reasons one would want to skip the "right way" to do things and just get it done.

    This is assuming that one understands the implications of skipping "the right way".

    There are likely millions of lines of code out there running in vertical and horizontal markets that use cross-threaded calls on Windows controls and they will never be changed, nor will they every be a problem.

    Yes, we OUGHT to code everthing the right way, but, in all seriousness, sometimes its just not a viable option.

    Curtis
    Monday, January 15, 2007 8:19 PM
  • Tobin,

    Your solution for Bruce is great.  Simple, and it works.

    For C# types, here's how I translated it to C#.  I used different names, but it works the same.  The label on my form is called lblStatus. 

    In my situation, the label is on the form, and therefore in the main thread.  I have an event that is triggered when a new email arrives in Outlook.  When the event is triggered, it automatically opens a new thread.  This code changes the text in the label from that new thread:

    Under the form declaration:

    public delegate void UpdateTextHandler(string mymessage);

    Add this method to the form code:

    private void UpdateText(string mymessage)
      {
        lblStatus.Text = mymessage;
      }

    To call the UpdateText method through the delegate from a method in any thread (where "Goodbye Again" is the new text for the label):

      this.Invoke(new UpdateTextHandler(UpdateText), new object[1] { "Goodbye Again" });

    That's it.

    Jon

     

    Tuesday, February 06, 2007 8:28 PM
  • Stupid question, maybe, but...

    Doesn't Windows know which thread owns a particular UI element?  If so, why can't Windows simply direct all UI commands to the owning thread, rather than the window itself?  Granted, that doesn't help us programmers out now, but it seems like this is something Microsoft could easily fix.

    Maybe I'm just showing my ignorance of operating system design -- I'm just a VB programmer.  I know how to use multithreading properly but I find it annoying and I can't help wondering why this problem hasn't been fixed... otherwise, if it can't be fixed easily for some reason or other, I'd be very interested to know why, just because I'm interested in these sorts of things.
    Wednesday, February 07, 2007 11:35 PM
  • Joe_MS, thanks for the code.  It is very helpful.  However, the UI is completely and totally unresposive in this application.  There are several problems:

    1. The list box's wm_paint is low priority, so you gotta call listbox.update or else you don't see anything happening.  I suppose if it ever got a time slice of the CPU, it could paint, and maybe it does on a multi-proc system, but it doesn't on MY dual core in which 1 core is running another app at 100% CPU.  Thus, this program is broken.

    2. I think because it is doing disk access, it is something windows doesn't handle well.  It seems like it doesn't know when one app is hogging all resources, since it is most using the disk, and not the CPU, so the SO keeps on giving it more and more time, when it shouldn't.  And maybe this is why the example doesn't work.  Maybe it is working, but the OS isn't, due to the vast amount of disk access occurring.

     

    Friday, February 09, 2007 4:06 PM
  • Try this - Not sure if it is EXACTLY what you are looking for but does solve the thread deadlock problem when accessing threads from threads they were not created on.

    Friend Sub ActivateModule
        If Me.InvokeRequired Then
            Me.Invoke(New MethodInvoker(AddressOf activateModule))
        Else
            Me.Activate()
        End If
    End Sub

    HTH
    Brendon

    Tuesday, March 27, 2007 11:02 AM
  • Bad  bad bad naughty developer! Doing this can and will lead to instability in the application. Small home built apps sure by all means as you can easily fix it IF it happens... For a while I was doing this in an enterprise application and the amount of crashes and stability issues that arose made me want to die and curl under a rock...

    CheckForCrossThreadIllegalCalls was a good intention IMHO - bad bad programming in practise...
    Tuesday, March 27, 2007 11:12 AM
  • Try this:

    System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;


    As always the only way to bypass MS bugs is to bypass them

    Saturday, May 12, 2007 10:52 AM
  • Blomm, shutting the warning off makes the problem go away?  Do pain killers make the injury go away?  No.  Leave the warning on, so it tells you when you're being stupid, so you know there's a problem to fix.

     

     

    Thursday, May 17, 2007 3:11 PM
  • thanks Zytan,

    you're genius is incomparable (except when compared to every other person who's said the same thing already.)

    Thursday, May 17, 2007 3:20 PM
  • Man, I have no idea what happened.  I got one of these stupid alerts from MS, and it got sent to the wrong email address.  But this time it got sent to the right email address, and I didn't even change anything.  The alert was an older post, not from blomm, and I clicked on it to reply to this post, which I shouldn't be replying to, since it was old, and it showed me YOUR old post, so I replied to something you said years ago.  Sorry.  If the link they gave me merely went to the most recent unread post, like every other forum in existance, the post I seen wouldn't have been yours, but the recent post that actually deserved attention.  Wow.
    Thursday, May 17, 2007 3:33 PM
  • And if you click on the thread topic, it opens up a new window.  And once you post, it asks something about alerts, and if you click OK, it goes to a 404 page.
    Thursday, May 17, 2007 3:35 PM
  • It would be even better to use "BeginInvoke" to asynchronously invoke the call to the function.

    Imagine you have to update large controls (i.e. DataGrids) then the time loading the Controls content your UI is NOT responsive. That means even if you have multithreading (doing long-running tasks in the background away from you UI) it could be possible that your UI does not respond. Just place a clock on your form (label and timer) then you'll probably see that the clock is hanging when you just Invoke a long-running method.

     

    I always use BeginInvoke.. my construct looks like this:

    I'm using .NET 3.0 already, so my call looks a little different than it was in 2.0

     

    Code Snippet

    private void setButtonEnabled(Button btn, bool enabled)

    {

       if (!this.Dispatcher.CheckAccess())

       {

          this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new setButtonEnabledDelegate(setButtonEnabled), btn, new object[] { enabled });

          return;

       }

       btn.IsEnabled = enabled;

    }

     

     

    Regards

    Sven

    Thursday, May 24, 2007 11:06 AM
  • Shaper, I do not follow everything you are saying, but you raised a point that interests me:

     

    If you want the GUI to reveal your current data, and you use BeginInvoke, then that means the actual call is delayed in the (hopefully) GUI thread.  That means by the time the code is run, the data could have changed.  If the original call to update the GUI had the data locked, it may no longer be locked, since BeginInvoke returns right away, and the actual call is delayed.

     

    How doy you solve this?

    Thursday, May 24, 2007 9:24 PM
  • This doesn't seem to work if you have the socket wrapped up in a class, as classes don't support the Invoke method.

    Is there a way of doing this in a class - I think putting everything in the main form gets a little messy.
    Tuesday, May 29, 2007 8:28 PM
  • The class only needs a reference back to a delegate.  Since the UI update would involve the form, where else would you put the code other than on the form?  Since your form likely creates the socket-wrapping class, you could pass a reference to a delegate in the constructor of your wrapper class. From that class, you could then call invoke on the passed delegate at any point to update the form.  Seems this has been hashed over many times in the past year. However, I'm willing to help you if you have something specific. Contact me via my blog at http://blogs.iis.net/tobintitus

    Tuesday, May 29, 2007 8:38 PM
  • This "fix" worked in a C# code example I downloaded. I do not code much in Windows (console apps or Unix) is my forte. Thanks.

     

     

    Thursday, July 26, 2007 5:06 PM
  • thank you soooooooo much, that is EXACTLY what i was looking for 

     

    Thursday, September 20, 2007 4:50 AM
  • Blomm.. your solution saved me a giant set of headaches.  Just trying to do a quick port from VC++ 2003 to 2005.  Thanks!

    FYI, for C++, the line looks like:
    System::Windows::Forms::Control::CheckForIllegalCrossThreadCalls = false;

    Greg
    Tuesday, September 25, 2007 10:56 PM
  •  

    sorry i didnt read the articles about... cant really understand it...

     

    i also encountered the exception...

     

    my situation is... i have this datagridview with databindingsource... i want my datagridview to check the database every 5 seconds... so i used a timer for this... and inside the handler for the event i want to create a thread that will will execute a stored procedure and return me some data... but i cant do this coz delegates does not return any value... and if i try to access the databindingsource.datasource = value i got by executing the sp... here i get the exception...

     

    im new to threading... i hope you could shed some light...

    Tuesday, October 09, 2007 2:45 AM
  • Please read the entire thread and the solution for an answer. In addition, it is really inefficient to check the database every 5 seconds to get the latest results, performance decrease and can also cause the UI not to appear to respond at times and the user will be annoyed at this. Accessing databases is expensive and should be used efficiently only when required.

    Tuesday, October 09, 2007 8:15 AM
  • Unfortinatelly, in some class imposible send reference to delegate. I also have socket-wrapper and it can be used both from ASP.NET and WinForm application. So, only possible reference type for me is object (or creates to different constructor with reference parament for ASP.NET and WinForms). All what I excatly need to get reference to UI control from delegate.

    E.g. like this:
     if (this.DataReceived != null)
                        {
                            foreach (EventHandler<SocketTraceEventArgs> del in this.DataReceived.GetInvocationList())
                            {
                                if (del.Target is Windows.Forms.Control)
                                {
                                    if ((del.Target as Windows.Forms.Control).InvokeRequired)
                                    {
                                        (del.Target as Windows.Forms.Control).Invoke(del, this, new SocketTraceEventArgs(m));
                                    }
                                }
                                else
                                {
                                    del(this, new SocketTraceEventArgs(m));
                                }
                            }

                        }

    Maybe not intelligent, but works properly..


    Saturday, October 13, 2007 8:19 PM
  • Quasi-religious baloney.  I've got a form that's supposed to show the progress of some long running calculations.  Someone in their infinite wisdom decided that no other messages would be processed while my button click event is running.  So I have to work around.  If I minimize and restore, my UI updates turn into an apparent freeze.

     

    My need for a sub-thread is only because somebody else didn't handle it in the development tool.  I am perfectly capable of disabling the controls that shouldn't be invoked while I am processing.  I want the sub-thread to have access to user input.

     

    Wednesday, December 05, 2007 10:22 PM
  •  

    Worked for me.  Thanks blomm!
    Tuesday, December 25, 2007 11:04 PM
  •  

    Thanks Tobin, great answer. Helped me a ton.

     

    Anyone know how much this will slow down my app, say if I am writing a very active chat room. And every time something needs to be written to the chatbox I need to invoke a method?

     

    Thanks.

    Monday, January 07, 2008 7:39 PM
  • I had the same problem working with offline application block. This solved all my cross threading errors i was receiving. you saved me blomm. thx

    Sunday, February 03, 2008 7:37 PM
  •  blomm wrote:
    or, you could just set

    ystem.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false

    and all your cross threading problems go away.

     

     

    You're a genius

    Tuesday, April 08, 2008 1:52 PM
  • Nobody has refuted this genius yet on this page.

    Setting CheckForIllegalCrossThreadCalls = False is simply ignoring the problem by allowing non thread safe  code, as has been stated many times already. The warning is there to save you hours of debugging when you get reports of intermittant bugs that you can't reproduce. Keep the check on, and code using the recommended practice.

    http://msdn2.microsoft.com/en-us/magazine/cc300429.aspx
    Tuesday, April 08, 2008 2:30 PM
  • In C#.Net,  You can use this invoked procedure if you want to set the value for control on running thread without cross thread problems...

     

    delegate void SetControlValueCallback(Control oControl, string propName, object propValue); //Leo Delegate

    private void SetControlPropertyValue(Control oControl, string propName, object propValue)

    {

    if (oControl.InvokeRequired)

    {

    SetControlValueCallback d = new SetControlValueCallback(SetControlPropertyValue);

    oControl.Invoke(d, new object[] { oControl, propName, propValue });

    }

    else

    {

    Type t = oControl.GetType();

    System.Reflection.PropertyInfo[] props = t.GetProperties();

    foreach (System.Reflection.PropertyInfo p in props)

    {

    if (p.Name.ToUpper() == propName.ToUpper())

    {

    p.SetValue(oControl, propValue, null);

    }

    }

    }

    }

     

    In Vb.Net 

    Public Sub SetControlPropertyValue(ByVal oControl As Control, ByVal propName As String, ByVal propValue As Object)

    If (oControl.InvokeRequired) Then

    Dim d As SetControlValueCallback = New SetControlValueCallback(AddressOf SetControlPropertyValue)

    oControl.Invoke(d, New Object() {oControl, propName, propValue})

    Else

    Dim t As Type = oControl.GetType()

    Dim props As System.Reflection.PropertyInfo() = t.GetProperties()

    'System.Reflection.PropertyInfo(props = t.GetProperties())

    For Each p As System.Reflection.PropertyInfo In props ' System.Reflection.PropertyInfo p in probs

    If p.Name.ToUpper() = propName.ToUpper() Then

    p.SetValue(oControl, propValue, Nothing)

    End If

    Next

    'foreach (System.Reflection.PropertyInfo p in props

    End If

    End Sub

     

     

    Regards,

    Antony Leo Edel

    Friday, April 18, 2008 9:22 AM
  •  jo0ls wrote:
    Nobody has refuted this genius yet on this page.

    Setting CheckForIllegalCrossThreadCalls = False is simply ignoring the problem by allowing non thread safe  code, as has been stated many times already. The warning is there to save you hours of debugging when you get reports of intermittant bugs that you can't reproduce. Keep the check on, and code using the recommended practice.

    http://msdn2.microsoft.com/en-us/magazine/cc300429.aspx

     

    Agreed...

     

    Just ignoring the problem in your code because VB6 hid all these details from you (and didn't let you thread at all) doesn't make your application "suddenly work", it just hides a serious existing code problem that will come back to bite you and be almost impossible to track down.

     

    I've had first hand experiance of the random behaviour of forms and cross threaded operations messing with them in a production application. It took the move to Visual Studio 2005 to start pointing out the problems we were having as the code "worked fine on my machine", but the threads were messing up the main form and causing the application to "vanish" in full scale runs on customers machines.

     

    Don't turn this flag off, use it to make yourself write better code. i know it's a pain, but thats just the way windows forms work.

    Friday, April 18, 2008 11:59 AM
  • I'm pretty new at this multi-threaded application writing.  I ran into the illegal cross thread error as everyone else did and stumbled upon this discussion.  I tried CheckForIllegalCrossThreadCalls = False and it did get eliminate the error message the application was raising.  I thought I was home free without having to learn how to do threading properly.  Well, I left my little application running because I forgot to terminate it.  I was using IE and surfing the web, and I couldn't figure out for the life of me why IE was running so erratic.  I was having trouble downloading.  It was moving slow.  It appeared to be locked up when opening links in new windows.  Like I said, it was acting very strange.  Then I realized my little app was still running, and I closed it.  IE went back to normal operation.  So, for anyone thinking that ignoring the problem is a good thing, you are only kidding yourself (like I was) and asking for system instability.

    Friday, May 23, 2008 5:00 PM
  • Why could there not be thread safe control where each property was already enclosed in a isinvokerequired block?

    That would be the best of both worlds.

     

    Wednesday, May 28, 2008 4:16 PM
  • for Use Global controls in thread.

    StringBuilder sb = new StringBuilder();

    public Form1()

    {}
    ......................

    private
    delegate void dlgInvoke(Control c,string str);

    private void Invoke(Control c,string str)

    {

    if (c.InvokeRequired)

    {

    this.Invoke(new dlgInvoke(Invoke), c,str);

    }

    else

    {

    c.Text = str;

    }

    }


    void threadmethodOne()
    {
    Invoke(Label1,"Sample");

    }

    void threadmethodtwo()
    {
    sb.Append("Values bla bla bla"+"\r\n")
    Invoke(textbox1,sb.ToString());

    }


    void threadmethodthree()
    {
    Invoke(this,"your name");

    }

    Is possible?

     



     

    Thursday, July 17, 2008 10:03 PM
  • Thanks for the great info Tobin!

    This is exactly what I needed to see. I followed your directions and my software works correctly now.


    Thursday, July 24, 2008 9:17 PM
  • I am guilty of turning off the Illegal Thread Calls property off when I wanted to do simple tasks. However, I forced my way to learn the correct way and I became very frustrated at times with how little examples there are for very simple tasks, written in VB 2005 or higher. Most examples were in C# or old VB 6 code. Even then, the examples were even not simple enough for some of us. Hopefully someone will benefit from this. So here goes:

    The goal of this code is to update a label's text from a different thread. We can't do this directly because we will throw the "Cross-threaded operation not valid" exception.

    First, begin by creating a new project and switch to the code view for Form1. I created one button, called Button1, and one label, called Label1. Note that those were the names automatically assigned; therefore, if your's are named differently then you need to change that in all of the code examples. Here is the code that will throw the exception...
    Imports System.Threading  
     
    Public Class Form1  
     
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click  
            'We want to do some work in the background... so create the thread to do that work now.  
            Dim NewThread As New Thread(AddressOf Counter1)  
            NewThread.Start()  
     
            'Now we can continue with anything else. The code that follows will be executed  
            'after the NewThread.Start line... WITHOUT waiting for the thread (that is, the  
            'thread that NewThread.Start 'started') to finish...  
     
            '  
            ' Anything else code...  
            '  
        End Sub  
     
     
        'Illegal, VS won't let you do this. One thread is accessing 'something'  
        'from another thread. The 'something' in our case is the Label1.  
        Private Sub Counter1()  
            Dim index As Integer  
     
            For index = 0 To 10000  
                'Try to update the label...  
                Me.Label1.Text = index.ToString  
            Next  
        End Sub  
    End Class 
    Run the above code and you will see the exception being thrown.


    Now, lets try the example with turning off the IllegalCrossThreaded property that so many of us do... Here is all of the code:
    Imports System.Threading  
     
    Public Class Form1  
     
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click  
            'We want to do some work in the background... so create the thread to do that work now.  
            Dim NewThread As New Thread(AddressOf Counter2)  
            NewThread.Start()  
     
            'Now we can continue with anything else. The code that follows will be executed  
            'after the NewThread.Start line... WITHOUT waiting for the thread (that is, the  
            'thread that NewThread.Start 'started') to finish...  
     
            '  
            ' Anything else code...  
            '  
        End Sub  
     
        'Works... but bad practice. This could cause serious problems in applications with  
        'multiple forms and/or multiple threads and etc... could just cause erratci behavior  
        '***Could, but not always... and could even cause that erratic behavior with applications  
        'outside of your own.  
        Private Sub Counter2()  
            Dim index As Integer  
     
            For index = 0 To 10000  
                System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False 
                Me.Label1.Text = index.ToString  
                System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = True 
            Next  
        End Sub  
    End Class 
    If you run the above code, it will work... But as argued this is very bad practice. I am not going to get into that, whether you choose to use the above method or the safe below method is your choice.


    Now... here is the final and "thread-safe" way to accomplish all of this. Code:
    Imports System.Threading  
     
    Public Class Form1  
     
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click  
            'We want to do some work in the background... so create the thread to do that work now.  
            Dim NewThread As New Thread(AddressOf Counter3)  
            NewThread.Start()  
     
            'Now we can continue with anything else. The code that follows will be executed  
            'after the NewThread.Start line... WITHOUT waiting for the thread (that is, the  
            'thread that NewThread.Start 'started') to finish...  
     
            '  
            ' Anything else code...  
            '  
        End Sub  
     
        'Cross-thread safe solution:  
        Private Delegate Sub LabelUpdater(ByVal value As Integer) 'This declares a type  
        'of which will be used next...   
        Private TempFuncCall As New LabelUpdater(AddressOf UpdateLabel) 'This basically  
        'declares a variable that holds an address to a function. (pointer to a function  
        'for you C/C++ coders.  
     
        Private Sub Counter3()  
            Dim index As Integer  
     
            For index = 0 To 10000  
                'I am using BeginInvoke to call the method Asynchronously...  
                TempFuncCall.BeginInvoke(index, Nothing, Nothing)  
                'Nothing and Nothing was used because I want to make this as simple  
                'as possible so we are simply "Not Careing" what those parameters do.  
                Thread.Sleep(10) 'Lets sleep for ten milliseconds so we can actually see  
                'the label update... NOTE!! THIS IS NOT REQUIRED: You can remove the line  
                'but the label will update so fast you will only see a few changes (depending  
                'on the speed of your computer)  
            Next  
        End Sub  
        'Finally, the method that makes this all possible... and really the only *extra* code  
        'required for "thread-safety"  
        Private Sub UpdateLabel(ByVal value As Integer)  
            If Me.InvokeRequired Then 'make sure we are on the correct thread.  
                Dim ReFuncCall As New LabelUpdater(AddressOf UpdateLabel)  
                Me.Invoke(ReFuncCall, New Object() {value}) 'Invoke it again if we aren't  
            Else  
                Me.Label1.Text = value.ToString 'Or update if we are...  
            End If  
        End Sub  
    End Class 
    If you run the above code you should get the expected results without doing anything illegally and your form will be "thread-safe".


    I am sure this is not the only way to do this. Its just one way and the simplest way I could think of to demonstrate. You could also use the BackgroundWorker but if you are stubburn like me and want to learn to use threads and delegates correctly, hopefully this will get you started.
    "Nothing Ventured, Nothing Gained"
    Thursday, August 07, 2008 3:49 PM
  • Thanks for the code it worked a treat :-)
    Friday, September 05, 2008 12:53 PM
  • Thanks Andrew.  What a great example!  I like the way you stepped through the 3 senarios.  I have been struggling with this for a while and it all makes sense to me now.  The code I'm working on has an infinate loop that listens for UDP data on the network.  The function:

    do

    bBytesRecieved = udpcUDPClient.Receive(ipeRemoteIpEndPoint)


    '--<process data>

    loop

    The problem with this function (udpcUDPClient.Receive) is that the next line of code does not execute until data is actaully recieved.

    Of course, none of the controls on the form would work.  But thanks to you example, it all works great now.

    "MC"
    Sunday, November 01, 2009 4:32 PM
  • I see there a a lot out there that have questions about threading.  i have a few working projects on my website in the projects page which show some simple examples to access controls from a seperate thread using delegate sub.  one of the examples connects to a database in the background thread and updates controls in the main user interface thread.

    hope they help


    FREE DEVELOPER TOOLS, CODE & PROJECTS at www.srsoft.us Database Code Generator and Tutorial
    • Proposed as answer by kokila1986 Saturday, February 06, 2010 11:49 AM
    Sunday, November 01, 2009 6:28 PM
  • Hi all,
    as it has been mentioned on http://msdn.microsoft.com/en-us/library/ms171728.aspx "You are strongly advised to fix this problem when you see it, but you can disable it by setting the CheckForIllegalCrossThreadCalls property to false . This causes your control to run like it would run under Visual Studio .NET 2003 and the .NET Framework 1.1.".

    So, it is better to maintain the error instead of using CheckForIllegalCrossThreadCalls = false;

    Here is an example below in order to solve the problem.

    using System.Threading;
    using System.Reflection;
    
    
    
    private void MyForm_Load(object sender, EventArgs e)
    {
         pictureBox1.Visible = false;
    }
    
    
    private Thread thread1 = null;
    private Thread thread2 = null;
    
    
    delegate void ControlValueDelegate(Control ctrl, string pName, object pValue);
    
    private void SetControlValue(Control ctrl, string pName, object pValue)
    {
        if (ctrl.InvokeRequired)
        {
             ControlValueDelegate d = new ControlValueDelegate(SetControlValue);
             ctrl.Invoke(d, new object [] { ctrl, pName, pValue });
        }
        else
        {
              Type t = ctrl.GetType();
              PropertyInfo[] props = t.GetProperties();
              foreach (PropertyInfo p in props)
              {
                  if(p.Name.ToUpper() == pName.ToUpper())
                  {
                      p.SetValue(ctrl, pValue, null);
                  }
              }
          }
    }
    
    void Work()
    {
          try
          {
              SetControlValue(pictureBox1,"image",true);
          }
          catch(ThreadAbortException)
          {
              //
          }
          finally
          {
              SetControlValue(pictureBox1, "visible", false);
          }
    }
    
    void Start()
    {
          Stop();
          thread1 = new Thread(new ThreadStart(Work));
          thread1.Start();
    }
    
    void Stop()
    {
          if (thread1 != null)
          {
                thread1.Abort();
                thread1 = null;
          }
    }
    
    private void btnStart_Click(object sender, EventArgs e)
    {
          Start();
          btnStart.Enabled = false;
          btnStop.Enabled = true;
    }
    
    private void btnStop_Click(object sender, EventArgs e)
    {
          btnStop.Enabled = false;
          btnStart.Enabled = true;
          Stop();
    }
    
    Kindest regards...
    • Proposed as answer by Murat YILDIZ Wednesday, February 10, 2010 7:38 AM
    Wednesday, February 10, 2010 7:38 AM
  • If you develop threading applications using Outlook Object Model (OOM) API, then do consider the following article: Evaluation Criteria for the Outlook Object Model(http://msdn.microsoft.com/en-us/library/dd278301.aspx).

    It talks about threading model supported by OOM: "All calls to the Outlook object model execute on Outlook’s main foreground thread. The only threading model supported by the Outlook object model is single-threaded apartment (STA). Calling the Outlook object model from a background thread is not supported."

    Hope this helps.


    DeVa, M.S., {MSFT}
    Monday, May 31, 2010 2:46 PM
  • <sarcasm>

    i realize this is an old thread, but i just wanted to give a great big thanks to anyone who uses this: Control.CheckForIllegalCrossThreadCalls = false

    you keep me working fixing the issues this causes and other ones that the 'hide my head in the sand' mentality causes!

    so to you, thanks! ;)

    </sarcasm>

    Sunday, June 06, 2010 1:50 PM
  • Thank you, SDKCodeMonkey, CheckForIllegalCrossThreadCalls proved to be the easiest remedy in my situation: I need to read controls created in another thread. Alternative solutions I could imagine are (1) duplication of the data from the control in a dynamically created and synchronized object before spawning the second thread - not good because my control contains huge amount of data, and (2) invoking a delegate that stores the needed data in a specially allocated place and waiting until that delegate is processed - looks monstrous... Do any better solutions exist?


    Thursday, April 21, 2011 4:51 PM
  • hehe i guess you missed the <sarcasm /> tags?

    anyway, yes, there is a better solution. here's an extension method that i use for example when writing multi-threaded WCF clients. anything fired back to the client (events for example) that were created on a different thread than the controls will need something like this.

    //static for extension..
    //this allows us to always make sure we're using our controls on the
    //thread they were created on, regardless of the thread we're calling from...
    public static class ISynchronizeInvokeExtensions
    {
       public static void InvokeEx<T>(this T @this, Action<T> action) where T : ISynchronizeInvoke
       {
           if (@this.InvokeRequired)
               @this.BeginInvoke(action, new object[] { @this });
           else
               action(@this);
       }
    }

    this will benefit any control that derives from ISynchronizeInvoke automatically adding the method 'InvokeEx' to the member list. it basically checks to see if an invoke is required for the calling control and if not, then it simply sets/gets/calls normally since you're on the same thread, otherwise it invokes to the thread that the control was created on.

    to use this, all you have to do is this:
    txtTest.InvokeEx(txt =>
    {
       txt.Text = "this was a cool test!";
    }); btw, txtTest is a textbox control that you will need to add to your form ;)

    • Edited by SDKCodeMonkey Wednesday, October 05, 2011 3:07 PM Additional Clarification
    Monday, October 03, 2011 8:21 PM
  • You are a genius thanks
    Monday, February 06, 2012 12:28 AM