How to use WaitforSingleObject on events

B

blisspikle

I see a lot of examples posted for Waitforsingleobject API for
Processes or threads, but not events. I cannot get
waitforsingleobject to subscribe to an event. I use a class
PLCEthernet which is a dll I have a reference to, and I would like to
wait for it to raise an event that it is done, before I continue on in
my code. Thanks,


Imports System.Runtime.InteropServices
Public Class Form1

Public Declare Function WaitForSingleObject Lib "kernel32" _
(ByVal hHandle As System.IntPtr, _
ByVal dwMilliseconds As Integer) As Integer


Dim WithEvents Processor As New PLCEthernet
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles Button1.Click

Processor.Control_Update()
WaitForSingleObject(Processor_Done, 20000)
Msgbox("Done")

End Sub

Public Sub Processor_Done() Handles Processor.Done
End Sub
End Class
 
A

Armin Zingler

blisspikle said:
I see a lot of examples posted for Waitforsingleobject API for
Processes or threads, but not events. I cannot get
waitforsingleobject to subscribe to an event. I use a class
PLCEthernet which is a dll I have a reference to, and I would like
to wait for it to raise an event that it is done, before I continue
on in my code. Thanks,


Imports System.Runtime.InteropServices
Public Class Form1

Public Declare Function WaitForSingleObject Lib "kernel32" _
(ByVal hHandle As System.IntPtr, _
ByVal dwMilliseconds As Integer) As Integer


Dim WithEvents Processor As New PLCEthernet
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles Button1.Click

Processor.Control_Update()
WaitForSingleObject(Processor_Done, 20000)
Msgbox("Done")

End Sub

Public Sub Processor_Done() Handles Processor.Done
End Sub
End Class


Maybe I misunderstood, but probably you are mixing up the events raised by
objects in an object oriented environment with the events as a mean of multi
threaded synchronization. They do not really have a relation.

If you want to handle a managed event, use the Addhandler statement (or the
Handles clause, as you already do). If, in Thread A, you want to /wait/ for
an event raised in a Thread B, you can use the managed ManualResetEvent
class:

dim mre as new manualresetevent

button1_click:
mre.waitone

Sub Processor_Done
mre.set
end sub

Note that you can also pass a timeout to WaitOne, otherwise the thread is
locked forever.

See also:
http://msdn2.microsoft.com/en-us/library/9xyf641a.aspx



Armin
 
B

blisspikle

Thank you, that seems to be close to working. I show my final code
here, but I am wondering why the msgbox "Done" shows up before the
msgbox "In Processor_Done" shows up. It seems like it should be the
other way around?

Imports System.Threading
Imports System.Runtime.InteropServices
Public Class Form1

Dim WaitforDone As New ManualResetEvent(False)
Dim WithEvents Processor As New PLCEthernet
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles Button1.Click


Processor.Control_Update()
WaitforDone.WaitOne(5000, True)
Msgbox("Done")


End Sub


Public Sub Processor_Done() Handles Processor.Done
Msgbox("In Processor_Done")
WaitforDone.Set()
End Sub
End Class
 
A

Armin Zingler

blisspikle said:
Thank you, that seems to be close to working. I show my final code
here, but I am wondering why the msgbox "Done" shows up before the
msgbox "In Processor_Done" shows up. It seems like it should be the
other way around?

Imports System.Threading
Imports System.Runtime.InteropServices
Public Class Form1

Dim WaitforDone As New ManualResetEvent(False)
Dim WithEvents Processor As New PLCEthernet
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles Button1.Click


Processor.Control_Update()
WaitforDone.WaitOne(5000, True)
Msgbox("Done")


End Sub


Public Sub Processor_Done() Handles Processor.Done
Msgbox("In Processor_Done")
WaitforDone.Set()
End Sub
End Class


- The timeout expired?
- It happens starting with the second event? I haven't mentioned yet: You
must call WaitforDone.Reset before calling "Processor.Control_Update()".
Otherwise, the event is in the signaled stated still from the event before.
The next time, WaitOne will immediatelly return even though the event
hasn't occured a second time. That's why it is called a *Manual*ResetEvent.
You can also use the AutoResetEvent.

I assume that the event can not occur prior to calling
Processor.Control_Update.


Armin
 
B

blisspikle

Yes, the event cannot occur before calling the control_update. Now
when I reset it before going into the wait, it will just wait until
the timeout. It does not seem to call the Event at all once the
manualresetevent is waiting, but the event does get called if not
using the manualresetevent. Any idea why that would be? Thanks,
 
A

Armin Zingler

blisspikle said:
Yes, the event cannot occur before calling the control_update. Now
when I reset it before going into the wait, it will just wait until
the timeout. It does not seem to call the Event at all once the
manualresetevent is waiting, but the event does get called if not
using the manualresetevent. Any idea why that would be? Thanks,


Are you sure that the event is raised in another thread?



Armin
 
B

blisspikle

Are you sure that the event is raised in another thread?

Armin

I guess that I am not sure if it is in another thread. If you
instantiate a class that you have a reference to its dll, and you use
it in your code, is it running on a seperate thread automatically? If
I do a control_update(), then I can continue on in my main code until
I get a Done() event that the dll fires. Doesn't that mean that it
is working on a seperate thread and still working while I continue on
in my main code, or maybe I do not know how this works? Thank you.
 
A

Armin Zingler

blisspikle said:
I guess that I am not sure if it is in another thread. If you
instantiate a class that you have a reference to its dll, and you
use it in your code, is it running on a seperate thread
automatically?

No, not automatically. Must be taken from the component's documentation
whether the event is raised in another thread. I assumed it because you were
using WaitforSingleObject which is used to synchronize threads.
If I do a control_update(), then I can continue on
in my main code

What does continue mean? Continue without the application becoming idle and
without using Application.DoEvents? If it doesn't become idle and still the
event fires, then it must be raised from a different thread. To check this,
you can also execute

Debug.WriteLine(InvokeRequired)

in Sub Processor_Done. If it returns True, the event is raised from another
thread.
until I get a Done() event that the dll fires.
Doesn't that mean that it is working on a seperate thread and still
working while I continue on in my main code, or maybe I do not know
how this works? Thank you.


Armin
 
B

blisspikle

Okay, I am getting "False" from the Debug.WriteLine(InvokeRequired).
That would mean that they are on the same thread right? When
does .NET run the code for my Control_Update() then? For the
following code it seems like .NET should run the control_update, and
not return to do the msgbox("Done") until everything has finished in
that dll. Then the Processor_Done should fire and then return back to
the msgbox("Done") to return back to idle? Instead, right now it runs
Control_Update and then just posts "Done" before it fires the Done()
event. I just do not see how it is not running on another thread?

Public Class Form1

Dim WithEvents Processor As New PLCEthernet
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles Button1.Click


Processor.Control_Update()
Msgbox("Done")


End Sub


Public Sub Processor_Done() Handles Processor.Done
End Sub
End Class


When you run a normal Sub Main like the following one, it will not go
on and display the msgbox("Done") until done with the called CalledSub
right?

Class MyClass

Private Sub Main()
CalledSub
MsgBox("Done")
End Sub

Private Sub CalledSub()
'A loop to hold here for awhile.
End Sub

End Class
 
A

Armin Zingler

blisspikle said:
Okay, I am getting "False" from the Debug.WriteLine(InvokeRequired).
That would mean that they are on the same thread right?
Right.

When does
.NET run the code for my Control_Update() then? For the following
code it seems like .NET should run the control_update, and not
return to do the msgbox("Done") until everything has finished in
that dll. Then the Processor_Done should fire and then return back
to the msgbox("Done") to return back to idle? Instead, right now it
runs Control_Update and then just posts "Done" before it fires the
Done() event. I just do not see how it is not running on another
thread?

Maybe internally there is another thread, but the event is raised by
receiving and processing a windows message that has been sent from the other
thread. I don't know, but in order to find it out, set a break point in Sub
Processor_Done, run til the break point is hit, open the callstack window,
display the "external code" (by enabling it in the call stack window's
context menu). Copy and past the call stack here.
Public Class Form1

Dim WithEvents Processor As New PLCEthernet
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles Button1.Click


Processor.Control_Update()
Msgbox("Done")


End Sub


Public Sub Processor_Done() Handles Processor.Done
End Sub
End Class


When you run a normal Sub Main like the following one, it will not
go on and display the msgbox("Done") until done with the called
CalledSub right?

Class MyClass

Private Sub Main()
CalledSub
MsgBox("Done")
End Sub

Private Sub CalledSub()
'A loop to hold here for awhile.
End Sub

End Class


I see what you mean, though... in the end, you don't need
WaitforSingleObject. Dealing with this new situation, is it still your
target to wait til the event returns? I wouldn't wait. Instead, I would let
the code continue and keep the UI responsive.


Armin
 
B

blisspikle

Here is the posting from the Call Stack Window. I have never used
this before, so it looks very messy. Thanks again for the help. I
called the solution deleteme. I am using a UI just to make it easier
to test this dll out for myself.
Deleteme.exe!Deleteme.Form1.Processor_Done() Line 26 Basic
Interop.vHMIABE.dll!vHMIABE.__CABEthernet_SinkHelper.Done() + 0x22
bytes
[Native to Managed Transition]
[Managed to Native Transition]
[Native to Managed Transition]
[Managed to Native Transition]
System.Windows.Forms.dll!
System.Windows.Forms.MessageBox.ShowCore(System.Windows.Forms.IWin32Window
owner = null, string text, string caption,
System.Windows.Forms.MessageBoxButtons buttons,
System.Windows.Forms.MessageBoxIcon icon,
System.Windows.Forms.MessageBoxDefaultButton defaultButton,
System.Windows.Forms.MessageBoxOptions options, bool showHelp) + 0x1f8
bytes
System.Windows.Forms.dll!
System.Windows.Forms.MessageBox.Show(System.Windows.Forms.IWin32Window
owner, string text, string caption,
System.Windows.Forms.MessageBoxButtons buttons,
System.Windows.Forms.MessageBoxIcon icon,
System.Windows.Forms.MessageBoxDefaultButton defaultButton,
System.Windows.Forms.MessageBoxOptions options) + 0x1b bytes
Microsoft.VisualBasic.dll!
Microsoft.VisualBasic.Interaction.MsgBox(object Prompt,
Microsoft.VisualBasic.MsgBoxStyle Buttons, object Title) + 0x221
bytes
Deleteme.exe!Deleteme.Form1.Button1_Click(Object sender = {Text =
"Button1"}, System.EventArgs e =
{System.Windows.Forms.MouseEventArgs}) Line 22 + 0xf bytes Basic
System.Windows.Forms.dll!
System.Windows.Forms.Control.OnClick(System.EventArgs e) + 0x57 bytes
System.Windows.Forms.dll!
System.Windows.Forms.Button.OnClick(System.EventArgs e) + 0x49 bytes
System.Windows.Forms.dll!
System.Windows.Forms.Button.OnMouseUp(System.Windows.Forms.MouseEventArgs
mevent = {X = 93 Y = 52 Button = Left}) + 0xc3 bytes
System.Windows.Forms.dll!System.Windows.Forms.Control.WmMouseUp(ref
System.Windows.Forms.Message m, System.Windows.Forms.MouseButtons
button, int clicks) + 0xf2 bytes
System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref
System.Windows.Forms.Message m) + 0x56f bytes
System.Windows.Forms.dll!System.Windows.Forms.ButtonBase.WndProc(ref
System.Windows.Forms.Message m) + 0xce bytes
System.Windows.Forms.dll!System.Windows.Forms.Button.WndProc(ref
System.Windows.Forms.Message m) + 0x2b bytes
System.Windows.Forms.dll!
System.Windows.Forms.Control.ControlNativeWindow.OnMessage(ref
System.Windows.Forms.Message m) + 0xd bytes
System.Windows.Forms.dll!
System.Windows.Forms.Control.ControlNativeWindow.WndProc(ref
System.Windows.Forms.Message m) + 0xd6 bytes
System.Windows.Forms.dll!
System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr
hWnd, int msg = 514, System.IntPtr wparam, System.IntPtr lparam) +
0x75 bytes
[Native to Managed Transition]
[Managed to Native Transition]
System.Windows.Forms.dll!
System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(int
dwComponentID, int reason = -1, int pvLoopData = 0) + 0x2ea bytes
System.Windows.Forms.dll!
System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int
reason = -1, System.Windows.Forms.ApplicationContext context =
{Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.WinFormsAppContext})
+ 0x17d bytes
System.Windows.Forms.dll!
System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int
reason, System.Windows.Forms.ApplicationContext context) + 0x53 bytes
System.Windows.Forms.dll!
System.Windows.Forms.Application.Run(System.Windows.Forms.ApplicationContext
context) + 0x15 bytes
Microsoft.VisualBasic.dll!
Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()
+ 0xc0 bytes
Microsoft.VisualBasic.dll!
Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()
+ 0xe4 bytes
Microsoft.VisualBasic.dll!
Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(string[]
commandLine) + 0x62 bytes
[Native to Managed Transition]
[Managed to Native Transition]
mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile,
System.Security.Policy.Evidence assemblySecurity, string[] args) +
0x32 bytes
Microsoft.VisualStudio.HostingProcess.Utilities.dll!
Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() +
0x2b bytes
mscorlib.dll!
System.Threading.ThreadHelper.ThreadStart_Context(object state) + 0x3b
bytes
mscorlib.dll!
System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext
executionContext, System.Threading.ContextCallback callback, object
state) + 0x81 bytes
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() + 0x40
bytes
 
A

Armin Zingler

blisspikle said:
Here is the posting from the Call Stack Window. I have never used
this before, so it looks very messy. Thanks again for the help. I
called the solution deleteme. I am using a UI just to make it
easier to test this dll out for myself.
Deleteme.exe!Deleteme.Form1.Processor_Done() Line 26 Basic
Interop.vHMIABE.dll!vHMIABE.__CABEthernet_SinkHelper.Done() + 0x22
bytes
[Native to Managed Transition]
[Managed to Native Transition]
[Native to Managed Transition]
[Managed to Native Transition]


The "..Transition" parts would have been most interesting. :) Nevertheless,
no need to trace this further.. in the end we can not change it. So... the
question still is (maybe you've overlooked it at the bottom of my last post)
whether it is an option, not to wait for the event.(?)


Armin
 
B

blisspikle

I could do some other ways to get by the problem, I just thought that
waiting would eat up less processor time then doing loops. I could do
something with the Done Event firing and indexing a counter to go on
to the next stage? I am going to look into the Doevents, because I
have never used this and not sure what it does.

If I add a few of these processor objects to a List(of T), how can I
watch for the event to fire? Am I going to have to use addhandler for
every object that I create?

Some people have mentioned using state machines? Is there any good
links for templates on how to do state machines to control a program?

For the output below, does that mean the dll is coded in a "unmanaged"
language?
[Native to Managed Transition]
[Managed to Native Transition]
[Native to Managed Transition]
[Managed to Native Transition]

Thank you,
Erick
 
A

Armin Zingler

blisspikle said:
I could do some other ways to get by the problem, I just thought
that waiting would eat up less processor time then doing loops. I
could do something with the Done Event firing and indexing a counter
to go on to the next stage?

That's the best approach IMO.
I am going to look into the Doevents,
because I have never used this and not sure what it does.

Naah, please not. :)
If I add a few of these processor objects to a List(of T), how can I
watch for the event to fire? Am I going to have to use addhandler
for every object that I create?
Yes

Some people have mentioned using state machines? Is there any good
links for templates on how to do state machines to control a
program?

I haven't heard of it, but if anybody reads this...... ?
For the output below, does that mean the dll is coded in a
"unmanaged" language?

In first place it means that MessageBox.ShowCore calls unmanaged code. It
also includes a kind of message loop like with other windows. Therefore,
also the message from the Procssor component can be processed. Though I
don't know whether it comes from a managed or unmanaged component. Mixed
mode is also possible.
[Native to Managed Transition]
[Managed to Native Transition]
[Native to Managed Transition]
[Managed to Native Transition]

You can try to enable "unmanaged debugging" in the project properties,
configuration settings. Then you should get the full call stack revealing
the unmanaged code. As I don't have you Processor component, I did it with a
Winforms Timer, setting a breakpoint in it's Tick event, so the follwing
should be similar to yours:

user32.dll!_InternalCallWinProc@20()
user32.dll!_UserCallWinProc@24()
user32.dll!_DispatchMessageWorker@8()
user32.dll!_DispatchMessageW@4()
user32.dll!_DialogBox2@16()
user32.dll!_InternalDialogBox@24()
user32.dll!_SoftModalMessageBox@4()
user32.dll!_MessageBoxWorker@4()
user32.dll!_MessageBoxTimeoutW@24()
user32.dll!_MessageBoxExW@20()
user32.dll!_MessageBoxW@16()
system.windows.forms.dll!System.Windows.Forms.MessageBox.ShowCore



Armin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top