Using the Windows Registry

Managing Persistent Data with the WFC

By Brian Johnson

When developing an application that requires a significant amount of user input, you usually want to store values between sessions, so users don't have to re-enter information. There are a number of options to retrieve and store such information. You can write a custom solution in which you write and parse your own information files. This might be a reasonable solution if you have a lot of custom information to store, but will almost certainly require a significant amount of work. Another option is to use J/Direct to tap into the Windows API to access the functions that allows you to write and access Windows initialization (.ini) files.

Or, you could use the recommended solution for storing application values in Windows - the Windows registry. This is the recommended solution and the subject of this article. Microsoft has provided a couple of WFC classes that make accessing the registry from Visual J++ 6.0 a piece of cake.

This article will discuss the Windows registry, and how to access it using two WFC classes: Registry and RegistryKey.

Registry Organization

The registry is a large database of information needed to run Windows and installed applications. The information stored in the registry includes user preferences, file information, hardware information, and a ton of information about COM controls. It's especially large if you have tools like Visual Studio installed. It's quite remarkable that as large as this database is, it chugs along day in and day out with few problems.

The Windows registry is considered to be one data store, but is actually made up of three files:

Knowledge of these files makes it easier to understand why these settings are accessed as if they were a single data store. With Windows taking care of the details, it's a lot easier to keep the application-specific information we want to add and retrieve from the registry.

The registry is divided into keys and values. A key can contain subkeys and values. The major keys in the Windows 98 registry are shown in Figure 1.

Top-level Registry Keys

Description

HKEY_CLASSES_ROOT

Points to HKEY_LOCAL_MACHINE\Software\Classes. Contains information about OLE, associations, shortcuts, and Windows UI settings.

HKEY_CURRENT_USER

Points to the branch of HKEY_USERS containing information about the user currently logged on.

HKEY_LOCAL_MACHINE

Stores computer-specific hardware information.

HKEY_USERS

Generic and user specific information about all the users who log onto the machine. Stores application settings and desktop configuration.

HKEY_CURRENT_CONFIG

Hardware settings. Points to HKEY_LOCAL_MACHINE\Config.

HKEY_DYN_DATA

Hardware information stored in RAM about devices that are added or removed from the system.

Figure 1: The major Windows 98 registry keys.

HKEY_CURRENT_USER points to a particular key in HKEY_USERS. On single-user machines, this is a default key. With multiple users, it's the user that's currently logged on, allowing you to make your changes without regard to the specific user logged on when a key is created or accessed. The two keys we're most interested in as software developers are HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER.

As you can deduce, where you put your registry information determines whether it's available to any user on the machine, or just the specific user. When identical keys exist in both HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER takes precedence. This implies that you can set default properties for the application in HKEY_LOCAL_MACHINE (e.g. during installation), and then specific preferences for the user in HKEY_CURRENT_USER. This way, when a new user logs on, he or she can run your application with the basics in place and then customize their own settings later. According to Microsoft, HKEY_LOCAL_MACHINE doesn't allow write access on some machines, so your application shouldn't require it. You can still use it, just don't let you application fail if it can't get write access to this key.

There are three types of data that can go into a registry value: text, binary, and DWORD (double word). The easiest way I've found to store data in the registry is to use strings. The main reason I like to use strings is because it's a lot easier to change settings using Regedit when the values aren't in hexadecimal format. The example I create later will show you how to convert a couple of common object values back and forth as strings.

WFC Registry Classes

There are two WFC registry classes that help you deal with the Windows registry. These classes use J/Direct to perform operations that are part of the Win32 API. They're also a part of the com.ms.wfc.app package, and include Registry and RegistryKey.

The Registry Class

The Registry class contains fields that act as constants that make it easier for you to access the top-level keys in the registry. Figure 2 lists these constants and the full key name they represent. You'll use these constants to drill down using the methods of the RegistryKey class to retrieve and manipulate values from the registry.

Constant

Represents

CLASSES_ROOT

HKEY_CLASSES_ROOT

CURRENT_USER

HKEY_CURRENT_USER

LOCAL_MACHINE

HKEY_LOCAL_MACHINE

DYN_DATA

HKEY_DYN_DATA

CURRENT_CONFIG

HKEY_CURRENT_CONFIG

USERS

HKEY_USERS

PERFORMANCE_DATA

HKEY_PERFORMANCE

Figure 2: Fields of the Registry class.

The RegistryKey Class

The methods of the RegistryKey class make it easy to access and change registry values. Since there aren't too many methods, I've listed them in Figure 3. The important keys that you'll use most often are createSubKey, getSubKey, setValue, and getValue. These methods will allow you to retrieve and key values in code. If you're interested in a large number of keys, you can use getSubKeyNames and getValueNames to access the keys and values that you want to use as arrays.

Method

Description

close

Closes open key and flushes the key in the registry if it has been modified.

createSubKey

Create a subkey.

deleteSubKey

Delete a subkey.

deleteSubKeyTree

Deletes subkey and its child keys.

deleteValue

Delete a value from a key.

finalize

Release resources.

getBaseKey

Gets a RegistryKey object representing the requested key.

getSubKey

Gets a subkey.

getSubKeyCount

Retrieves the number of subkeys from a particular key.

getSubKeyNames

Retrieves an array of strings containing the subkey names.

getValue

Gets a value from the registry.

getValueCount

Gets the number of values under a key.

getValueNames

Gets an array of strings containing the value names under a key.

setValue

Sets a specific value under a key.

toString

Returns the string value of a key.

Figure 3: Methods of the RegistryKey class.

In brief: The close method is used to close the open key. When the key is closed, the value then becomes available for assignment to another key. When close is called without a parameter, a modified key is flushed to disk. The key can be closed with a false argument, which will be closed without flushing.

Let's look at how a RegistryKey object is created, and then build a small demonstration that sets and uses registry values.

Example

First, the RegistryKey object is created by calling the createSubKey or getSubKey methods. For example, to create a new subkey, the createSubKey method is called as follows:

RegistryKey newKey = Registry.CURRENT_USER.createSubKey(

                       "Software\\Company\\Application");

This code snippet shows you exactly how your application should create a key in the registry for its own use. CURRENT_USER is the HKEY_CURRENT_USER I mentioned earlier. The subkey created goes down the line. "Software" is the major key where the applications that your company creates should store information. "Company" should be replaced with the name of your company. "Application" should be replaced with the name of the application creating the subkeys and values. Once you have a new RegistryKey object, you can make your changes using a method such as setValue. For example:

newKey.setValue( Name, Value);

In this statement, Name should be the name of the key you want to add or edit, and Value should contain the object that you want to add to the registry. Listing One contains the source for a small WFC application that gets and sets values in the registry.

You can see the form set up for this project in Figure 4. Notice the text, color, and font of the label.


Figure 4: The program's form in Visual J++ 6.0.


Figure 5 is a shot of the form after some of the variables have been changed.


Figure 5: The running form after changing some variables.


Figure 6 shows you how these variables look in regedit.exe.


Figure 6: The entries made in the registry after saving.


In this program I've created four methods used to access the registry to get, save, or delete entries. The first method I want to mention from the example is setDefaults. In this method, we create a RegistryKey object using createSubKey. This creates our keys and gives us an object to work with. The setValue method is used to create values used to store the data from our program.

All data stored will be in string format, so it needs to be converted from objects and ints for some values before the data is written. For example, the font for the label is converted by creating a Font.Editor object. This object is converted to a string using the getTextFromValue method. Color works in a similar manner. The color value of the label in the form is retrieved and then converted from an integer value to a string. The string is then stored using the setValue method. The last two settings are easy. First, we determine which radio button is checked by checking one of them, in this case radioButtonYes. If it's checked, our value is true, otherwise, the value is false. Finally, we store the label's text property in a value directly.

In getting our data back, the process is reversed. I called the method getDefaults. (This method can be called from the constructor for the form.) It creates a RegistryKey object using getSubKey. From there it checks to see if an object was created by ensuring the value of the object isn't null. (Of course, it will be null the first time we read the values, so we need this.) The necessary objects are then initialized to receive the data. In this case, a Font.Editor and Font object. After converting the font back from the string value, it's passed to the label using setFont.

Converting the Color object back is just a little trickier than it is to make it a string. The Integer.parseInt method is used to turn the string back into an integer value. Notice that we need to catch a NumberFormatException if there's a problem. The integer is then passed to the setForeColor method for the label to change the color.

Again, converting a text value back to boolean is a little tougher than just converting a boolean to a string. To do this we take the string value sChecked and convert it to a Boolean (notice the capital B) object called boolChecked. This value is then converted to a boolean variable using the booleanValue method. An if statement then determines which radio button to check. Finally, the label text is set by simply getting the string from the "Label Text" value.

Two other methods in the program show you how to delete values, and recursively delete subTrees. The clearKeys method uses the deleteValue method to delete a value in a key. (In this case I mean both the data and the name of the value.) To do this, the getSubKey method needs to be accessed with two parameters. The first parameter is the path to the subkey we want to access. The second is a boolean value, which toggles read-only access.

The deleteTree method in the program works in a similar fashion. The getSubKey method needs to be called with read only set to false. Notice that it also trims the application key from the subkey string. The key "RegExample" is then deleted from the registry using deleteSubKeyTree.

I shouldn't have to mention the care you should take when using this method. Setting this up the wrong way could have catastrophic results. For example, I would suggest using this method only to delete application keys or subkeys. If you want to delete your company key, go into the registry and do it by hand to make sure that there's not another application under that key you forgot about.

Conclusion

There are a number of ways you can access the Windows registry from Microsoft Visual J++. This article looked only at the easiest method, using Registry and RegistryKey classes that are part of the WFC. Storing application options like fonts, form position, and color preference can make your application look more professional and easier to use.

To learn more about the registry, check out the documentation in the Resource Kit for the version of Windows that you're working with. This will provide you with the best data about how the information in the registry is stored and used. There's also a short registry article at the Microsoft Visual J++ tutorials page that you might find useful at http://msdn.microsoft.com/visualj/technical/tutorial/wfcregistry/default.asp.

The example solution referenced in this article is available for download.

Brian Johnson is a freelance writer and programmer in Orlando, FL. You can reach him at mailto:brianjay@gate.net.

Listing One

      
import 
com.ms.wfc.app.*;
import 
      com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
import 
      com.ms.wfc.html.*;
 
/**
* This class can take a variable number 
      of parameters on
* the command line. Program execution 
      begins with the
* main() method. The class constructor is 
      not invoked
* unless an object of type 'Form1' is 
      created in the
* main() method. 
*/
public class Form1 extends Form
{
  public 
      Form1()
  {
    // 
      Required for Visual J++ Forms Designer support. 
    initForm(); 
    
    // Get 
      any values already set up in the registry. 
    getDefaults();
  }
 
  
  public void dispose()
  {
    super.dispose();
    components.dispose();
  }
 
  private void getDefaults()
  {
    // Create 
      a RegistryKey object to use. 
    RegistryKey appKey = 
      Registry.CURRENT_USER.getSubKey(

                           "Software\\VJInformant\\RegExample");

    if 
      (appKey != null)
    {
      Font.Editor fe = new Font.Editor();
      
      Font newFont = 
      (Font)fe.getValueFromText(

                        (String)appKey.getValue("Font"));

      label1.setFont(newFont);

      
      String lc = 
      ((String)appKey.getValue("Color"));
 
      // Convert the string value back to an int/color. 
      
      try
      {
        int iColor = Integer.parseInt(lc);

        label1.setForeColor(new Color(iColor));

      }
      
      catch (NumberFormatException nfe)
      {};
      
      // Get the value of Yes key and convert it to 
      a
      // boolean value. 
      
      String sChecked = 
      (String)appKey.getValue("Yes");
      Boolean boolChecked = 
      new Boolean(sChecked);
      boolean bChecked = 
      boolChecked.booleanValue();
      
      // Check the proper radio button. 
      if (bChecked) 
      {

        radioButtonYes.setChecked(true);

      }
      else if (!bChecked)
      {

        radioButtonNo.setChecked(true);

      }
      else
      {

        radioButtonYes.setChecked(true);

      }
      
      //radioButtonYes.setChecked((boolean)((String)appKey.getValue("Yes"))); 
      

      edit1.setText((String)appKey.getValue("Label Text"));

    }
  }
  
  private void setDefaults()
  {
    // Create 
      an appKey object to operate on. 
    RegistryKey appKey = 
      Registry.CURRENT_USER.createSubKey(

                           "Software\\VJInformant\\RegExample");

    
    // Create 
      a Font.Editor object and set the font value. 
    Font.Editor fe = new Font.Editor();
    appKey.setValue("Font", 
      fe.getTextFromValue(label1.getFont()));
    
    // 
      Convert the color to an int, the int to a string
    // and 
      save the value. 
    Color lc = 
label1.getForeColor();
    int 
      test = lc.getValue();
    String sTest = 
      Integer.toString(test);
    appKey.setValue("Color", sTest);
    
    // Check 
      whether Yes is checked and set the string as a value. 
    if 
      (radioButtonYes.getChecked())
    {
      appKey.setValue("Yes", 
      "true");
    }
    else
    {
      appKey.setValue("Yes", 
      "false");
    }
    
    // Set 
      the string contained in the label. 
    appKey.setValue("Label Text", 
      label1.getText());
  }
  
  private void clearKeys()
  {
    // Clear 
      all the registry values so we can start over. 
    // Set 
      the appKey object to Read/Write access by setting
    // the 
      read-only parameter to false. 
    RegistryKey appKey = 
      Registry.CURRENT_USER.getSubKey(

      "Software\\VJInformant\\RegExample", false);

    
    if 
      (appKey != null)
    {

      appKey.deleteValue("Color");

      appKey.deleteValue("Font");

      appKey.deleteValue("Yes");

      appKey.deleteValue("Label Text");

    }
  }
  
  private void deleteTree()
  {
    // Clear 
      the entire subtree for the application. Set
    // the 
      appKey object to Read/Write access by specifying
    // the 
      read-only parameter to false. 
    RegistryKey appKey = 
      Registry.CURRENT_USER.getSubKey(

                           "Software\\VJInformant", false);

    appKey.deleteSubKeyTree("RegExample");

  }
  
  private void buttonColor_click(Object source, Event 
      e)
  {
    int 
      dlgResult = colorDialog1.showDialog();
    if 
      (dlgResult == DialogResult.OK)
    {
      Color objColor = 
      colorDialog1.getColor();

      label1.setForeColor(objColor);  

    }
    
  }
 
  private void buttonFont_click(Object source, Event 
      e)
  {
    int 
      dlgResult = fontDialog1.showDialog();
    if 
      (dlgResult == DialogResult.OK)
    {
      Font objFont = 
      fontDialog1.getFont();

      label1.setFont(objFont);

    }
  }
 
  private void edit1_textChanged(Object source, Event 
      e)
  {
    label1.setText(edit1.getText());
  }
 
  private void buttonSave_click(Object source, Event 
      e)
  {
    setDefaults();
  }
 
  private void buttonClear_click(Object source, Event 
      e)
  {
    clearKeys();
  }
 
  private void buttonDelTree_click(Object source, Event 
      e)
  {
    deleteTree();
  }
 
  /**
   * NOTE: The 
      following code is required by the Visual J++ Forms
   * 
      Designer.   It can be modified using the form 
      editor.   Do not
   * modify it 
      using the code editor. 
   */
  Container components = new Container();
  Label label1 = new Label();
  Edit edit1 = new Edit();
  GroupBox groupBox1 = new GroupBox();
  RadioButton radioButtonYes = new RadioButton();
  RadioButton radioButtonNo = new RadioButton();
  FontDialog fontDialog1 = new FontDialog();
  ColorDialog colorDialog1 = new ColorDialog();
  Button buttonColor = new Button();
  Button buttonFont = new Button();
  Button buttonSave = new Button();
  Button buttonClear = new Button();
  Button buttonDelTree = new Button();
 
  private void initForm()
  {
    this.setText("RegExample");
    this.setAutoScaleBaseSize(new Point(5, 13));
    this.setBorderStyle(FormBorderStyle.FIXED_SINGLE);
    this.setClientSize(new Point(395, 231));
 
    label1.setFont(new Font("MS Sans Serif", 17.0f,
      FontSize.POINTS, 
      FontWeight.BOLD, false, false, false,
      CharacterSet.DEFAULT, 
      0));
    label1.setLocation(new Point(15, 32));
    label1.setSize(new Point(222, 72));
    label1.setTabIndex(0);
    label1.setTabStop(false);
    label1.setText("The cow jumped over 
      the moon.");
 
    edit1.setLocation(new Point(15, 141));
    edit1.setSize(new Point(214, 20));
    edit1.setTabIndex(1);
    edit1.setText(");
    edit1.addOnTextChanged(
      new EventHandler(this.edit1_textChanged));
 
    groupBox1.setLocation(new Point(263, 20));
    groupBox1.setSize(new Point(113, 113));
    groupBox1.setTabIndex(2);
    groupBox1.setTabStop(false);
    groupBox1.setText("Is it 
true?");
 
    radioButtonYes.setLocation(new Point(19, 28));
    radioButtonYes.setSize(new Point(75, 23));
    radioButtonYes.setTabIndex(0);
    radioButtonYes.setTabStop(true);
    radioButtonYes.setText("Yes");
    radioButtonYes.setChecked(true);
 
    radioButtonNo.setLocation(new Point(19, 64));
    radioButtonNo.setSize(new Point(66, 23));
    radioButtonNo.setTabIndex(1);
    radioButtonNo.setText("No");
 
    /* 
      @designTimeOnly fontDialog1.setLocation(new Point(116, 6)); */
 
    /* 
      @designTimeOnly colorDialog1.setLocation(new Point(11, 5)); */
 
    buttonColor.setLocation(new Point(15, 109));
    buttonColor.setSize(new Point(75, 23));
    buttonColor.setTabIndex(3);
    buttonColor.setText("Color");
    buttonColor.addOnClick(new EventHandler(this.buttonColor_click));
 
    buttonFont.setLocation(new Point(106, 109));
    buttonFont.setSize(new Point(75, 23));
    buttonFont.setTabIndex(4);
    buttonFont.setText("Font");
    buttonFont.addOnClick(new EventHandler(this.buttonFont_click));
 
    buttonSave.setLocation(new Point(109, 189));
    buttonSave.setSize(new Point(85, 23));
    buttonSave.setTabIndex(8);
    buttonSave.setText("Save 
      Values");
    buttonSave.addOnClick(new EventHandler(this.buttonSave_click));
 
    buttonClear.setLocation(new Point(206, 189));
    buttonClear.setSize(new Point(85, 23));
    buttonClear.setTabIndex(7);
    buttonClear.setText("Clear 
      Values");
    buttonClear.addOnClick(new EventHandler(this.buttonClear_click));
 
    buttonDelTree.setLocation(new Point(303, 189));
    buttonDelTree.setSize(new Point(85, 23));
    buttonDelTree.setTabIndex(9);
    buttonDelTree.setText("Delete 
      Tree");
    buttonDelTree.addOnClick(new EventHandler(this.buttonDelTree_click));
 
    this.setNewControls(new Control[] {

              buttonDelTree,

              buttonClear,

              buttonSave,

              buttonFont,

              buttonColor,

              groupBox1,

              edit1,

              label1});

    groupBox1.setNewControls(new Control[] {

                 radioButtonNo,

                 radioButtonYes});

  }
 
  /**
   * The main 
      entry point for the application. 
   *
   * @param args 
      Array of parameters passed to the application
   * via the 
      command line. 
   */
  public static void 
      main(String args[])
  {
    Application.run(new Form1());
  }
}
End Listing One

Copyright © 1999 Informant Communications Group. All Rights Reserved. • Site Use Agreement • Send feedback to the Webmaster • Important information about privacy