VB.Net Timer Hanging Window - vb.net

I am trying to use the System.Threading class to create a timer which will not prevent the Form from repainting with results or hanging:
Imports System.Threading
Module TimerExample
Private TimerCallback As TimerCallback = New TimerCallback(AddressOf TimerTask)
Sub Main()
Dim Timer As Timer = New Timer(TimerCallback, Nothing, 0, 1000)
Console.WriteLine("Timer has started.")
Console.WriteLine("Press the Enter key to stop the timer...")
Console.ReadLine()
End Sub
Private Sub TimerTask(state As Object)
Console.WriteLine("Timer task executed at: " & DateTime.Now)
End Sub
End Module
Am I doing something incorrect?
From my understanding this must be done with the Threading class so that the code runs in a background thread, however it still seems to be hanging the window

Here is another way of using System.Timers.
Timer throw ElapsedEventArgs each timer.interval mS. it can be started or stopped at any time.
Imports System.Timers
Module TimerExample
Sub Main()
Dim mytimer As New System.Timers.Timer
AddHandler mytimer.Elapsed, New ElapsedEventHandler(AddressOf MyTimerElapsed)
mytimer.Interval = 1000
mytimer.Start()
Console.WriteLine("Timer has started.")
Console.WriteLine("Press the Enter key to stop the timer...")
Console.ReadLine()
End Sub
Public Sub MyTimerElapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs)
Console.WriteLine("Timer task executed at: " & DateTime.Now)
End Sub
End Module

Related

GUI form is randomly freeze

I am studying VB.NET and threading.
I tried thread method to dynamically update status on GUI part.
However, GUI randomly freeze after 10-20 mins running.
I am not sure what i missing.
On form1 : I have textbox and label to update on GUI
Imports System.IO
Imports System.Threading
Public Class form1
package.add("sleep_time",3)
Private Sub BT_START_Click(sender As Object, e As EventArgs) Handles BT_START.Click
Start(package)
end sub
public sub Start(package as hashtable)
Dim t As System.Threading.Thread
t = New Thread(New ThreadStart(Sub() Update.run(Me, Package)))
t.IsBackground = True
t.Start()
end sub
End Class
On run.vb file we update label: Label_status and textbox : text_box_status
Imports System.Threading
Imports System.IO
Public Class Update
public sub run(myform as form1, pakcage as hashtable)
for i to 10000
myform.Label_status.text = "Try: " + i.tostring()
update_text_box(myform.text_box_status, "Try: " + i.tostring())
Thread.Sleep(3* 1000)
end for
end sub
public sub update_text_box(MF as textbox, SYS_MSG as string)
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
MF.AppendText(SYS_MSG & vbNewLine)
MF.Select(MF.TextLength, 0)
MF.ScrollToCaret()
files.Write(SYS_MSG & vbNewLine)
end sub
end class
i check Thread updating GUI freezes in random moments
Windows Form application freeze randomly when run overnight
I am assuming that I am missing invoke or delegate methods
However, I am not clear sure why this happens. Is there anything that i missed to use thread and update Label and textbox on form?
thanks!
Try it like this instead:
private t As System.Threading.Thread
public sub Start(package as hashtable)
if isnothing(t) then
t = New Thread(New ThreadStart(Sub() Update.run(Me, Package)))
t.IsBackground = True
t.Start()
end if
end sub
With:
Public Sub run(myform As Form1, package As Hashtable)
For i As Integer = 1 To 10000
Dim msg As String = "Try: " + i.ToString()
myform.Invoke(Sub()
myform.Label_status.text = msg
Dim MF As TextBox = myform.text_box_status
MF.AppendText(SYS_MSG & vbNewLine)
MF.Select(MF.TextLength, 0)
MF.ScrollToCaret()
update_text_box(myform.text_box_status, msg)
End Sub)
files.Write(SYS_MSG & vbNewLine)
Thread.Sleep(TimeSpan.FromSeconds(3).TotalMilliseconds)
Next
End Sub

Windows Service started and then stopped while I did not found any error in the codes

I have created a service named "IranStockCodal" and Built and installed it "Sucessfully" on windows services, but when I start It a warning comes on windows services window:
"The "IranStockCodal" service on Local Computer started and then stopped. Some services stop automatically if they are not in use by other service or programs". The task "Call vbIranStock.VBNetLib.vbScript4.VbsRun4(False)" which is called works truly in console application, but when I import and call it from service it does not.
please help me what should be done to solve this.
Imports System.IO
Imports System.Threading
Imports System.Configuration
Imports vbIranStock.VBNetLib.vbScript4
Imports vbIranStock
Public Class Service1
Protected Overrides Sub OnStart(ByVal args() As String)
Me.WriteToFile("Iran Stock Codal started at " + DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss tt"))
Call vbIranStock.VBNetLib.vbScript4.VbsRun4(False)
Me.WriteToFile("Iran Stock Codal Task Called at " + DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss tt"))
Me.ScheduleService()
End Sub
Protected Overrides Sub OnStop()
Me.WriteToFile("Iran Stock Codal Service stopped at " + DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss tt"))
Me.Schedular.Dispose()
End Sub
Private Schedular As Timer
Public Sub ScheduleService()
Try
Schedular = New Timer(New TimerCallback(AddressOf SchedularCallback))
Dim mode As String = ConfigurationManager.AppSettings("Mode").ToUpper()
Me.WriteToFile((Convert.ToString("Iran Stock Codal Service Mode: ") & mode) + " {0}")
'Set the Default Time.
Dim scheduledTime As DateTime = DateTime.MinValue
If mode.ToUpper() = "DAILY" Then
'Get the Scheduled Time from AppSettings.
scheduledTime = DateTime.Parse(System.Configuration.ConfigurationManager.AppSettings("ScheduledTime"))
If DateTime.Now > scheduledTime Then
'If Scheduled Time is passed set Schedule for the next day.
scheduledTime = scheduledTime.AddDays(1)
End If
End If
If mode.ToUpper() = "INTERVAL" Then
'Get the Interval in Minutes from AppSettings.
Dim intervalMinutes As Integer = Convert.ToInt32(ConfigurationManager.AppSettings("IntervalMinutes"))
'Set the Scheduled Time by adding the Interval to Current Time.
scheduledTime = DateTime.Now.AddMinutes(intervalMinutes)
If DateTime.Now > scheduledTime Then
'If Scheduled Time is passed set Schedule for the next Interval.
scheduledTime = scheduledTime.AddMinutes(intervalMinutes)
End If
End If
Dim timeSpan As TimeSpan = scheduledTime.Subtract(DateTime.Now)
Dim schedule As String = String.Format("{0} day(s) {1} hour(s) {2} minute(s) {3} seconds(s)", timeSpan.Days, timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds)
Me.WriteToFile((Convert.ToString("Iran Stock Codal Service scheduled to run after: ") & schedule) + " {0}")
'Get the difference in Minutes between the Scheduled and Current Time.
Dim dueTime As Integer = Convert.ToInt32(timeSpan.TotalMilliseconds)
'Change the Timer's Due Time.
Schedular.Change(dueTime, Timeout.Infinite)
Catch ex As Exception
WriteToFile("Iran Stock Codal Service Error on: {0} " + ex.Message + ex.StackTrace)
'Stop the Windows Service.
Using serviceController As New System.ServiceProcess.ServiceController("IranStockCodal")
serviceController.[Stop]()
End Using
End Try
End Sub
Private Sub SchedularCallback(e As Object)
Me.WriteToFile("Iran Stock Codal Service Log: " + DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss tt"))
Me.ScheduleService()
End Sub
Private Sub WriteToFile(text As String)
Dim path As String = "C:\ServiceLog.txt"
Using writer As New StreamWriter(path, True)
writer.WriteLine(String.Format(text, DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss tt")))
writer.Close()
End Using
End Sub
End Class
Here is Project Installed Codes:
Imports System.ComponentModel
Imports System.Configuration.Install
Public Class ProjectInstaller
Public Sub New()
MyBase.New()
'This call is required by the Component Designer.
InitializeComponent()
'Add initialization code after the call to InitializeComponent
End Sub
Protected Overrides Sub OnAfterInstall(savedState As IDictionary)
MyBase.OnAfterInstall(savedState)
'The following code starts the services after it is installed.
Using serviceController As New System.ServiceProcess.ServiceController(ServiceInstaller1.ServiceName)
serviceController.Start()
End Using
End Sub
Private Sub ServiceInstaller1_AfterInstall(sender As Object, e As InstallEventArgs) Handles ServiceInstaller1.AfterInstall
End Sub
Private Sub ServiceProcessInstaller1_AfterInstall(sender As Object, e As InstallEventArgs) Handles ServiceProcessInstaller1.AfterInstall
End Sub
End Class
Here is ComponentModel codes
<System.ComponentModel.RunInstaller(True)> Partial Class ProjectInstaller
Inherits System.Configuration.Install.Installer
'Installer 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 Component Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Component Designer
'It can be modified using the Component Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()>
Private Sub InitializeComponent()
Me.ServiceProcessInstaller1 = New System.ServiceProcess.ServiceProcessInstaller()
Me.ServiceInstaller1 = New System.ServiceProcess.ServiceInstaller()
'
'ServiceProcessInstaller1
'
Me.ServiceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem
'
'ServiceInstaller1
'
Me.ServiceInstaller1.ServiceName = "IranStockCodal"
Me.ServiceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic
'
'ProjectInstaller
'
Me.Installers.AddRange(New System.Configuration.Install.Installer() {Me.ServiceProcessInstaller1, Me.ServiceInstaller1})
End Sub
Friend WithEvents ServiceProcessInstaller1 As System.ServiceProcess.ServiceProcessInstaller
Friend WithEvents ServiceInstaller1 As System.ServiceProcess.ServiceInstaller
End Class

Raise Event Vb.net from worker Thread

I'm looking at a console app for vb.net. I'm trying to get a worker thread to raise an event to the main thread to display data on the screen (the word "HIT" everytime the worker thread completes a cycle). My code is below.
I'm not sure why but the main thread's Private Sub CounterClass_GivingUpdate() Handles _counter.AboutToDistributeNewupdate isn't executing.
Imports System.Threading
Module Module1
Private WithEvents _counter As CounterClass
Private trd As Thread
Sub Main()
While True
Dim s As String = Console.ReadLine()
Dim started As Boolean
Select Case s
Case "status"
WriteStatusToConsole("You typed status")
Case "startcounter"
If started = False Then
starttheThread()
started = True
WriteStatusToConsole("You Have Started The Timer")
Else
WriteStatusToConsole("YOU HAVE ALREADY STARTED THE TIMER!!!")
End If
End Select
End While
End Sub
Private Sub CounterClass_GivingUpdate() Handles _counter.AboutToDistributeNewupdate
WriteStatusToConsole("Hit")
End Sub
Private Sub starttheThread()
Dim c As New CounterClass
trd = New Thread(AddressOf c.startProcess)
trd.Start()
End Sub
Sub WriteStatusToConsole(ByVal stringToDisplay As String)
Console.WriteLine(stringToDisplay)
End Sub
End Module
Public Class CounterClass
Public Event AboutToDistributeNewupdate()
Public Sub sendStatusUpdateEvent(ByVal updatestatus As String)
RaiseEvent AboutToDistributeNewupdate()
End Sub
Public Sub startProcess()
Dim i As Int64
Do
Thread.Sleep(1000)
i = i + 1
sendStatusUpdateEvent(i.ToString)
Loop
End Sub
End Class
Your CounterClass_GivingUpdate() only handles the _counter variable's event (the variable that you do not use!). Every time you declare a new CounterClass it has its own instance of the event that it raises.
You know have two options:
Option 1
Subscribe to the event for each new CounterClass instance you create. Meaning you must use the AddHandler statement every time you create a new instance of your class:
Private Sub starttheThread()
Dim c As New CounterClass
AddHandler c.AboutToDistributeNewupdate, AddressOf CounterClass_GivingUpdate
trd = New Thread(AddressOf c.startProcess)
trd.Start()
End Sub
Option 2
Mark the event as Shared to make it available without needing to create an instance of the class. For this you must also change how you subscribe to the event, by subscribing to it in your method Main():
Sub Main()
AddHandler CounterClass.AboutToDistributeNewupdate, AddressOf CounterClass_GivingUpdate
...the rest of your code...
End Sub
Private Sub CounterClass_GivingUpdate() 'No "Handles"-statement here.
WriteStatusToConsole("Hit")
End Sub
Public Class CounterClass
Public Shared Event AboutToDistributeNewupdate() 'Added the "Shared" keyword.
...the rest of your code...
End Class

Handling a timer from a class

I want some values in a class to decrease whenever the timer in the main form ticks. I am creating multiple instances of the same class as my program is a simulation application and I am not storing these instances in an array or any list in that matter. I simply declare them and add their picture box to the controls on the main form. However I am hoping to have a sub routine inside the class that triggers whenever the timer in the main form ticks. I thought of something like this:
Public Class Jimmy
Dim _a As Integer = 10
Sub decreseNum(sender As Object, e As EventArgs) Handles mainapp.tmrLog.Tick
_a -= 1
End Sub
End Class
with mainapp being the name of the main form and tmrLog being the timer I want to associate my sub routine with. However the above code doesn't work
You could try defining a local reference to the timer in the Jimmy class:
Public Class Jimmy
Dim _a As Integer = 10
Private WithEvents tmr As Timer
Public Sub New(ByRef MainTmr As Timer)
tmr = MainTmr
End Sub
Sub decreseNum(sender As Object, e As EventArgs) Handles tmr.Tick
_a -= 1
End Sub
End Class
If you want all your classes react to timer.elapsed event, just sign up for it. The program below is fully operational. It is example what you can do to have your children to react to timer events of single parent/timer
Imports System
imports system.timers
Public Module Module1
Public Sub Main()
dim mc as new MainClass()
mc.CreateChildren(5)
System.Threading.Thread.Sleep(60000) ' wait and monitor output of childern
mc.Stop()
Console.WriteLine("All should stop now...")
Console.Read()
End Sub
End Module
public class MainClass 'This class could be your form
private _timer as new Timer(5000)
public sub CreateChildren(count as integer)
For i as integer = 1 to count
dim c as new Child(i)
Addhandler _timer.Elapsed, addressof c.DoWhentimerTicks
next
Console.WriteLine("timer should run now...")
_timer.Start()
end sub
public sub [Stop]()
_timer.Stop()
End Sub
End class
public class Child
private _myNO as integer
public sub new (no as integer)
_myNo = no
end sub
public sub DoWhentimerTicks(sender as object , e as ElapsedEventArgs)
Console.WriteLine(string.format("Child #{0} just ticked. Time = {1}", _myNo, e.signaltime))
end sub
End class
I found my solution, posting here for further reference.
My situation was trying to have my timer in the mainform triggering a sub in a class, and I used the following solution.
Class:
Sub addHandlesToSub
AddHandler Form1.Timer1.Tick, AddressOf subToBeTriggered
End Sub
Sub subToBeTriggered(sender As Object, e As EventArgs)
'My code
End Sub
The parameters in subToBeTriggered are useful when you want to remove the handler with
RemoveHandler Form1.Timer1.Tick, AddressOf subToBeTriggered
Otherwise, there will be an error without the parameters.
Thanks for all the answers though.

Is there away to switch from a Worker Thread to the Main (UI) thread?

I apologize in advance if my question is too long-winded. I looked at the question “How to update data in GUI with messages that are being received by a thread of another class?” and it is very close to what I am trying to do but the answer was not detailed enough to be helpful.
I have converted a VB6 app to VB.NET (VS2013). The main function of the app is to send queries to a Linux server and display the results on the calling form. Since the WinSock control no longer exists, I’ve created a class to handle the functions associated with the TcpClient class. I can successfully connect to the server and send and receive data.
The problem is that I have multiple forms that use this class to send query messages to the server. The server responds with data to be displayed on the calling form. When I try to update a control on a form, I get the error "Cross-thread operation not valid: Control x accessed from a thread other than the thread it was created on." I know I’m supposed to use Control.InvokeRequired along with Control.Invoke in order to update controls on the Main/UI thread, but I can’t find a good, complete example in VB. Also, I have over 50 forms with a variety of controls on each form, I really don’t want to write a delegate handler for each control. I should also mention that the concept of threads and delegates is very new to me. I have been reading everything I can find on this subject for the past week or two, but I’m still stuck!
Is there some way to just switch back to the Main Thread? If not, is there a way I can use Control.Invoke just once to cover a multitude of controls?
I tried starting a thread just after connecting before I start sending and receiving data, but netStream.BeginRead starts its own thread once the callback function fires. I also tried using Read instead of BeginRead. It did not work well if there was a large amount of data in the response, BeginRead handled things better. I feel like Dorothy stuck in Oz, I just want to get home to the main thread!
Thanks in advance for any help you can provide.
Option Explicit On
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
Friend Class ATISTcpClient
Public Event Receive(ByVal data As String)
Private Shared WithEvents oRlogin As TcpClient
Private netStream As NetworkStream
Private BUFFER_SIZE As Integer = 8192
Private DataBuffer(BUFFER_SIZE) As Byte
Public Sub Connect()
Try
oRlogin = New Net.Sockets.TcpClient
Dim localIP As IPAddress = IPAddress.Parse(myIPAddress)
Dim localPrt As Int16 = myLocalPort
Dim ipLocalEndPoint As New IPEndPoint(localIP, localPrt)
oRlogin = New TcpClient(ipLocalEndPoint)
oRlogin.NoDelay = True
oRlogin.Connect(RemoteHost, RemotePort)
Catch e As ArgumentNullException
Debug.Print("ArgumentNullException: {0}", e)
Catch e As Net.Sockets.SocketException
Debug.Print("SocketException: {0}", e)
End Try
If oRlogin.Connected() Then
netStream = oRlogin.GetStream
If netStream.CanRead Then
netStream.BeginRead(DataBuffer, 0, BUFFER_SIZE, _
AddressOf DataArrival, DataBuffer)
End If
Send(vbNullChar)
Send(User & vbNullChar)
Send(User & vbNullChar)
Send(Term & vbNullChar)
End If
End Sub
Public Sub Send(newData As String)
On Error GoTo send_err
If netStream.CanWrite Then
Dim sendBytes As [Byte]() = Encoding.UTF8.GetBytes(newData)
netStream.Write(sendBytes, 0, sendBytes.Length)
End If
Exit Sub
send_err:
Debug.Print("Error in Send: " & Err.Number & " " & Err.Description)
End Sub
Private Sub DataArrival(ByVal dr As IAsyncResult)
'This is where it switches to a WorkerThread. It never switches back!
On Error GoTo dataArrival_err
Dim myReadBuffer(BUFFER_SIZE) As Byte
Dim myData As String = ""
Dim numberOfBytesRead As Integer = 0
numberOfBytesRead = netStream.EndRead(dr)
myReadBuffer = DataBuffer
myData = myData & Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)
Do While netStream.DataAvailable
numberOfBytesRead = netStream.Read(myReadBuffer, 0, myReadBuffer.Length)
myData = myData & Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)
Loop
'Send data back to calling form
RaiseEvent Receive(myData)
'Start reading again in case we don‘t have the entire response yet
If netStream.CanRead Then
netStream.BeginRead(DataBuffer, 0,BUFFER_SIZE,AddressOf DataArrival,DataBuffer)
End If
Exit Sub
dataArrival_err:
Debug.Print("Error in DataArrival: " & err.Number & err.Description)
End Sub
Instead of using delegates one could use anonymous methods.
Singleline:
uicontrol.Window.Invoke(Sub() ...)
Multiline:
uicontrol.Window.Invoke(
Sub()
...
End Sub
)
If you don't want to pass an UI control every time you need to invoke, create a custom application startup object.
Friend NotInheritable Class Program
Private Sub New()
End Sub
Public Shared ReadOnly Property Window() As Form
Get
Return Program.m_window
End Get
End Property
<STAThread()> _
Friend Shared Sub Main()
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
Dim window As New Form1()
Program.m_window = window
Application.Run(window)
End Sub
Private Shared m_window As Form
End Class
Now, you'll always have access to the main form of the UI thread.
Friend Class Test
Public Event Message(text As String)
Public Sub Run()
Program.Window.Invoke(Sub() RaiseEvent Message("Hello!"))
End Sub
End Class
In the following sample code, notice that the Asynchronous - Unsafe run will throw a Cross-thread exception.
Imports System.Threading
Imports System.Threading.Tasks
Public Class Form1
Public Sub New()
Me.InitializeComponent()
Me.cbOptions = New ComboBox() With {.TabIndex = 0, .Dock = DockStyle.Top, .DropDownStyle = ComboBoxStyle.DropDownList} : Me.cbOptions.Items.AddRange({"Asynchronous", "Synchronous"}) : Me.cbOptions.SelectedItem = "Asynchronous"
Me.btnRunSafe = New Button() With {.TabIndex = 1, .Dock = DockStyle.Top, .Text = "Run safe!", .Height = 30}
Me.btnRunUnsafe = New Button() With {.TabIndex = 2, .Dock = DockStyle.Top, .Text = "Run unsafe!", .Height = 30}
Me.tbOutput = New RichTextBox() With {.TabIndex = 3, .Dock = DockStyle.Fill}
Me.Controls.AddRange({Me.tbOutput, Me.btnRunUnsafe, Me.btnRunSafe, Me.cbOptions})
Me.testInstance = New Test()
End Sub
Private Sub _ButtonRunSafeClicked(s As Object, e As EventArgs) Handles btnRunSafe.Click
Dim mode As String = CStr(Me.cbOptions.SelectedItem)
If (mode = "Synchronous") Then
Me.testInstance.RunSafe(mode)
Else 'If (mode = "Asynchronous") Then
Task.Factory.StartNew(Sub() Me.testInstance.RunSafe(mode))
End If
End Sub
Private Sub _ButtonRunUnsafeClicked(s As Object, e As EventArgs) Handles btnRunUnsafe.Click
Dim mode As String = CStr(Me.cbOptions.SelectedItem)
If (mode = "Synchronous") Then
Me.testInstance.RunUnsafe(mode)
Else 'If (mode = "Asynchronous") Then
Task.Factory.StartNew(Sub() Me.testInstance.RunUnsafe(mode))
End If
End Sub
Private Sub TestMessageReceived(text As String) Handles testInstance.Message
Me.tbOutput.Text = (text & Environment.NewLine & Me.tbOutput.Text)
End Sub
Private WithEvents btnRunSafe As Button
Private WithEvents btnRunUnsafe As Button
Private WithEvents tbOutput As RichTextBox
Private WithEvents cbOptions As ComboBox
Private WithEvents testInstance As Test
Friend Class Test
Public Event Message(text As String)
Public Sub RunSafe(mode As String)
'Do some work:
Thread.Sleep(2000)
'Notify any listeners:
Program.Window.Invoke(Sub() RaiseEvent Message(String.Format("Safe ({0}) # {1}", mode, Date.Now)))
End Sub
Public Sub RunUnsafe(mode As String)
'Do some work:
Thread.Sleep(2000)
'Notify any listeners:
RaiseEvent Message(String.Format("Unsafe ({0}) # {1}", mode, Date.Now))
End Sub
End Class
End Class
Thank you to those who took the time to make suggestions. I found a solution. Though it may not be the preferred solution, it works beautifully. I simply added MSWINSCK.OCX to my toolbar, and use it as a COM/ActiveX component. The AxMSWinsockLib.AxWinsock control includes a DataArrival event, and it stays in the Main thread when the data arrives.
The most interesting thing is, if you right click on AxMSWinsockLib.DMSWinsockControlEvents_DataArrivalEvent and choose Go To Definition, the object browser shows the functions and delegate subs to handle the asynchronous read and the necessary delegates to handle BeginInvoke, EndInvoke, etc. It appears MicroSoft has already done the hard stuff that I did not have the time or experience to figure out on my own!