Results 1 to 9 of 9

Thread: [RESOLVED] Thread.Join vs Application.DoEvents

  1. #1

    Thread Starter
    New Member
    Join Date
    Jan 2022
    Posts
    5

    Resolved [RESOLVED] Thread.Join vs Application.DoEvents

    Hello everyone!

    I am developing a VB console application (.NET 4.7.2). The Main method of this particular application goes into a loop at a certain point, creating tasks if certain conditions are met. Since it's basically polling for information, I use Thread.Join(Int32) after every iteration of the loop to give it a break, while allowing my tasks (some of which access COM components) to continue with their work. So far, so good.

    Now - my application does also create a tray icon for itself (utilizing the NotifyIcon class). This icon does have a context menu and corresponding event handlers, of course.

    Unfortunately, I have only a limited understanding of the Windows kernel, but when I read the MSDN for Thread.Join, which says:

    Blocks the calling thread until the thread represented by this instance terminates, while continuing to perform standard COM and SendMessage pumping.
    then this leads me to believe that it should also fire my event handlers. But it doesn't. Nothing will happen when you click on the tray icon, nor will the context menu show up. However, putting an Application.DoEvents right after the Thread.Join solves the issue and everything works smoothly.

    I thought that mouse events are "messages" as well, so Thread.Join should process them? Can someone provide me some insight about why it does not work the way I think?

    Thanks, baelin

  2. #2
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Pointless Forest 38.517,-92.023
    Posts
    9,450

    Re: Thread.Join vs Application.DoEvents

    I think it is something like this,

    Code:
    Module Module1
    
        Private WithEvents NI As New Windows.Forms.NotifyIcon()
        Sub Main()
            Dim initTSK As Task = Task.Run(Sub()
                                               InitNI()
                                           End Sub)
    
    
            Console.ReadKey()
    
    
            INITMRE.Set()
            initTSK.Wait(100)
        End Sub
    
        Private INITMRE As New Threading.ManualResetEvent(False)
        Private Sub InitNI()
            NI.Text = "TestApp"
            NI.Icon = My.Resources.FOO
            NI.Visible = True
            'etc.
            Windows.Forms.Application.Run() '<<----------------<<<<<<<
            INITMRE.WaitOne(Threading.Timeout.Infinite)
        End Sub
    
        Private Sub NI_Click(sender As Object, e As EventArgs) Handles NI.Click
            Debug.WriteLine("NI")
        End Sub
    End Module
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  3. #3
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    37,332

    Re: Thread.Join vs Application.DoEvents

    I hadn't seen that, and can understand the confusion. SendMessage and pumping don't seem to go together all that well. The term pumping tends to be used in terms of the message queue, but SendMessage isn't the same as processing messages, which is what you need for handling events. I have always expected that Join simply blocks execution of the thread that it is called on (in this case the UI thread), until the thread it's waiting for completes. Blocking the UI thread would stop messages from being handled, but wouldn't stop messages from being queued. I'm not sure that it's the right understanding, as I can't say I have looked at this in any depth, but I'm not surprised by the result.

    Of course, if you had that whole loop running on a background thread, including the Join, then the UI thread would be free to do whatever it needed to do.
    My usual boring signature: Nothing

  4. #4
    .NUT jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    108,373

    Re: Thread.Join vs Application.DoEvents

    I'd say that your primary mistake was creating a Console application in the first place. I would suggest that you follow the CodeBank link in my signature below and check out my thread on creating a Formless Tray Application. That will show you how to create a Windows Forms Application project with no startup form and a tray icon. You can then keep your UI thread free to maintain the UI, as you would do in any WinForms app, and do everything else with tasks or secondary threads.

  5. #5
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    7,392

    Re: Thread.Join vs Application.DoEvents

    I have to double down on what jmc said here. A Console application is absolutely the wrong project type for an application that's meant to be in the system tray. You need a project type that provides a UI so you get a message pump. A Console application does not have a message pump to handle UI events.

    Now that being said, you could manually install a message pump into your Console application but I think that's just giving yourself unnecessary work. Just use a WinForms application.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  6. #6

    Thread Starter
    New Member
    Join Date
    Jan 2022
    Posts
    5

    Re: Thread.Join vs Application.DoEvents

    Hey everyone!

    Wow, thanks for the quick responses.

    First of all, I am really sorry because I made a typing mistake in my opening post that is really important to the question.
    I meant to write that the context menu does not show up on a right click, but the left click handler (... Handles Icon.Click) works flawlessly.
    It's only the context menu that won't show up unless I use Application.DoEvents().
    My sincerest apologies for my sloppiness there.

    Code:
    Private Sub Icon_Click(sender As Object, e As EventArgs) Handles Icon.Click
    
    'This handler makes a call to the ShowWindow API-Function in user32.dll to bring the hidden console window back.
    'Works perfectly with Join, does not need Application.DoEvents
    
    End Sub
    Regarding dbasnett's code snippet, I'm not sure this is what I need, since Application.Run() initializes a message loop, which I already have, otherwise there wouldn't be anything to do for Application.DoEvents? From my understanding, this would either throw an exception right away or initialize a second message loop on another thread and I am not really clear about the side implications, given that I do set up my NotifyIcon on the main thread, in fact - before any task creations occur.

    Aside from that, I tried it. Application.Run() on the main thread will just block and putting it in a task will not do anything.

    Quote Originally Posted by Shaggy Hiker
    Of course, if you had that whole loop running on a background thread, including the Join, then the UI thread would be free to do whatever it needed to do.
    But I would still need to prevent Sub Main from exiting somehow, because otherwise my console application would terminate. How would I do that?

    Quote Originally Posted by jmcilhinney
    I'd say that your primary mistake was creating a Console application in the first place. I would suggest that you follow the CodeBank link in my signature below and check out my thread on creating a Formless Tray Application.
    I'm sorry, maybe this isn't as clear as it should be from my opening post: It is literally a console application. Like, there is in fact a console window that takes input and produces output. The point of the tray icon is that I'm allowing the user to hide the console window by pressing a key (Console.Readkey in a forever looping task + ShowWindow API function) and a click on the tray icon is supposed to bring it back.
    Last edited by baelin; Jan 26th, 2022 at 04:17 AM.

  7. #7
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Pointless Forest 38.517,-92.023
    Posts
    9,450

    Re: Thread.Join vs Application.DoEvents

    Quote Originally Posted by Niya View Post
    I have to double down on what jmc said here. A Console application is absolutely the wrong project type for an application that's meant to be in the system tray. You need a project type that provides a UI so you get a message pump. A Console application does not have a message pump to handle UI events.

    Now that being said, you could manually install a message pump into your Console application but I think that's just giving yourself unnecessary work. Just use a WinForms application.
    Agreed...

    This works.

    Code:
    Module Module1
    
        Private WithEvents NI As New Windows.Forms.NotifyIcon()
        Private WithEvents menuItem As New Windows.Forms.MenuItem("Exit")
        Sub Main()
            Dim initTSK As Task = Task.Run(Sub()
                                               InitNI()
                                           End Sub)
    
    
            Console.ReadKey()
    
    
            INITMRE.Set()
            initTSK.Wait(100)
        End Sub
    
        Private INITMRE As New Threading.ManualResetEvent(False)
        Private Sub InitNI()
            NI.Text = "TestApp"
            NI.Icon = My.Resources.FOO
            NI.Visible = True
            Dim contextMenu As New Windows.Forms.ContextMenu
            contextMenu.MenuItems.Add(menuItem)
            NI.ContextMenu = contextMenu
            'etc.
            Windows.Forms.Application.Run() '<<----------------<<<<<<<
            INITMRE.WaitOne(Threading.Timeout.Infinite)
        End Sub
    
        Private Sub NI_Click(sender As Object, e As EventArgs) Handles NI.Click
            Debug.WriteLine("NI")
        End Sub
    
        Private Sub menuItem_Click(sender As Object, e As EventArgs) Handles menuItem.Click
            Debug.WriteLine("mi")
        End Sub
    End Module
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  8. #8

    Thread Starter
    New Member
    Join Date
    Jan 2022
    Posts
    5

    Re: Thread.Join vs Application.DoEvents

    Honestly, dbasnett, I have no idea what you're trying to tell me with this code snippet or how it relates to my question.

    That being said, I found out that the contextmenu does in fact show up without using Application.DoEvents(), but only for a split second. It's observable when aggressively right-clicking on the tray icon. So things are definitely happening, but apparently it takes DoEvents to finish the job.

    Thanks to everyone who would try to help, though.

    Cheers, baelin

  9. #9
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Pointless Forest 38.517,-92.023
    Posts
    9,450

    Re: Thread.Join vs Application.DoEvents

    Quote Originally Posted by baelin View Post
    Honestly, dbasnett, I have no idea what you're trying to tell me with this code snippet or how it relates to my question...
    Here is my idea, with a LOOP. I improvised since I had none of your loop code. The point is that the NotifyIcon and ContextMenu work. Good luck.

    Code:
    Module Module1
    
        Private WithEvents NI As New Windows.Forms.NotifyIcon()
        Private WithEvents menuItem As New Windows.Forms.MenuItem("Exit")
        Sub Main()
            Dim ch As ConsoleKeyInfo
            Dim initTSK As Task = Task.Run(Sub()
                                               InitNI()
                                           End Sub)
    
            ' The Main method of this particular application 
            '  GOES INT A LOOP
            '  at a certain point, creating tasks if certain conditions 
            '  are met. Since it's basically polling for information, I use 
            '  Thread.Join(Int32) after every iteration of the loop to give 
            '  it a break, while allowing my tasks (some of which access COM components) 
            '  to continue with their work...
    
            Do
                If Console.KeyAvailable Then
                    ch = Console.ReadKey
                    If ch.KeyChar = "q"c Then
                        Exit Do
                    End If
                End If
    
            Loop While Not INITMRE.WaitOne(250) 'quarter of a second
    
            INITMRE.Set()
            initTSK.Wait(100)
        End Sub
    
        Private INITMRE As New Threading.ManualResetEvent(False)
        Private Sub InitNI()
            NI.Text = "TestApp"
            NI.Icon = My.Resources.FOO
            NI.Visible = True
            Dim contextMenu As New Windows.Forms.ContextMenu
            contextMenu.MenuItems.Add(menuItem)
            NI.ContextMenu = contextMenu
            'etc.
            Windows.Forms.Application.Run() '<<----------------<<<<<<< ADDED
            INITMRE.WaitOne(Threading.Timeout.Infinite)
        End Sub
    
        Private Sub NI_Click(sender As Object, e As EventArgs) Handles NI.Click
            Debug.WriteLine("NI")
        End Sub
    
        Private Sub menuItem_Click(sender As Object, e As EventArgs) Handles menuItem.Click
            Debug.WriteLine("mi")
        End Sub
    End Module
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width