Esempio n. 1
0
            public MorphicResult <MorphicUnit, MorphicUnit> RegisterForValueChangeNotification(RegistryKeyChangedEvent eventHandler)
            {
                if (_disposed == true)
                {
                    return(MorphicResult.ErrorResult());
                }

                var waitHandle = new ManualResetEvent(false);

                // NOTE: REG_NOTIFY_CHANGE_LAST_SET will trigger on any changes to the key's values
                // NOTE: registration will auto-unregister after the wait handle is trigger once.  Registration will also auto-unregister when the RegistryKey is closed/disposed
                PInvoke.Win32ErrorCode regNotifyErrorCode;
                try
                {
                    // NOTE: if _handle has been disposed, this will throw an ObjectDisposedException
                    regNotifyErrorCode = PInvoke.AdvApi32.RegNotifyChangeKeyValue(_handle, false, PInvoke.AdvApi32.RegNotifyFilter.REG_NOTIFY_CHANGE_LAST_SET, waitHandle.SafeWaitHandle, true);
                }
                catch (ObjectDisposedException ex)
                {
                    return(MorphicResult.ErrorResult());
                }
                //
                switch (regNotifyErrorCode)
                {
                case PInvoke.Win32ErrorCode.ERROR_SUCCESS:
                    break;

                default:
                    return(MorphicResult.ErrorResult());
                }

                // add our registry key (and accompanying wait handle) to the notify pool
                // NOTE: this must be the only code which is allowed to add to the notify pool; if we change this behavior, we must re-evaluate and QA the corresponding lock strategy change
                lock (s_registryKeyNotifyPoolLock)
                {
                    var notifyInfo = new RegistryKeyNotificationInfo(this, waitHandle, eventHandler);
                    s_registerKeyNotifyPool.Add(notifyInfo);

                    if (s_registryKeyNotifyPoolThread is null)
                    {
                        s_registryKeyNotifyPoolThread = new Thread(RegistryKey.ListenForRegistryKeyChanges);
                        s_registryKeyNotifyPoolThread.IsBackground = true; // set up as a background thread (so that it shuts down automatically with our application, even if all the RegistryKeys weren't fully disposed)
                        s_registryKeyNotifyPoolThread.Start();
                    }
                    else
                    {
                        // trigger our notify pool thread to see and watch for the new entries
                        s_registryKeyNotifyPoolUpdatedEvent.Set();
                    }
                }

                return(MorphicResult.OkResult());
            }
Esempio n. 2
0
            private static void ListenForRegistryKeyChanges()
            {
                while (true)
                {
                    // get a copy of our current registry key notification pool (i.e. all registry keys which we are watching)
                    RegistryKeyNotificationInfo[] copyOfNotificationPool;
                    lock (s_registryKeyNotifyPoolLock)
                    {
                        // NOTE: we intentionally copy the list into an array to make sure we have a clone of the original list (not a shared reference)
                        copyOfNotificationPool = s_registerKeyNotifyPool.ToArray();

                        // if there are no registry keys which we are subscribed to, exit our function (and shut down our thread) now
                        if (copyOfNotificationPool.Length == 0)
                        {
                            s_registryKeyNotifyPoolThread = null;
                            return;
                        }
                    }

                    // if any of the registry keys have been disposed, then remove them from our list
                    int index = 0;
                    while (index < copyOfNotificationPool.Length)
                    {
                        if (copyOfNotificationPool[index].RegistryKey._disposed == true)
                        {
                            // remove this item from the list
                            var newCopyOfNotificationPool = new RegistryKeyNotificationInfo[copyOfNotificationPool.Length - 1];
                            Array.Copy(copyOfNotificationPool, 0, newCopyOfNotificationPool, 0, index);
                            Array.Copy(copyOfNotificationPool, index + 1, newCopyOfNotificationPool, index, copyOfNotificationPool.Length - index - 1);
                            copyOfNotificationPool = newCopyOfNotificationPool;
                        }
                        else
                        {
                            // continue to the next item
                            index++;
                        }
                    }

                    // create a list of handles to wait on (first the ones which we are watching...and then the one that triggers when the list is updated)
                    var handlesToWaitOn = new WaitHandle[copyOfNotificationPool.Length + 1];
                    for (index = 0; index < copyOfNotificationPool.Length; index++)
                    {
                        handlesToWaitOn[index] = copyOfNotificationPool[index].WaitHandle;
                    }
                    handlesToWaitOn[handlesToWaitOn.Length - 1] = s_registryKeyNotifyPoolUpdatedEvent;

                    // now wait on the handles
                    var indexOfSetHandle = WaitHandle.WaitAny(handlesToWaitOn);
                    if (indexOfSetHandle == handlesToWaitOn.Length - 1)
                    {
                        // list has been updated; start processing the list again
                        continue;
                    }
                    else
                    {
                        // wait handle was triggered!
                        //
                        // capture the notification pool entry which triggered
                        var notificationPoolEntry = copyOfNotificationPool[indexOfSetHandle];
                        // call the event handler on a thread pool thread
                        Task.Run(() => { notificationPoolEntry.EventHandler(notificationPoolEntry.RegistryKey, EventArgs.Empty); });
                        // remove the registry key from our pool
                        lock (s_registryKeyNotifyPoolLock)
                        {
                            for (index = 0; index < s_registerKeyNotifyPool.Count; index++)
                            {
                                // NOTE: type WaitHandle is a class, so this comparison is a reference comparison
                                if (s_registerKeyNotifyPool[index].WaitHandle == notificationPoolEntry.WaitHandle)
                                {
                                    s_registerKeyNotifyPool.RemoveAt(index);
                                    break;
                                }
                            }
                        }
                        //
                        // if the entry we just removed hasn't been disposed, re-register it for notifications
                        if (notificationPoolEntry.RegistryKey._disposed == false)
                        {
                            // re-register the registry key for notification (using its existing event handler), since Windows auto-unregisters registrations every time the handle is triggered
                            var registerForValuechangeNotificationResult = notificationPoolEntry.RegistryKey.RegisterForValueChangeNotification(notificationPoolEntry.EventHandler);
                            if (registerForValuechangeNotificationResult.IsError)
                            {
                                Debug.Assert(false, "Could not re-register registry key for notification after raising event.");
                            }
                        }
                    }
                }
            }