I have used extensivly the event driven mechanism that Windows provide in manydifferent programming aspects(RDO, ADO, ODBC, Windows Sockets, Winlogon, mutexes, semaphores etc) and usedWaitForSingleObject when i was in need of an event monitor API command. The WaitForSingleObject is located in kernel32.dll and waits until a specific event object gets signaled or when a time limit is reached. It accepts two parameters; a handle to the event object and a time-out interval.
**The main benefit of this function is that it uses no processor time while waiting for the object state to become signaled or the time-out interval to elapse.
Hereis the declaration :
Public Declare Function WaitForSingleObject Lib "kernel32" Alias "WaitForSingleObject" _(ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Let's see an example of this command's usage :
In this example we are going to run the Windows calculator. We will open this shelled process and we will monitor the process handle; if it gets 0 then the process was ended.
Public Const WAIT_FAILED = &HFFFFFFFF 'Our WaitForSingleObject failed to wait and returned -1 Public Const WAIT_OBJECT_0 = &H0& 'The waitable object got signaled Public Const WAIT_ABANDONED = &H80& 'We got out of the waitable object Public Const WAIT_TIMEOUT = &H102& 'the interval we used, timed out. Public Const STANDARD_RIGHTS_ALL = &H1F0000 'No special user rights needed to open this process
Public Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long Public Declare Function WaitForSingleObject Lib"kernel32" (ByVal hHandle AsLong, ByVal dwMilliseconds AsLong) As Long Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Public Sub ShelledAPP() Dim shProcID As Long Dim hProcess As Long Dim WaitRet As Long
shProcID = Shell("calc.exe", vbNormalFocus) hProcess = OpenProcess(STANDARD_RIGHTS_ALL, False, shProcID)
'This is the proper and optimized way to use the WaitForSingleObject function.
'Isaw many programmers use the INFINITE constant as forthe dwMilliseconds field. 'IfdwMilliseconds is INFINITE, the function's time-out interval neverelapses. 'That'swrong 'cause the program won't refresh thus giving the impression that is a hungapplication. 'InWindows XP specially you might see a popup screen informing you about this.
'The problem also appears when you apply WaitForSingleObject with INFINITE in an application that uses windows. 'Always use a reasonable number of milliseconds and always use DoEvents to refresh the program's message queue
Do WaitRet = WaitForSingleObject(hProcess, 10) 'wait for 10ms to see if the hProcess was signaled Select Case WaitRet
Case WAIT_TIMEOUT 'The first case must always be WAIT_TIMEOUT 'cause it is the most used option DoEvents 'until the shelled process terminates Case WAIT_FAILED or WAIT_ABANDONED MsgBox "Wait failed or abandoned" Exit Do
Case WAIT_OBJECT_0 'The object got signaled soinform user and get out of the loop MsgBox "The shelled application has ended" Exit Do
End Select Loop
CallCloseHandle(hProcess) 'Close the process handle
Call CloseHandle(shProcID) 'Close the process id handle
DoEvents 'free any pending messages from the message queue End Sub
Now what if we had to monitor two or more shelled applications? are we going to use multithreading?
I haven't yet implemented multithreading api in a vb.net project of mine but as you most know, ultithreading is lethal (basically for those who will implement the CreateThread API function) when used within Visual Basic 6 (or prior). Crashes, unexpected terminations, exceptions and many other "beautifull" encounters are some of the experiences a programmer can get.
The answer comes from WaitForMultipleObjects API function which is also included in kernel32.dll
Here is the declaration :
Public Declare Function WaitForMultipleObjects Lib "kernel32" Alias "WaitForMultipleObjects"(ByVal nCount As Long, lpHandles As Long, ByVal bWaitAll As Long, ByVal dwMilliseconds AsLong) As Long
it accepts four values :
nCount as the maximum number of events to monitor,
lpHandles as the array of different event handles (not multiple copies of the same one),
bWaitAll (True/False) True if it must return when the state of all objects is signaled, False if it must return when the state of any one of these objects gets signaled,
dwMilliseconds as a maximum time-out interval
Like WaitForSingleObject, WaitForMultipleObjects can accept event handles of any of the following object types in the lpHandles : Change notification, Console input, Waitable timer,Event, Job, Mutex, Process, Semaphore and Threads. In the following example we are going to try something else than monitoring multiple shelled apps;
Those of you that have ICQ installed, have noticed that "red flower" icon, placed on the system tray.
When you are not connected on the internet, ICQ makes this icon look like inactive. Now when you connect, it suddently starts to get one by one of it's leaf green,meaning that it tries to connect to it's main server and when the connection completes, the flower get's green.
How do they do it? I mean. do they have an IsConnected() function on a timer with some interval? Definetly no!
What they do is take advantage of WaitForMultipleObjects with another function located in rasapi32.dll; RasConnectionNotification
The RasConnectionNotification function specifies an event object that the system sets to the signaled state when a RAS connection is created or terminated. The function accepts three values :
hrasconnas the handle to a RAS connection hEvent as the handle to an event object dwFlagsas the type of event to receive notifications for (RASCN_Connection or RASCN_Disconnection) Nowwe are going to use WaitForMultipleObjects to monitor both events Public Const RASCN_Connection = &H1 'Our two flags Public Const RASCN_Disconnection = &H2
Public Const WAIT_FAILED = &HFFFFFFFF Public Const WAIT_OBJECT_0 = &H0& Public Const WAIT_ABANDONED = &H80& Public Const WAIT_TIMEOUT = &H102&
Public Type SECURITY_ATTRIBUTES nLength As Long lpSecurityDescriptor As Long bInheritHandle As Long End Type
Public Declare Function CreateEvent Lib "kernel32" Alias"CreateEventA" (lpEventAttributes AsSECURITY_ATTRIBUTES, ByVal bManualReset AsLong, ByVal bInitialState AsLong, ByVal lpName AsString) As Long Public Declare Function RasConnectionNotification Lib "rasapi32.dll" Alias "RasConnectionNotificationA" (hRasConn AsLong, ByVal hEvent AsLong, ByVal dwFlags As Long) As Long Public Declare Function WaitForMultipleObjects Lib "kernel32" (ByVal nCount As Long, lpHandles As Long, ByVal bWaitAll As Long, ByVal dwMilliseconds AsLong) As Long Public Declare Function ResetEvent Lib "kernel32"(ByVal hEvent As Long) As Long Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Public Sub MonitorRASStatusAsync()
Dim hEvents(1) As Long 'Array of event handles. Since there are two events we'd like to monitor, i have already dimention it.
Dim RasNotif As Long Dim WaitRet As Long Dim sd As SECURITY_ATTRIBUTES Dim hRasConn As Long
hRasConn = 0
'We are going to create and register two event objects with CreateEvent API function
'There aren't any special treated events that need any kind of security attributes sowe just initialize the structure
With sd .nLength = Len(sd) 'we pass the length of sd .lpSecurityDescriptor = 0 .bInheritHandle = 0 End With
'We create the event by passing in CreateEvent any security attributes, 'we want to manually reset the event after it gets signaled, 'we also want it's initial state not signaled assuming that we don't have yet any connection to the internet, 'last but not least we give the event a name (RASStatusNotificationObject1)
hEvents(0) = CreateEvent(sd, True, False, "RASStatusNotificationObject1") 'If the returned value was zero, something went wrong so exit the sub If hEvents(0) = 0 Then MsgBox "Couldn't assign an event handle": Exit Sub
'If we succesfully created the first event object we pass it toRasConnectionNotification 'with the flag RASCN_Connection so that this event will monitor for internet connection RasNotif = RasConnectionNotification(ByVal hRasConn, hEvents(0), RASCN_Connection)
If RasNotif <> 0 Then MsgBox "Ras Notification failure": GoTo ras_TerminateEvent 'We create the second event object exactly like the first one 'but we name it RASStatusNotificationObject2 hEvents(1) = CreateEvent(sd,True, False, "RASStatusNotificationObject2")
If hEvents(1) = 0 Then MsgBox "Couldn't assignan event handle": Exit Sub
'If we succesfully created the second event object too, we pass it toRasConnectionNotification 'with the flag RASCN_Disconnection. This event will monitor for disconnection RasNotif = RasConnectionNotification(ByVal hRasConn, hEvents(1), RASCN_Disconnection)
If RasNotif <> 0 Then MsgBox "Ras Notification failure": GoTo ras_TerminateEvent
'We then issue the loop 'Notice that we have put hEvents array to it's first array item. 'and we used False cause we want to get notifications 'whenany of the two events occur.
Do WaitRet = WaitForMultipleObjects(2, hEvents(0),False, 20) Select Case WaitRet Case WAIT_TIMEOUT DoEvents
Case WAIT_FAILED Or WAIT_ABANDONEDOr WAIT_ABANDONED + 1 GoTo ras_TerminateEvent
Case WAIT_OBJECT_0 MsgBox "Connected"
ResetEvent hEvents(0) 'Reset the event to avoid a second message box
DoEvents 'Free any pending messages
Case WAIT_OBJECT_0 + 1 MsgBox "Disconnected" ResetEvent hEvents(1) 'Reset the event to place it in nosignal state (Manual reset, remember?) DoEvents
End Select
Loop
ras_TerminateEvent: 'Close all event handles 'For more than two events you could apply a For.. Next CallCloseHandle(hEvents(1)) Call CloseHandle(hEvents(0))
DoEvents 'Free any pending messages from the application message queue
End Sub
Now imagine that you could monitor events from different objects like a file or folder change, along with connection status, shelled applications, multiple printer objects, different processes and threads etc etc etc.
(64 maximum event objects i think)
I twill appear that you program is multithreading but the truth behind that, is that you will be taking advantage of WaitForMultipleObjects internal multithreading mechanism.
Labels: createevent, RasConnectionNotification, WaitForMultipleObjects |
hi i need some professional opinion on my blogger please can you comment at kingjabzter.blogspot.com