Link to home
Start Free TrialLog in
Avatar of sidwelle
sidwelleFlag for United States of America

asked on

Update listbox from another thread

I am trying to update a listbox from another thread, but I never see ..invokerequired return 'true' and then then when it updates the control, it appears to be loading a new instance of the form.

Public Delegate Function DelListEvent(ByVal Msg As String) As Integer

    Public Function ListEvent(ByVal Msg As String) As Integer

        Dim iRet As Integer = 0
        Dim sDS As String = ""

        If lstEvents.InvokeRequired Then
            'iRet = lstEvents.Invoke(New MethodInvoker(AddressOf ListEvent))
            Dim d As New DelListEvent(AddressOf ListEvent)

            'lstEvents.BeginInvoke(New DelListEvent(AddressOf ListEvent), Msg)
            lstEvents.BeginInvoke(d, New Object() {Msg})
        Else
            sDS = Now.ToString("yy/MM/dd HH:mm:ss.fff")

            Me.lstEvents.Items.Add(sDS & " > " & Msg)
            Application.DoEvents()
        End If

    End Function

Open in new window

Avatar of it_saige
it_saige
Flag of United States of America image

Invoke required would only be called if the list was in fact updated from a separate thread; e.g. -

Form1.vb -
Imports System.Threading.Tasks

Public Class Form1
	Private Sub OnClick(sender As Object, e As EventArgs) Handles Button2.Click, Button1.Click
		If TypeOf sender Is Button Then
			Dim btn = DirectCast(sender, Button)
			If btn.Equals(Button1) Then
				Task.Factory.StartNew(Sub()
									  For Each [Item] In (From i In Enumerable.Range(ListBox1.Items.Count, 20) Select String.Format("Message{0}", i))
										  AddItem(ListBox1, [Item])
									  Next
								  End Sub)
			ElseIf btn.Equals(Button2) Then
				Task.Factory.StartNew(Sub()
									  For Each [Item] In ListBox1.Items.Cast(Of Object).Reverse()
										  RemoveItem(ListBox1, [Item])
									  Next
								  End Sub)
			End If
		End If
	End Sub

	Delegate Sub AddItemDelegate(lb As ListBox, message As String)
	Delegate Sub RemoveItemDelegate(lb As ListBox, item As Object)

	Sub AddItem(lb As ListBox, message As String)
		If lb.InvokeRequired Then
			Console.WriteLine("Invoking the addition of {0}", message)
			lb.Invoke(New AddItemDelegate(AddressOf AddItem), New Object() {lb, message})
		Else
			Console.WriteLine("Adding {0}; to Listbox", message)
			lb.Items.Add(message)
		End If
	End Sub

	Sub RemoveItem(lb As ListBox, item As Object)
		If lb.InvokeRequired Then
			Console.WriteLine("Invoking the removal of {0}", item)
			lb.Invoke(New RemoveItemDelegate(AddressOf RemoveItem), New Object() {lb, item})
		Else
			Console.WriteLine("Removing {0}; from Listbox", item)
			lb.Items.Remove(item)
		End If
	End Sub
End Class

Open in new window

Form1.Designer.vb -
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class Form1
    Inherits System.Windows.Forms.Form

    'Form overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
		Me.ListBox1 = New System.Windows.Forms.ListBox()
		Me.Button1 = New System.Windows.Forms.Button()
		Me.Button2 = New System.Windows.Forms.Button()
		Me.SuspendLayout()
		'
		'ListBox1
		'
		Me.ListBox1.FormattingEnabled = True
		Me.ListBox1.Location = New System.Drawing.Point(13, 13)
		Me.ListBox1.Name = "ListBox1"
		Me.ListBox1.Size = New System.Drawing.Size(259, 212)
		Me.ListBox1.TabIndex = 0
		'
		'Button1
		'
		Me.Button1.Location = New System.Drawing.Point(96, 226)
		Me.Button1.Name = "Button1"
		Me.Button1.Size = New System.Drawing.Size(95, 23)
		Me.Button1.TabIndex = 1
		Me.Button1.Text = "Add 20 Items"
		Me.Button1.UseVisualStyleBackColor = True
		'
		'Button2
		'
		Me.Button2.Location = New System.Drawing.Point(197, 226)
		Me.Button2.Name = "Button2"
		Me.Button2.Size = New System.Drawing.Size(75, 23)
		Me.Button2.TabIndex = 2
		Me.Button2.Text = "Clear"
		Me.Button2.UseVisualStyleBackColor = True
		'
		'Form1
		'
		Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
		Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
		Me.ClientSize = New System.Drawing.Size(284, 261)
		Me.Controls.Add(Me.Button2)
		Me.Controls.Add(Me.Button1)
		Me.Controls.Add(Me.ListBox1)
		Me.Name = "Form1"
		Me.Text = "Form1"
		Me.ResumeLayout(False)

	End Sub
	Friend WithEvents ListBox1 As System.Windows.Forms.ListBox
	Friend WithEvents Button1 As System.Windows.Forms.Button
	Friend WithEvents Button2 As System.Windows.Forms.Button

End Class

Open in new window

Produces the following output -

Initial load:User generated imagePressing the add button (with output to console window):User generated imagePressing the remove button (with output to console window):User generated image
You could also just use an anonymous method invoker instead of the delegates:
Imports System.Threading.Tasks

Public Class Form1
	Private Sub OnClick(sender As Object, e As EventArgs) Handles Button2.Click, Button1.Click
		If TypeOf sender Is Button Then
			Dim btn = DirectCast(sender, Button)
			If btn.Equals(Button1) Then
				Task.Factory.StartNew(Sub()
									  For Each [Item] In (From i In Enumerable.Range(ListBox1.Items.Count, 20) Select String.Format("Message{0}", i))
										  AddItem(ListBox1, [Item])
									  Next
								  End Sub)
			ElseIf btn.Equals(Button2) Then
				Task.Factory.StartNew(Sub()
									  For Each [Item] In ListBox1.Items.Cast(Of Object).Reverse()
										  RemoveItem(ListBox1, [Item])
									  Next
								  End Sub)
			End If
		End If
	End Sub

	Sub AddItem(lb As ListBox, message As String)
		If lb.InvokeRequired Then
			Console.WriteLine("Invoking the addition of {0}", message)
			lb.Invoke(New MethodInvoker(Sub()
									   AddItem(lb, message)
								   End Sub))
		Else
			Console.WriteLine("Adding {0}; to Listbox", message)
			lb.Items.Add(message)
		End If
	End Sub

	Sub RemoveItem(lb As ListBox, item As Object)
		If lb.InvokeRequired Then
			Console.WriteLine("Invoking the removal of {0}", item)
			lb.Invoke(New MethodInvoker(Sub()
									   RemoveItem(lb, item)
								   End Sub))
		Else
			Console.WriteLine("Removing {0}; from Listbox", item)
							 lb.Items.Remove(item)
		End If
	End Sub
End Class

Open in new window

Which would produce the same output as above.

However, there is a problem with these methods.  They are only for ListBox controls and only for specific listbox control methods.  What if we could make this more generic?  Well we can:
Imports System.Threading.Tasks
Imports System.ComponentModel
Imports System.Runtime.CompilerServices

Public Class Form1
	Private Sub OnClick(sender As Object, e As EventArgs) Handles Button2.Click, Button1.Click
		If TypeOf sender Is Button Then
			Dim btn = DirectCast(sender, Button)
			If btn.Equals(Button1) Then
				Task.Factory.StartNew(Sub()
									  For Each [Item] In (From i In Enumerable.Range(ListBox1.Items.Count, 20) Select String.Format("Message{0}", i))
										  ListBox1.HandleInvokeRequired(Sub(lb) lb.Items.Add([Item]))
									  Next
								  End Sub)
			ElseIf btn.Equals(Button2) Then
				Task.Factory.StartNew(Sub()
									  For Each [Item] In ListBox1.Items.Cast(Of Object).Reverse()
										  ListBox1.HandleInvokeRequired(Sub(lb) lb.Items.Remove([Item]))
									  Next
								  End Sub)
			End If
		End If
	End Sub
End Class

Module Extensions
	<Extension()> _
	Public Sub HandleInvokeRequired(Of T As {Control, ISynchronizeInvoke})(control As T, action As Action(Of T))
		'Check to see is the control is not null
		If control Is Nothing Then
			Throw New ArgumentNullException(String.Format("Cannot execute {0} on {1}.  {1} is null.", action, control))
		End If

		'Check to see if the control is disposed.
		If TypeOf control Is Control AndAlso TryCast(control, Control).IsDisposed Then
			Throw New ObjectDisposedException(String.Format("Cannot execute {0} on {1}.  {1} is disposed.", action, control))
		End If

		'Check to see if the handle is created for the control.
		If TypeOf control Is Control AndAlso Not TryCast(control, Control).IsHandleCreated Then
			Throw New InvalidOperationException(String.Format("Cannot execute {0} on {1}.  Handle is not created for {1}.", action, control))
		End If

		'Check to see if the control's InvokeRequired property is true
		If control.InvokeRequired Then
			Try
				'Use Invoke() to invoke your action
				Console.WriteLine("Invoking the action {0}; on {1}", action, control)
				control.Invoke(action, New Object() {control})
			Catch ex As Exception
				Throw New Exception(String.Format("Cannot execute {0} on {1}.  {2}.", action, control, ex.Message))
			End Try
		Else
			Try
				'Perform the action
				Console.WriteLine("Performing the action {0}; on {1}", action, control)
				action(control)
			Catch ex As Exception
				Throw New Exception(String.Format("Cannot execute {0} on {1}.  {2}.", action, control, ex.Message))
			End Try
		End If
	End Sub
End Module

Open in new window

Which produces a different output to the console but the same results to the listbox -User generated imageUser generated image
In most circumstances this is fine, but you have the potential for race conditions.

-saige-
Avatar of sidwelle

ASKER

its going to take me a day to read all that out.

Thank you for the reply, but I don't see how line 27 from your first code block in different from my line 13 ?  I am still 'Invoking' the listbox and passing a value.

When I step through it line by line, I see it go through from_load like its loading a new instance of the form that you can't see ?!  
Then it goes through the ListEvent Procedure, the Invokerequired is false and it adds the item to the listbox w/o errors.

But there are no new items visible in the box !
Did it create a new instance of the form that not show ?

Thanks
The first difference that I see is that your delegate and method do not have the control as a parameter.  Let's attack this from the opposite path, where are you calling your method from?

-saige-
Why, the control is on the form.  Can't I just call the control by its name w/the form name ?

Me.lstEvents.Items.Add...   'what's wrong with this ?
I am calling the procedure on the form from a function a thread that I started.

Tried passing a reference to the from in via the constructor of the class, but that didn't work either.

when I hover over the reference, I see a property :
"AccessibilityObject = {"Cross-thread operation not valid: Control 'frmListener' accessed from a thread other than the thread it was created on."}"
You need to tell the system to not check for cross thread as you state your proble.
"AccessibilityObject = {"Cross-thread operation not valid: Control 'frmListener' accessed from a thread other than the thread it was created on."}"
Try to add this at your form_load as well as at your function at the very top of your function and see.
Control.CheckForIllegalCrossThreadCalls = False

Open in new window

I added the above line to "form_shown".

Also, instead of passing a reference to the parent form, I passed a reference to the listbox and was able to add items to it directly.

But not sure if this is correct, why can I not call the function "ListEvents(...)" and have it perform the task for me ?
Why did you do this?
Public Delegate Function DelListEvent(ByVal Msg As String) As Integer

    Public Function ListEvent(ByVal Msg As String) As Integer

Open in new window

I used a delegate to make the call thread safe ?
You *can* use your method.  I showed this in my examples.  There are also many articles discussing using delegates to perform tasks on controls; It is not an uncommon request.

As I cannot reproduce your issue directly, why don't you put together a really simple example that shows the issue you are having.

-saige-
My issue is that I never see the control update the item that I added, when its added from the other thread. Tried using a delegate, tried background worker.

The only way I can get it work is to pass a reference to the thread and then use that reference to update the control. Very much like what you did by passing the "LB".

I have never seen the controls "InvokeRequired" property return 'true'
But the control updates.  Is this thread safe ?
Put it all back and included the way that you did it, don't know if it will ever use the delegate ?

Works now, but I don't understand why I have to pass the reference all around ?
If your control updates, then you are not trying to update from a different thread.  If you do not Invoke from a different thread you will get a Cross-Thread exception thrown.

Proof of concept -

We know my code above works because the example clearly shows where invoke is being called via the messages that are written to the console.  What happens if I don't use the delegate:

Form1.vb -
Imports System.Threading.Tasks
Imports System.ComponentModel
Imports System.Runtime.CompilerServices

Public Class Form1
	Private Sub OnClick(sender As Object, e As EventArgs) Handles Button2.Click, Button1.Click
		If TypeOf sender Is Button Then
			Dim btn = DirectCast(sender, Button)
			If btn.Equals(Button1) Then
				Task.Factory.StartNew(Sub()
									  For Each [Item] In (From i In Enumerable.Range(ListBox1.Items.Count, 20) Select String.Format("Message{0}", i))
										  ListBox1.Items.Add([Item])
									  Next
								  End Sub)
			ElseIf btn.Equals(Button2) Then
				Task.Factory.StartNew(Sub()
									  For Each [Item] In ListBox1.Items.Cast(Of Object).Reverse()
										  ListBox1.Items.Remove([Item])
									  Next
								  End Sub)
			End If
		End If
	End Sub
End Class

Open in new window

Form1.Designer.vb -
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class Form1
    Inherits System.Windows.Forms.Form

    'Form overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
		Me.ListBox1 = New System.Windows.Forms.ListBox()
		Me.Button1 = New System.Windows.Forms.Button()
		Me.Button2 = New System.Windows.Forms.Button()
		Me.SuspendLayout()
		'
		'ListBox1
		'
		Me.ListBox1.FormattingEnabled = True
		Me.ListBox1.Location = New System.Drawing.Point(13, 13)
		Me.ListBox1.Name = "ListBox1"
		Me.ListBox1.Size = New System.Drawing.Size(259, 212)
		Me.ListBox1.TabIndex = 0
		'
		'Button1
		'
		Me.Button1.Location = New System.Drawing.Point(96, 226)
		Me.Button1.Name = "Button1"
		Me.Button1.Size = New System.Drawing.Size(95, 23)
		Me.Button1.TabIndex = 1
		Me.Button1.Text = "Add 20 Items"
		Me.Button1.UseVisualStyleBackColor = True
		'
		'Button2
		'
		Me.Button2.Location = New System.Drawing.Point(197, 226)
		Me.Button2.Name = "Button2"
		Me.Button2.Size = New System.Drawing.Size(75, 23)
		Me.Button2.TabIndex = 2
		Me.Button2.Text = "Clear"
		Me.Button2.UseVisualStyleBackColor = True
		'
		'Form1
		'
		Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
		Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
		Me.ClientSize = New System.Drawing.Size(284, 261)
		Me.Controls.Add(Me.Button2)
		Me.Controls.Add(Me.Button1)
		Me.Controls.Add(Me.ListBox1)
		Me.Name = "Form1"
		Me.Text = "Form1"
		Me.ResumeLayout(False)

	End Sub
	Friend WithEvents ListBox1 As System.Windows.Forms.ListBox
	Friend WithEvents Button1 As System.Windows.Forms.Button
	Friend WithEvents Button2 As System.Windows.Forms.Button

End Class

Open in new window

InvalidOperationException caused by an invalid Cross-thread operation -User generated image
Let's correct this:

Form1.vb -
Imports System.Threading.Tasks
Imports System.ComponentModel
Imports System.Runtime.CompilerServices

Public Class Form1
	Private Sub OnClick(sender As Object, e As EventArgs) Handles Button2.Click, Button1.Click
		If TypeOf sender Is Button Then
			Dim btn = DirectCast(sender, Button)
			If btn.Equals(Button1) Then
				Task.Factory.StartNew(Sub()
									  For Each [Item] In (From i In Enumerable.Range(ListBox1.Items.Count, 20) Select String.Format("Message{0}", i))
										  If ListBox1.InvokeRequired Then
											  Console.WriteLine("Invoking the additon of {0}", [Item])
											  ListBox1.Invoke(New MethodInvoker(Sub()
																			 Console.WriteLine("Adding {0}; to ListBox", [Item])
																			 ListBox1.Items.Add([Item])
																		 End Sub))
										  Else
											  Console.WriteLine("Adding {0}; to ListBox", [Item])
											  ListBox1.Items.Add([Item])
										  End If
									  Next
								  End Sub)
			ElseIf btn.Equals(Button2) Then
				Task.Factory.StartNew(Sub()
									  For Each [Item] In ListBox1.Items.Cast(Of Object).Reverse()
										  If ListBox1.InvokeRequired Then
											  Console.WriteLine("Invoking the removal of {0}", [Item])
											  ListBox1.Invoke(New MethodInvoker(Sub()
																			 Console.WriteLine("Removing {0}; from ListBox", [Item])
																			 ListBox1.Items.Remove([Item])
																		 End Sub))
										  Else
											  Console.WriteLine("Removing {0}; from ListBox", [Item])
											  ListBox1.Items.Remove([Item])
										  End If
									  Next
								  End Sub)
			End If
		End If
	End Sub
End Class

Module Extensions
	<Extension()> _
	Public Sub HandleInvokeRequired(Of T As {Control, ISynchronizeInvoke})(control As T, action As Action(Of T))
		'Check to see is the control is not null
		If control Is Nothing Then
			Throw New ArgumentNullException(String.Format("Cannot execute {0} on {1}.  {1} is null.", action, control))
		End If

		'Check to see if the control is disposed.
		If TypeOf control Is Control AndAlso TryCast(control, Control).IsDisposed Then
			Throw New ObjectDisposedException(String.Format("Cannot execute {0} on {1}.  {1} is disposed.", action, control))
		End If

		'Check to see if the handle is created for the control.
		If TypeOf control Is Control AndAlso Not TryCast(control, Control).IsHandleCreated Then
			Throw New InvalidOperationException(String.Format("Cannot execute {0} on {1}.  Handle is not created for {1}.", action, control))
		End If

		'Check to see if the control's InvokeRequired property is true
		If control.InvokeRequired Then
			Try
				'Use Invoke() to invoke your action
				Console.WriteLine("Invoking the action {0}; on {1}", action, control)
				control.Invoke(action, New Object() {control})
			Catch ex As Exception
				Throw New Exception(String.Format("Cannot execute {0} on {1}.  {2}.", action, control, ex.Message))
			End Try
		Else
			Try
				'Perform the action
				Console.WriteLine("Performing the action {0}; on {1}", action, control)
				action(control)
			Catch ex As Exception
				Throw New Exception(String.Format("Cannot execute {0} on {1}.  {2}.", action, control, ex.Message))
			End Try
		End If
	End Sub
End Module

Open in new window


Now gives us the expected output (notice without even using a delegate).

-saige-
it_saige:  Do you recommend the following statement recommended my MCI on this question:

Control.CheckForIllegalCrossThreadCalls = False

Also, I am using an older version of VB.Net 8. It does not recognize "Imports System.Threading.Tasks"
Do I need a new version ?  it this what I am fighting ?
No I don't recommend using the recommendation by MCI.  You are not solving the issue, you are simply stopping the exception from being thrown.  The whole purpose of the exception is to tell you that something is wrong; in this case the problem, in a nutshell, is that two (or more) threads are being allowed to access and update a control at the same time.  This is a problem because now you have no guarantee as to the state of the control.

The thread that created the control may think that the control is disabled while any of your additional threads believe that the control is enabled; Or worse, the thread that created the handle for the control closes thereby disposing of the control handle, now one of your additional threads try to access, whoops Object disposed exception (or vice versa).  In these cases, you are experiencing concurrency discrepancies caused by race conditions.

To demonstrate race conditions let's take our original application and add a race button to it -

Form1.vb -
Imports System.Threading.Tasks
Imports System.ComponentModel

Public Class Form1
	Private Sub OnClick(sender As Object, e As EventArgs) Handles Button3.Click, Button2.Click, Button1.Click
		If TypeOf sender Is Button Then
			Dim btn = DirectCast(sender, Button)
			If btn.Equals(Button1) Then
				Task.Factory.StartNew(Sub()
									  For Each [Item] In (From i In Enumerable.Range(ListBox1.Items.Count, 20) Select String.Format("Message{0}", i))
										  If ListBox1.InvokeRequired Then
											  Console.WriteLine("Invoking the additon of {0}", [Item])
											  ListBox1.Invoke(New MethodInvoker(Sub()
																			 Console.WriteLine("Adding {0}; to ListBox", [Item])
																			 ListBox1.Items.Add([Item])
																		 End Sub))
										  Else
											  Console.WriteLine("Adding {0}; to ListBox", [Item])
											  ListBox1.Items.Add([Item])
										  End If
									  Next
								  End Sub)
			ElseIf btn.Equals(Button2) Then
				Task.Factory.StartNew(Sub()
									  For Each [Item] In ListBox1.Items.Cast(Of Object).Reverse()
										  If ListBox1.InvokeRequired Then
											  Console.WriteLine("Invoking the removal of {0}", [Item])
											  ListBox1.Invoke(New MethodInvoker(Sub()
																			 Console.WriteLine("Removing {0}; from ListBox", [Item])
																			 ListBox1.Items.Remove([Item])
																		 End Sub))
										  Else
											  Console.WriteLine("Removing {0}; from ListBox", [Item])
											  ListBox1.Items.Remove([Item])
										  End If
									  Next
								  End Sub)
			ElseIf btn.Equals(Button3) Then
				Dim updaters As New List(Of Task)
				For i = 0 To 10
					Dim j As Integer = i
					updaters.Add(Task.Factory.StartNew(Sub() UpdateButtons(j + 1)))
				Next

				Task.WaitAll(updaters.ToArray())
				Console.WriteLine("Main Thread:  Button1: {0}; Button2: {1}", If(Button1.Enabled, "Enabled", "Disabled"), If(Button2.Enabled, "Enabled", "Disabled"))
			End If
		End If
	End Sub

	Sub UpdateButtons(i As Integer)
		Button1.Enabled = Not Button1.Enabled
		Button2.Enabled = Not Button2.Enabled
		Console.WriteLine("Thread {0}: Button1: {1}; Button2: {2}", i, If(Button1.Enabled, "Enabled", "Disabled"), If(Button2.Enabled, "Enabled", "Disabled"))
	End Sub

	Private Sub OnLoad(sender As Object, e As EventArgs) Handles MyBase.Load
		Button1.Enabled = False
		Button2.Enabled = False
		Console.WriteLine("Main Thread:  Button1: {0}; Button2: {1}", If(Button1.Enabled, "Enabled", "Disabled"), If(Button2.Enabled, "Enabled", "Disabled"))
		CheckForIllegalCrossThreadCalls = False
	End Sub
End Class

Open in new window

Form1.Designer.vb -
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class Form1
    Inherits System.Windows.Forms.Form

    'Form overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
		Me.ListBox1 = New System.Windows.Forms.ListBox()
		Me.Button1 = New System.Windows.Forms.Button()
		Me.Button2 = New System.Windows.Forms.Button()
		Me.Button3 = New System.Windows.Forms.Button()
		Me.SuspendLayout()
		'
		'ListBox1
		'
		Me.ListBox1.FormattingEnabled = True
		Me.ListBox1.Location = New System.Drawing.Point(13, 13)
		Me.ListBox1.Name = "ListBox1"
		Me.ListBox1.Size = New System.Drawing.Size(259, 212)
		Me.ListBox1.TabIndex = 0
		'
		'Button1
		'
		Me.Button1.Location = New System.Drawing.Point(96, 226)
		Me.Button1.Name = "Button1"
		Me.Button1.Size = New System.Drawing.Size(95, 23)
		Me.Button1.TabIndex = 1
		Me.Button1.Text = "Add 20 Items"
		Me.Button1.UseVisualStyleBackColor = True
		'
		'Button2
		'
		Me.Button2.Location = New System.Drawing.Point(197, 226)
		Me.Button2.Name = "Button2"
		Me.Button2.Size = New System.Drawing.Size(75, 23)
		Me.Button2.TabIndex = 2
		Me.Button2.Text = "Clear"
		Me.Button2.UseVisualStyleBackColor = True
		'
		'Button3
		'
		Me.Button3.Location = New System.Drawing.Point(12, 226)
		Me.Button3.Name = "Button3"
		Me.Button3.Size = New System.Drawing.Size(75, 23)
		Me.Button3.TabIndex = 3
		Me.Button3.Text = "Race"
		Me.Button3.UseVisualStyleBackColor = True
		'
		'Form1
		'
		Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
		Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
		Me.ClientSize = New System.Drawing.Size(284, 261)
		Me.Controls.Add(Me.Button3)
		Me.Controls.Add(Me.Button2)
		Me.Controls.Add(Me.Button1)
		Me.Controls.Add(Me.ListBox1)
		Me.Name = "Form1"
		Me.Text = "Form1"
		Me.ResumeLayout(False)

	End Sub
	Friend WithEvents ListBox1 As System.Windows.Forms.ListBox
	Friend WithEvents Button1 As System.Windows.Forms.Button
	Friend WithEvents Button2 As System.Windows.Forms.Button
	Friend WithEvents Button3 As System.Windows.Forms.Button
End Class

Open in new window

The expectation is that when we click the race button that both the add and clear buttons are enabled.  The results, however, are hardly the expectation:
First run -User generated imageSecond run -User generated imageThird run -User generated imageAs you can see on two of the runs we got the expected results, but one run gave us unexpected results.  Also take note of the thread ordering (not that it matters for our situation because 11 state changes should ultimately result in each button being enabled from an initial disabled state).

What about with using the Invoke method, does this correct our inconsistent results?  Let's find out:

Form1.vb -
Imports System.Threading.Tasks
Imports System.ComponentModel

Public Class Form1
	Private Sub OnClick(sender As Object, e As EventArgs) Handles Button3.Click, Button2.Click, Button1.Click
		If TypeOf sender Is Button Then
			Dim btn = DirectCast(sender, Button)
			If btn.Equals(Button1) Then
				Task.Factory.StartNew(Sub()
									  For Each [Item] In (From i In Enumerable.Range(ListBox1.Items.Count, 20) Select String.Format("Message{0}", i))
										  If ListBox1.InvokeRequired Then
											  Console.WriteLine("Invoking the additon of {0}", [Item])
											  ListBox1.Invoke(New MethodInvoker(Sub()
																			 Console.WriteLine("Adding {0}; to ListBox", [Item])
																			 ListBox1.Items.Add([Item])
																		 End Sub))
										  Else
											  Console.WriteLine("Adding {0}; to ListBox", [Item])
											  ListBox1.Items.Add([Item])
										  End If
									  Next
								  End Sub)
			ElseIf btn.Equals(Button2) Then
				Task.Factory.StartNew(Sub()
									  For Each [Item] In ListBox1.Items.Cast(Of Object).Reverse()
										  If ListBox1.InvokeRequired Then
											  Console.WriteLine("Invoking the removal of {0}", [Item])
											  ListBox1.Invoke(New MethodInvoker(Sub()
																			 Console.WriteLine("Removing {0}; from ListBox", [Item])
																			 ListBox1.Items.Remove([Item])
																		 End Sub))
										  Else
											  Console.WriteLine("Removing {0}; from ListBox", [Item])
											  ListBox1.Items.Remove([Item])
										  End If
									  Next
								  End Sub)
			ElseIf btn.Equals(Button3) Then
				Dim updaters As New List(Of Task)
				For i = 0 To 10
					Dim j As Integer = i
					updaters.Add(Task.Factory.StartNew(Sub() UpdateButtons(j + 1)))
				Next

				Task.WaitAll(updaters.ToArray())
				Console.WriteLine("Main Thread:  Button1: {0}; Button2: {1}", If(Button1.Enabled, "Enabled", "Disabled"), If(Button2.Enabled, "Enabled", "Disabled"))
			End If
		End If
	End Sub

	Sub UpdateButtons(i As Integer)
		If Button1.InvokeRequired Then
			Button1.BeginInvoke(New MethodInvoker(Sub() Button1.Enabled = Not Button1.Enabled))
		Else
			Button1.Enabled = Not Button1.Enabled
		End If

		If Button2.InvokeRequired Then
			Button2.BeginInvoke(New MethodInvoker(Sub() Button2.Enabled = Not Button2.Enabled))
		Else
			Button2.Enabled = Not Button2.Enabled
		End If
		Console.WriteLine("Thread {0}: Button1: {1}; Button2: {2}", i, If(Button1.Enabled, "Enabled", "Disabled"), If(Button2.Enabled, "Enabled", "Disabled"))
	End Sub

	Private Sub OnLoad(sender As Object, e As EventArgs) Handles MyBase.Load
		Button1.Enabled = False
		Button2.Enabled = False
		Console.WriteLine("Main Thread:  Button1: {0}; Button2: {1}", If(Button1.Enabled, "Enabled", "Disabled"), If(Button2.Enabled, "Enabled", "Disabled"))
	End Sub
End Class

Open in new window

First run -User generated imageSecond run -User generated imageThird run -User generated imageConsistency is obtained.

As for the second part of your question, your assumption is correct in that System.Threading.Tasks was not made available until .NET 4.0.  VB8 (released as Visual Basic 2005) does not support .NET 4.0.  It does, however, support .NET 2.0 and .NET 3.0 (with an update if I remember correctly).

That being said, the following example should work for you in VS 2005/VB 2005 -

Form1.vb -
Imports System.Threading

Public Class Form1
	Private Sub OnClick(sender As Object, e As EventArgs) Handles Button2.Click, Button1.Click
		If TypeOf sender Is Button Then
			Dim btn = DirectCast(sender, Button)
			If btn.Equals(Button1) Then
				Dim lbUpdater = New Thread(AddressOf AddItems)
				lbUpdater.IsBackground = True
				lbUpdater.Start()
			ElseIf btn.Equals(Button2) Then
				Dim lbUpdator = New Thread(AddressOf RemoveItems)
				lbUpdator.IsBackground = True
				lbUpdator.Start()
			End If
		End If
	End Sub

	Delegate Sub AddItemDelegate(lb As ListBox, message As String)
	Delegate Sub RemoveItemDelegate(lb As ListBox, item As Object)

	Sub AddItems()
		For i As Integer = ListBox1.Items.Count To ListBox1.Items.Count + 20
			AddItem(ListBox1, String.Format("Message{0}", i))
		Next
	End Sub

	Sub RemoveItems()
		For i As Integer = ListBox1.Items.Count - 1 To 0 Step -1
			RemoveItem(ListBox1, ListBox1.Items(i))
		Next
	End Sub

	Sub AddItem(lb As ListBox, message As String)
		If lb.InvokeRequired Then
			Console.WriteLine("Invoking the addition of {0}", message)
			lb.Invoke(New AddItemDelegate(AddressOf AddItem), New Object() {lb, message})
		Else
			Console.WriteLine("Adding {0}; to Listbox", message)
			lb.Items.Add(message)
		End If
	End Sub

	Sub RemoveItem(lb As ListBox, item As Object)
		If lb.InvokeRequired Then
			Console.WriteLine("Invoking the removal of {0}", item)
			lb.Invoke(New RemoveItemDelegate(AddressOf RemoveItem), New Object() {lb, item})
		Else
			Console.WriteLine("Removing {0}; from Listbox", item)
			lb.Items.Remove(item)
		End If
	End Sub
End Class

Open in new window

Form1.Designer.vb -
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class Form1
	Inherits System.Windows.Forms.Form

	'Form overrides dispose to clean up the component list.
	<System.Diagnostics.DebuggerNonUserCode()> _
	Protected Overrides Sub Dispose(ByVal disposing As Boolean)
		Try
			If disposing AndAlso components IsNot Nothing Then
				components.Dispose()
			End If
		Finally
			MyBase.Dispose(disposing)
		End Try
	End Sub

	'Required by the Windows Form Designer
	Private components As System.ComponentModel.IContainer

	'NOTE: The following procedure is required by the Windows Form Designer
	'It can be modified using the Windows Form Designer.  
	'Do not modify it using the code editor.
	<System.Diagnostics.DebuggerStepThrough()> _
	Private Sub InitializeComponent()
		Me.ListBox1 = New System.Windows.Forms.ListBox()
		Me.Button1 = New System.Windows.Forms.Button()
		Me.Button2 = New System.Windows.Forms.Button()
		Me.SuspendLayout()
		'
		'ListBox1
		'
		Me.ListBox1.FormattingEnabled = True
		Me.ListBox1.Location = New System.Drawing.Point(13, 13)
		Me.ListBox1.Name = "ListBox1"
		Me.ListBox1.Size = New System.Drawing.Size(259, 212)
		Me.ListBox1.TabIndex = 0
		'
		'Button1
		'
		Me.Button1.Location = New System.Drawing.Point(96, 226)
		Me.Button1.Name = "Button1"
		Me.Button1.Size = New System.Drawing.Size(95, 23)
		Me.Button1.TabIndex = 1
		Me.Button1.Text = "Add 20 Items"
		Me.Button1.UseVisualStyleBackColor = True
		'
		'Button2
		'
		Me.Button2.Location = New System.Drawing.Point(197, 226)
		Me.Button2.Name = "Button2"
		Me.Button2.Size = New System.Drawing.Size(75, 23)
		Me.Button2.TabIndex = 2
		Me.Button2.Text = "Clear"
		Me.Button2.UseVisualStyleBackColor = True
		'
		'Form1
		'
		Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
		Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
		Me.ClientSize = New System.Drawing.Size(284, 261)
		Me.Controls.Add(Me.Button2)
		Me.Controls.Add(Me.Button1)
		Me.Controls.Add(Me.ListBox1)
		Me.Name = "Form1"
		Me.Text = "Form1"
		Me.ResumeLayout(False)

	End Sub
	Friend WithEvents ListBox1 As System.Windows.Forms.ListBox
	Friend WithEvents Button1 As System.Windows.Forms.Button
	Friend WithEvents Button2 As System.Windows.Forms.Button

End Class

Open in new window

-saige-
Your example is easy to follow, but I can't reproduce it because I never got beyond:
"Imports System.Threading.Tasks"  

"Does not contain a public member and cannot be found"

Do I need to use another version of VB other than 8 ?
Yes, in order to use the code dependent upon System.Threading.Tasks, you need VB 10 (Visual Studio 2010) or higher, targeting .NET 4.0 or higher.  The most current version, VS 2015, is available in a community edition for free if you qualify.

https://www.visualstudio.com/products/visual-studio-community-vs

-saige-
Can I get it done with using vb 8 using .Net 3.5 Using Delegates ?
That's currently what we have.
It may have been lost in the message, but I did include a version that should work with VB 8 in my response here: https:/Q_28964240.html?anchor=a41765714#a41765639

Reposting just the compatible portion of the code:

Form1.vb -
Imports System.Threading

Public Class Form1
	Private Sub OnClick(sender As Object, e As EventArgs) Handles Button2.Click, Button1.Click
		If TypeOf sender Is Button Then
			Dim btn = DirectCast(sender, Button)
			If btn.Equals(Button1) Then
				Dim lbUpdater = New Thread(AddressOf AddItems)
				lbUpdater.IsBackground = True
				lbUpdater.Start()
			ElseIf btn.Equals(Button2) Then
				Dim lbUpdator = New Thread(AddressOf RemoveItems)
				lbUpdator.IsBackground = True
				lbUpdator.Start()
			End If
		End If
	End Sub

	Delegate Sub AddItemDelegate(lb As ListBox, message As String)
	Delegate Sub RemoveItemDelegate(lb As ListBox, item As Object)

	Sub AddItems()
		For i As Integer = ListBox1.Items.Count To ListBox1.Items.Count + 20
			AddItem(ListBox1, String.Format("Message{0}", i))
		Next
	End Sub

	Sub RemoveItems()
		For i As Integer = ListBox1.Items.Count - 1 To 0 Step -1
			RemoveItem(ListBox1, ListBox1.Items(i))
		Next
	End Sub

	Sub AddItem(lb As ListBox, message As String)
		If lb.InvokeRequired Then
			Console.WriteLine("Invoking the addition of {0}", message)
			lb.Invoke(New AddItemDelegate(AddressOf AddItem), New Object() {lb, message})
		Else
			Console.WriteLine("Adding {0}; to Listbox", message)
			lb.Items.Add(message)
		End If
	End Sub

	Sub RemoveItem(lb As ListBox, item As Object)
		If lb.InvokeRequired Then
			Console.WriteLine("Invoking the removal of {0}", item)
			lb.Invoke(New RemoveItemDelegate(AddressOf RemoveItem), New Object() {lb, item})
		Else
			Console.WriteLine("Removing {0}; from Listbox", item)
			lb.Items.Remove(item)
		End If
	End Sub
End Class

Open in new window


Form1.Designer.vb -
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class Form1
	Inherits System.Windows.Forms.Form

	'Form overrides dispose to clean up the component list.
	<System.Diagnostics.DebuggerNonUserCode()> _
	Protected Overrides Sub Dispose(ByVal disposing As Boolean)
		Try
			If disposing AndAlso components IsNot Nothing Then
				components.Dispose()
			End If
		Finally
			MyBase.Dispose(disposing)
		End Try
	End Sub

	'Required by the Windows Form Designer
	Private components As System.ComponentModel.IContainer

	'NOTE: The following procedure is required by the Windows Form Designer
	'It can be modified using the Windows Form Designer.  
	'Do not modify it using the code editor.
	<System.Diagnostics.DebuggerStepThrough()> _
	Private Sub InitializeComponent()
		Me.ListBox1 = New System.Windows.Forms.ListBox()
		Me.Button1 = New System.Windows.Forms.Button()
		Me.Button2 = New System.Windows.Forms.Button()
		Me.SuspendLayout()
		'
		'ListBox1
		'
		Me.ListBox1.FormattingEnabled = True
		Me.ListBox1.Location = New System.Drawing.Point(13, 13)
		Me.ListBox1.Name = "ListBox1"
		Me.ListBox1.Size = New System.Drawing.Size(259, 212)
		Me.ListBox1.TabIndex = 0
		'
		'Button1
		'
		Me.Button1.Location = New System.Drawing.Point(96, 226)
		Me.Button1.Name = "Button1"
		Me.Button1.Size = New System.Drawing.Size(95, 23)
		Me.Button1.TabIndex = 1
		Me.Button1.Text = "Add 20 Items"
		Me.Button1.UseVisualStyleBackColor = True
		'
		'Button2
		'
		Me.Button2.Location = New System.Drawing.Point(197, 226)
		Me.Button2.Name = "Button2"
		Me.Button2.Size = New System.Drawing.Size(75, 23)
		Me.Button2.TabIndex = 2
		Me.Button2.Text = "Clear"
		Me.Button2.UseVisualStyleBackColor = True
		'
		'Form1
		'
		Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
		Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
		Me.ClientSize = New System.Drawing.Size(284, 261)
		Me.Controls.Add(Me.Button2)
		Me.Controls.Add(Me.Button1)
		Me.Controls.Add(Me.ListBox1)
		Me.Name = "Form1"
		Me.Text = "Form1"
		Me.ResumeLayout(False)

	End Sub
	Friend WithEvents ListBox1 As System.Windows.Forms.ListBox
	Friend WithEvents Button1 As System.Windows.Forms.Button
	Friend WithEvents Button2 As System.Windows.Forms.Button

End Class

Open in new window


-saige-
Ok, found issue, changed:

      Sub AddItem(lb As ListBox, message As String)
            If lstBox.InvokeRequired Then    'My Line


      Sub AddItem(lb As ListBox, message As String)
            If lb.InvokeRequired Then   'To your line

This shows me how to update the controls, Now I need to get to vars on form module ?
In this case lb is the ListBox, but if you need form instance specific access, you should be able to use the FindForm method to find the form that the control is bound to; e.g. -
Imports System.Threading

Public Class Form1
	Private SomeText As String = "SomeText"

	Private Sub OnClick(sender As Object, e As EventArgs) Handles Button2.Click, Button1.Click
		If TypeOf sender Is Button Then
			Dim btn = DirectCast(sender, Button)
			If btn.Equals(Button1) Then
				Dim lbUpdater = New Thread(AddressOf AddItems)
				lbUpdater.IsBackground = True
				lbUpdater.Start()
			ElseIf btn.Equals(Button2) Then
				Dim lbUpdator = New Thread(AddressOf RemoveItems)
				lbUpdator.IsBackground = True
				lbUpdator.Start()
			End If
		End If
	End Sub

	Delegate Sub AddItemDelegate(lb As ListBox, message As String)
	Delegate Sub RemoveItemDelegate(lb As ListBox, item As Object)

	Sub AddItems()
		For i As Integer = ListBox1.Items.Count To ListBox1.Items.Count + 20
			AddItem(ListBox1, String.Format("Message{0}", i))
		Next
	End Sub

	Sub RemoveItems()
		For i As Integer = ListBox1.Items.Count - 1 To 0 Step -1
			RemoveItem(ListBox1, ListBox1.Items(i))
		Next
	End Sub

	Sub AddItem(lb As ListBox, message As String)
		If lb.InvokeRequired Then
			Console.WriteLine("Invoking the addition of {0}", message)
			lb.Invoke(New AddItemDelegate(AddressOf AddItem), New Object() {lb, message})
		Else
			Console.WriteLine("Adding {0}; to Listbox", message)
			Dim instance = lb.FindForm()
			Console.WriteLine(CType(instance, Form1).SomeText)
			lb.Items.Add(message)
		End If
	End Sub

	Sub RemoveItem(lb As ListBox, item As Object)
		If lb.InvokeRequired Then
			Console.WriteLine("Invoking the removal of {0}", item)
			lb.Invoke(New RemoveItemDelegate(AddressOf RemoveItem), New Object() {lb, item})
		Else
			Console.WriteLine("Removing {0}; from Listbox", item)
			lb.Items.Remove(item)
		End If
	End Sub
End Class

Open in new window


-saige-
it_saige, still working on this project. its just a little busy here.

I plan on posting a mock of project demoing the issue in a day or two.

Thank you.
Thanks for the update.  No worries, been busy on this side of the internet as well.  ;)

-saige-
Here is a mock up of the project.
For the most part it works.
Issues:
   1.      You have to pass a reference to the listbox (or control you want to update) to your new threads.
   2.      If you try to broadcast a banner on the new connection, the app becomes un-stable. (event just the first one!?)
   3.      when I test the app from a VB Classic app, I always get double/repeated text strings arriving at the client ?
      -> Can't reproduce this from a telnet client.



'*** form Code ***
Public Class frmTest01

    Private Sub cmdStart_Click(sender As System.Object, e As System.EventArgs) Handles cmdStart.Click

        bListening01 = True
        StartListening01(Me.lstEvents)	'Call the listening function, and pass a reference to the listbox "lstEvents"

    End Sub

    Public Function ListEvent00(ByVal Msg As String) As Integer
        ListEvent01(Me.lstEvents, Msg)

    End Function

    Public Delegate Function DelListEvent(ByVal LB As ListBox, ByVal Msg As String) As Integer

    Public Function ListEvent01(ByVal msg As String) As Integer

        ListEvent01(lstEvents, msg)

    End Function

    Public Function ListEvent01(ByVal LB As ListBox, ByVal Msg As String) As Integer

        Dim iRet As Integer = 0
        Dim sDS As String = ""
        Dim sMsg As String = ""
        Dim sMsg_p As String = ""
        Dim iPos As Integer = -1

        'If lstEvents.InvokeRequired Then
        If LB.InvokeRequired Then
            'iRet = lstEvents.Invoke(New MethodInvoker(AddressOf ListEvent))
            Dim d As New DelListEvent(AddressOf ListEvent01)

            'lstEvents.BeginInvoke(New DelListEvent(AddressOf ListEvent), Msg)
            LB.BeginInvoke(d, New Object() {LB, Msg})
        Else
            sDS = Now.ToString("yy/MM/dd HH:mm:ss.fff")
            sMsg = sDS & " > " & Msg

            iPos = sMsg.IndexOf("~")
            If iPos > -1 Then
                Do Until iPos = -1
                    sMsg_p = sMsg.Substring(0, iPos)
                    LB.Items.Add(sMsg_p)

                    sMsg = sMsg.Substring(iPos + 1)
                    iPos = sMsg.IndexOf("~")
                Loop

                LB.Items.Add(sMsg)
            Else
                LB.Items.Add(sMsg)
            End If

            'Select the last item.
            LB.SelectedIndex = LB.Items.Count - 1

            Do Until LB.Items.Count < 11
                LB.Items.RemoveAt(0)
                Application.DoEvents()
            Loop

            Application.DoEvents()
        End If

    End Function

    Private Sub frmTest01_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        bListening01 = False
    End Sub

End Class

Open in new window

'*** Module Code ***
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
'Imports System.Threading.tasks
Imports System.ComponentModel

Module modTesting01

    Public sBanner As String = " Your are connected to the TCP Listener !" & vbCrLf
    Public iThreadCount01 As Integer = 0
    Public iConnectionCnt01 As Integer = 0
    Public dServiceUpTime As Date = Now
    Public dStartedListening01 As Date
    Public bSendBanner As Boolean = True
    Public bListening01 As Boolean = False
    Public sTargetFolder As String = "C:\Projects\TestFiles\"
    Public iCounter As Integer = 0
    Public bLogDataRec01 As Boolean = False

    Sub StartListening01(ByVal lsb As ListBox)
        On Error Resume Next

        Dim iPort As Integer = 9100

	'Here its ok not to pass a reference to the listbox, we are not in a separate thread.
        frmTest01.ListEvent01("Listener Started on Port: " & iPort.ToString)

        sTargetFolder = frmListener.txtTargetFolder.Text
        Dim LclIP As IPAddress = IPAddress.Parse("127.0.0.1")
        Dim serverSocket As New TcpListener(LclIP, iPort)
        Dim clientSocket As TcpClient

        serverSocket.Start()
        iCounter = 0
        While (bListening01)

            If serverSocket.Pending Then
                iCounter += 1
                clientSocket = serverSocket.AcceptTcpClient()
                Application.DoEvents()
                Dim client As New handleClinet01

		'Start the thread and pass a reference to the listbox "lstEvents"
                client.startClient(clientSocket, lsb, iCounter)
                frmTest01.ListEvent01(lsb, "Connection accepted from " & clientSocket.Client.RemoteEndPoint.ToString & "  #" & iCounter.ToString("#,0"))

                'frmTest01.ListEvent01(lsb, "Process Thread Count: " & Process.GetCurrentProcess().Threads.Count.ToString("#,0"))

            End If
            Threading.Thread.Sleep(100)
            Application.DoEvents()

        End While

        clientSocket.Close()
        serverSocket.Stop()

        frmTest01.ListEvent01("Listener Stopped.")

    End Sub

    Sub msg(ByVal mesg As String)
        mesg.Trim()
        Console.WriteLine(" >> " + mesg)
    End Sub

    Public Class handleClinet01
        Dim clientSocket As TcpClient
        Dim frmLsb As ListBox
        Dim iCounter As Integer = 0

        Public Sub startClient(ByVal inClientSocket As TcpClient, ByVal lsb As ListBox, Counter As Integer)
            Me.clientSocket = inClientSocket
            Me.frmLsb = lsb
            Dim ctThread As Threading.Thread = New Threading.Thread(AddressOf Capture)
            ctThread.Priority = ThreadPriority.Highest
            ctThread.Start(lsb)	'and pass a reference to the listbox "lstEvents"
            iCounter = Counter

            ctThread = Nothing
            MyClass.Finalize()

        End Sub

        Private Sub Capture(ByVal lsb As ListBox)

            Dim requestCount As Integer = 0
            Dim bytesEmpty(10024) As Byte
            Dim bytesFrom(10024) As Byte
            Dim sDataFromClient As String = ""
            Dim sLineFromClient As String = ""
            Dim sendBytes As [Byte]()
            Dim serverResponse As String = ""
            Dim rCount As String = ""
            Dim iRead As Integer = -1
            Dim sFileName As String = Now.ToString("yyMMdd_HHmmss_fff") & ".txt"
            Dim sTargetFolder As String = "C:\Projects\TestFiles\"

            Dim sPath As String = sTargetFolder & sFileName
            Dim bBannerSent As Boolean = False
            Dim sRemoteAddr As String = clientSocket.Client.RemoteEndPoint.ToString
            Dim iByteCount As Integer = 0
            Dim iThread0 As Integer = 0

            Dim networkStream As NetworkStream = clientSocket.GetStream()

            'Writing anything back to the Socket causes the app to crash ??
            'If iCounter < 4 Then        'Only try to write the first few connections, otherwise the app will crash !
            '    sendBytes = Encoding.ASCII.GetBytes(sBanner)
            '    networkStream.Write(sendBytes, 0, sendBytes.Length)
            '    networkStream.Flush()
            'End If

            While (bListening01)
                Try
                    requestCount = requestCount + 1

                    'bytesFrom = bytesEmpty
                    iRead = networkStream.Read(bytesFrom, 0, CInt(clientSocket.ReceiveBufferSize))
                    'networkStream.Flush()
                    sDataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom)
                    sDataFromClient = sDataFromClient.Substring(0, sDataFromClient.IndexOf(Chr(0)))

                    iByteCount += sDataFromClient.Length

                    sLineFromClient &= sDataFromClient
                    sDataFromClient = ""

                    If sLineFromClient.IndexOf(vbCr) > -1 Then
                        IO.File.AppendAllText(sPath, sLineFromClient)
                        sLineFromClient = ""
                    End If

                    If iRead = 0 Then   'We are disconnected.

			'Now the remote connection has closed, pass a reference to the listbox "lstEvents" or you will never get your form updated.
                        frmTest01.ListEvent01(frmLsb, "Connection closed from !   " & sRemoteAddr)

                        If sLineFromClient.Length > 0 Then

                            IO.File.AppendAllText(sPath, sLineFromClient)
                            sLineFromClient = ""
                        End If

                        Exit While
                    End If

                Catch ex As Exception
                    'MsgBox(ex.ToString)
                End Try

            End While


	    'Close everything up.
            networkStream.Dispose()
            Me.clientSocket.Close()

            Me.clientSocket = Nothing
            'Thread.CurrentThread.Abort()
            Application.DoEvents()

            'Beep()
        End Sub

    End Class

End Module

Open in new window

Look at this link:
http://www.codeproject.com/Articles/10533/When-a-thread-will-terminate-or-dispose

That makes this comment:
Remember: You don't have to declare a delegate for the procedure you want to use for threading in VB.NET, it will be done behind the scenes in contrast to C#.
ASKER CERTIFIED SOLUTION
Avatar of it_saige
it_saige
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I will try to upload those files tonight when I get home.
The "frmListener" is left over from a previous project. (just comment that line please)

The "frmTest01" is just a form with a button named "cmdStart" and a listbox named "lstEvents".  

I thought I provided everything, let me know if anything is left out.

Thanks
Thanks for the help, your code examples really helped.