/// <summary> /// Configure change monitoring for this monitor object /// </summary> /// <param name="subKey"></param> private void ConfigureChangeMonitoring(RegistryKeyMonitor monitor, bool throwOnNotExist) { string key = monitor.Key; // assert preconditions Debug.Assert(monitor.hRawKey == UIntPtr.Zero); Debug.Assert(monitor.settingsChangedEvent == null); // open handle to registry key int result = Advapi32.RegOpenKeyEx( monitor.HKey, key, 0, KEY.READ, out monitor.hRawKey); if (result != ERROR.SUCCESS) { if (throwOnNotExist) { throw new Exception("Failed to open registry key :" + key); } else { Debug.WriteLine("ConfigureChangeMonitoring error: " + result); return; } } // create settings changed event monitor.settingsChangedEvent = new ManualResetEvent(false); // start monitoring changes RegisterForRegistryKeyChanged(monitor); }
/// <summary> /// Deregister for registry change events. /// </summary> /// <param name="hkey"></param> /// <param name="key"></param> /// <param name="callback"></param> public void RemoveRegistryChangeListener(UIntPtr hkey, string key, RegistryKeyEventHandler callback) { lock (this) { RegistryKeyMonitor keyMonitor = GetRegistryKeyMonitor(hkey, key); keyMonitor.RemoveChangeListener(callback); if (!keyMonitor.HasListeners()) { //TODO: deregister for change key events //Debug.WriteLine("stop listening for changes to registry key: " + keyMonitor.FullKey, "RegistryMonitor"); keyMonitors.Remove(keyMonitor.FullKey); keyMonitor.settingsChangedEvent.Close(); //Reset the list of monitor handles. ResetMonitorHandles(); //if there are no more listeners, abort the monitor thread. if (keyMonitors.Count == 0) { try { monitorAbortEvent.Set(); } catch (ObjectDisposedException) { //occurs when removing change listeners while shutting down. } } } } }
/// <summary> /// Creates a key monitor object and registers it with the OS for registry change events. /// </summary> /// <param name="key"></param> /// <returns></returns> private RegistryKeyMonitor CreateRegistryKeyMonitor(UIntPtr hkey, string key, bool autoCreateKey) { if (autoCreateKey) { RegistryHelper.CreateKey(hkey, key); } RegistryKeyMonitor monitor = new RegistryKeyMonitor(this, hkey, key); ConfigureChangeMonitoring(monitor, false); return(monitor); }
/// <summary> /// Retreives the RegistryKeyMonitor associated with the specified registry key /// </summary> /// <param name="key"></param> /// <returns></returns> private RegistryKeyMonitor GetRegistryKeyMonitor(UIntPtr hkey, string key, bool autoCreateKey) { string fullKey = RegistryHelper.GetKeyString(hkey, key); RegistryKeyMonitor monitor = keyMonitors[fullKey] as RegistryKeyMonitor; if (monitor == null) { monitor = CreateRegistryKeyMonitor(hkey, key, autoCreateKey); keyMonitors[monitor.FullKey] = monitor; ResetMonitorHandles(); //Debug.WriteLine("start listening for changes to registry key: " + monitor.FullKey, "RegistryMonitor"); } return(monitor); }
/// <summary> /// Monitor changes in the registry key. /// </summary> private static void RegisterForRegistryKeyChanged(RegistryKeyMonitor monitor) { // reset the settings changed event so it will not be considered signaled until // another change is made to the specified key monitor.settingsChangedEvent.Reset(); // request that the event be signaled when the registry key changes int result = Advapi32.RegNotifyChangeKeyValue(monitor.hRawKey, false, REG_NOTIFY_CHANGE.LAST_SET, monitor.settingsChangedEvent.SafeWaitHandle, true); if (result != ERROR.SUCCESS) { Trace.WriteLine("Unexpeced failure to monitor reg key (Error code: " + result); } }
/// <summary> /// Register a method for callbacks when the specified registry key is changed. /// Note: this operation does not recursively register for notifcations from subkeys! /// </summary> /// <param name="hkey">the HKEY constant (HKEY.CURRENT_USER or HKEY.CLASSES_ROOT)</param> /// <param name="key">the registry key path (example: @"Software\AppDataLow\Software\Onfolio\Preferences\Appearance"</param> /// <param name="callback">the callback delegate that is invoked when the registry key changes</param> public void AddRegistryChangeListener(UIntPtr hkey, string key, RegistryKeyEventHandler callback, bool autoCreateKey) { if (!autoCreateKey && !RegistryHelper.KeyExists(hkey, key)) { return; } lock (this) { RegistryKeyMonitor keyMonitor = GetRegistryKeyMonitor(hkey, key, autoCreateKey); keyMonitor.AddChangeListener(callback); //launch the monitor thread if it hasn't been started. if (monitorThread == null) { monitorThread = new Thread(new ThreadStart(this.MonitorChanges)); monitorThread.IsBackground = true; monitorThread.Name = "Registry Monitor"; monitorThread.Start(); } } }
/// <summary> /// Configure change monitoring for this monitor object /// </summary> /// <param name="subKey"></param> private void ConfigureChangeMonitoring(RegistryKeyMonitor monitor, bool throwOnNotExist) { string key = monitor.Key; // assert preconditions Debug.Assert(monitor.hRawKey == UIntPtr.Zero); Debug.Assert(monitor.settingsChangedEvent == null); // open handle to registry key int result = Advapi32.RegOpenKeyEx( monitor.HKey, key, 0, KEY.READ, out monitor.hRawKey); if (result != ERROR.SUCCESS) { if (throwOnNotExist) throw new Exception("Failed to open registry key :" + key); else { Debug.WriteLine("ConfigureChangeMonitoring error: " + result); return; } } // create settings changed event monitor.settingsChangedEvent = new ManualResetEvent(false); // start monitoring changes RegisterForRegistryKeyChanged(monitor); }
/// <summary> /// Creates a key monitor object and registers it with the OS for registry change events. /// </summary> /// <param name="key"></param> /// <returns></returns> private RegistryKeyMonitor CreateRegistryKeyMonitor(UIntPtr hkey, string key, bool autoCreateKey) { if (autoCreateKey) { RegistryHelper.CreateKey(hkey, key); } RegistryKeyMonitor monitor = new RegistryKeyMonitor(this, hkey, key); ConfigureChangeMonitoring(monitor, false); return monitor; }
/// <summary> /// Monitor changes in the registry. /// Note: This operation is the main Run() loop for the Registry Monitor Thread. /// </summary> private void MonitorChanges() { //use a hashtable to keep track of bulk changes to the registry so that we only fire one event each //if multiple changes are made to the registry at once (like when a key is renamed) Hashtable regChangeList = new Hashtable(); while (true) { SafeHandle[] monitorHandles; RegistryKeyMonitor[] monitors; lock (this) //lock here so that we don't encounter race conditions if registry listeners are being added/removed { monitorHandles = MonitorHandles; monitors = (RegistryKeyMonitor[])ArrayHelper.CollectionToArray(keyMonitors.Values, (typeof(RegistryKeyMonitor))); } using (SafeHandleArrayHelper safeHandleArrayHelper = new SafeHandleArrayHelper(monitorHandles)) { uint result = Kernel32.WaitForMultipleObjects((uint)monitorHandles.Length, safeHandleArrayHelper.IntPtrs, false, Kernel32.INFINITE); if (result >= 0 && result < FIXED_MONITOR_EVENTS.Length) { //a FIXED_MONITOR_EVENT occured, so handle it. if (result == MONITOR_HANDLES_UPDATED_INDEX) { //the monitorHandlesUpdatedEvent was signalled, so reset it and re-loop //so that we get the latest list of events to monitor. monitorHandlesUpdatedEvent.Reset(); } else if (result == MONITOR_ABORT_INDEX) { //the monitorAbortEvent was signaled, so exit the method. //Debug.WriteLine("registry monitor stopped", "RegistryMonitor"); monitorAbortEvent.Reset(); return; } else { Debug.Fail("unknown FIXED_MONITOR_EVENT detected!"); } } else if (result >= WAIT.OBJECT_0 && result <= (WAIT.OBJECT_0 + monitorHandles.Length - 1)) { //one or more registry keys have changed, so loop to gather up all of the signaled events uint nextResult = result; while ((result >= WAIT.OBJECT_0 && result <= (WAIT.OBJECT_0 + monitorHandles.Length - 1)) && !(result >= 0 && result < FIXED_MONITOR_EVENTS.Length)) { //a registry change event occured, so add the monitor to the notification list. RegistryKeyMonitor keyMonitor = monitors[result - FIXED_MONITOR_EVENTS.Length]; regChangeList[keyMonitor.FullKey] = keyMonitor; //re-register the monitor for change events from the OS RegisterForRegistryKeyChanged(keyMonitor); //get the next pending event from the kernel (without blocking) result = Kernel32.WaitForMultipleObjects((uint)monitorHandles.Length, safeHandleArrayHelper.IntPtrs, false, 0); } //notify the listeners the keys have changed (use the backgroundWorkerQueue so that this thread can't get hosed!) RegistryKeyMonitor[] changedMonitors = (RegistryKeyMonitor[]) ArrayHelper.CollectionToArray(regChangeList.Values, typeof(RegistryKeyMonitor)); workerQueue.AddWorker(new WaitCallback(backgroundWorker_HandleRegistryKeyChanged), changedMonitors); //clear the change list. regChangeList.Clear(); } else if (result == WAIT.FAILED) { Debug.Fail("Wait failed: " + Marshal.GetLastWin32Error()); } else if (result == WAIT.TIMEOUT) { //just re-loop so that we have the latest list of events to monitor. } else { Debug.Fail("unexpected WaitForMultipleObjects() return code!: " + result); } } } }