Re: threading question

Tech-Archive recommends: Fix windows errors by optimizing your registry

From: Lucvdv (replace_name_at_null.net)
Date: 09/03/04


Date: Fri, 03 Sep 2004 09:31:06 +0200

On Fri, 03 Sep 2004 05:14:38 GMT, v-phuang@online.microsoft.com ("Peter
Huang") wrote:

> Hi,
>
> When did you create the control, if it is created in the working thread,
> there is no need to invoke across thread.

The control is created in the class constructor (sub New()), which means
it's created in the form's UI thread.

My problem was that ctl.InvokeRequired *always* returned False, whatever
thread it was called from, which meant some code in my app was executed in
the wrong thread while I expected the InvokeRequired/Invoke to take care of
that. I added sample code below to illustrate it.

The problem is solved, I just want to signal it because I found nothing in
the documetation that warns for this possibility, and someone else might be
bitten by it too.

Through some more experimenting, I found that [control].InvokeRequired
always returns False as long as the control hasn't been added to a form's
controls collection. The documentation says "Property Value: True if the
control's Handle was created on a different thread than the calling
thread", but just creating it in a form's UI thread isn't enough.

Sample code: the class is created in the form's constructor. The control
is created in the class's New(), so it is created by the UI thread.

FireTest calls InvokeRequired and Invoke on the control to ensure that the
event is fired in the UI thread, yet the event handler says it isn't.

The only place where FireTest is called is from within another thread, so
upon entry it should always log "InvokeRequired: True" in the debug pane,
followed by "InvokeRequired: False" when it's re-entered in the UI thread.
Instead, it reports False right away.

I included the full code, including auto-generated. You can create a new
standard windows app in VS.Net 2003 and paste everything below over the
generated code, it should work.

'----------------------------------------------------------------
Imports System.Threading

Public Class Form1
    Inherits System.Windows.Forms.Form

    Private tst As TestClass

#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call
        tst = New TestClass
        AddHandler tst.TestEvent, AddressOf TestEventHandler
    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    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.
    Friend WithEvents Button1 As System.Windows.Forms.Button
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
        Me.Button1 = New System.Windows.Forms.Button
        Me.SuspendLayout()
        '
        'Button1
        '
        Me.Button1.Location = New System.Drawing.Point(104, 80)
        Me.Button1.Name = "Button1"
        Me.Button1.TabIndex = 0
        Me.Button1.Text = "Test"
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(292, 181)
        Me.Controls.Add(Me.Button1)
        Me.Name = "Form1"
        Me.Text = "Form1"
        Me.ResumeLayout(False)

    End Sub

#End Region

    Private Class TestClass
        Public Event TestEvent()
        Private thr As Thread
        Private m_Ctl As Control

        Public Sub New()
            m_Ctl = New Control
        End Sub

        Private Delegate Sub d_FireTest()
        Private Sub FireTest()
            Debug.WriteLine(m_Ctl.InvokeRequired, "InvokeRequired")
            If m_Ctl.InvokeRequired Then
                m_Ctl.Invoke(New d_FireTest(AddressOf FireTest))
            Else
                RaiseEvent TestEvent()
            End If
        End Sub

        Public Sub Test()
            thr = New Thread(AddressOf ThreadProc)
            thr.Start()
        End Sub

        Private Sub ThreadProc()
            Thread.Sleep(250)
            FireTest()
        End Sub
    End Class

    Private Sub TestEventHandler()
        If Me.InvokeRequired Then
            MsgBox("Event handler not running in UI thread")
        Else
            MsgBox("Test event OK")
        End If
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles Button1.Click
        tst.Test()
    End Sub
End Class


Quantcast