Resolved Cross-thread operation not valid

jcardana

Old to VB6, New to VB.NET
Joined
Oct 26, 2015
Messages
43
Location
Rio Rancho, NM
Programming Experience
Beginner
I have a Listbox "lbxUSBFiles" that I'm trying to populate from within an Arrived Event when a USB drive is attached. It's my uneducated conclusion, I don't have access to a control on my form???
 

Attachments

  • lbxUSBFiles.BeginUpdate.Error.png
    lbxUSBFiles.BeginUpdate.Error.png
    566 KB · Views: 7
Solution
As for the issue, presumably your event handler is executed on a secondary thread, which means that you cannot access the UI directly. You will need to marshal a call to the UI thread in order to update the control. In its simplest form, that might look like this:
VB.NET:
lbxUsbFile.Invoke(Sub() lbxUsbFiles.Items.AddRange(My.Computer.FileSystem.GetFiles(UsbLowroyDir).ToArray()))
Please don't post code or error messages as pictures. You can provide a screenshot as well if it adds value but always post code and error messages as text, formatted appropriately. It's easier to read and it means we can copy the text as required.
 
As for the issue, presumably your event handler is executed on a secondary thread, which means that you cannot access the UI directly. You will need to marshal a call to the UI thread in order to update the control. In its simplest form, that might look like this:
VB.NET:
lbxUsbFile.Invoke(Sub() lbxUsbFiles.Items.AddRange(My.Computer.FileSystem.GetFiles(UsbLowroyDir).ToArray()))
 
Solution
Thank you for the resolution. Worked just fine. I'll go back and find that rule I violated. Thank you for your extra time.
 
So, yes... the initial problem has been resolved. But I'm not understanding why I'm getting the Cross-Thread error.
The control I'm trying to populate is a ListBox "lbxUSBFiles" with the filenames of a USB drive in a "G:\Lowrey" folder.

What's causing this to run in a new thread which makes me have to use the Invoke command? Can I prevent it?

VB.NET:
    Private Sub GetUSBDriveLetter()
        ' Get Drive Letter
        lbxUSBFiles.Invoke(Sub() lbxUSBFiles.Items.Clear())
        Dim USBLowreyDir As String = ""
        Dim allDrives() As DriveInfo = DriveInfo.GetDrives()
        Dim drvLetter As String = ""
        'Dim d As DriveInfo
        For Each drvInfo In allDrives
            If drvInfo.DriveType = 2 Then
                drvLetter = drvInfo.Name
                USBLowreyDir = drvLetter & "Lowrey"
                Exit For
            End If
        Next
        If drvLetter <> Nothing Then
            If Not (My.Computer.FileSystem.DirectoryExists(USBLowreyDir)) Then
                Dim mbText, mbTitle As String, mbResp As Integer
                Dim mbBtns As MsgBoxStyle
                mbTitle = "NO LOWREY FOLDER"
                mbBtns = CType(vbInformation + vbOKOnly, MsgBoxStyle)
                mbText = "There is no LOWREY folder on this thumbdrive."
                mbResp = MsgBox(mbText, mbBtns, mbTitle)
                Exit Sub
            Else
                lbxUSBFiles.Invoke(Sub() lbxUSBFiles.Items.AddRange(My.Computer.FileSystem.GetFiles(USBLowreyDir, FileIO.SearchOption.SearchAllSubDirectories, FileType).ToArray()))
            End If
        End If
    End Sub
 
That method will execute on the same thread as the method that calls it. You haven't shown us that so we can only guess. If you haven't done something to explicitly run on a different thread, the call to that method probably originates from a handler for an event that is raised on a secondary thread, e.g. the Elapsed event of a System.Timers.Timer.
 
I'm thinking it's in the StartDetection subroutine.
EDIT: So is it the ARRIVE event? Why would this then prevent me from making changes to the listbox without the invoke command. I'm thinking because it's checking every second???
I'm still learning, thank you for your patience.

This is to detect when a USB drive is inserted.
VB.NET:
    Private Sub StartDetection()
        Dim query2 As New WqlEventQuery("SELECT * FROM __InstanceOperationEvent WITHIN 1 " & "WHERE TargetInstance ISA 'Win32_DiskDrive'")
        MediaConnectWatcher = New ManagementEventWatcher With {
                .Query = query2
                }
        MediaConnectWatcher.Options.Timeout = New TimeSpan(1000)
        MediaConnectWatcher.Start()
    End Sub

This is after a USB is detected.
VB.NET:
    Private Sub Arrived(ByVal Sender As Object, ByVal E As System.Management.EventArrivedEventArgs) Handles MediaConnectWatcher.EventArrived
        Dim USBLowreyDir As String = ""
        Dim mbo, obj As ManagementBaseObject
        mbo = CType(E.NewEvent, ManagementBaseObject)
        obj = CType(mbo("TargetInstance"), ManagementBaseObject)

        Select Case mbo.ClassPath.ClassName
            Case "__InstanceCreationEvent"
                GetUSBDriveLetter()

            Case "__InstanceDeletionEvent"
                If obj("InterfaceType").ToString.ToUpper = "USB" Then
                    lbxUSBFiles.Invoke(Sub() lbxUSBFiles.Items.Clear())
                End If
        End Select
 
Last edited:
Various types can raise events at any time and they will do so on a secondary thread so as not to freeze your UI at random times. An example is the SerialPort.DataArrived event. The FileSystemwatcher class raises its events on a secondary thread too, for the same reason. It appears that the ManagementEventWatcher.EventArrived event can be added to that list. That event can be raised at any time and they don't want to interrupt what the user is doing in the UI at the time so the event is raised on a secondary thread, meaning that the event handler is executed on that same secondary thread. If you want to affect the UI, you need to marshal a call to the UI thread to do so.
 
As I was going to bed I had this thought... If I'm driving and my wife wants to change our destination, she has to INVOKE her directions because she's in the car, but not driving. I'm starting to understand. Thanks.
 
Back
Top