Contents
Do you want your application to be notified when there is a change in the Windows Registry? Do you need to watch a specific value in the Registry and get notified when it is changed/deleted/renamed? If yes, you can use the component presented in this article.
The component in this article uses WMI events in order to get notified when the system Registry is changed, so a basic understanding of WMI and events in WMI is desired.
The Registry provider supplies four event classes for events in the system Registry. All of them reside in the root\DEFAULT namespace. The Registry event classes in WMI are: RegistryEvent
, RegistryTreeChangeEvent
, RegistryKeyChangeEvent
, and RegistryValueChangeEvent
. Here is a short description of these classes: RegistryEvent
is an abstract base class for changes in the Registry. RegistryTreeChangeEvent
is a class that monitors changes to a hierarchy of keys. RegistryKeyChangeEvent
monitors changes to a single key. RegistryValueChangeEvent
monitors changes to a single value. All these classes have a property called Hive
that identifies the hierarchy of keys to be monitored for change. HKEY_CLASSES_ROOT and HKEY_CURRENT_USER are not supported by these classes and changes can not be detected.
In order to handle WMI events in .NET, we can use classes from the System.Management
namespace. For example, this is a piece of code produced by the WMI Code Creator:
using System;
using System.Management;
using System.Windows.Forms;
namespace WMISample
{
public class WMIReceiveEvent
{
public static void Main()
{
try
{
WqlEventQuery query = new WqlEventQuery(
"SELECT * FROM RegistryKeyChangeEvent WHERE " +
"Hive = 'HKEY_LOCAL_MACHINE'" +
@"AND KeyPath = 'SOFTWARE\\Microsoft\\.NETFramework'");
ManagementEventWatcher watcher = new ManagementEventWatcher(query);
Console.WriteLine("Waiting for an event...");
ManagementBaseObject eventObj = watcher.WaitForNextEvent();
Console.WriteLine("{0} event occurred.", eventObj["__CLASS"]);
watcher.Stop();
return;
}
catch(ManagementException err)
{
MessageBox.Show("An error occurred while trying to receive an event: " +
err.Message);
}
}
}
}
The above code works, but has two major drawbacks:
- Events are received synchronously
- Weakly typed WMI classes are used
In order to receive events asynchronously, you should subscribe to the EventArrived
event of the ManagementEventWatcher
class instead of calling the WaitForNextEvent
method which blocks the thread. After subscribing to the event, you should call the Start
method of the ManagementEventWatcher
class. Calling the Stop
method will stop listening to events. The event is fired when a new WMI event arrives. Data about the event can be retrieved through the NewEvent
property of the EventArrivedEventArgs
class. Here is how it works:
using System;
using System.Management;
using System.Windows.Forms;
namespace WMISample
{
public class WMIReceiveEvent
{
public WMIReceiveEvent()
{
try
{
WqlEventQuery query = new WqlEventQuery(
"SELECT * FROM RegistryKeyChangeEvent WHERE " +
"Hive = 'HKEY_LOCAL_MACHINE'" +
@"AND KeyPath = 'SOFTWARE\\Microsoft\\.NETFramework'");
ManagementEventWatcher watcher = new ManagementEventWatcher(query);
Console.WriteLine("Waiting for an event...");
watcher.EventArrived += new EventArrivedEventHandler(HandleEvent);
watcher.Start();
System.Threading.Thread.Sleep(10000);
watcher.Stop();
return;
}
catch(ManagementException err)
{
MessageBox.Show("An error occurred while trying to receive an event: " +
err.Message);
}
}
private void HandleEvent(object sender,
EventArrivedEventArgs e)
{
Console.WriteLine("RegistryKeyChangeEvent event occurred.");
}
public static void Main()
{
WMIReceiveEvent receiveEvent = new WMIReceiveEvent();
return;
}
}
}
Strongly-typed WMI classes can be generated by using the Mgmtclassgen.exe tool which is included in the .NET Framework SDK. Every property and every method in the WMI class has an equivalent property and method in the generated class. These are the classes generated by this tool: RegistryEvent
, RegistryKeyChangeEvent
, RegistryTreeChangeEvent
, and RegistryValueChangeEvent
. They can be found in the WMI classes folder.
In order to get notified when the Registry changes, you do not need to create the query yourself and subscribe to the event. You just pass the Hive
and KeyPath
you want to watch, to the component presented in this article. The component consists of an abstract class RegistryChangeBase
which inherits from ManagementEventWatcher
. There are three classes inheriting from RegistryChangeBase
: RegistryKeyChange
, RegistryTreeChange
, and RegistryValueChange
. All of these classes provide an event which is raised when the corresponding event is detected by WMI. This is how it all works:
public class RegistryKeyChange : RegistryChangeBase
{
public event EventHandler<RegistryKeyChangedEventArgs> RegistryKeyChanged;
private const string queryString =
"SELECT * FROM RegistryKeyChangeEvent WHERE Hive = '{0}' AND ({1})";
public RegistryKeyChange(string Hive, string KeyPath)
: this(Hive, new List<string>(new string[] { KeyPath }))
{ }
public RegistryKeyChange(string Hive, List<string> KeyPathCollection)
: base(Hive, KeyPathCollection)
{
this.Query.QueryString = BuildQueryString(Hive, KeyPathCollection);
this.EventArrived += new EventArrivedEventHandler(RegistryKeyChange_EventArrived);
}
private void RegistryKeyChange_EventArrived(object sender, EventArrivedEventArgs e)
{
RegistryKeyChangeEvent RegValueChange = new RegistryKeyChangeEvent(e.NewEvent);
OnRegistryKeyChanged(RegValueChange);
}
protected virtual void OnRegistryKeyChanged(RegistryKeyChangeEvent RegValueChange)
{
if (RegistryKeyChanged != null)
{
RegistryKeyChanged(this, new RegistryKeyChangedEventArgs(RegValueChange));
}
}
}
As you can see from the above code, the constructor of the class calls a method that builds the query string and sets up the event handler for the EventArrived
event. In the event handler, it uses the data of the event to create a new instance of the RegistryKeyChangeEvent
class which is a strongly typed wrapper around the WMI class. After that, it just raises the event if there are any subscribers. Other classes inheriting from RegistryChangeBase
are designed in the same way.
When building queries for receiving WMI events, the where clause must contain the values for every property in the specified event class. Moreover, the system Registry provider must be able to build a list of possible values for each property. Here are some examples:
SELECT * FROM RegistryTreeChangeEvent
WHERE Hive = 'HKEY_LOCAL_MACHINE' AND Rootpath = 'Software'
SELECT * FROM RegistryTreeChangeEvent WHERE (hive = 'hkey_local_machine'
AND rootpath = 'software') OR
(hive = 'hkey_current_user' AND rootpath = 'console')
SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_LOCAL_MACHINE' AND
KeyPath = 'SOFTWARE\\MICROSOFT\\WBEM\\CIMOM' AND
(ValueName = 'Backup Interval Threshold' OR ValueName = 'Logging')
This shows a query that will not work:
SELECT * FROM RegistryTreeChangeEvent
WHERE hive = hkey_local_machine' OR rootpath ='software'
You can find detailed information here: Creating a Proper WHERE Clause for the Registry Provider
The constructor of the RegistryChangeBase
class ensures that these conditions are met when constructing queries.
When I initially published the article, the component worked on Vista but not on XP. This was really strange as usually things break on Vista and work on XP but not the other way around. In his comment Uros Calakovic posted how to make the component work in XP. The only thing that I changed is adding the following line to constructor of RegistryChangeBase
class:
this.Scope.Path.NamespacePath = @"root\default";
Using the classes should be quite straightforward. Just choose the class you need according to what kind of notification you need to receive, create an instance, subscribe to the RegistryXXXChanged
event, and call the Start
method to start receiving notification. Here is an example:
Subscribe()
{
RegistryKeyChange keychange =
new RegistryKeyChange("HKEY_LOCAL_MACHINE", @"SOFTWARE\\Microsoft");
keychange.RegistryKeyChanged +=
new EventHandler<RegistryKeyChangedEventArgs>(keychange_RegistryKeyChanged);
keychange.Start();
}
void keychange_RegistryKeyChanged(object sender, RegistryKeyChangedEventArgs e)
{
lsbDisplay.Invoke((MethodInvoker)(() =>
{
lsbDisplay.Items.Add(string.Format("RegistryKeyChangeEvent detected at {0}
Event data: Hive: {1} KeyPath: {2}",
DateTime.FromFileTime((long)e.RegistryKeyChangeData.TIME_CREATED).ToString(),
e.RegistryKeyChangeData.Hive,
e.RegistryKeyChangeData.KeyPath));
}));
}
As the events are fired asynchronously, you cannot access controls created by other threads directly. Note that the parameter of the event handler is an instance of the strongly typed WMI class. Also note the use of the TIME_CREATED
property to retrieve the time when the event was generated. To stop receiving notifications, just call Stop
method.
Constructors that accept List<String>
as one of the parameters can be used to build queries like this:
SELECT * FROM RegistryKeyChangeEvent WHERE
hive = 'hkey_local_machine' AND (keypath = 'software' OR keypath = 'console')
The RegistryValueChange
class allows to build queries like this:
SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_LOCAL_MACHINE' AND
KeyPath = 'SOFTWARE\\MICROSOFT\\WBEM\\CIMOM' AND
(ValueName = 'Backup Interval Threshold' OR ValueName = 'Logging')
Here are some questions that no one has asked yet, but I suspect they will be asked.
- Is it possible to find out which application triggered a Registry event?
Not with this component.
- Is it possible to cancel the event?
Not with this component.
- Is there any other way to get notified when the Registry changes?
Yes, there is. Have a look at this article: RegistryMonitor - a .NET wrapper class for RegNotifyChangeKeyValue.
- I need to get notified when there is a change in the HKEY_CURRENT_USER hive, but it is not supported by this component. What should I do?
Read the answer to a previous question.
- Which platforms are supported?
I have tested this component on 64 bit Vista Ultimate SP1 and XP (see history). It should also work on other platforms where WMI is present.
- When I rename a key in the Registry, I get two events. Why?
When you rename a key, it is deleted and a key with a new name is created.
- November 2, 2008 - Initial release.
- November 14, 2008 - Version 1.1
- Now works on XP too. Credits go to Uros Calakovic - Urke for posting about it in comments