/// <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); } } } }