Invoke wont invoke? - vb.net

I'm doing this:
Delegate Sub SetTextBoxText_Delegate(ByVal [Label] As TextBox, ByVal [text] As String)
' The delegates subroutine.
Public Sub SetTextBoxText_ThreadSafe(ByVal [Label] As TextBox, ByVal [text] As String)
' InvokeRequired required compares the thread ID of the calling thread to the thread ID of the creating thread.
' If these threads are different, it returns true.
If [Label].InvokeRequired Then
MsgBox("invoke")
Dim MyDelegate As New SetTextBoxText_Delegate(AddressOf SetTextBoxText_ThreadSafe)
Me.Invoke(MyDelegate, New Object() {[Label], [text]})
Else
MsgBox("noinvoke")
[Label].Text = [text]
End If
End Sub
However it always uses noinvoke. If I try setting it normaly it gives me a thread-safe warning and doesn't work. If I force invoke then it says the control isn't created?
Could someone help?

It's most likely because the control has not yet been created when you try to access it. Wait until the control has loaded, or check it using Label.Created. Like so:
Public Sub SetTextBoxText_ThreadSafe(ByVal Label As TextBox, ByVal text As String)
If Label.Created Then
If Label.InvokeRequired Then
MsgBox("invoke")
Dim MyDelegate As New SetTextBoxText_Delegate(AddressOf SetTextBoxText_ThreadSafe)
Me.Invoke(MyDelegate, New Object() {Label, text})
Else
MsgBox("noinvoke")
Label.Text = text
End If
End If
End Sub
P.S. You don't need a custom delegate type, just use Action(Of TextBox, String). You also don't need square brackets around Label or text.

Related

VB form cross thread method call with parameters

I am writing a VB form application that redirects the standard output stream of a process and uses it in a UI.
I am having trouble calling methods with parameters that update controls on the form from the OutputHandler sub.
I can call a method without parameters like so
Me.Invoke(New MyDelSub(AddressOf ServerStarted))
Which works fine.
And a bit of googling told me that to call a method with parameters I should do this:
Dim del As JoinDelegate = AddressOf PlayerJoins
del.Invoke(username)
With this delegate and method pair:
Private Delegate Sub JoinDelegate(ByVal username As String)
Private Sub PlayerJoins(ByVal username As String)
PlayersBox.Items.Add(username)
'Do other stuff
End Sub
But this produces an IllegalOperationException the first time the method tries to access a control.
1) Supposing you have a method like this:
Public Sub DoSomething(value1 As String, value2 As String)
MessageBox.Show(String.Format("{0} {1}", value1, value2))
End Sub
You can call it using invoke this way:
Me.Invoke(Sub() DoSomething("Hello", "World!"))
2) If you want to make thread safe call to a control you can write the method this way:
Public Sub AddItemToListBox1(item As String)
If (ListBox1.InvokeRequired) Then
ListBox1.Invoke(Sub() AddItemToListBox1(item))
Else
ListBox1.Items.Add(item)
End If
End Sub
Then it's enough to call it in a the UI thread or in another thread the same way simply:
AddItemToListBox1("some item")
The call would be thread safe.
E.g.
Private Sub SetControlText(control As Control, text As String)
If control.InvokeRequired Then
control.Invoke(New Func(Of Control, String)(AddressOf SetControlText), control, text)
Else
control.Text = text
End If
End Sub
Call that method from any thread.

Target parameter count exception on delegate Sub

I have the following code to write some text from different functions and subs but that has been working ok but now I'm getting the target parameter count exception when I call the delegate from SerialPort DataReceived event.
I can't figure out what I'm doing wrong, Any ideas?
Delegate Sub PrintSmsLogDelegate(ByVal NewText As String, ByVal NewLine As Boolean)
Protected Friend Sub PrintSmsLog(ByVal NewText As String, Optional ByVal NewLine As Boolean = True)
If Me.InvokeRequired Then
Dim Txt As New PrintSmsLogDelegate(AddressOf PrintSmsLog)
'Me.Invoke(Txt, NewText)'This fail too
Me.Invoke(Txt, New Object() {NewText}) '<--- TargetParameterCountException
Else
'...
End If
End Sub
Private Sub SmsSerialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SmsSerialPort.DataReceived
'... code to receive data and save it in "Lines" variable
Dim Lines as String
Me.PrintSmsLog(Lines, False)
End Sub
The problem is that your PrintSmsLogDelegate delegate declaration contains 2 required parameter.
So you have to provide the 2nd parameter as well.
The method signature for Invoke method is this:
Function Control.Invoke(method As [Delegate], ParamArray args As Object()) As Object
So you should call your PrintSmsLogDelegate delegate instance (which is Txt) with two parameters even if the PrintSmsLog method does not require the 2nd parameter.
Me.Invoke(Txt, NewText, True)
You cannot call the Invoke method with a single array parameter. Due to ParamArray keyword an array will be automatically created of the multiple parameter you specify.

VB.Net Multithreaded SerialPort

,Im new with serialport programming. Is there any way to pass SerialPort(ex. .name, COM, etc..) properties in UI Thread in multithreaded application?
What I want is similar in the code below but set my properties as variables. For example(property=.name, property = .text). So that i can return it to the UI thread by calling object.property
Public Delegate Sub SetTextCallback(ByVal control As Control, ByVal text As String)
Public Sub SetText(ByVal control As Control, ByVal text As String)
If control.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
frmMain.Invoke(d, New Object() {control, text})
Else
control.Text = text
End If
End Sub
I want is something like this (there's equivalent snippet in c#.Net but I can't do it in VB.NET):
Public Delegate Sub SetTextCallback(ByVal control As Control, ByVal prop as property, ByVal text As String)
Public Sub SetText(ByVal control As Control, byVal prop as property, ByVal text As String)
If control.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
frmMain.Invoke(d, New Object() {control, prop, text})
Else
control.prop = text
End If
End Sub
Thanks in advance...

How to pass multiple parameters in thread in VB

I'm looking to pass two or more parameters to a thread in VB 2008.
The following method (modified) works fine without parameters, and my status bar gets updated very cool-y.
But I can't seem to make it work with one, two or more parameters.
This is the pseudo code of what I'm thinking should happen when the button is pressed:
Private Sub Btn_Click()
Dim evaluator As New Thread(AddressOf Me.testthread(goodList, 1))
evaluator.Start()
Exit Sub
This is the testthread method:
Private Sub testthread(ByRef goodList As List(Of OneItem), ByVal coolvalue As Integer)
StatusProgressBar.Maximum = 100000
While (coolvalue < 100000)
coolvalue = coolvalue + 1
StatusProgressBar.Value = coolvalue
lblPercent.Text = coolvalue & "%"
Me.StatusProgressBar.Refresh()
End While
End Sub
First of all: AddressOf just gets the delegate to a function - you cannot specify anything else (i.e. capture any variables).
Now, you can start up a thread in two possible ways.
Pass an Action in the constructor and just Start() the thread.
Pass a ParameterizedThreadStart and forward one extra object argument to the method pointed to when calling .Start(parameter)
I consider the latter option an anachronism from pre-generic, pre-lambda times - which have ended at the latest with VB10.
You could use that crude method and create a list or structure which you pass to your threading code as this single object parameter, but since we now have closures, you can just create the thread on an anonymous Sub that knows all necessary variables by itself (which is work performed for you by the compiler).
Soo ...
Dim Evaluator = New Thread(Sub() Me.TestThread(goodList, 1))
It's really just that ;)
Something like this (I'm not a VB programmer)
Public Class MyParameters
public Name As String
public Number As Integer
End Class
newThread as thread = new Thread( AddressOf DoWork)
Dim parameters As New MyParameters
parameters.Name = "Arne"
newThread.Start(parameters);
public shared sub DoWork(byval data as object)
{
dim parameters = CType(data, Parameters)
}
Dim evaluator As New Thread(Sub() Me.testthread(goodList, 1))
With evaluator
.IsBackground = True ' not necessary...
.Start()
End With
Well, the straightforward method is to create an appropriate class/structure which holds all your parameter values and pass that to the thread.
Another solution in VB10 is to use the fact that lambdas create a closure, which basically means the compiler doing the above automatically for you:
Dim evaluator As New Thread(Sub()
testthread(goodList, 1)
End Sub)
In addition to what Dario stated about the Delegates you could execute a delegate with several parameters:
Predefine your delegate:
Private Delegate Sub TestThreadDelegate(ByRef goodList As List(Of String), ByVal coolvalue As Integer)
Get a handle to the delegate, create parameters in an array, DynamicInvoke on the Delegate:
Dim tester As TestThreadDelegate = AddressOf Me.testthread
Dim params(1) As Object
params(0) = New List(Of String)
params(1) = 0
tester.DynamicInvoke(params)
Just create a class or structure that has two members, one List(Of OneItem) and the other Integer and send in an instance of that class.
Edit: Sorry, missed that you had problems with one parameter as well. Just look at Thread Constructor (ParameterizedThreadStart) and that page includes a simple sample.
Pass multiple parameter for VB.NET 3.5
Public Class MyWork
Public Structure thread_Data
Dim TCPIPAddr As String
Dim TCPIPPort As Integer
End Structure
Dim STthread_Data As thread_Data
STthread_Data.TCPIPAddr = "192.168.2.2"
STthread_Data.TCPIPPort = 80
Dim multiThread As Thread = New Thread(AddressOf testthread)
multiThread.SetApartmentState(ApartmentState.MTA)
multiThread.Start(STthread_Data)
Private Function testthread(ByVal STthread_Data As thread_Data)
Dim IPaddr as string = STthread_Data.TCPIPAddr
Dim IPport as integer = STthread_Data.TCPIPPort
'Your work'
End Function
End Class
I think this will help you...
Creating Threads and Passing Data at Start Time!
Imports System.Threading
' The ThreadWithState class contains the information needed for
' a task, and the method that executes the task.
Public Class ThreadWithState
' State information used in the task.
Private boilerplate As String
Private value As Integer
' The constructor obtains the state information.
Public Sub New(text As String, number As Integer)
boilerplate = text
value = number
End Sub
' The thread procedure performs the task, such as formatting
' and printing a document.
Public Sub ThreadProc()
Console.WriteLine(boilerplate, value)
End Sub
End Class
' Entry point for the example.
'
Public Class Example
Public Shared Sub Main()
' Supply the state information required by the task.
Dim tws As New ThreadWithState( _
"This report displays the number {0}.", 42)
' Create a thread to execute the task, and then
' start the thread.
Dim t As New Thread(New ThreadStart(AddressOf tws.ThreadProc))
t.Start()
Console.WriteLine("Main thread does some work, then waits.")
t.Join()
Console.WriteLine( _
"Independent task has completed main thread ends.")
End Sub
End Class
' The example displays the following output:
' Main thread does some work, then waits.
' This report displays the number 42.
' Independent task has completed; main thread ends.
With VB 14, you can do the following with Tuples:
Shared Sub _runner(data as (goodList As List(Of OneItem), coolvalue As Integer))
Console.WriteLine($"goodList: {data.goodList}")
Console.WriteLine($"coolvalue: {data.coolvalue}")
' do stuff...
End Sub
Dim thr As New Thread(AddressOf _runner)
thr.Start((myGoodList, cval))

Calling Sub from EventHandler

I'm using the below to update controls from another thread (works great) How would I call a Sub (Named UpdateList)? The UpdateList updates a listview with a list of databases on a selected SQL instance, requires no arguments.
Private Sub CompleteEventHandler(ByVal sender As Object, ByVal e As Microsoft.SqlServer.Management.Common.ServerMessageEventArgs)
SetControlPropertyValue(Label8, "text", e.ToString)
UpdateList()
MessageBox.Show("Restore Complete")
End Sub
Delegate Sub SetControlValueCallback(ByVal oControl As Control, ByVal propName As String, ByVal propValue As Object)
Private Sub SetControlPropertyValue(ByVal oControl As Control, ByVal propName As String, ByVal propValue As Object)
If (oControl.InvokeRequired) Then
Dim d As New SetControlValueCallback(AddressOf SetControlPropertyValue)
oControl.Invoke(d, New Object() {oControl, propName, propValue})
Else
Dim t As Type = oControl.[GetType]()
Dim props As PropertyInfo() = t.GetProperties()
For Each p As PropertyInfo In props
If p.Name.ToUpper() = propName.ToUpper() Then
p.SetValue(oControl, propValue, Nothing)
End If
Next
End If
End Sub
Based On:
http://www.shabdar.org/cross-thread-operation-not-valid.html
Calling a method from an event handler is not a special case – use a normal call.
The issue here is the cross-thread call from a background thread to the GUI thread. In order to overcome it, you can place the following code at the beginning of the UpdateList code:
If Me.InvokeRequired Then
Me.Invoke(New Action(AddressOf UpdateList))
Return
End If