0

I'm having trouble using threading in a VB.net application I'm writing. I have a really simple version here to illustrate the problem I'm having.

It works perfectly if I load my form and have the VNC control connect to my VNC server as a single thread application using this code:

Imports VncSharp
Imports System.Windows.Forms
Imports System.ComponentModel
Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        If Not RemoteDesktop1.IsConnected Then
            Dim Host As String = "10.0.0.1"
            RemoteDesktop1.Connect(Host)
        End If
    End Sub
End Class

In order to make the connection occur in the background, this is the code I've tried using the backgroundworker control. I followed the information in this article to handle referencing the controls on the form using an instance, but I still get an error when it tries to use the VNCSharp control (RemoteDesktop1) to connect to a VNC server at the line: Instance.RemoteDesktop1.Connect(Host)

The error is:

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

Here's the code:

Imports VncSharp
Imports System.Windows.Forms
Imports System.ComponentModel
Public Class Form1
    
    'Taken from https://stackoverflow.com/questions/29422339/update-control-on-main-form-via-background-worker-with-method-in-another-class-v
    Private Shared _instance As Form1
    Public ReadOnly Property Instance As Form1
        Get
            Return _instance
        End Get
    End Property

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        _instance = Me
        BackgroundWorker1.RunWorkerAsync()
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        System.Threading.Thread.Sleep(1000)
        If Not Instance.RemoteDesktop1.IsConnected Then
            Dim Host As String = "10.0.0.1"
            Instance.RemoteDesktop1.Connect(Host)
            'Connect()
        End If
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        MsgBox("BG1 complete")
    End Sub
End Class
Ian
  • 11
  • 1
  • What is RemoteDesktop1.Connect? Guessing that is some text/label control you're updating, If so, that error makes 100% sense. I would look at setting Host as return value(.Result) in your DoWork method, then in the RunWorkerCompleted event, update your UI elements as required – Hursey Aug 23 '22 at 01:18
  • Thanks for the suggestion - RemoteDesktop1. is a VNCSharp control on my form. .Connect is a property that returns true if the control is connected to a VNC server. – Ian Aug 23 '22 at 02:32

1 Answers1

-2

I figured it out - one part of the code in the post I linked to on threading held the answer: I needed to use Invoke() to reference the form RemoteDesktop control:

Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
  System.Threading.Thread.Sleep(1000)
    If Not Instance.RemoteDesktop1.IsConnected Then
      Me.Instance.Invoke(Sub()
        Dim Host As String = "10.61.41.7"
        Me.RemoteDesktop1.Connect(Host)
      End Sub)
  End If
End Sub

instead of just putting Instance.RemoteDesktop1.Connect(Host)

Ian
  • 11
  • 1
  • 2
    This is not the solution. What's the point of the `BackgroundWorker` in the first place if you're just going to invoke a method call back to the UI thread anyway? The whole point of the `BackgroundWorker` is to do background work. If you're not doing background work then don't use one in the first place. – John Aug 23 '22 at 02:42
  • If you are going to use a `BackgroundWorker` because you have other genuine background work to do then the proper way to get back to the UI thread is to call `ReportProgress` and handle the `ProgressChanged` event. – John Aug 23 '22 at 02:50
  • Hmmm, well the background work I am using the BackgroundWorker for is connecting to a VNC server. In my actual program, the BackgroundWorker is actually pinging the VNC server, waiting for that to respond, then trying getting the VNC control on the form to connect to the server and finally stopping when the connection is complete. However I want people to be able to interact with other controls on the form while the VNC control is waiting to connect, hence the BackgroundWorker. – Ian Aug 23 '22 at 03:18
  • So I guess I could just have the `BackgroundWorker` ping the server and when it responds, use `ProgressChanged` to signal that to the UI thread and trigger a VNC control connect call from the `ProgressChanged` event? – Ian Aug 23 '22 at 03:25
  • You could do that but I'm not sure why you would. If that's all the `BackgroundWorker` was doing then you'd just update the UI in the `RunWorkerCompleted` event handler, which is executed on the UI thread. There still doesn't seem to be a reason to use a `BackgroundWorker` though. If you want to wait for a second then either use a `Timer` or `Task.Delay`. The `Ping` class has asynchronous methods so there's no need for the `BackgroundWorker` there either. As far as i can tell, you are trying to solve a problem that you have created for no good reason. – John Aug 23 '22 at 03:55
  • Ok, I was going to use a `Timer` control but then saw an article about the `BackgroundWorker` and thought that would be a better option. I'll see what i can do with the `Ping` asynchronous methods. I'm already using 4 Timers on my form for other purposes. Thanks! – Ian Aug 23 '22 at 04:14
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 27 '22 at 04:42