|
| 1 | +import platform |
| 2 | +if platform.system().lower() == "windows": |
| 3 | + import logging |
| 4 | + import ctypes |
| 5 | + from ctypes.wintypes import LONG, HKEY, LPCWSTR, DWORD, BOOL, HANDLE, LPVOID |
| 6 | + |
| 7 | + LPSECURITY_ATTRIBUTES = LPVOID |
| 8 | + |
| 9 | + RegOpenKeyEx = ctypes.windll.advapi32.RegOpenKeyExW |
| 10 | + RegOpenKeyEx.restype = LONG |
| 11 | + RegOpenKeyEx.argtypes = [HKEY, LPCWSTR, DWORD, DWORD, ctypes.POINTER(HKEY)] |
| 12 | + |
| 13 | + RegCloseKey = ctypes.windll.advapi32.RegCloseKey |
| 14 | + RegCloseKey.restype = LONG |
| 15 | + RegCloseKey.argtypes = [HKEY] |
| 16 | + |
| 17 | + RegNotifyChangeKeyValue = ctypes.windll.advapi32.RegNotifyChangeKeyValue |
| 18 | + RegNotifyChangeKeyValue.restype = LONG |
| 19 | + RegNotifyChangeKeyValue.argtypes = [HKEY, BOOL, DWORD, HANDLE, BOOL] |
| 20 | + |
| 21 | + CloseHandle = ctypes.windll.kernel32.CloseHandle |
| 22 | + CloseHandle.restype = BOOL |
| 23 | + CloseHandle.argtypes = [HANDLE] |
| 24 | + |
| 25 | + CreateEvent = ctypes.windll.kernel32.CreateEventW |
| 26 | + CreateEvent.restype = BOOL |
| 27 | + CreateEvent.argtypes = [LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCWSTR] |
| 28 | + |
| 29 | + WaitForSingleObject = ctypes.windll.kernel32.WaitForSingleObject |
| 30 | + WaitForSingleObject.restype = DWORD |
| 31 | + WaitForSingleObject.argtypes = [HANDLE, DWORD] |
| 32 | + |
| 33 | + ERROR_SUCCESS = 0x00000000 |
| 34 | + |
| 35 | + KEY_READ = 0x00020019 |
| 36 | + KEY_QUERY_VALUE = 0x00000001 |
| 37 | + |
| 38 | + REG_NOTIFY_CHANGE_NAME = 0x00000001 |
| 39 | + REG_NOTIFY_CHANGE_LAST_SET = 0x00000004 |
| 40 | + |
| 41 | + WAIT_OBJECT_0 = 0x00000000 |
| 42 | + WAIT_TIMEOUT = 0x00000102 |
| 43 | + |
| 44 | +class RegistryMonitor: |
| 45 | + |
| 46 | + def __init__(self, root, subkey): |
| 47 | + self._root = root |
| 48 | + self._subkey = subkey |
| 49 | + self._event = CreateEvent(None, False, False, None) |
| 50 | + |
| 51 | + self._key = None |
| 52 | + self._open_key() |
| 53 | + if self._key: |
| 54 | + self._set_key_update_notification() |
| 55 | + |
| 56 | + def close(self): |
| 57 | + CloseHandle(self._event) |
| 58 | + if self._key: |
| 59 | + RegCloseKey(self._key) |
| 60 | + self._key = None |
| 61 | + |
| 62 | + def is_updated(self): |
| 63 | + wait_result = WaitForSingleObject(self._event, 0) |
| 64 | + |
| 65 | + # previously watched |
| 66 | + if wait_result == WAIT_OBJECT_0: |
| 67 | + self._set_key_update_notification() |
| 68 | + return True |
| 69 | + |
| 70 | + # no changes or no key before |
| 71 | + if wait_result != WAIT_TIMEOUT: |
| 72 | + # unexpected error |
| 73 | + logging.warning("Unexpected WaitForSingleObject result %s", wait_result) |
| 74 | + return False |
| 75 | + |
| 76 | + if self._key is None: |
| 77 | + self._open_key() |
| 78 | + |
| 79 | + if self._key is None: |
| 80 | + return False |
| 81 | + |
| 82 | + self._set_key_update_notification() |
| 83 | + return True |
| 84 | + |
| 85 | + def _set_key_update_notification(self): |
| 86 | + filter_ = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET |
| 87 | + status = RegNotifyChangeKeyValue(self._key, True, filter_, self._event, True) |
| 88 | + if status != ERROR_SUCCESS: |
| 89 | + # key was deleted |
| 90 | + RegCloseKey(self._key) |
| 91 | + self._key = None |
| 92 | + |
| 93 | + def _open_key(self): |
| 94 | + access = KEY_QUERY_VALUE | KEY_READ |
| 95 | + self._key = HKEY() |
| 96 | + rc = RegOpenKeyEx(self._root, self._subkey, 0, access, ctypes.byref(self._key)) |
| 97 | + if rc != ERROR_SUCCESS: |
| 98 | + self._key = None |
0 commit comments