Reading and Writing to the Registry


In all versions of Windows since Windows 95, the registry has been the central repository for all configuration information relating to Windows setup, user preferences, and installed software and devices. Almost all commercial software these days uses the registry to store information about itself, and COM components must place information about themselves in the registry in order to be called by clients. The .NET Framework and its accompanying concept of zero-impact installation has slightly reduced the significance of the registry for applications in the sense that assemblies are entirely self-contained, so no information about particular assemblies needs to be placed in the registry, even for shared assemblies. In addition, the .NET Framework has brought the concept of isolated storage, by which applications can store information that is particular to each user in files, with the .NET Framework taking care of making sure that data is stored separately for each user registered on a machine. (Isolated storage is beyond the scope of this book, but if you are interested, you can find the relevant .NET base classes in the System.IO.IsolatedStorage namespace.)

The fact that applications can now be installed using the Windows Installer also frees developers from some of the direct manipulation of the registry that used to be involved in installing applications. However, despite this, the possibility exists that if you distribute any complete application, your application will use the registry to store information about its configuration. For instance, if you want your application to show up in the Add/Remove Programs dialog box in the Control Panel, this will involve appropriate registry entries. You may also need to use the registry for backward compatibility with legacy code.

As you’d expect from a library as comprehensive as the .NET library, it includes classes that give you access to the registry. Two classes are concerned with the registry, and both are in the Microsoft.Win32 namespace. The classes are Registry and RegistryKey. Before you examine these classes, the following section briefly reviews the structure of the registry itself.

The Registry

The registry has a hierarchical structure much like that of the file system. The usual way to view or modify the contents of the registry is with one of two utilities: regedit or regedt32. Of these, regedit comes standard with all versions of Windows since Windows 95. regedt32 comes with Windows NT and Windows 2000; it is less user-friendly than regedit, but allows access to security information that regedit is unable to view. Windows Server 2003 has merged regedit and regedt32 into a single new editor simply called regedit. For the discussion here, you’ll use regedit from Windows XP Professional, which you can launch by typing in regedit at the Run dialog or command prompt.

Figure 24-16 shows what you get when you launch regedit for the first time.

image from book
Figure 24-16

regedit has a similar tree view/list view–style user interface to Windows Explorer, which matches the hierarchical structure of the registry itself. However, there are some key differences that you’ll see shortly.

In a file system, the topmost-level nodes can be thought of as being the partitions on your disks, C:\, D:\, and so on. In the registry, the equivalent to a partition is the registry hive. It is not possible to change the existing hives - they are fixed, and there are seven of them, although only five are actually visible through regedit:

  • HKEY_CLASSES_ROOT (HKCR) contains details of types of files on the system (.txt, .doc, and so on) and which applications are able to open files of each type. It also contains registration information for all COM components (this latter area is usually the largest single area of the registry, because Windows these days comes with a huge number of COM components).

  • HKEY_CURRENT_USER (HKCU) contains details of user preferences for the user currently logged on to the machine locally. These settings include desktop settings, environment variables, network and printer connections, and other settings that define the user operating environment of the user.

  • HKEY_LOCAL_MACHINE (HKLM) is a huge hive that contains details of all software and hardware installed on the machine. These settings are not user-specific, but are for all users that log on to the machine. This hive also includes the HKCR hive; HKCR is actually not really an independent hive in its own right but is simply a convenient mapping onto the registry key HKLM/SOFTWARE/Classes.

  • HKEY_USERS (HKUSR) contains details of user preferences for all users. As you might guess, it also contains the HKCU hive, which is simply a mapping onto one of the keys in HKEY_USERS.

  • HKEY_CURRENT_CONFIG (HKCF) contains details of hardware on the machine.

The remaining two keys contain information of a temporary nature, and which changes frequently:

  • HKEY_DYN_DATA is a general container for any volatile data that needs to be stored somewhere in the registry.

  • HKEY_PERFORMANCE_DATA contains information concerning the performance of running applications.

Within the hives is a tree structure of registry keys. Each key is in many ways analogous to a folder or file on the file system. However, there is one very important difference. The file system distinguishes between files (which are there to contain data) and folders (which are primarily there to contain other files or folders), but in the registry there are only keys. A key may contain both data and other keys.

If a key contains data, this will be presented as a series of values. Each value will have an associated name, data type, and data. In addition, a key can have a default value, which is unnamed.

You can see this structure by using regedit to examine registry keys. Figure 24-17 shows the contents of the key HKCU\Control Panel\Appearance, which contains the details of the chosen color scheme of the currently logged-in user. regedit shows which key is being examined by displaying it with an open folder icon in the tree view.

image from book
Figure 24-17

The HKCU\Control Panel\Appearance key has three named values set, although the default value does not contain any data. The column in the screenshot marked Type details the data type of each value. Registry entries can be formatted as one of three data types:

  • REG_SZ (which roughly corresponds to a .NET string instance; the matching is not exact because the registry data types are not .NET data types)

  • REG_DWORD (corresponds roughly to uint)

  • REG_BINARY (array of bytes)

An application that stores data in the registry will do so by creating a number of registry keys, usually under the key HKLM\Software\<CompanyName>. Note that it is not necessary for these keys to contain any data. Sometimes the very fact that a key exists provides the data that an application needs.

The .NET Registry Classes

Access to the registry is available through two classes in the Microsoft.Win32 namespace: Registry and RegistryKey. A RegistryKey instance represents a registry key. This class implements methods to browse child keys, to create new keys, or to read or modify the values in the key - in other words, to do everything you would normally want to do with a registry key, including setting the security levels for the key. RegistryKey will be the class you use for a lot your work with the registry. Registry, by contrast, is a class that allows for singular access to registry keys for simple operations. Another role of the Registry class is simply to provide you with RegistryKey instances that represent the top-level keys, the different hives, in order to enable you to navigate the registry. Registry provides these instances through static properties, and there are seven of them called, respectively, ClassesRoot, CurrentConfig, CurrentUser, DynData, LocalMachine, PerformanceData, and Users. It should be obvious which property corresponds to which hive.

So, for example, to obtain a RegistryKey instance that represents the HKLM key, you would write:

  RegistryKey hklm = Registry.LocalMachine; 

The process of obtaining a reference to a RegistryKey object is known as opening the key.

Although you might expect that the methods exposed by RegistryKey would be similar to those implemented by DirectoryInfo, given that the registry has a similar hierarchical structure to the file system, this actually isn’t the case. Often, the way that you access the registry is different from the way that you would use files and folders, and RegistryKey implements methods that reflect this.

The most obvious difference is in how you open a registry key at a given location in the registry. The Registry class does not have any public constructor that you can use, nor does it have any methods that let you go directly to a key, given its name. Instead, you are expected to browse down to that key from the top of the relevant hive. If you want to instantiate a RegistryKey object, the only way is to start off with the appropriate static property of Registry, and work down from there. So, for example, if you want to read some data in the HKLM/Software/Microsoft key, you’d get a reference to it like this:

  RegistryKey hklm = Registry.LocalMachine; RegistryKey hkSoftware = hklm.OpenSubKey("Software"); RegistryKey hkMicrosoft = hkSoftware.OpenSubKey("Microsoft"); 

A registry key accessed in this way will give you read-only access. If you want to be able to write to the key (that includes writing to its values or creating or deleting direct children of it), you need to use another override to OpenSubKey, which takes a second parameter, of type bool, that indicates whether you want read-write access to the key. For example, if you want to be able to modify the Microsoft key (and assuming that you are a system administrator with permission to do this), you would write this:

 RegistryKey hklm = Registry.LocalMachine; RegistryKey hkSoftware = hklm.OpenSubKey("Software"); RegistryKey hkMicrosoft = hkSoftware.OpenSubKey("Microsoft", true); 

Incidentally, because this key contains information used by Microsoft’s applications, in most cases you probably shouldn’t be modifying this particular key.

The OpenSubKey() method is the one you will call if you are expecting the key to be present. If the key isn’t there, it will return a null reference. If you want to create a key, you should use the CreateSubKey() method (which automatically gives you read-write access to the key through the reference returned):

 RegistryKey hklm = Registry.LocalMachine; RegistryKey hkSoftware = hklm.OpenSubKey("Software"); RegistryKey hkMine = hkSoftware.CreateSubKey("MyOwnSoftware"); 

The way that CreateSubKey() works is quite interesting. It will create the key if it doesn’t already exist, but if it does already exist, then it will quietly return a RegistryKey instance that represents the existing key. The reason for the method behaving in this manner has to do with how you will normally use the registry. The registry, on the whole, contains long-term data such as configuration information for Windows and for various applications. It’s not very common, therefore, that you find yourself in a situation where you need to explicitly create a key.

What is much more common is that your application needs to make sure that some data is present in the registry - in other words create the relevant keys if they don’t already exist, but do nothing if they do. CreateSubKey() fills that need perfectly. Unlike the situation with FileInfo.Open(), for example, there is no chance with CreateSubKey() of accidentally removing any data. If deleting registry keys is your intention, you’ll need to call the RegistryKey.DeleteSubKey() method. This makes sense given the importance of the registry to Windows. The last thing you want is to completely break Windows accidentally by deleting a couple of important keys while you’re debugging your C# registry calls!

Once you’ve located the registry key you want to read or modify, you can use the SetValue() or GetValue() methods to set or get at the data in it. Both of these methods take a string giving the name of the value as a parameter, and SetValue() requires an additional object reference containing details of the value. Because the parameter is defined as an object reference, it can actually be a reference to any class you want. SetValue() will decide from the type of class actually supplied whether to set the value as a REG_SZ, REG_DWORD, or REG_BINARY value. For example:

 RegistryKey hkMine = HkSoftware.CreateSubKey("MyOwnSoftware"); hkMine.SetValue("MyStringValue", "Hello World"); hkMine.SetValue("MyIntValue", 20); 

This code will set the key to have two values: MyStringValue will be of type REG_SZ, while MyIntValue will be of type REG_DWORD. These are the only two types you will consider here, and use in the example that presented later.

RegistryKey.GetValue() works in much the same way. It is defined to return an object reference, which means that it is free to actually return a string reference if it detects the value is of type REG_SZ, and an int if that value is of type REG_DWORD:

  string stringValue = (string)hkMine.GetValue("MyStringValue"); int intValue = (int)hkMine.GetValue("MyIntValue"); 

Finally, after you’ve finished reading or modifying the data, close the key:

  hkMine.Close(); 

RegistryKey implements a large number of methods and properties. The following table lists the most useful properties.

Open table as spreadsheet

Property Name

Description

Name

Name of the key (read-only)

SubKeyCount

The number of children of this key

ValueCount

How many values the key contains

The following table lists the most useful methods.

Open table as spreadsheet

Method Name

Purpose

Close()

Closes the key.

CreateSubKey()

Creates a subkey of a given name (or opens it if it already exists).

DeleteSubKey()

Deletes a given subkey.

DeleteSubKeyTree()

Recursively deletes a subkey and all its children.

DeleteValue()

Removes a named value from a key.

GetAccessControl()

Returns the access control list (ACL) for a specified registry key. This method is new to the .NET Framework 2.0.

GetSubKeyNames()

Returns an array of strings containing the names of the subkeys.

GetValue()

Returns a named value.

GetValueKind()

Returns a named value whose registry data type is to be retrieved. This method is new to the .NET Framework 2.0.

GetValueNames()

Returns an array of strings containing the names of all the values of the key.

OpenSubKey()

Returns a reference to a RegistryKey instance that represents a given subkey.

SetAccessControl()

Allows you to apply an access control list (ACL) to a specified registry key. This method is new to the .NET Framework 2.0.

SetValue()

Sets a named value.

Example: SelfPlacingWindow

The use of the registry classes is illustrated with an application called SelfPlacingWindow. This example is a simple C# Windows application that has almost no features. The only thing you can do with it is click a button, which brings up a standard Windows color dialog box (represented by the System.Windows.Forms.ColorDialog class) to let you choose a color, which will become the background color of the form.

Despite this lack of features, the self-placing window scores over just about every other application that you have developed in this book in one important and very user-friendly way. If you drag the window around the screen, change its size, or maximize or minimize it before you exit the application, it will remember the new position, as well as the background color, so that the next time it is launched it can automatically reappear the way you chose last time. It remembers this information because it writes it to the registry whenever it shuts down. In this way, it demonstrates not only the .NET registry classes themselves but also a very typical use for them, which you’ll almost certainly want to replicate in any serious commercial Windows Forms application you write.

The location in which SelfPlacingWindow stores its information in the registry is the key HKLM\Software\WroxPress\SelfPlacingWindow. HKLM is the usual place for application configuration information, but note that it is not user-specific. If you wanted to be more sophisticated in a real application, you’d probably want to replicate the information inside the HK_Users hive as well, so that each user can have his or her own profile.

Tip 

It’s also worth noting that, if you are implementing this in a real .NET application, you may want to consider using isolated storage instead of the registry to store this information. On the other hand, because isolated storage is only available in .NET, you’ll need to use the registry if you need any interoperability with non-.NET apps.

The very first time that you run the example, it will look for this key and not find it (obviously). Therefore, it is forced to use a default size, color, and position that you set in the developer environment. The example also features a list box in which it displays any information read in from the registry. On its first run, it will look similar to Figure 24-18.

image from book
Figure 24-18

If you now modify the background color and resize SelfPlacingWindow or move it around on the screen a bit before exiting, it will create the HKLM\Software\WroxPress\SelfPlacingWindow key and write its new configuration information into it. You can examine the information using regedit. The details are shown in Figure 24-19.

image from book
Figure 24-19

As this figure shows, SelfPlacingWindow has placed a number of values in the registry key.

The values Red, Green, and Blue give the color components that make up the selected background color (see Chapter 30, “Graphics with GDI+”). For now, just take it that any color display on the system can be completely described by these three components, which are each represented by a number between 0 and 255 (or 0x00 and 0xff in hexadecimal). The values given here make up a bright green color. There are also four more REG_DWORD values, which represent the position and size of the window: X and Y are the coordinates of top left of the window on the desktop - that is to say the numbers of pixels across from the top left of the screen and the numbers of pixels down. Width and Height give the size of the window. WindowsState is the only value for which you have used a string data type (REG_SZ), and it can contain one of the strings Normal, Maximized, or Minimized, depending on the final state of the window when you exited the application.

When you launch SelfPlacingWindow again, it will read this registry key and automatically position itself accordingly (see Figure 24-20).

image from book
Figure 24-20

This time when you exit SelfPlacingWindow, it will overwrite the previous registry settings with whatever new values are relevant at the time that you exit it. To code the example, you create the usual Windows Forms project in Visual Studio .NET and add the list box and button, using the developer environment’s toolbox. You will change the names of these controls, respectively, to listBoxMessages and buttonChooseColor. You also need to ensure that you use the Microsoft.Win32 namespace:

 using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using Microsoft.Win32; 

You need to add one field (chooseColorDialog) to the main Form1 class, which will represent the color dialog box:

 public class Form1 : System.Windows.Forms.Form {    private System.Windows.Forms.ListBox listBoxMessages;    private System.Windows.Forms.Button buttonChooseColor;    private ColorDialog chooseColorDialog = new ColorDialog(); 

Quite a lot of action takes place in the Form1 constructor:

 public Form1() {    InitializeComponent();    buttonChooseColor.Click += new EventHandler(OnClickChooseColor);    try    {      if (!ReadSettings())         listBoxMessages.Items.Add("No information in registry");      else         listBoxMessages.Items.Add("Information read in from registry");         StartPosition = FormStartPosition.Manual;    }    catch (Exception e)    {       listBoxMessages.Items.Add("A problem occurred reading in data                                  from registry:");       listBoxMessages.Items.Add(e.Message);    } }

In this constructor, you begin by setting up the event handler for when the user clicks the button. The handler is a method called OnClickChooseColor, which is covered shortly. Reading in the configuration information is done using another method that you have to write, called ReadSettings(). ReadSettings() returns true if it finds the information in the registry, and false if it doesn’t (which it should be because this is the first time you have run the application). You place this part of the constructor in a try block, just in case any exceptions are generated while reading in the registry values (this might happen if some user has come in and played around with the registry using regedit).

The StartPosition = FormStartPosition.Manual; statement tells the form to take its initial starting position from the DeskTopLocation property instead of using the Windows default location (the default behavior). Possible values are taken from the FormStartPosition enumeration.

SelfPlacingWindow is also one of the few applications in this book in which you have a serious use for adding code to the Dispose() method. Remember that Dispose() is called whenever the application terminates normally, so this is the ideal place from which to save the configuration information to the registry. You will find the Dispose() method in the Form1.Designer.cs file. Within this method, you will place another method that you have to write, SaveSettings():

 protected override void Dispose(bool disposing) {     if (disposing && (components != null))     {         components.Dispose();     }     SaveSettings();     base.Dispose(disposing); }

The SaveSettings() and ReadSettings() methods are the ones that contain the registry code you are interested in, but before you examine them you have one more piece of housekeeping to do: to handle the event of the user clicking that button. This involves displaying the color dialog and setting the background color to whatever color the user chose:

  void OnClickChooseColor(object Sender, EventArgs e) {    if(chooseColorDialog.ShowDialog() == DialogResult.OK)       BackColor = chooseColorDialog.Color; } 

Now, look at how you save the settings:

  void SaveSettings() {    RegistryKey softwareKey =                Registry.LocalMachine.OpenSubKey("Software", true);    RegistryKey wroxKey = softwareKey.CreateSubKey("WroxPress");    RegistryKey selfPlacingWindowKey =                wroxKey.CreateSubKey("SelfPlacingWindow");    selfPlacingWindowKey.SetValue("BackColor",                BackColor.ToKnownColor());    selfPlacingWindowKey.SetValue("Red", (int)BackColor.R);    selfPlacingWindowKey.SetValue("Green", (int)BackColor.G);    selfPlacingWindowKey.SetValue("Blue", (int)BackColor.B);    selfPlacingWindowKey.SetValue("Width", Width);    selfPlacingWindowKey.SetValue("Height", Height);    selfPlacingWindowKey.SetValue("X", DesktopLocation.X);    selfPlacingWindowKey.SetValue("Y", DesktopLocation.Y);    selfPlacingWindowKey.SetValue("WindowState",                (object)WindowState.ToString()); } 

There’s quite a lot going on here. You start off by navigating through the registry to get to the HKLM\Software\WroxPress\SelfPlacingWindow registry key using the technique demonstrated earlier, starting with the Registry.LocalMachine static property that represents the HKLM hive.

Then you use the RegistryKey.OpenSubKey() method, rather than RegistryKey.CreateSubKey(), to get to the HKLM/Software key. That’s because you can be very confident that this key already exists. If it doesn’t, there’s something very seriously wrong with your computer, because this key contains settings for a lot of system software! You also indicate that you need Write access to this key. That’s because if the WroxPress key doesn’t already exist, you will need to create it, which involves writing to the parent key.

The next key to navigate to is HKLM\Software\WroxPress - and here you are not certain whether the key already exists, so you use CreateSubKey() to automatically create it if it doesn’t. Note that CreateSubKey() automatically gives you write access to the key in question. Once you have reached HKLM\Software\WroxPress\SelfPlacingWindow, it is simply a matter of calling the RegistryKey.SetValue() method a number of times to either create or set the appropriate values. There are, however, a couple of complications.

First, you might notice that you are using a couple of classes that you’ve not encountered before. The DeskTopLocation property of the Form class indicates the position of the top-left corner of the screen and is of type Point. (The Point is discussed in Chapter 30, “Graphics with GDI+”.) What you need to know here is that it contains two int values, X and Y, which represent the horizontal and vertical position on the screen. You also look up three member properties of the Form.BackColor property, which is an instance of the Color class: R, G, and B: Color, which represents a color, and these properties on it give the red, green, and blue components that make up the color and are all of type byte. You also use the Form.WindowState property, which contains an enumeration that gives the current state of the window: Minimized, Maximized, or Normal.

The other complication here is that you need to be a little careful about your casts: SetValue() takes two parameters: a string that gives the name of the key and a System.Object instance, which contains the value. SetValue() has a choice of format for storing the value - it can store it as REG_SZ, REG_BINARY, or REG_DWORD - and it is actually pretty intelligent about making a sensible choice depending on the data type that has been given. Hence for the WindowState, you pass it a string, and SetValue() determines that this should be translated to REG_SZ. Similarly, for the various positions and dimensions you supply ints, which will be converted into REG_DWORD. However, the color components are more complicated, as you want these to be stored as REG_DWORD too because they are numeric types. However, if SetValue() sees that the data is of type byte, it will store it as a string - as REG_SZ in the registry. To prevent this, you cast the color components to ints.

You’ve also explicitly cast all the values to the type object. You don’t really need to do this because the cast from any other data type to object is implicit, but you are doing so in order to make it clear what’s going on and remind yourself that SetValue() is defined to take just an object reference as its second parameter.

The ReadSettings() method is a little longer because for each value read in, you also need to interpret it, display the value in the list box, and make the appropriate adjustments to the relevant property of the main form. ReadSettings() looks like this:

  bool ReadSettings() {    RegistryKey softwareKey =                Registry.LocalMachine.OpenSubKey("Software");    RegistryKey wroxKey = softwareKey.OpenSubKey("WroxPress");    if (wroxKey == null)       return false;    RegistryKey selfPlacingWindowKey =                wroxKey.OpenSubKey("SelfPlacingWindow");    if (selfPlacingWindowKey == null)       return false;    else       listBoxMessages.Items.Add("Successfully opened key " +                selfPlacingWindowKey.ToString());    int redComponent = (int)selfPlacingWindowKey.GetValue("Red");    int greenComponent = (int)selfPlacingWindowKey.GetValue("Green");    int blueComponent = (int)selfPlacingWindowKey.GetValue("Blue");    this.BackColor = Color.FromArgb(redComponent, greenComponent,                blueComponent);    listBoxMessages.Items.Add("Background color: " + BackColor.Name);    int X = (int)selfPlacingWindowKey.GetValue("X");    int Y = (int)selfPlacingWindowKey.GetValue("Y");    this.DesktopLocation = new Point(X, Y);    listBoxMessages.Items.Add("Desktop location: " +                DesktopLocation.ToString());    this.Height = (int)selfPlacingWindowKey.GetValue("Height");    this.Width = (int)selfPlacingWindowKey.GetValue("Width");    listBoxMessages.Items.Add("Size: " + new                Size(Width,Height).ToString());    string initialWindowState =                (string)selfPlacingWindowKey.GetValue("WindowState");    listBoxMessages.Items.Add("Window State: " + initialWindowState);    this.WindowState = (FormWindowState)FormWindowState.Parse                (WindowState.GetType(), initialWindowState);    return true; } 

In ReadSettings() you first have to navigate to the HKLM/Software/WroxPress/SelfPlacingWindow registry key. In this case, however, you are hoping to find the key there so that you can read it. If it’s not there, it’s probably the first time you have run the example. In this case, you just want to abort reading the keys, and you certainly don’t want to create any keys. Now you use the RegistryKey.OpenSubKey() method all the way down. If at any stage OpenSubkey() returns a null reference, then you know that the registry key isn’t there and you can simply return the value false to the calling code.

When it comes to actually reading the keys, you use the RegistryKey.GetValue() method, which is defined as returning an object reference (which means this method can actually return an instance of literally any class it chooses). Like SetValue(), it will return a class of object appropriate to the type of data it found in the key. Hence, you can usually assume that the REG_SZ keys will give you a string and the other keys will give you an int. You also cast the return reference from SetValue() accordingly. If there is an exception, say someone has fiddled with the registry and mangled the value types, then your cast will cause an exception to be thrown - which will be caught by the handler in the Form1 constructor.

The rest of this code uses one more data type, the Size structure. This is similar to a Point structure but is used to represent sizes rather than coordinates. It has two member properties, Width and Height, and you use the Size structure here simply as a convenient way of packaging up the size of the form for displaying in the list box.




Professional C# 2005 with .NET 3.0
Professional C# 2005 with .NET 3.0
ISBN: 470124725
EAN: N/A
Year: 2007
Pages: 427

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net