Link to home
Start Free TrialLog in
Avatar of DrDamnit
DrDamnitFlag for United States of America

asked on

Use RegNotifyChangeKeyValue without WaitForSingleObject

I have a loop that I want to use to tell me whether or not a registry key has changed:

    'Monitoring Loop
    Do
        DoEvents
        hEvent = CreateEvent(lpEventAttributes, True, False, lpName)
        lRet = RegOpenKey(hKey, sRegKeyPath, lKeyHandle)
        notify = RegNotifyChangeKeyValue(lKeyHandle, bWatchSubTree, dwFilters, hEvent, True)
        lWait = WaitForSingleObject(hEvent, INFINITE)
        If lWait = WAIT_FAILED Then
            MsgBox "Wait Failed!"
        End If
       
        If Not notify = error_sucess Then
            FormatMessage FORMAT_MESSAGE_FROM_SYSTEM, ByVal 0&, notify, LANG_NEUTRAL, buffer, 200, ByVal 0&
            MsgBox buffer
        End If
        lRet = RegCloseKey(lKeyHandle)
        MsgBox "A registry key has changed!" & vbCrLf & mvarMonitoredKey
        If mvarStart = False Then Exit Do
    Loop


I want to be able to use the RegNotifyChangeKeyValue API call without having to answer either FALSE to the asynchronus parameter of the API itself OR using sometype of wait routine (WaitForSingleObject). I want this to run in a loop so I can execute a doevents and paint the screen while waiting for a registry change.

How do I do that?
Avatar of Ark
Ark
Flag of Russian Federation image

lWait = WaitForSingleObject(hEvent, INFINITE)
should be
lWait = WaitForSingleObject(hEvent, 0):

'=======bas module code========
Public Enum ROOT_KEYS
       HKEY_CLASSES_ROOT = &H80000000
       HKEY_CURRENT_USER = &H80000001
       HKEY_LOCAL_MACHINE = &H80000002
       HKEY_USERS = &H80000003
       HKEY_PERFORMANCE_DATA = &H80000004
       HKEY_CURRENT_CONFIG = &H80000005
       HKEY_DYN_DATA = &H80000006
End Enum

Public Enum NOTIFY_EVENTS
       REG_NOTIFY_CHANGE_NAME = &H1
       REG_NOTIFY_CHANGE_ATTRIBUTES = &H2
       REG_NOTIFY_CHANGE_LAST_SET = &H4
       REG_NOTIFY_CHANGE_SECURITY = &H8
End Enum

Private Declare Function RegNotifyChangeKeyValue Lib "advapi32" (ByVal hKey As Long, ByVal bWatchSubTree As Boolean, ByVal dwNotifyFilter As Long, ByVal hEvent As Long, ByVal fAsynchronous As Boolean) As Long
Private Declare Function RegOpenKey Lib "advapi32.dll" Alias "RegOpenKeyA" (ByVal hKey As Long, ByVal lpSubKey As String, phkResult As Long) As Long
Private Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) As Long

Private Declare Function CreateEvent Lib "kernel32" Alias "CreateEventA" (ByVal lpEventAttributes As Long, ByVal bManualReset As Long, ByVal bInitialState As Long, ByVal lpName As String) As Long
Private Declare Function ResetEvent Lib "kernel32" (ByVal hEvent As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Const WAIT_ABANDONED = &H80
Private Const WAIT_FAILED = &HFFFFFFFF
Private Const WAIT_OBJECT_0 = &H0
Private Const WAIT_TIMEOUT = &H102&

Public hEvent As Long, bExit As Boolean

Public Sub RegMonitor(hKey As ROOT_KEYS, sRegKeyPath As String, bWatchSubTree As Boolean, dwFilters As NOTIFY_EVENTS)
   Dim lKeyHandle As Long, lRet As Long
   hEvent = CreateEvent(0, True, False, vbNullString)
   lRet = RegOpenKey(hKey, sRegKeyPath, lKeyHandle)
   RegNotifyChangeKeyValue lKeyHandle, bWatchSubTree, dwFilters, hEvent, True
   Do
     DoEvents
     lRet = WaitForSingleObject(hEvent, 0)
     If bExit Then Exit Do
     If lRet = WAIT_OBJECT_0 Then
        MsgBox "Change!"
        ResetEvent hEvent
        RegNotifyChangeKeyValue lKeyHandle, bWatchSubTree, dwFilters, hEvent, True
     End If
   Loop
   lRet = RegCloseKey(lKeyHandle)
   End
End Sub

'=========Form code=========
Private Sub Command1_Click()
  Dim cap As String
  sChange = ""
  cap = Caption
  Caption = "Monitoring..."
  Label1 = "Try to change HKEY_CURRENT_USER\Software\VB and VBA Program Settings\Registry Notification\Hello\Testing"
  Label1 = Label1 & vbCrLf & "or any other key under HKCU\Software\VB and VBA Program Settings with RegEdit"
  Call RegMonitor(HKEY_CURRENT_USER, "Software\VB and VBA Program Settings", True, REG_NOTIFY_CHANGE_ATTRIBUTES + REG_NOTIFY_CHANGE_LAST_SET + REG_NOTIFY_CHANGE_NAME + REG_NOTIFY_CHANGE_SECURITY)
  Caption = cap
End Sub

Private Sub Form_Load()
    Label1 = "Application created a temporary registry key:"
    Label1 = Label1 & vbCrLf & "HKEY_CURRENT_USER\Software\VB and VBA Program Settings\Registry Notification\Hello\Testing"
    Label1 = Label1 & vbCrLf & "Press command button to start monitoring."
    'Create a temporary registry key
    SaveSetting "Registry Notification", "Hello", "Testing", "123"
End Sub

Private Sub Form_Unload(Cancel As Integer)
  bExit = True
  CloseHandle hEvent
End Sub

Avatar of AlexFM
AlexFM

lWait = WaitForSingleObject(hEvent, INFINITE)

Replace this line with loop:

While True
    lWait = WaitForSingleObject(hEvent, 100)

    if lWait = WAIT_TIMEOUT Then
         DoEvents
         ' continue waiting
    else
         ' handle lWait as you do this now
         Exit While
    end if
WEnd

This code fragment replaces infinite wait for hEvent with waiting and handling of messages. When WaitForSingleObject returns WAIT_TIMEOUT, call DoEvents and continue to wait. Any other return value (success - WAIT_OBJECT_0, error code) should be handled according to your current program logic, and program exits from wait loop.
Avatar of DrDamnit

ASKER

Ark,
I've seen that. I am trying to monitor more than one key without threading anything. So I can't use waitforsingleobject.
AlexFM,
I don't want to use waitforsingleobject at all.
There are two ways to use RegNotifyChangeKeyValue function:
1) With fAsynchronous = FALSE. In this case you don't need WaitForSingleObject, but function is synchronous and returns only when there is change in the Registry.
2) With fAsynchronous = TRUE. In this case function returns immidiately and event is signaled later when Registry is changed. The only way to test event state is using WaitForSingleObject or other wait functions like WaitForSingleObject, which is basically the same. If you don't want to use wait functions (BTW, why?), there is no solution to your problem.

>> I am trying to monitor more than one key without threading anything.
Using wait functions doesnt require threading.
Because the program freezes until a registry change occurs.
This happens because you use WaitForSingleObject by wrong way:

lWait = WaitForSingleObject(hEvent, INFINITE)

And I suggest you to wait fore some short time and handle events, exactly as you need:

While True
    lWait = WaitForSingleObject(hEvent, 100)   ' wait 100 ms (or 50, 20 as you need)

    if lWait = WAIT_TIMEOUT Then
         DoEvents
         ' continue waiting - go to the beginning of the loop again
    else
         ' handle lWait as you do this now
         Exit While   ' to external loop: call RegNotifyChangeKeyValue again
    end if
WEnd
Hi again
>>I've seen that<< - This is my own code - probably you've seen my sample at freevbcode.com
As for monitoring multiple key - just set top root key for all subkeys, set bWatchSubTree = True and compare old values for your subkeys with current values for each change event
Your code? Cool! Good Job!

I have thought about setting up a compare routine, but the execution is not as "lean" as I would like it to be. I have specific keys that I want to monitor, and I don't want to monitor the whole route. For instance, I don't want to monitor all of HKLM\SOFTWARE, and compare changes. I would rather montior HKLM\Software\Specific Key\ and HKCU\Software\Specific Key 2\ only.

Can that be done with what you're talking about? I know I can do it with C++, but the problem is....I don't know C++ yet. :-)
Hi
You can create an array of events - one for each key, then use WaitForMultipleObjects and check return values
What would the code for that look like?
ASKER CERTIFIED SOLUTION
Avatar of Ark
Ark
Flag of Russian Federation image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Excellent info, Ark. If you;re still monitoring this thread, I'd appreciate if you can help me. I use this code, and getting the error mentioned at the bottom.

====CODE=====

Dim sArray() As String
Dim i as integer

For ListViewIndex = 1 To ListView1.ListItems.Count
   ReDim Preserve strKeys(i)
   RNumber = ListView1.ListItems(ListViewIndex).Text
   RPath = "Software\MyKey\" & RNumber
   strKeys(i) = RPath
   i = i + 1
Next

Call RegMonitor(HKEY_CURRENT_USER, strKeys, True, REG_NOTIFY_CHANGE_ATTRIBUTES + REG_NOTIFY_CHANGE_LAST_SET + REG_NOTIFY_CHANGE_NAME + REG_NOTIFY_CHANGE_SECURITY)

====End of CODE=====

ERROR:  "ByRef Argument type mismatch" in variable "strKeys"

Perhaps I did something wrong in declaring the Array ? If you've found where the error is, pls let me know so that I can post a new question.
Revised code:
------------------

====CODE=====

Dim strKeys() As String
Dim i as integer

For ListViewIndex = 1 To ListView1.ListItems.Count
  ReDim Preserve strKeys(i)
  RNumber = ListView1.ListItems(ListViewIndex).Text
  RPath = "Software\MyKey\" & RNumber
  strKeys(i) = RPath
  i = i + 1
Next

Call RegMonitor(HKEY_CURRENT_USER, strKeys, True, REG_NOTIFY_CHANGE_ATTRIBUTES + REG_NOTIFY_CHANGE_LAST_SET + REG_NOTIFY_CHANGE_NAME + REG_NOTIFY_CHANGE_SECURITY)

====End of CODE=====

ERROR:  "ByRef Argument type mismatch" in variable "strKeys"

Perhaps I did something wrong in declaring the Array ? If you've found where the error is, pls let me know so that I can post a new question.