使用响应式扩展监控注册表项

时间:2015-12-21 12:41:37

标签: windows registry system.reactive reactive-programming

我想跟踪注册表项的更改,例如添加/删除子项,添加/删除/编辑值。我怎样才能创建一个暴露这些变化的IObservable序列?

1 个答案:

答案 0 :(得分:1)

一种方法是p / invoke RegNotifyChangeKeyValue,这是一个Win32函数,它通知调用者有关指定注册表项的属性或内容的更改。只要检测到更改,此函数就会设置事件。请注意,它必须在持久线程上调用,否则它将在线程退出时发出信号(即使没有发生更改)。请参阅下文,了解Rx.Net的可能实现方法。

using System;
using System.ComponentModel;
using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
using System.Threading;

using Microsoft.Win32;

public class RegistryMonitoringOperations
{
    [Flags]
    public enum RegChangeNotifyFilter
    {
        /// <summary>Notify the caller if a subkey is added or deleted.</summary>
        Key = 1,
        /// <summary>Notify the caller of changes to the attributes of the key,
        /// such as the security descriptor information.</summary>
        Attribute = 2,
        /// <summary>Notify the caller of changes to a value of the key. This can
        /// include adding or deleting a value, or changing an existing value.</summary>
        Value = 4,
        /// <summary>Notify the caller of changes to the security descriptor
        /// of the key.</summary>
        Security = 8
    }

    private const int KeyQueryValue = 0x0001;
    private const int KeyNotify = 0x0010;
    private const int StandardRightsRead = 0x00020000;

    public static IObservable<Unit> CreateKeyValuesChangedObservable(
        RegistryHive hive,
        string subKey,
        RegChangeNotifyFilter filter,
        IScheduler registrationScheduler)
    {
        return Observable.Create<Unit>(
            obs =>
                {
                    try
                    {
                        var key = OpenKey(hive, subKey);
                        return new CompositeDisposable(
                            CreateKeyValuesChangedObservable(key, filter).SubscribeOn(registrationScheduler).Subscribe(obs),
                            Disposable.Create(() => RegCloseKey(key)));
                    }
                    catch (Win32Exception e)
                    {
                        obs.OnError(e);
                        return Disposable.Empty;
                    }
                });
    }

    private static IDisposable SetCallbackWhenSignalled(WaitHandle waitObject, Action action)
    {
        var registeredWait = ThreadPool.RegisterWaitForSingleObject(waitObject, (s, t) => action(), null, -1, true);
        return Disposable.Create(() => registeredWait.Unregister(null));
    }

    private static IObservable<Unit> CreateKeyValuesChangedObservable(IntPtr key, RegChangeNotifyFilter filter)
    {
        return Observable.Create<Unit>(
            obs =>
                {
                    var eventNotify = new AutoResetEvent(false);
                    var result = RegNotifyChangeKeyValue(key, true, filter, eventNotify.SafeWaitHandle.DangerousGetHandle(), true);
                    if (result != 0)
                    {
                        obs.OnError(new Win32Exception(Marshal.GetLastWin32Error()));
                    }
                    return new CompositeDisposable(
                        eventNotify,
                        SetCallbackWhenSignalled(
                            eventNotify,
                            () =>
                                {
                                    obs.OnNext(Unit.Default);
                                    obs.OnCompleted();
                                }));
                }).Repeat();
    }

    private static IntPtr OpenKey(RegistryHive hive, string subKey)
    {
        IntPtr registryKey;
        var result = RegOpenKeyEx((int)hive, subKey, 0, StandardRightsRead | KeyQueryValue | KeyNotify, out registryKey);
        if (result != 0)
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        return registryKey;
    }

以下是此功能的典型用法:

 RegistryMonitoringOperations.CreateKeyValuesChangedObservable(
                RegistryHive.LocalMachine,
                "somepath",
                RegistryMonitoringOperations.RegChangeNotifyFilter.Value,
                DispatcherScheduler.Instance)

正如您在上面所看到的,避免专用线程来调用此函数的一种方法是使用持久性的UI线程(因此在rx术语中,使用调度程序调度程序)。 RegNotifyChangeKeyValue在异步模式下立即返回,因此它不会阻止UI。