2010. szeptember 19., vasárnap

Personal settings and the windows registry


Problem/Question/Abstract:

What is the structure of the registry?
What settings to store in the registry, how to do so?

Answer:

Scope

This article is the third article in a series of four articles about personal settings. This article deals with the usage of the registry for retaining personal settings. It explains the working of the registry, how to read & write date in the registry from Delphi, and contains an example: a form which reads its previous position on the screen.

One thing every application i.m.h.o. should contain is a registration in the registry -the registry should have a clue in which directory your application has been stored. You may choose to have this handled by your installtion program, which is a logical place to do so. But the program can also do it itself, so that the user moving the directory with your application from the windows explorer can fix its own settings by simply starting your program.

The registry - its structure

The registry was introduced by Microsoft with Windows 95. In Windows 95 and Windows 98, there are 2 files in your windows directory called user.dat and system.dat which contain the data in your registry. However, in all cases Microsoft provides a utility regedit to browse and change the registry.

When you use this tool to open your registry, you will see 5 base keys:

HKEY_CLASSES_ROOT

HKEY_CURRENT_USER

HKEY_LOCAL_MACHINE

HKEY_USERS

HKEY_CURRENT_CONFIGURATION


These five keys are identical in all window versions. Each one has many subkeys, which in turn have subkeys again. The registry has a strict hierarchical structure. For our purpose only 2 of them are interesting: HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE. The first is, as the name suggests, user dependant, the second is applicable to all the machine. You might think this key contains just hardware related data, but it has a subkey for software as well. The local machine root is a good choice for items applicable to all users.

Reading data

The reading of data from the registry is illustrated with an example. We will cerate a new form which will remember its previous location on the screen. The location is determined by the Top and Left properties of the form. In addition to these, we will read and store the Height and Width properties of the form.  

const

  cKey = 'SOFTWARE\Test_Company\Test_Application';
  // key where item values will be stored and read

procedure TRememberForm.FormCreate(Sender:
  TObject);
var
  lReg: TRegistry;
  getInt: integer;
begin
  inherited;
  // general purpose:
  // read latest Left, Top, Width and Height
  lReg := TRegistry.Create;
  // create registry object
  lReg.RootKey := HKEY_CURRENT_USER;
  // set root to current user root,
  // to ensure different users on this
  // machine have their own setting
  lReg.OpenKey(cKey, True);
  // Open key where we will read items
  if lReg.ValueExists(self.name +
    '.Left') then // check if
    value for item formname.left exists
    begin
      getint := lReg.ReadInteger(self.name
        + '.Left'); // Read left position of actual form
      if getint > 0 then
        self.Left := getint;
    end;
  if lReg.ValueExists(self.name +
    '.Top') then
  begin
    getint := lReg.ReadInteger(self.name + '.Top');
    if getint > 0 then
      self.Top := getint;
  end;
  if lReg.ValueExists(self.name + '.Width') then
  begin
    getint := lReg.ReadInteger(self.name + '.Width');
    if getint > 0 then
      self.Width := getint;
  end;
  if lReg.ValueExists(self.name + '.Height') then
  begin
    getint := lReg.ReadInteger(self.name + '.Height');
    if getint > 0 then
      self.Height := getint;
  end;
  // Close and free
  lReg.CloseKey;
  lReg.Free;
end;
  
Let's have a short look at the main points:

lReg := TRegistry.Create;
// create registry object

This line creates the Registry object.

lReg.RootKey := HKEY_CURRENT_USER;
// set root to current user root,
// to ensure different users on this
// machine have their own setting

We set the root to indicate in which of the 5 roots we want to work. HKEY_CURRENT_USER is the default value. Alternately, we might have chosen HKEY_LOCAL_MACHINE if we wanted to save settings for all users on this machine.

lReg.OpenKey(cKey, True);
// Open key where we will read items

The key where items will be stored and read is opened. If the key does not exist, windows automatically creates it. Note that capitalization is important when the registry key is created. Most entries in the registry have first letter uppercase and the rest lowercase, and it is good practice to comply.

if lReg.ValueExists(self.name + '.Left') then
  // check if value for item formname. left exists

This statement tests if the item FormName.left exists within the open key.

begin
  getint := lReg.ReadInteger(self.name
    + '.Left'); // Read left position of actual form

As an alternative for ReadInteger, the TRegistry object also provides functions to read strings, booleans, dates, datetimes, times, floats, and binary data.

if getint > 0 then
  self.Left := getint;
end;

By putting all these code in the FormCreate, the window is automatically positioned at the right spot at creation. Of course the code above can not be really tested when we don't have any data, so the next step is to write these items in the FormDestroy.

Writing data

The FormDestroy event is a good moment to write the data to the registry. The code is a lot shorter:  

procedure TRememberForm.FormDestroy(Sender: TObject);
var
  lReg: TRegistry;
begin
  // open registry, set root and key
  lReg := TRegistry.Create;
  lReg.RootKey := HKEY_CURRENT_USER;
  lReg.OpenKey(cKey, True);
  // write last Left, Top, Width and Height
  lReg.WriteInteger(self.name + '.Left', self.Left);
  lReg.WriteInteger(self.name + '.Top', self.Top);
  lReg.WriteInteger(self.name + '.Width', self.Width);
  lReg.WriteInteger(self.name + '.Height', self.Height);
  // close all
  lReg.CloseKey;
  lReg.Free;
  inherited;
end;

As with reading data, the same applies with writing data. You can easily write booleans, currencies, dates, datetimes, floats, strings and times, and binary data. In addition, there is a WriteExpandString, which is meant for unexpanded strings with "%" in it, such as "Open file %s failed".

WriteInteger creates a name when the name does not yet exist. If a write operation fails, an exception is raised.

Alternatives

There are 2 other objects, TRegIniFile and TRegistryInifile available.

TRegistryIniFile presents a simple interface to the system registry and hides the need to know about the underlying structure of the registry. TRegistryIniFile enables handling the Windows system registry as if it were a Windows 3.x INI file. Instead of processing an INI file, however, TRegistryIniFile reads from and writes to the system registry.

TRegIniFile presents a simple interface to the system registry, hiding the need to know about the underlying structure of the registry. TRegIniFile acts as a helper object to TRegistryIniFile.

Personally I avoid using these 2 classes. The programmer saves some time by not having to understand the registry. But the structure of the registry is rather straightforward hierarchical, and using the registry as if it was an ini-file limits its usage.

Limitations and final words

There are a few limitations which we haven't discussed. First, The registry is not meant a a database for storing vast quantities of data. It is meant for storing initialization and configuration data. Anything over 2Kb had better be stored in a separate file. If you wish, you can mention the corresponding file location in the registry. Then WriteBinary is especially useful for storing recordtypes. Using a record type saves you typing code, and it can be written to and read from the registry with 1 statement, saving both time and storage. And a large registry might slow down all applications.

There is a small bug in the MoveKey procedure under WNT. It should move a key including Subkeys, but it does not, at least not in D2-4. In the help of Delphi 5 this problem has been documented. The problem does not seem to appear under W2K.

Third, it seems Windows NT & Windows 2000 administrators can limit Registry access. Using the registry may conflict with IT-policies in some companies.

Should you like the form we created, right click on the form and choose 'add to repository'. You can then create descendants by choosing File / New / forms.


Component Download: http://www.xs4all.nl/~spaanszt/Delphi/DemoRegistry.zip

Nincsenek megjegyzések:

Megjegyzés küldése