2015-12-21 80 views

回答

1

一種方法是調用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。