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.
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.
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.
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 notificationsACCESS_WRITE
— For setting values and creating sub keysACCESS_ALL
— Access for all operationsIn 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.
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 usefulTYPE_STRING
— A Unicode string valueTYPE_BINARY
— Binary dataTYPE_INT
— A 32 bit integerTYPE_INT64
— A 64 bit integerEach 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 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();
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();
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.
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();
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! */
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");
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");
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 }