Search
Module:
Directory

   Desktop Functions:

   Smart Device Functions:


Show Recent Changes
Subscribe (RSS)
Misc. Pages
Comments
FAQ
Helpful Tools
Playground
Suggested Reading
Website TODO List
Download Visual Studio Add-In

RegNotifyChangeKeyValue (advapi32)
 
.
Summary
Notifies the caller about changes to the attributes or contents of a specified registry key.

C# Signature:

[DllImport("Advapi32.dll")]
private static extern int RegNotifyChangeKeyValue(
   IntPtr        hKey,
   bool          watchSubtree,
   REG_NOTIFY_CHANGE notifyFilter,
   IntPtr        hEvent,
   bool          asynchronous
   );

[Flags]
public enum REG_NOTIFY_CHANGE : uint
{
   /// <summary>
   /// Notify the caller if a subkey is added or deleted
   /// </summary>
     NAME       = 0x1,
   /// <summary>
   /// Notify the caller of changes to the attributes of the key,
   /// such as the security descriptor information
   /// </summary>
     ATTRIBUTES = 0x2,
   /// <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>
     LAST_SET   = 0x4,
   /// <summary>
   /// Notify the caller of changes to the security descriptor of the key
   /// </summary>
     SECURITY   = 0x8
}

C++ Signature:

    LONG WINAPI RegNotifyChangeKeyValue(
    __in      HKEY hKey,
    __in      BOOL bWatchSubtree,
    __in      DWORD dwNotifyFilter,
    __in_opt  HANDLE hEvent,
    __in      BOOL fAsynchronous
       );

VB Signature:

Declare Function RegNotifyChangeKeyValue Lib "advapi32.dll" (hKey As IntPtr, _
   watchSubtree As Boolean, dwNotifyFilter As Integer, hEvent As IntPtr,
   fAsynchronous As Boolean) As Integer

Notes:

Starting with the .NET Framework 2.0, the IntPtr parameters could be defined as SafeHandle instead.

Tips & Tricks:

Please add some!

Sample Code:

// RegistryChangeMonitor.cs

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Win32;

namespace BBooth
{
    #region Delegates
    public delegate void RegistryChangeHandler(object sender, RegistryChangeEventArgs e);
    #endregion

    public class RegistryChangeMonitor : IDisposable
    {
    #region Fields
    private string _registryPath;
    private REG_NOTIFY_CHANGE _filter;
    private Thread _monitorThread;
    private RegistryKey _monitorKey;
    #endregion

    #region Imports
    [DllImport("Advapi32.dll")]
    private static extern int RegNotifyChangeKeyValue(
       IntPtr hKey,
       bool watchSubtree,
       REG_NOTIFY_CHANGE notifyFilter,
       IntPtr hEvent,
       bool asynchronous
       );
    #endregion

    #region Enumerations
    [Flags]
    public enum REG_NOTIFY_CHANGE : uint
    {
        NAME = 0x1,
        ATTRIBUTES = 0x2,
        LAST_SET = 0x4,
        SECURITY = 0x8
    }
    #endregion

    #region Constructors
    public RegistryChangeMonitor(string registryPath) : this(registryPath, REG_NOTIFY_CHANGE.LAST_SET) { ; }
    public RegistryChangeMonitor(string registryPath, REG_NOTIFY_CHANGE filter)
    {
        this._registryPath = registryPath.ToUpper();
        this._filter = filter;
    }
    ~RegistryChangeMonitor()
    {
        this.Dispose(false);
    }
    #endregion

    #region Methods
    private void Dispose(bool disposing)
    {
        if (disposing)
           GC.SuppressFinalize(this);

        this.Stop();
    }
    public void Dispose()
    {
        this.Dispose(true);
    }
    public void Start()
    {
        lock (this)
        {
           if (this._monitorThread == null)
           {
               ThreadStart ts = new ThreadStart(this.MonitorThread);
               this._monitorThread = new Thread(ts);
               this._monitorThread.IsBackground = true;
           }

           if (!this._monitorThread.IsAlive)
           {
               this._monitorThread.Start();
           }
        }
    }
    public void Stop()
    {
        lock (this)
        {
           this.Changed = null;
           this.Error = null;

           if (this._monitorThread != null)
           {
               this._monitorThread = null;
           }

           // The "Close()" will trigger RegNotifyChangeKeyValue if it is still listening
           if (this._monitorKey != null)
           {
               this._monitorKey.Close();
               this._monitorKey = null;
           }
        }
    }
    private void MonitorThread()
    {
        try
        {
        IntPtr ptr = IntPtr.Zero;

        lock (this)
        {
            if (this._registryPath.StartsWith("HKEY_CLASSES_ROOT"))
            this._monitorKey = Registry.ClassesRoot.OpenSubKey(this._registryPath.Substring(18));
            else if (this._registryPath.StartsWith("HKCR"))
            this._monitorKey = Registry.ClassesRoot.OpenSubKey(this._registryPath.Substring(5));
            else if (this._registryPath.StartsWith("HKEY_CURRENT_USER"))
            this._monitorKey = Registry.CurrentUser.OpenSubKey(this._registryPath.Substring(18));
            else if (this._registryPath.StartsWith("HKCU"))
            this._monitorKey = Registry.CurrentUser.OpenSubKey(this._registryPath.Substring(5));
            else if (this._registryPath.StartsWith("HKEY_LOCAL_MACHINE"))
            this._monitorKey = Registry.LocalMachine.OpenSubKey(this._registryPath.Substring(19));
            else if (this._registryPath.StartsWith("HKLM"))
            this._monitorKey = Registry.LocalMachine.OpenSubKey(this._registryPath.Substring(5));
            else if (this._registryPath.StartsWith("HKEY_USERS"))
            this._monitorKey = Registry.Users.OpenSubKey(this._registryPath.Substring(11));
            else if (this._registryPath.StartsWith("HKU"))
            this._monitorKey = Registry.Users.OpenSubKey(this._registryPath.Substring(4));
            else if (this._registryPath.StartsWith("HKEY_CURRENT_CONFIG"))
            this._monitorKey = Registry.CurrentConfig.OpenSubKey(this._registryPath.Substring(20));
            else if (this._registryPath.StartsWith("HKCC"))
            this._monitorKey = Registry.CurrentConfig.OpenSubKey(this._registryPath.Substring(5));

            // Fetch the native handle
            if (this._monitorKey != null)
            {
                object hkey = typeof(RegistryKey).InvokeMember(
                   "hkey",
                   BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic,
                   null,
                   this._monitorKey,
                   null
                   );

                ptr = (IntPtr)typeof(SafeHandle).InvokeMember(
                   "handle",
                   BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic,
                   null,
                   hkey,
                   null);
            }
        }

        if (ptr != IntPtr.Zero)
        {
            while (true)
            {
            // If this._monitorThread is null that probably means Dispose is being called. Don't monitor anymore.
            if ((this._monitorThread == null) || (this._monitorKey == null))
                break;

            // RegNotifyChangeKeyValue blocks until a change occurs.
            int result = RegNotifyChangeKeyValue(ptr, true, this._filter, IntPtr.Zero, false);

            if ((this._monitorThread == null) || (this._monitorKey == null))
                break;

            if (result == 0)
            {
                if (this.Changed != null)
                {
                 RegistryChangeEventArgs e = new RegistryChangeEventArgs(this);
                 this.Changed(this, e);

                 if (e.Stop) break;
                }
            }
            else
            {
                if (this.Error != null)
                {
                Win32Exception ex = new Win32Exception();

                // Unless the exception is thrown, nobody is nice enough to set a good stacktrace for us. Set it ourselves.
                typeof(Exception).InvokeMember(
                "_stackTrace",
                BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField,
                null,
                ex,
                new object[] { new StackTrace(true) }
                );

                RegistryChangeEventArgs e = new RegistryChangeEventArgs(this);
                e.Exception = ex;
                this.Error(this, e);
                }

                break;
            }
            }
        }
        }
        catch (Exception ex)
        {
        if (this.Error != null)
        {
            RegistryChangeEventArgs e = new RegistryChangeEventArgs(this);
            e.Exception = ex;
            this.Error(this, e);
        }
        }
        finally
        {
        this.Stop();
        }
    }
    #endregion

    #region Events
    public event RegistryChangeHandler Changed;
    public event RegistryChangeHandler Error;
    #endregion

    #region Properties
    public bool Monitoring
    {
        get
        {
        if (this._monitorThread != null)
            return this._monitorThread.IsAlive;

        return false;
        }
    }
    #endregion
    }
}

// RegistryChangeEventArgs.cs

using System;

namespace BBooth
{
    public class RegistryChangeEventArgs : EventArgs
    {
    #region Fields
    private bool _stop;
    private Exception _exception;
    private RegistryChangeMonitor _monitor;
    #endregion

    #region Constructor
    public RegistryChangeEventArgs(RegistryChangeMonitor monitor)
    {
        this._monitor = monitor;
    }
    #endregion

    #region Properties
    public RegistryChangeMonitor Monitor
    {
        get { return this._monitor; }
    }

    public Exception Exception
    {
        get { return this._exception; }
        set { this._exception = value; }
    }

    public bool Stop
    {
        get { return this._stop; }
        set { this._stop = value; }
    }
    #endregion
    }
}

Alternative Managed API:

Do you know one? Please contribute it!

Documentation

Please edit this page!

Do you have...

  • helpful tips or sample code to share for using this API in managed code?
  • corrections to the existing content?
  • variations of the signature you want to share?
  • additional languages you want to include?

Select "Edit This Page" on the right hand toolbar and edit it! Or add new pages containing supporting types needed for this API (structures, delegates, and more).

 
Access PInvoke.net directly from VS:
Terms of Use
Edit This Page
Find References
Show Printable Version
Revisions