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", SetLastError=true)]
static extern int RegNotifyChangeKeyValue(IntPtr hKey, bool watchSubtree,
   int dwNotifyFilter, IntPtr hEvent, 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.

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

Tips & Tricks:

Please add some!

[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
}

Sample Code:

using System;

using System.ComponentModel;

using System.Diagnostics;

using System.Reflection;

using System.Runtime.InteropServices;

using System.Threading;

using Microsoft.Win32;

C++ Signature:

namespace BBooth

{

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

    LONG WINAPI RegNotifyChangeKeyValue(
    __in      HKEY hKey,
    __in      BOOL bWatchSubtree,
    __in      DWORD dwNotifyFilter,
    __in_opt  HANDLE hEvent,
    __in      BOOL fAsynchronous
       );
    public class RegistryChangeMonitor : IDisposable
    {
    #region Fields
    private string _registryPath;
    private REG_NOTIFY_CHANGE _filter;
    private Thread _monitorThread;
    private RegistryKey _monitorKey;
    #endregion

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
    #region Imports
    [DllImport("Advapi32.dll")]
    private static extern int RegNotifyChangeKeyValue(
        IntPtr hKey,
        bool watchSubtree,
        REG_NOTIFY_CHANGE notifyFilter,
        IntPtr hEvent,
        bool asynchronous
        );
    #endregion

Notes:

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

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

Tips & Tricks:

Please add some!

    #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

Sample Code:

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

// RegistryChangeMonitor.cs

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Win32;
        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;
        }

namespace BBooth
{
    #region Delegates
    public delegate void RegistryChangeHandler(object sender, RegistryChangeEventArgs e);
    #endregion
        if (!this._monitorThread.IsAlive)
        {
            this._monitorThread.Start();
        }
        }
    }
    public void Stop()
    {
        lock (this)
        {
        this.Changed = null;
        this.Error = null;

    public class RegistryChangeMonitor : IDisposable
    {
    #region Fields
    private string _registryPath;
    private REG_NOTIFY_CHANGE _filter;
    private Thread _monitorThread;
    private RegistryKey _monitorKey;
    #endregion
        if (this._monitorThread != null)
        {
            this._monitorThread = null;
        }

    #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
        // The "Close()" will trigger RegNotifyChangeKeyValue if it is still listening
        if (this._monitorKey != null)
        {
            this._monitorKey.Close();
            this._monitorKey = null;
        }
        }
    }
    #endregion

    #region Constructors
    public RegistryChangeMonitor(string registryPath) : this(registryPath, REG_NOTIFY_CHANGE.LAST_SET) { ; }
    public RegistryChangeMonitor(string registryPath, REG_NOTIFY_CHANGE filter)
    private void MonitorThread()
    {
        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)
        try
        {
           if (this._monitorThread == null)
           {
               ThreadStart ts = new ThreadStart(this.MonitorThread);
               this._monitorThread = new Thread(ts);
               this._monitorThread.IsBackground = true;
           }
        IntPtr ptr = IntPtr.Zero;

           if (!this._monitorThread.IsAlive)
           {
               this._monitorThread.Start();
           }
        }
    }
    public void Stop()
    {
        lock (this)
        {
           this.Changed = null;
           this.Error = null;
        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));

           if (this._monitorThread != null)
           {
               this._monitorThread = null;
           }
            // Fetch the native handle
            if (this._monitorKey != null)
            {
            object hkey = typeof(RegistryKey).InvokeMember(
                "hkey",
                BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic,
                null,
                this._monitorKey,
                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;
            ptr = (IntPtr)typeof(SafeHandle).InvokeMember(
                "handle",
                BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic,
                null,
                hkey,
                null);
            }
        }

        lock (this)
        if (ptr != IntPtr.Zero)
        {
            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)
            while (true)
            {
                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);
                }
                this.Changed(this, e);

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

    #region Events
    public event RegistryChangeHandler Changed;
    public event RegistryChangeHandler Error;
    #endregion
                // 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) }
                    );

    #region Properties
    public bool Monitoring
    {
        get
        {
        if (this._monitorThread != null)
            return this._monitorThread.IsAlive;
                RegistryChangeEventArgs e = new RegistryChangeEventArgs(this);
                e.Exception = ex;
                this.Error(this, e);
                }

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

// RegistryChangeEventArgs.cs

using System;

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

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

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

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

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

}

using System;

namespace BBooth

{

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

Alternative Managed API:

Do you know one? Please contribute it!

    #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