Manipulating the Windows Registry with WFC

 

Microsoft Corporation

Introduction

Java programmers targeting the Windows platform must use the Registry to seamlessly integrate their applications with this environment. WFC wraps all the necessary API's into two Java classes, Registry and RegistryKey. This article will cover some basic features of these objects and even a little detail on the registry itself. We'll build a simple tool to search the Registry and explore how it's currently being used.

The Windows Registry

The registry is a hierarchical database used to centrally store application-specific and system-specific information. Older versions of Windows used special text files (.INI) for storing configuration information. The registry provides a single means of locating, editing, and administering this data for both users and machines.

Technical Details

Each location in the Registry is called a key. Each key can contain both subkeys and data entries called values. Values can be named or default to a single value per key. The obvious question is why have both keys and names? The answer is that names take up less storage than keys and are more efficient. User settings should be stored separately from computer settings. The respective keys are HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE. A total of seven top-level keys are statically defined within WFC. The Registry class exists to encapsulate these values and most work is done with RegistryKey.

Practical Guidelines

Sometimes the presence of a key is all the data an application requires to configure itself. Other times a single key with a default value will establish where to locate additional configuration information. The documentation suggests grouping similar data together as a structure and to store that structure as a value rather than storing each of the structure members as a separate key. Unfortunately this advice is rarely followed and lack of registry space can be a problem. Various standard keys exist to control things like application paths and automatic start-ups. Check the docs for Registry Entries appropriate to whatever API, application, or platform you are using.

The Tool

The code presented here was built using Visual J++™ 6.0 and WFC. It performs a recursive search of the LOCAL_MACHINE branch and displays matches in a ListBox. Two RadioButtons control the matching method, a TextField accepts input, and a Button starts the search. Subsequently clicking on a match in the list box will generate a summary of the names/values for that entry.

Uses

I've already used RegistrySearch to track down several "pesky" applications that somehow took up residence and couldn't be removed. Use your imagination and enter terms like "java" and "classpath" and "ODBC" and "modem" to learn more about your configuration.

**Note   **This is just a sample and doesn't do lots of things a professional application would. The minimalist implementation is used to focus on the registry lesson. A real application would at least thread and separate the view from the data. Future samples will explore these issues and others with the very rich WFC.

The Code

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++ Form Designer support
      initForm();      

      // TODO: Add any constructor code after initForm call
   }

   /**
    * Form1 overrides dispose so it can clean up the
    * component list.
    */
   public void dispose()
   {
      super.dispose();
      components.dispose();
   }

   private void button1_click(Object source, Event e)
   {
   /// Get current UI state
      boolean startsWith = radioButton1.getChecked();
      String searchFor = edit1.getText().toLowerCase();

   /// Make search obvious to user
      listBox1.removeAll();
      listBox1.invalidate();
      button1.setEnabled(false);
      button1.setText("Wait..");
      label1.setText(" ");
      
   ///   Start with one of the top Registry Keys
      RegistryKey theKey = Registry.LOCAL_MACHINE;
      String[] keyNames = theKey.getSubKeyNames();
      int nameCount = theKey.getSubKeyCount();
      int i = 0;

      while (i < nameCount){
         if (!startsWith){
            if (keyNames[i].indexOf(searchFor) != -1 )
               listBox1.addItem(keyNames[i]);   
         }
         else{
            if (keyNames[i].startsWith(searchFor))
               listBox1.addItem(keyNames[i]);
         }
         RegistryKey tKey = theKey.getSubKey(keyNames[i]);
         this.descend(tKey,keyNames[i]);
         ++i;
      }
      
      button1.setText("Go..");
      button1.setEnabled(true);

   }


   private void descend(RegistryKey aKey, String fullName){
            
      boolean startsWith = radioButton1.getChecked();
      String searchFor = edit1.getText().toLowerCase();

      String dKeyNames[] = aKey.getSubKeyNames();
      int dCount = aKey.getSubKeyCount();
      int j = 0;
      
      while (j < dCount){
         if (!startsWith){
            if (dKeyNames[j].toLowerCase().indexOf(searchFor) != -1)
               listBox1.addItem(fullName+"\\"+dKeyNames[j]);   
         }
         else{
            if (dKeyNames[j].toLowerCase().startsWith(searchFor))
               listBox1.addItem(fullName+"\\"+dKeyNames[j]);
         }

         RegistryKey nKey = aKey.getSubKey(dKeyNames[j]);
         this.descend(nKey,fullName+"\\"+dKeyNames[j]);
         ++j;
      }
      
   }
   


   /// User has changed the selected key in the list box
   private void listBox1_selectedIndexChanged(Object source, Event e)
   {
      Object currentItem = listBox1.getSelectedItem();

   /// Find all the names in a single key
      RegistryKey currentKey = Registry.LOCAL_MACHINE.createSubKey(currentItem.toString());
      String names[] = currentKey.getValueNames();      
      System.out.println(currentItem.toString());      

   /// A Hive is a key with no values, just more keys.  
      if (currentKey.getValueCount() == 0)
         label1.setText("Hive");
      else
         label1.setText(" ");

   /// If it's not a hive, use names to find their corresponding values
      int i = 0;
      while (i < currentKey.getValueCount()){
         String name = null;
         String value = null;

         if (names[i].length() > 0)
            name = names[i];
         else
            name = new String("default");
         
         value = currentKey.getValue(names[i]).toString();
         if (value == null)
            value = new String("none");
         
         System.out.println(name+" = "+value);
         ++i;
      }
         
   }


   /**
    * NOTE: The following code is required by the Visual J++ form
    * designer. It can be modified using the form editor. Do not
    * modify it using the code editor.
    */
   Container components = new Container();
   Edit edit1 = new Edit();
   RadioButton radioButton1 = new RadioButton();
   RadioButton radioButton2 = new RadioButton();
   ListBox listBox1 = new ListBox();
   Button button1 = new Button();
   Label label1 = new Label();

   private void initForm()
   {
      this.setText("Registry Key Search");
      this.setAutoScaleBaseSize(13);
      this.setClientSize(new Point(437, 280));

      edit1.setLocation(new Point(85, 14));
      edit1.setSize(new Point(240, 20));
      edit1.setTabIndex(2);
      edit1.setText("");

      radioButton1.setLocation(new Point(8, 8));
      radioButton1.setSize(new Point(72, 16));
      radioButton1.setTabIndex(0);
      radioButton1.setTabStop(true);
      radioButton1.setText("starts with");
      radioButton1.setChecked(true);

      radioButton2.setLocation(new Point(8, 24));
      radioButton2.setSize(new Point(72, 16));
      radioButton2.setTabIndex(1);
      radioButton2.setText("contains");

      listBox1.setLocation(new Point(8, 80));
      listBox1.setSize(new Point(424, 186));
      listBox1.setTabIndex(4);
      listBox1.setText("listBox1");
      listBox1.setUseTabStops(true);
      listBox1.addOnSelectedIndexChanged(new EventHandler(this.listBox1_selectedIndexChanged));

      button1.setLocation(new Point(344, 13));
      button1.setSize(new Point(75, 23));
      button1.setTabIndex(3);
      button1.setText("Go..");
      button1.addOnClick(new EventHandler(this.button1_click));

      label1.setLocation(new Point(13, 56));
      label1.setSize(new Point(419, 17));
      label1.setTabIndex(5);
      label1.setTabStop(false);

      this.setNewControls(new Control[] {
                     label1, 
                     radioButton2, 
                     button1, 
                     radioButton1, 
                     listBox1, 
                     edit1});
   }

   /**
    * 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());
   }
}

Discussion

Our sample is implemented as a subclass of Form. Most of the code is straight from the wizard. The entire UI was dragged from the toolbox and hooked up in a matter of minutes. Only three methods were added: one to trap the button click and start our main loop; another to recursively descend the tree; and a third to access the individual name/values for a selected key in the listbox. The two UI oriented methods were added by clicking on the components within the VJ++ 6.0 Designer. Notice how we hook our methods in with the EventHandler() calls in initForm(). The recursive method (descend) demonstrates getSubKeyNames with a passed key instead of a static Registry.KEY. It also keeps track of the complete key name as we go depth first. Trapping the listbox's selectedIndexChanged() method clearly demonstrates the separation of key transversal from name/value pairs. I wimped out a bit with System.out.println for output but the purpose was to retrieve and document the sometimes nonexistence of both names and values for a given key. You could easily develop a "Registry Snapshot" application or even a full-blown Registry Editor using this code as a starting point.

Conclusion

The Registry is a good thing. Use it wisely. Never hard code installation directory or other application configuration info. Use WFC whenever possible instead of native calls. WFC uses J/Direct™, so there is minimal overhead. MFC programmers will have no trouble understanding WFC and Visual C++® programmers will want to switch to Visual J++ 6.0 and Java for increased productivity!