0

I have a logging function that get called from different threads. The logging function uses InvokeRequired to determine if the caller is in the same thread as the textbox was created in. If all the code is put into the same class it works fine. But when separated out InvokeRequired always returns false.

Imports System.Threading

Public Class Test_Main
    Dim tt_object As Test_Thread = Test_Thread.Get_Test_Thread_Class()
    Private Shared logger_lock As New Object
    Delegate Sub WriteLogMessage_Callback(text As String)

    Private Sub btn_start_Click(sender As Object, e As EventArgs) Handles btn_start.Click
        WriteLogMessage("From - btn_start_Click")
        tt_object.Start_Threads()
    End Sub

    Public Sub WriteLogMessage(ByVal message As String)
        If (Me.InvokeRequired) Then
            Dim d As New WriteLogMessage_Callback(AddressOf WriteLogMessage)
            Me.Invoke(d, New Object() {[message]})
        Else
            SyncLock logger_lock
                log_box.Text += System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") & "   " & message & vbCrLf
                log_box.SelectionStart = log_box.Text.Length
                log_box.ScrollToCaret()
            End SyncLock
        End If
        Application.DoEvents()
    End Sub

End Class


Public Class Test_Thread
    Private Shared _thisInstance As Test_Thread = Nothing
    Private t_1 As Thread
    Private t_2 As Thread

    Protected Sub New()                             'Class constructor
        t_1 = New Thread(AddressOf Thread_1)
        t_2 = New Thread(AddressOf Thread_2)
    End Sub

    Public Shared Function Get_Test_Thread_Class() As Test_Thread
        If _thisInstance Is Nothing Then        'Initialize object if it hasn't laready been done
            _thisInstance = New Test_Thread()
        End If
        Return _thisInstance                    'Return the object instance
    End Function

    Public Sub Start_Threads()
        Dim counter As Integer = 0

        Test_Main.WriteLogMessage("BEGIN - Start_Threads")

        t_1.Start()
        t_2.Start()

        Do While counter < 50
            Application.DoEvents()
            Thread.Sleep(500)
            counter += 1
        Loop

        t_1.Abort()
        t_2.Abort()
        Test_Main.WriteLogMessage("END - Start_Threads")
    End Sub

    Public Sub Thread_1()
        Dim counter As Integer = 0

        Test_Main.WriteLogMessage("BEGIN - Thread_1")
        Do While True
            Test_Main.WriteLogMessage("Thread_1: " & counter)
            counter += 1
            Thread.Sleep(1000)
        Loop
    End Sub

    Public Sub Thread_2()
        Dim counter As Integer = 0

        Test_Main.WriteLogMessage("BEGIN - Thread_2")
        Do While True
            Test_Main.WriteLogMessage("Thread_2: " & counter)
            counter += 1
            Thread.Sleep(1500)
        Loop
    End Sub

End Class
GSerg
  • 76,472
  • 17
  • 159
  • 346
hudsora
  • 1
  • 1
  • 1
    That `Test_Main.WriteLogMessage()` is evil code always has to be learned the hard way in vb.net. It creates a *new* instance of your form class, one whose InvokeRequired property always returns False because that new instance is owned by the worker thread. You can't see it, its Show() method was never called. – Hans Passant Jun 06 '16 at 14:37
  • @HansPassant Why so? I hate they [preserved](http://stackoverflow.com/a/6049062/11683) accessing form instances with the class name too, but it should refer to the same instance like in VB6. I would rather say it might be a duplicate of http://stackoverflow.com/q/4013954/11683. – GSerg Jun 06 '16 at 14:38
  • So how could WriteLogMessage be rewritten to allow access from a different thread? – hudsora Jun 06 '16 at 14:40
  • You should look into using the ASYNC method declaration. – Michael Z. Jun 06 '16 at 14:42
  • Or just modify the `Get_Test_Thread_Class` method, and the constructor so it takes a `Test_Main` instance as a parameter. – theB Jun 06 '16 at 14:47

1 Answers1

0

I solve it with the following code! Thanks for all the help.

Imports System.Threading

Public Class Test_Main
    Private Sub btn_start_Click(sender As Object, e As EventArgs) Handles btn_start.Click
        Dim log_object As Logger = Logger.Get_Logger_Class(log_box)
        Dim tt_object As Test_Thread = Test_Thread.Get_Test_Thread_Class(log_object)
        log_object.WriteLogMessage("From - btn_start_Click")
        tt_object.Start_Threads()
    End Sub
End Class


Public Class Logger
    Private Shared _thisInstance As Logger = Nothing
    Private log_box As TextBox
    Delegate Sub WriteLogMessage_Callback(text As String)
    Private Shared logger_lock As New Object

    Public Shared Function Get_Logger_Class(log_box_addr As TextBox) As Logger
        If _thisInstance Is Nothing Then        'Initialize object if it hasn't laready been done
            _thisInstance = New Logger()
            _thisInstance.log_box = log_box_addr
        End If
        Return _thisInstance                    'Return the object instance
    End Function

    Public Sub WriteLogMessage(ByVal message As String)

        If (log_box.InvokeRequired) Then
            Dim d As New WriteLogMessage_Callback(AddressOf WriteLogMessage)
            log_box.Invoke(d, New Object() {[message]})
        Else
            SyncLock logger_lock
                Me.log_box.Text += System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff") & "   " & message & vbCrLf
                Me.log_box.SelectionStart = log_box.Text.Length
                Me.log_box.ScrollToCaret()
            End SyncLock
        End If
        Application.DoEvents()
    End Sub
End Class


Public Class Test_Thread
    Private Shared _thisInstance As Test_Thread = Nothing
    Private log_addr As Logger
    Public t_1 As Thread
    Public t_2 As Thread

    Protected Sub New()                             'Class constructor
        t_1 = New Thread(AddressOf Thread_1)
        t_2 = New Thread(AddressOf Thread_2)
    End Sub

    Public Shared Function Get_Test_Thread_Class(logger_addr As Logger) As Test_Thread
        If _thisInstance Is Nothing Then        'Initialize object if it hasn't laready been done
            _thisInstance = New Test_Thread()
            _thisInstance.log_addr = logger_addr
        End If
        Return _thisInstance                    'Return the object instance
    End Function

    Public Sub Start_Threads()
        Dim counter As Integer = 0

        log_addr.WriteLogMessage("BEGIN - Start_Threads")

        t_1.Start()
        t_2.Start()

        Do While counter < 50
            Application.DoEvents()
            Thread.Sleep(500)
            counter += 1
        Loop

        t_1.Abort()
        t_2.Abort()
        log_addr.WriteLogMessage("END - Start_Threads")
    End Sub

    Public Sub Thread_1()
        Dim counter As Integer = 0

        log_addr.WriteLogMessage("BEGIN - Thread_1")
        Do While True
            log_addr.WriteLogMessage("Thread_1: " & counter)
            counter += 1
            Thread.Sleep(1000)
        Loop
    End Sub

    Public Sub Thread_2()
        Dim counter As Integer = 0

        log_addr.WriteLogMessage("BEGIN - Thread_2")
        Do While True
            log_addr.WriteLogMessage("Thread_2: " & counter)
            counter += 1
            Thread.Sleep(1500)
        Loop
    End Sub

End Class
GSerg
  • 76,472
  • 17
  • 159
  • 346
hudsora
  • 1
  • 1