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))