Accessing the Windows Registry Using XPCOM

Introduction

When implementing Windows-specific functionality, it is often useful to access the Windows registry for information about the environment or other installed programs. To this end, there exist XPCOM interfaces to read and write registry data. This article will show you how to use the available interfaces in several Mozilla products.

The examples in this document are all written in JavaScript using XPCOM.

Support in Firefox 1.5 or newer

In Firefox 1.5, a new API was added, nsIWindowsRegKey, which provides extensive registry functionality. The interface follows the Windows API fairly closely, but with many of the low-level details taken care of for you. If you are writing an extension that only needs to support Firefox 1.5 or newer, then you only need to read this section.

A simple example

Here's a simple example showing how to read your Windows ProductId:

var wrk = Components.classes["@mozilla.org/windows-registry-key;1"]
                    .createInstance(Components.interfaces.nsIWindowsRegKey);
wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE,
         "SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
         wrk.ACCESS_READ);
var id = wrk.readStringValue("ProductId");
wrk.close();

This example, while simple, shows several important things about using the interface. First, you must use createInstance() to get an object implementing this interface, not getService(). Second, you must call open() on the key before attempting to read a value.

Notice in the open() call that the root key to use is specified using the named constants available on the nsIWindowsRegKey interface, in this case ROOT_KEY_LOCAL_MACHINE, which corresponds to HKEY_LOCAL_MACHINE in the Windows registry. Also notice that the path to the key has backslashes escaped, a necessity in JavaScript and C++ string constants.

The desired access rights are specified using a named constant from the interface, in this example ACCESS_READ. This can be very important when dealing with non-Administrator accounts with restricted privileges.

The value is read using readStringValue(). You have to specify what type of data you expect to read, which we will expand on later. Finally, note that you should close the key when you are done to avoid wasting system resources.

Opening Registry Keys

Before doing anything with a registry key you must first open the key you are interested in. The example above demonstates this using the open() method. If you want to create a new key, you can use the create() method, which takes the same parameters as open(). Note that it is not an error to call create() on an existing key, and doing so has the same result as calling open().

Both of these methods take a root key as the first parameter. From JavaScript, you will want to use the named constants on the interface for this parameter. They are:

  • ROOT_KEY_CLASSES_ROOT — Corresponds to HKEY_CLASSES_ROOT
  • ROOT_KEY_CURRENT_USER — Corresponds to HKEY_CURRENT_USER
  • ROOT_KEY_LOCAL_MACHINE — Corresponds to HKEY_LOCAL_MACHINE

The second parameter for open() and create() is the path to the key. As noted in the example above, you will need to escape backslashes within the string.

The third parameter for open() and create() is the access mode. It is specified as a bitwise combination of flags defined on the interface. You can read the interface documentation for a full explanation, but we will show only the three most commonly used modes here:

  • ACCESS_READ — For reading values, enumerating keys, and receiving notifications
  • ACCESS_WRITE — For setting values and creating sub keys
  • ACCESS_ALL — Access for all operations

In addition to open() and create(), there are the openChild() and createChild() methods. You can call these methods on an already-opened registry key to open a child key. Both methods take a relative path and access mode as parameters and return a new object implementing nsIWindowsRegKey. Here's the simple example again, but using openChild():

var wrk = Components.classes["@mozilla.org/windows-registry-key;1"]
                    .createInstance(Components.interfaces.nsIWindowsRegKey);
wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE,
         "SOFTWARE\\Microsoft",
         wrk.ACCESS_READ);
var subkey = wrk.openChild("Windows\\CurrentVersion", wrk.ACCESS_READ);
var id = subkey.readStringValue("ProductId");
subkey.close();
wrk.close();

Once you've opened a registry key, you can begin to make use of it.

Reading Registry Values

Probably the most common action associated with the Windows registry is reading values. The simple example above shows how to read an existing string value. However, Windows registry values can have several data types, so you need to ensure that you read the correct type. You can check the type of a value using the method getValueType(). This method returns an integer indicating the data type of the value. The data types supported by this interface are defined as named constants on the interface as follows:

  • TYPE_NONE — Probably not useful
  • TYPE_STRING — A Unicode string value
  • TYPE_BINARY — Binary data
  • TYPE_INT — A 32 bit integer
  • TYPE_INT64 — A 64 bit integer

Each of these types (except TYPE_NONE) has a corresponding method to read the value data:

  • readStringValue()
  • readBinaryValue()
  • readIntValue()
  • readInt64Value()

Since JavaScript is a dynamically-typed language, you may wish to use the following code to handle all types of data. In this function, wrk is expected to be an already opened nsIWindowsRegKey.

function readRegistryValue(wrk, value) {
  switch (wrk.getValueType(value)) {
    case wrk.TYPE_STRING:
      return wrk.readStringValue(value);
    case wrk.TYPE_BINARY:
      return wrk.readBinaryValue(value);
    case wrk.TYPE_INT:
      return wrk.readIntValue(value);
    case wrk.TYPE_INT64:
      return wrk.readInt64Value(value);
  }
  // unknown type
  return null;
}

Writing Registry Values

Writing registry values is quite similar to reading. For each supported data type, there is a write*Value() method complementary to the read*Value() method. Don't forget that if you are writing a new value, you may need to create() the parent key first. This example demonstrates writing a new string value:

var wrk = Components.classes["@mozilla.org/windows-registry-key;1"]
                    .createInstance(Components.interfaces.nsIWindowsRegKey);
wrk.create(wrk.ROOT_KEY_CURRENT_USER,
           "SOFTWARE\\MDC\\Test",
           wrk.ACCESS_WRITE);
wrk.writeStringValue("TestValue", "Hello World!");
wrk.close();

Checking the Existence of Keys and Values

Before you attempt to read a value or open a child key, you should check to see whether it exists first. The nsIWindowsRegKey interface provides methods for both of these—hasValue() and hasChild()—as demonstrated in this example:

var wrk = Components.classes["@mozilla.org/windows-registry-key;1"]
                    .createInstance(Components.interfaces.nsIWindowsRegKey);
wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE,
           "SOFTWARE\\Microsoft",
           wrk.ACCESS_READ);
if (wrk.hasChild("Windows")) {
  var subkey = wrk.openChild("Windows\\CurrentVersion", wrk.ACCESS_READ);
  var id;
  if (subkey.hasValue("ProductId"))
    id = subkey.readStringValue("ProductId");
  subkey.close();
}
wrk.close();

Enumerating Registry Keys and Values

In some situations, you may want to enumerate a number of keys or values whose names you do not know. The nsIWindowsRegKey interface provides the childCount, getChildName(), valueCount, and getValueName() properties and methods for enumerating keys and values respectively. You can use these methods to read a list of values or recursively access a branch of the registry. This example reads all the startup programs in one key of the registry.

var wrk = Components.classes["@mozilla.org/windows-registry-key;1"]
                    .createInstance(Components.interfaces.nsIWindowsRegKey);
wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE,
         "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
         wrk.ACCESS_READ);
for (var i=0; i<wrk.valueCount; i++) {
  var name  = wrk.getValueName(i);
  var value = readRegistryValue(wrk, name);
  // do something interesting here...
}
wrk.close();

For simplicity, this example assumes the existence of the readRegistryValue() function defined above.

Removing Registry Keys and Values

To remove child keys and values from the registry, you can use the removeChild() and removeValue() methods. removeChild() removes a child key and all of its values, but will fail if the key has any child keys of its own. In that case you must manually enumerate the children and remove them individually. This example shows how to recursively delete a registry key and all of its children. Use with caution!

function removeChildrenRecursive(wrk) {
  // we count backwards because we're removing them as we go
  for (var i = wrk.childCount - 1; i >= 0; i--) {
    var name   = wrk.getChildName(i);
    var subkey = wrk.openChild(name, wrk.ACCESS_ALL);
    removeChildrenRecursive(subkey);
    subkey.close();
    wrk.removeChild(name);
  }
}

var wrk = Components.classes["@mozilla.org/windows-registry-key;1"]
                    .createInstance(Components.interfaces.nsIWindowsRegKey);
wrk.open(wrk.ROOT_KEY_CURRENT_USER,
         "SOFTWARE\\MDC\\Test",
         wrk.ACCESS_ALL);
removeChildrenRecursive(wrk);
wrk.close();

Monitoring Registry Keys

If you would like to know whether a registry key has changed since you last checked it, you can use the startWatching(), stopWatching(), and hasChanged() methods. You must call startWatching() for the key to be monitored. The method takes one parameter, a boolean indicating whether child keys should be watched. After that, you can call hasChanged() to determine whether or not you need to reread the value. Calling hasChanged() automatically resets the watch, so you can be sure that if it returns true there are changes. This example demonstrates a trivial registry value cache for one key:

var cache = {};

function readRegistryValueNoCache(wrk, value) {
  switch (wrk.getValueType(value)) {
    case wrk.TYPE_STRING:
      return wrk.readStringValue(value);
    case wrk.TYPE_BINARY:
      return wrk.readBinaryValue(value);
    case wrk.TYPE_INT:
      return wrk.readIntValue(value);
    case wrk.TYPE_INT64:
      return wrk.readInt64Value(value);
  }
  // unknown type
  return null;
}

function readRegistryValue(wrk, value) {
  if (wrk.hasChanged()) {
    // wipe out the cache
    cache = {};
  }

  if (value in cache) {
    return cache[value];
  }
  
  cache[value] = readRegistryValueNoCache(wrk, value);
  return cache[value];
}

var wrk = Components.classes["@mozilla.org/windows-registry-key;1"]
                    .createInstance(Components.interfaces.nsIWindowsRegKey);
wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE,
         "SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
         wrk.ACCESS_READ);
wrk.startWatching(false); // only watch the values on this key, not child keys
var id = readRegistryValue(wrk, "ProductId");
/* later you can read this again,
   and it should come from the cache unless
   there have been changes to the registry.
   Remember to call wrk.close() when you
   are finished!
*/

Support in Firefox 1.0

Firefox 1.0 includes a much simpler interface to the Windows registry, without most of the functionality supported in newer versions. The functionality is exposed in the nsIWindowsShellService interface. It consists of only one method, getRegistryEntry(), and a set of named constants to specify the root key. You can use it as shown in the following example:

var wss = Components.classes["@mozilla.org/browser/shell-service;1"]
                    .getService(Components.interfaces.nsIWindowsShellService);
var id = wss.getRegistryEntry(wss.HKLM,
                              "SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
                              "ProductId");
Note: There's no way to set a registry value using this interface.

Support in SeaMonkey and Other Non-toolkit Applications

In older versions of SeaMonkey and other non-toolkit-based applications, an interface existed called nsIWindowsRegistry, containing the same method and named constants as the methods described above for Firefox 1.0. It can be used as follows:

var wss = Components.classes["@mozilla.org/winhooks;1"]
                    .getService(Components.interfaces.nsIWindowsRegistry);
var id = wss.getRegistryEntry(wss.HKLM,
                              "SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
                              "ProductId");

Backwards Compatibility

If you need to support Firefox 1.0 and other older browser versions, you should check to see which interfaces are available. The following skeleton code will allow you to determine which interface to use:

if ("@mozilla.org/windows-registry-key;1" in Components.classes) {
  // Firefox 1.5 or newer
}
else if ("@mozilla.org/winhooks;1" in Components.classes) {
  // SeaMonkey or other older non-toolkit application
}
else if ("@mozilla.org/browser/shell-service;1" in Components.classes) {
  var wss = Components.classes["@mozilla.org/browser/shell-service;1"]
                      .getService(Components.interfaces.nsIWindowsShellService);
  if ("getRegistryEntry" in wss) {
    // Firefox 1.0
  }
  else {
    // nothing supported
  }
}
else {
  // nothing supported
}

Document Tags and Contributors

Contributors to this page: Brettz9, trevorh, Mgjbot, Ptak82, LwjCxg, Chbok, Nickolay, Sheppy, Ted_Mielczarek
Last updated by: Brettz9,