Home > Software engineering >  Cross-thread operation not valid: Control 'RichTextBox' accessed from a thread other than
Cross-thread operation not valid: Control 'RichTextBox' accessed from a thread other than

Time:03-04

I'm using telegram APis to send a message to the client. The code I'm using is:

Imports System.Threading
Imports Telegram.Bot
Imports Telegram.Bot.Extensions.Polling
Imports Telegram.Bot.Types
Imports Telegram.Bot.Types.Enums
    
Public Class form1
    Private Async Function HandleUpdateAsync(botClient As ITelegramBotClient,
                                             update As Update,
                                             cancellationToken As CancellationToken) As Task
    
        If update.Message IsNot Nothing Then
            If Not String.IsNullOrEmpty(update.Message.Text) Then
                Select Case update.Message.Text
                    Case "/0"
                         Await botClient.SendTextMessageAsync("chatid", RichTextBox1.Text)
                    Case "/star2t"
                        Await botClient.SendTextMessageAsync("chatid", "text")
                End Select
            End If
        End If
    End Function
    
    Private Function HandleErrorAsync(botClient As ITelegramBotClient,
                                      exception As Exception,
                                      cancellationToken As CancellationToken) As Task
        Console.WriteLine(exception)
        Return Task.CompletedTask
    End Function
    
    Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        'invio messaggio 
        Dim bot = New TelegramBotClient("token id")
        Await bot.SendTextMessageAsync("chatid", "Test")
    End Sub
    
    Private Sub form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim bot = New TelegramBotClient("My id")
  
        Dim cts = New CancellationTokenSource()
        Dim options = New ReceiverOptions With {
            .AllowedUpdates = Array.Empty(Of UpdateType)()
        }
        bot.StartReceiving(updateHandler:=AddressOf HandleUpdateAsync, errorHandler:=AddressOf HandleErrorAsync, options, cts.Token)
        Console.ReadLine()
    End Sub
End Class

It's sending the message with

Await botClient.SendTextMessageAsync("chatid", "text")

but its not sending it

with

Await botClient.SendTextMessageAsync("chatid", Richtextbox5.text)

I'm not getting any error at runtime or at debug, but checking the Immediate Window on VS while debugging I see that there is a cross threading issue:

exception thrown: 'System.InvalidOperationException' in System.Windows.Forms.dll
System.InvalidOperationException: Cross-thread operation not valid: Control 'RichtextBox5' accessed from a thread other than the thread it was created on
in System.Windows.Forms.Control.get_Handle()
in System.Windows.Forms.RichTextBox.StreamOut(Stream data, Int32 flags, Boolean includeCrLfs)
in System.Windows.Forms.RichTextBox.StreamOut(Int32 flags)
in System.Windows.Forms.RichTextBox.get_Text()
in HotBit_APIs.Form1.VB$StateMachine_17_HandleUpdateAsync.MoveNext()
in C:\Users\Mattia\source\repos\HotBit APIs\HotBit APIs\Form1.vb:line 81

I've been looking online for a solution and I came accross this code

Private Sub UpdateTextBox(text As String)
    If TextBox1.InvokeRequired Then
        'We are on a secondary thread.
        TextBox1.Invoke(New Action(Of String)(AddressOf UpdateTextBox), text)
    ecc

or yourForm.Invoke(new Action(() => {GUI code here}))

but I don't know exactly how to implement with my code. Any idea on how I can make it?

CodePudding user response:

You can't access UI elements from just any thread - it must be on the UI thread. With a handler like that, you can't assume you are on the UI thread and actually you should assume you may not be. The way you ensure a UI control is accessed properly, always check if InvokeRequired before accessing it.

Dim text As String
If control.InvokeRequired Then
    control.Invoke(Sub() text = control.Text)
Else
    text = control.Text
End If

In your case, the issue is in HandleUpdateAsync. The line of code

Await botClient.SendTextMessageAsync("chatid", RichTextBox1.Text)

could be changed to

Dim text As String
If RichTextBox1.InvokeRequired Then
    RichTextBox1.Invoke(Sub() text = RichTextBox1.Text)
Else
    text = RichTextBox1.Text
End If
Await botClient.SendTextMessageAsync("chatid", text)

The accepted way to do this is not inline in the code like that. Your google search has found it. Generally, it's like this

Private Function GetRichTextBox1Text() As String
    If RichTextBox1.InvokeRequired Then
        Return RichTextBox1.Invoke(AddressOf GetRichTextBox1Text)
    Else
        Return RichTextBox1.Text
    End If
End Function
Await botClient.SendTextMessageAsync("chatid", GetRichTextBox1Text())

Nobody wants to do this all the time. You can write extension methods (for action and simple function) in a Module like this, and of course you can extrapolate on these for any number of arguments

<Extension(), DebuggerHidden()>
Public Sub InvokeIfRequired(control As Control, action As MethodInvoker)
    If control.InvokeRequired Then control.Invoke(action) Else action()
End Sub
<Extension(), DebuggerHidden()>
Public Function InvokeIfRequired(Of TR)(control As Control, func As Func(Of TR)) As TR
    If control.InvokeRequired Then
        Return control.Invoke(func)
    Else
        Return func.Invoke()
    End If
End Function

In your case, use the function

Await botClient.SendTextMessageAsync("chatid", RichTextBox1.InvokeIfRequired(Function() RichTextBox1.Text))
  • Related