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 OrganizationThe 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 ClassesThere 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 ClassThe 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 ClassThe 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.
ExampleFirst, 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.
ConclusionThere 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 OneEnd Listing Oneimport 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());
}
}
Copyright © 1999 Informant Communications Group. All Rights Reserved. • Site Use Agreement • Send feedback to the Webmaster • Important information about privacy