Skip to content

Instantly share code, notes, and snippets.

@thenadz
Last active December 23, 2023 10:29
Show Gist options
  • Save thenadz/edfcd52a02d479e443e0 to your computer and use it in GitHub Desktop.
Save thenadz/edfcd52a02d479e443e0 to your computer and use it in GitHub Desktop.
Class to monitor one or more registry values and notify when value changes.
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
public class Program
{
private const string REG_KEY = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\policies\system";
private const string CAPTION_VALUE_NAME = "legalnoticecaption";
private const string TEXT_VALUE_NAME = "legalnoticetext";
/// <summary>
/// The watcher instance.
/// </summary>
private static RegistryWatcher watcher;
/// <summary>
/// Mains the specified arguments.
/// </summary>
/// <param name="args">The arguments.</param>
public static void Main(string[] args)
{
watcher = new RegistryWatcher(
new Tuple<string, string>(REG_KEY, CAPTION_VALUE_NAME),
new Tuple<string, string>(REG_KEY, TEXT_VALUE_NAME));
watcher.RegistryChange += RegistryChanged;
Registry.SetValue(REG_KEY, CAPTION_VALUE_NAME, string.Empty);
Registry.SetValue(REG_KEY, TEXT_VALUE_NAME, string.Empty);
Console.ReadLine();
}
/// <summary>
/// Registries the changed.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="args">The <see cref="RegistryWatcher.RegistryChangeEventArgs"/> instance containing the event data.</param>
private static void RegistryChanged(object sender, RegistryWatcher.RegistryChangeEventArgs args)
{
if (!string.IsNullOrEmpty(args.Value.ToString()))
{
Registry.SetValue(args.KeyName, args.ValueName, string.Empty);
}
}
}
public class RegistryWatcher : IDisposable
{
/// <summary>
/// The period in ms between registry polls.
/// </summary>
private const int PERIOD = 30000;
/// <summary>
/// The current reg values to be compared against.
/// </summary>
private readonly Dictionary<Tuple<string, string>, object> currentRegValues;
/// <summary>
/// Registry entries to watch.
/// </summary>
private readonly Tuple<string, string>[] toWatch;
/// <summary>
/// The timer to trigger registry polls.
/// </summary>
private readonly Timer timer;
/// <summary>
/// Occurs when registry value changes.
/// </summary>
public event EventHandler<RegistryChangeEventArgs> RegistryChange;
/// <summary>
/// Initializes a new instance of the <see cref="RegistryWatcher"/> class.
/// </summary>
/// <param name="toWatch">Registry entries to watch.</param>
public RegistryWatcher(params Tuple<string, string>[] toWatch)
{
this.toWatch = toWatch;
if (toWatch.Length > 0)
{
currentRegValues = toWatch.ToDictionary(key => key, key => Registry.GetValue(key.Item1, key.Item2, null));
timer = new Timer(CheckRegistry, null, PERIOD, Timeout.Infinite);
}
}
/// <summary>
/// Checks the registry.
/// </summary>
/// <param name="state">The state.</param>
private void CheckRegistry(object state)
{
foreach (Tuple<string, string> reg in toWatch)
{
object newValue = Registry.GetValue(reg.Item1, reg.Item2, null);
if (currentRegValues[reg] != newValue)
{
RegistryChange?.Invoke(this, new RegistryChangeEventArgs(reg.Item1, reg.Item2, newValue));
currentRegValues[reg] = newValue;
}
}
timer.Change(PERIOD, Timeout.Infinite);
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
try
{
timer?.Dispose();
}
catch { }
}
/// <summary>
/// Event args provided upon registry value changing.
/// </summary>
public class RegistryChangeEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="RegistryChangeEventArgs"/> class.
/// </summary>
/// <param name="keyName">Name of the key.</param>
/// <param name="valueName">Name of the value.</param>
/// <param name="value">The value.</param>
public RegistryChangeEventArgs(string keyName, string valueName, object value)
{
KeyName = keyName;
ValueName = valueName;
Value = value;
}
/// <summary>
/// Gets the name of the key.
/// </summary>
public string KeyName { get; }
/// <summary>
/// Gets the name of the value.
/// </summary>
public string ValueName { get; }
/// <summary>
/// Gets the value.
/// </summary>
public object Value { get; }
}
}
@JuergenGeiss
Copy link

In CheckRegistry this does not work:
if (currentRegValues[reg] != newValue)
It is always true even if the values are the same because it is an object.
You must use the object version:
if (!Equals(currentRegValues[reg], newValue))

I saw this when debugging with single step in Visual Studio.

@De2th
Copy link

De2th commented Dec 23, 2023

VB>NET Registry Watcher Class

Imports Microsoft.Win32
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Threading

Public Class Program
Private Const REG_KEY As String = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\policies\system"
Private Const CAPTION_VALUE_NAME As String = "legalnoticecaption"
Private Const TEXT_VALUE_NAME As String = "legalnoticetext"
Private Shared watcher As RegistryWatcher

Public Shared Sub Main(ByVal args As String())
    watcher = New RegistryWatcher(New Tuple(Of String, String)(REG_KEY, CAPTION_VALUE_NAME), New Tuple(Of String, String)(REG_KEY, TEXT_VALUE_NAME))
    AddHandler watcher.RegistryChange, AddressOf RegistryChanged
    Registry.SetValue(REG_KEY, CAPTION_VALUE_NAME, String.Empty)
    Registry.SetValue(REG_KEY, TEXT_VALUE_NAME, String.Empty)
    Console.ReadLine()
End Sub

Private Shared Sub RegistryChanged(ByVal sender As Object, ByVal args As RegistryWatcher.RegistryChangeEventArgs)
    If Not String.IsNullOrEmpty(args.Value.ToString()) Then
        Registry.SetValue(args.KeyName, args.ValueName, String.Empty)
    End If
End Sub

End Class

Public Class RegistryWatcher
Implements IDisposable

Private Const PERIOD As Integer = 30000
Private ReadOnly currentRegValues As Dictionary(Of Tuple(Of String, String), Object)
Private ReadOnly toWatch As Tuple(Of String, String)()
Private ReadOnly timer As Timer
Public Event RegistryChange As EventHandler(Of RegistryChangeEventArgs)

Public Sub New(ParamArray toWatch As Tuple(Of String, String)())
    Me.toWatch = toWatch

    If toWatch.Length > 0 Then
        currentRegValues = toWatch.ToDictionary(Function(key) key, Function(key) Registry.GetValue(key.Item1, key.Item2, Nothing))
        timer = New Timer(AddressOf CheckRegistry, Nothing, PERIOD, Timeout.Infinite)
    End If
End Sub

Private Sub CheckRegistry(ByVal state As Object)
    For Each reg As Tuple(Of String, String) In toWatch
        Dim newValue As Object = Registry.GetValue(reg.Item1, reg.Item2, Nothing)

        If currentRegValues(reg) <> newValue Then
            RegistryChange?.Invoke(Me, New RegistryChangeEventArgs(reg.Item1, reg.Item2, newValue))
            currentRegValues(reg) = newValue
        End If
    Next

    timer.Change(PERIOD, Timeout.Infinite)
End Sub

Public Sub Dispose()
    Try
        timer?.Dispose()
    Catch
    End Try
End Sub

Public Class RegistryChangeEventArgs
    Inherits EventArgs

    Public Sub New(ByVal keyName As String, ByVal valueName As String, ByVal value As Object)
        KeyName = keyName
        ValueName = valueName
        Value = value
    End Sub

    Public ReadOnly Property KeyName As String
    Public ReadOnly Property ValueName As String
    Public ReadOnly Property Value As Object
End Class

End Class

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment