-
Jan 25th, 2022, 12:46 PM
#1
Thread Starter
New Member
[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
-
Jan 25th, 2022, 02:43 PM
#2
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
-
Jan 25th, 2022, 03:34 PM
#3
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
 
-
Jan 25th, 2022, 08:17 PM
#4
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.
-
Jan 26th, 2022, 04:04 AM
#5
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.
-
Jan 26th, 2022, 04:13 AM
#6
Thread Starter
New Member
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.
 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?
 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.
-
Jan 26th, 2022, 10:54 AM
#7
Re: Thread.Join vs Application.DoEvents
 Originally Posted by Niya
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
-
Jan 26th, 2022, 12:40 PM
#8
Thread Starter
New Member
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
-
Jan 26th, 2022, 05:57 PM
#9
Re: Thread.Join vs Application.DoEvents
 Originally Posted by baelin
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
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|