Project JEDI Knowledgebase Article (original) (raw)

!! WARNING !! Until you feel absolutely comfortable with the registry only read information from it. DO NOT attempt to change existing information unless you know what the ramifications are! If you make undesirable changes your computer may not run properly or not at all. The demo code supplied here has been tested many times on Windows95/98. No testing has been done on Windows NT4/2000 or Windows XP.

Download the Article and Demos (122Kb)

What is the Registry?

The Registry is a database used to store settings and options for the 32 bit versions of Microsoft Windows; including Windows 95, 98 and NT. It contains information and settings for hardware, software, users, and preferences of the PC. Whenever a user makes changes to a Control Panel settings, or File Associations, System Policies, or installed software, the changes are reflected and stored in the Registry. In short, the registry is a shared resource to all programs running under Windows and NT.

Where is the Registry found?

The physical files that make up the registry are stored differently depending on your version of Windows; under Windows 95 & 98 it is contained in two hidden files in your Windows directory, called USER.DAT and SYSTEM.DAT, while under Windows NT the files are contained seperately in the "Windows / System32 / Config" directory.

What's the structure of the Registry?

As stated in Delphi's online help for TRegistry,

The registry is a database that an applications can use to store and retrieve configuration information. Configuration information is stored in a hierarchical tree. Each node in the tree is called a key. Every key can contain subkeys and data values that represent part of the configuration information for an application.

The Registry has a hierarchical structure. Although it looks complicated the structure is similar to the directory structure on your hard disk, with Regedit being similar to Windows Explorer. Each main branch (denoted by a folder icon in the Registry Editor, see below) is called a Hive, and Hives contains Keys. Each key can contain other keys (sometimes referred to as sub-keys), as well as Values. The values contain the actual information stored in the Registry. There are three types of values; String, Binary (similar to a Pascal record), and DWORD (Delphi specifies them as an integer) - the use of these depends upon the context.

There are six main branches, each containing a specific portion of the information stored in the Registry. They are as follows:

Using the Registry

Not only can Windows itself read and write information to the registry, but so can you through your Delphi application. Many programmers will write information to the registry during the installation process of their application. When their application is running they retrieve the information using various methods of TRegistry. You are not limited to simply reading static information in the registry, but can write, edit, modify and delete information from the registry.

Caveat :: Just about every minute that your computer is running, something is being read from or written to the registry. It is not very obvious this is going on. To see what is going on, there is a tool called RegMon. It is a freeware utility which can be downloaded from

www.sysinternals.com

The RegMon tool can be of great service to you in learning what goes on behind the what you see. For instance, let's look to see what activity goes on by simply pressing the Windows Start button:

As you can see, Windows need to get information even for this simple operation. Once you have found something interesting simply double click on the line item to open up RegEdit. Remember that once RegEdit is open to be careful not to change something you don't know what that item does. Another nice tool to own is from Norton Utilities, it is called Registry Editor. One of the nice features is, bookmarks. To locate a key or value in RegEdit each time you fire it up you must search for it, or traverse the registry. In Norton's Registry Editor you can bookmark an item, then use the bookmark window to instantly go to that item.

To Access the Registry in Delphi

  1. Add registry to the uses clause of the unit you are going to open the registry with.
  2. In the routine which will open the registry declare a variable of type TRegistry.
    Example:
    Reg: TRegistry;
  3. Use the Create method to instantiate a TRegistry object.
    Example
    Reg := TRegistry.Create ;
    For Delphi 5 the Create method has been overloaded:
    constructor Create(AAccess:LongWord); overload;
    AAccess sets the value of the Access property (new) which is used to specify the desired access whenever a key is opened. An example:
    Reg.Create(KEY_ALL_ACCESS);
  4. Next we must tell our object which branch of the registry to work with:
    Reg.RootKey := HKEY_LOCAL_MACHINE;
    By default the rootkey is HKEY_CURRENT_USER, so if this is the branch you intend to work with there is no need to set the rootkey.
  5. Now you can use OpenKey to open up the desired key.
  6. Finally use the Read and Write methods to read and write information from and to the Registry.

Access Level in TRegistry

Delphi 5 introduced a new property for TRegistry which allows you to set the access level to use when opening a key:

property Access: LongWord;

Description

Use Access to specify the level of security access to use when opening keys. The OpenKey method uses the value of Access when opening a registry key. Access is initialized by the TRegistry constructor, but can be altered before calling OpenKey.

The value of Access is a set of flags or�d together, where each flag indicates a type of permission. Choose a value that grants sufficient permission to allow the desired actions while not exceeding the access level of the system running the application. The following table lists the flags that can be included:

Flag Meaning
KEY_ALL_ACCESS combination of KEY_READ, KEY_WRITE, and KEY_CREATE_LINK.
KEY_READ combination of KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY.
KEY_WRITE combination of KEY_SET_VALUE and KEY_CREATE_SUB_KEY.
KEY_QUERY_VALUE grants permission to query subkey data.
KEY_ENUMERATE_SUB_KEYS grants permission to enumerate subkeys.
KEY_NOTIFY grants ability to receive change notifications.
KEY_SET_VALUE grants permission to set subkey data.
KEY_CREATE_SUB_KEY grants permission to create subkeys.
KEY_CREATE_LINK grants permission to create symbolic links.
KEY_EXECUTE restricts permission to read-only access.

Note: Access is ignored by the CreateKey method, which always uses KEY_ALL_ACCESS.

Uses for Registry Access

A good example for writing new keys and values to the registry is when your application does not use an installation program to write information to the registry.

An example for modifying information is, each time your application closes it stores various pieces of information about the applications current status (form placement, menu item configuration etc) which is used when the application fires up next time.

An example for deleting information, your application only stores information if the user customizes various parts of the application. If they press a "restore defaults" button then you delete all the custom settings.

Understanding the Registry Structure

Before you begin to actually change data in the registry you must understand the structure of the registry. Key elements of the registry are illustrated in the image below:

You can see the hierarchy of the registry. Each folder is a key within the structure of the registry. On the right hand side there are two columns, on the left (name) are keys, on the right values (data).

Suppose you want to know the color the current user has selected for ButtonFace. The first thing you will need to do is find the key in the registry which stores the colors used by the current user.

Now, all we need to do is figure out how to open the value for the key. Our starting point is to create an object to reference the key. As with manually creating any object, a declaration is in order. Once the object has been created we can begin using it.

The key elements to accessing data are:

end;

The above example was fairly straightforward, open a key and read the data. There will be times when the information needed from the registry is not an easy path. For instance there are some values stored in the registry which can only be found by first going to one key, pick up some directions to another key which then houses the data you want. Other times there will be a need to use API calls to access sections of the registry. Let's examine both cases.

Unless you have been living on an island you have heard of Microsoft ADO. To get version information for ADO you need to dig into the Registry. Your first thought might be to search for ADO in the Registry but this will lead to several findings. First, ADO will most likely appear about 600 times, secondly you will see several version numbers if you had installed, then updated it.

So a different approach needs to be taken! To accomplish the task you need to do some research. Rather than keep you guessing I will tell you the information for locating this data is located on the home page at Microsoft for ADO/DCOM.

Here is the is the key to start off with. The root key is HKEY_CLASSES_ROOT and the sub-key is CLSID\{bdc67890-4fc0-11d0-a805-00aa006d2ea4}\InstalledVersion. Once here we then read the default value of InstalledVersion. This returns a value formatted with commas. This is the version of DCOM installed on the computer. This is a necessary component of ADO. Compare the returned value to what ADO expects to find. If the version is correct then we move to the next query (see below).

function GetDCOMVersion: String ; const sSubKey = 'CLSID{bdc67890-4fc0-11d0-a805-00aa006d2ea4}\InstalledVersion' ; var Reg: TRegistry ; begin if not DCOMEnabled then begin Result := 'DCOM not installed' ; end ; Reg := TRegistry.Create ; try Reg.RootKey := HKEY_CLASSES_ROOT ; try if Reg.OpenKey(sSubKey,False) then if Reg.ValueExists('') then Result := Reg.ReadString('') ; except Result := 'ERROR' ; end ; finally Reg.Free ; end ; end ;

Now comes a "seek and find" operation. The version for ADO is stored in a DLL with a name that appears to indicate the version of ADO, it is not. Microsoft has no reasoning for this. To obtain the version we first start by reading HKEY_CLASSES_ROOT\ADODB.Connection\CLSID, read the default value for the key, remember it within a string variable so that it can be used to read yet another key:

'CLSID\' + cKey +'\InprocServer32'

In the code above cKey represents the value taken from the first key read. Now by reading the key shown above we can get the version of ADO (although the function name is MDAC_Version. At this point we have the name of the file which contains the version number. Simply take the file name and use a component which can extract the version number and we are done. The attached example contains all the code required to retrieve the version number.

Concerns using straight API routines to access the Registry

The class TRegistry can run into problems if the value it needs to read is locked by another application. This means that another program has denied access to anyone else attempting to read the information we want. A good example is, you are on a computer with a modem to connect to the internet and your browser is Microsoft Internet Explorer. IE will open up the registry and lock up a section. Using the Delphi wrapper TRegistry to read the value we need will end up with a Windows violation message and fail to open the required key.

The only way around this problem is to either write a new class or write a function which uses the same functions TRegistry does but by altering some parameters and logic. Below is an example which will properly open the key which IE might have open.

unit IE_Connected;

interface uses Windows, Messages, SysUtils, Controls, Dialogs, StdCtrls, Registry;

function IsConnected: boolean ;

implementation

function IsConnected: boolean ; const cMsg = 'Place your error message here ' ; { for demoing only } sSubKey = 'System\CurrentControlSet\Services\RemoteAccess' ; sKeyValue = 'Remote Connection' ; var pKey: hKey ; lpData, DataSize: Longint ; begin lpData := $0 ; if RegOpenKeyEx(HKEY_LOCAL_MACHINE,sSubKey,0,KEY_QUERY_VALUE,pKey) = 0 then begin try DataSize := Sizeof(lpData); if RegQueryValueEx(pKey,sKeyValue,nil,nil,@lpData,@DataSize) <> 0 then raise Exception.Create(cMsg + #13 + SysErrorMessage(GetLastError)) ; finally RegCloseKey(pKey) ; end; if lpData > 0 then Result := True else Result := False ; end else raise Exception.Create(cMsg + #13 + SysErrorMessage(GetLastError)) ; end ; end.

How to read information from the registry

There are nine different types of data which you can read from the Registry. Each is shown below along with the method used to read the specific data.

Type Description
Binary Returns data similar to an Object Pascal record type. Use ReadBinaryData method to read data
Boolean Returns data resembling Logical type i.e. True or False Use ReadBool method to read data. Note: The registry stores Boolean values as integers, Delphi transposes them to Boolean type for you.
Currency Returns the value of a key as currency. If the key being read is not the proper type then an exception is raised. Use ReadCurrency method to read data.
Date Returns the date part of a TDateTime type. Use ReadDate method read data. Note: Many data types can be checked using TRegistry.GetDataType, but not with dates. As stated in Delphi's online help, only using the method ReadDate when you have written a value to a key with WriteDate method.
DateTime Returns a TDateTime value from a specific key in the registry. Use ReadDateTime to read data.
Float Returns a number with the format 00.00 Use ReadFloat to read data.
Integer Returns a number with no fractional part. Use ReadInteger to read data.
String Returns a string. Use ReadString to read data.
Time Returns the time portion of a TDateTime type. Use ReadTime to read data. Note: Many data types can be checked using TRegistry.GetDataType, but not with dates/times. As stated in Delphi's online help, only use the method ReadDate when you have written a value to a key with WriteTime method.

Simple demo of reading a string from the registry

Reading a string from a key. The key, which the following code reads, is for finding out the keyboard speed and delay factor.

Create a new form, add registry to the uses clause. Next place a button and a ListBox on the form. Double click the button and place the following code into it.

procedure TForm1.Button1Click(Sender: TObject); var Reg: TRegistry ; begin Reg := TRegistry.Create ; try Reg.RootKey := HKEY_CURRENT_USER ; if Reg.OpenKey('Control Panel\Keyboard', False) then begin ListBox1.Items.add(Reg.ReadString('KeyboardDelay')) ; ListBox1.Items.add(Reg.ReadString('KeyboardSpeed')) ; end ; finally Reg.Free ; end ; end;

The first statement creates a local instance of a TRegistry object. The remaining statements are enclosed with in an exception block to ensure that the object is destroyed and the reference to the registry is closed. To tell the object were to read from we use the property RootKey to point to HKEY_CURRENT_USER (you can also read from HKEY_CLASSES_ROOT, HKEY_CURRENT_CONFIG, HKEY_DYN_DATA, HKEY_LOCAL_MACHINE and HKEY_USERS). Next using the method OpenKey we pass in the branch of HKEY_CURRENT_USER to read from. An if statement is used to determine if the key was opened or not. The second argument to OpenKey determines if the key is to be created if not located. Once the key has been opened the method ReadString is used to read the entries of two values from the keys we have indicated to read. If either of the keys was not located then nothing is returned. One failure does not mean a valid call to ReadString will fail.

Caveat: Delphi supplies (not in the standard version) a file called RegStr which is filled with constants for many known pieces of information and paths into the registry. You should take time to examine the file's content.

Example

Reading the current color scheme for the current user

The following example requires a new form, and command button. It will retrieve the colors for the current user. Enter the code and run the project.

Partial image of the end result

procedure TForm1.Button3Click(Sender: TObject); procedure ShowColors(cCaption, Value: String; TheTop: Integer) ; var L: TLabel ; P: TPanel ; x: Integer ; R: Integer ; G: Integer ; B: Integer ; S: String ; begin S := Value ; { Create labels for each system color } l := TLabel.Create(Self); l.Name := 'Label' + IntToStr(TheTop) ; l.Caption := cCaption ; l.Parent := Self; l.Top := TheTop ; l.Left := 10 ; l.Visible := True ; { Create a panel to show the color for corresponding label above } P := TPanel.Create(Self) ; P.Name := 'Panel' + IntToStr(TheTop) ; P.Visible := False ; P.Caption := '' ; P.Parent := Self ; { Convert string RGB to individual integers for showing color below } x := Pos(#32,S) ; R := StrToInt(Copy(S,1,x -1)) ; Delete(S,1, x) ; x := Pos(#32,S) ; G := StrToInt(Copy(S,1,x -1)) ; Delete(S,1, x ) ; B := StrToInt(S) ; P.Color := TColor(Windows.rgb(R,G,B)) ; P.Top := L.Top + 1 ; P.Left := 150 ; P.Width := 11 ; P.Height := 11 ; P.Visible := True ; end ; //------------------------------------ const BaseKey = 'Control Panel\Colors' ; var Reg: TRegistry ; aList: TStringList ; iCount: Integer ; iValue: Integer ; cValue: String ; iTop: Integer ; begin Reg := TRegistry.Create ; try with Reg do begin RootKey := HKEY_CURRENT_USER ; if OpenKey(BaseKey, False) then begin aList := TStringList.Create ; lockWindowUpdate(handle) ; try GetValueNames(aList) ; iCount := aList.Count ; CloseKey ; iTop := 5 ; for iValue := 0 to iCount -1 do begin if OpenKey(BaseKey,False) then begin cValue := ReadString(aList.Strings[iValue]) ; CloseKey ; ShowColors(aList.Strings[iValue],cValue,iTop) ; { label height plus fill between next label } Inc(iTop, 16) ; end ; end ; finally aList.Free ; Sleep(1000) ; lockWindowUpdate(0) ; end ; end ; finally Reg.Free ; end ; Button3.Enabled := False ; end;

Reading unknown keys in a branch

Suppose that you need to obtain some string values for a branch of the registry where you do not know how many keys there are. Building on the first example we can get the number of entries and their values. Note that an assumption that all the keys are of type string. The key to getting all the entries is a method called GetValueNames. This method will return all the keys under the branch of the registry currently open. The current branch for this example is HKEY_CURRENT_USER\ Control Panel\Keyboard.

procedure TForm1.Button1Click(Sender: TObject); var Reg: TRegistry ; SomeValues: TStringList ; iCount, i: Integer ; begin Reg := TRegistry.Create ; SomeValues := TStringList.Create ; try Reg.RootKey := HKEY_CURRENT_USER ; if Reg.OpenKey('Control Panel\Keyboard', False) then begin Reg.GetValueNames(SomeValues) ; iCount := SomeValues.Count ; if iCount >0 then for i := 0 to iCount -1 do Listbox1.Items.Add(SomeValues.Strings[i] + '-->' + Reg.ReadString(SomeValues.Strings[i])) ; end ; finally Reg.Free ; SomeValues.Free ; end ; end ;

How to Enumerate an entire Branch of the registry

This example goes through the registry searching for &^quot;CurrentVersion" under the root key HKEY_CURRENT_USER. Once it finds the key it increments a counter, when completed the times which "CurrentVersion" was found is displayed. In a real life example you might need to narrow your search to a key or a value. This is a simple example.

To run this example, on a new form place a memo control and a command button.

type TEnumerateRegBranch = class(TForm) Memo1: TMemo; Button1: TButton; procedure Button1Click(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private iRanIt:boolean ; public end; var EnumerateRegBranch: TEnumerateRegBranch;

implementation {$R *.DFM} uses registry ; procedure TEnumerateRegBranch.Button1Click(Sender: TObject); var indent: Integer; iMatches: Integer ; procedure EnumAllKeys( hkey: THandle ); var l: TStringList; n: Integer; begin Inc(indent, 2); with Tregistry.Create do try RootKey := hkey; OpenKey( EmptyStr, false ); l := TStringLIst.Create; try GetKeynames(l); CloseKey; for n := 0 To l.Count-1 do begin memo1.lines.add( StringOfChar(' ',indent) +l [n]); try if CompareStr('CurrentVersion',L.Strings[n]) = 0 then Inc(iMatches) ; except end ; if OpenKey( l[n], false ) then begin EnumAllKeys( CurrentKey ); CloseKey; end; end; finally l.Free end; finally Free; end; Dec(indent, 2); end; //-----------------------------------------

begin { Searches Key and Values for "CurrentVersion" while traversing. } Memo1.Clear; memo1.lines.add('Keys under HKEY_CURRENT_USER'); indent := 0; Button1.Enabled := False ; EnumAllKEys( HKEY_CURRENT_USER ); Button1.Enabled := True ; iRanIt := True ; if iMatches >0 then ShowMessage('Found ' + IntToStr(iMatches) + ' of "CurrentVersion"') else ShowMessage('Did not find CurrentVersion') ; end ; //------------------------------------------

procedure TEnumerateRegBranch.FormClose(Sender: TObject; var Action: TCloseAction); begin if iRanIt then ShowMessage('This may take a while') ; end;

Writing data to the registry

Delphi provides all the routines which are needed to write information to the registry as shown below:

Type Description
WriteBinaryData Used to write binary data to the Registry
WriteBool Used to write Boolean data to the registry
WriteCurrency Used to write Currency data to the registry
WriteDate Used to write the date part of a TDateTime type to the registry
WriteDateTime Used to write a TDateTime type to the registry
WriteExpandString Used to store a string that contains unexpanded references to environment variables such as "%PATH%. The string is stored in a data value associated with the current key.
WriteFloat Used to store a float to the registry
WriteInteger Used to store an integer to the registry
WriteString Used to store an ordinary string to the registry
WriteTime Used to store the Time part of a TDateTime type to the registry.

Demo for writing information to the registry

The following example creates a new key under the HKEY_CURRENT_USER branch of the registry. Once created several keys and values are set. After the values are written to the registry they are read back to confirm they are indeed in the registry. Lastly all the data which was written is removed.

Create a new form, place a ListBox and a command button on the form followed by the code below.

The following code shows some simple examples of reading, writing and returning information back from the registry.

NOTE 1: Little exception handling is done since I want to keep the code clear.

NOTE 2: Since I don't have Windows-NT there is no way for me to check how to deal with NT-security.

procedure TForm1.Button1Click(Sender: TObject); { This record is used to demo read/write operations to the registry } type TSomeData = record Top, Left, Width, Height:Integer ; Caption:String ; end;

{ Key beneath HKEY_CURRENT_USER to work with } const> BaseKey = 'a_test' ; var Reg: TRegistry ; MyCurrency: Currency ; FSomeData : TSomeData ; begin ListBox1.Clear ; { Assign a value to store into the registry } FSomeData.Top := Top ; FSomeData.Left := Left ;> FSomeData.Width := Width ; FSomeData.Height := Height ; FSomeData.Caption := Caption ; MyCurrency := 1000000.90 ; { Create an object to access the system registry } Reg := TRegistry.Create ; try Reg.RootKey := HKEY_CURRENT_USER ; if not Reg.CreateKey(BaseKey) then begin ShowMessage('Failed to make it new entry for "' + BaseKey + '"'); exit ; end ; Reg.OpenKey(BaseKey,False) ; { Write our data to the currency and binary values to the registry } Reg.WriteCurrency('Currency Example',MyCurrency) ; Reg.WriteBinaryData('BinaryData Example',FSomeData,SizeOf(TSomeData)) ;

{ Now read back the data for currency }
ListBox1.Items.Add(Format('Currency Example = %m',
[Reg.ReadCurrency('Currency Example')])) ;

{ Nullify the record to ensure we get back the data }
FSomeData.Top := -1 ;
FSomeData.Left := -1 ;
FSomeData.Width := -1 ;
FSomeData.Height := -1 ;
FSomeData.Caption := '' ;

{ Screw a bit with the form, later we restore it with the
  registry data }
Top := Top - 100 ;
Left := Left - 100 ;
Caption := 'Just demoing...' ;

{ Timely pause for reflection }
Sleep(2000) ;

{ Okay let's get the data back }
Reg.ReadBinaryData('BinaryData Example',
FSomeData,SizeOf(TSomeData)) ;

{ Show the returned members retrieved from the registry }
ListBox1.Items.Add('Top = ' + IntToStr(FSomeData.Top)) ;
ListBox1.Items.Add('Left = ' + IntToStr(FSomeData.Left)) ;
ListBox1.Items.Add('Width = ' + IntToStr(FSomeData.Width)) ;
ListBox1.Items.Add('Height = ' + IntToStr(FSomeData.Height)) ;
ListBox1.Items.Add('Caption = ' + FSomeData.Caption) ;

{ Using registry data restore the form's position }
Top := FSomeData.Top ;
Left := FSomeData.Left ;
Caption := FSomeData.Caption ;

{ Close the current key }
Reg.CloseKey ;

{ Remove it since we are done with it }
Reg.DeleteKey(BaseKey) ;

finally Reg.CloseKey ; Reg.Free ; end ; end;

Simple demo for reading and writing to the registry

The following example shows how to save the position of a form on exit and restoring the forms position next time it is shown. Note, this is a simplified version, it was kept simple to show how to work with the registry rather then clouding the example with code, which is not really relevant to exploring the registry.

Start with a new project with one form, add the code shown below:

procedure TForm1.FormCreate(Sender: TObject); var PriorState:TWindowState ; Reg: TRegistry ; begin { Restore form state as saved in the registry on closure of this form } Reg := TRegistry.Create ; try if Reg.OpenKey(MyBaseKey,False) then begin if Reg.OpenKey(MyAppSettings,False) then begin with Reg do begin if ValueExists('Top') and ValueExists('Left') and ValueExists('Width') and ValueExists('Height') and ValueExists('WindowState') then begin Self.SetBounds(ReadInteger('Left'), ReadInteger('Top'), ReadInteger('Width'), ReadInteger('Height')) ; PriorState := TWindowState( ReadInteger('WindowState') ) ; if PriorState = wsMinimized then begin Visible := True ; Application.Minimize ; end else WindowState := PriorState ; end ; end ; end else ShowMessage('Failed to open ' + MyAppSettings) ; end else ShowMessage('Failed to open ' + MyBaseKey) ; finally Reg.Free ; end ; end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); var Reg: TRegistry ; begin Reg := TRegistry.Create ; try if Reg.OpenKey(MyBaseKey,True) then if Reg.OpenKey(MyAppSettings,True) then begin with Reg do begin { Store the current form settings } WriteInteger('Top',Top) ; WriteInteger('Left',Left) ; WriteInteger('Width',Width) ; WriteInteger('Height',Height) ; WriteInteger('WindowState',Ord(WindowState)) ; end ; end ; finally Reg.Free ; end ; end;

The form's Close event opens the key HKEY_CURRENT_USER\JEDI_Registry_Demo if it exists. If the key does not exist it is created. Notice that the second argument to OpenKey is set to True, if it was set to False then the call to OpenKey would fail if the key didn't exist. The first time this code runs it creates the key, if the key gets deleted then it will be recreated.

if Reg.OpenKey(MyBaseKey,True) then

The line following the above uses the exact same logic for

HKEY_CURRENT_USER\JEDI_Registry_Demo\FormSettings

since the second parameter to OpenKey is set to True.

if Reg.OpenKey(MyAppSettings,True) then

The next several lines of code check to see if values exist under

HKEY_CURRENT_USER\JEDI_Registry_Demo\FormSettings

and if not found are created. If they exist then we move on to the next several lines of code which place current form properties into the values.

Now when the form is used again, there is code in the Create event which reads back the data we stored in the Close event. Notice that the code in the Create event opens our keys using OpenKey with the second parameter set to False. This means that if the keys do not exists a message is displayed informing us of an error. If the keys do exist we then check to see if the values are found, if they are not an error message is displayed alerting you of a problem. If the values are located then they are retrieved and used to restore the forms position.

You could take the code presented here and expand upon it, or surf on over to your favorite Delphi web site and find several good components which handle positioning without any coding. The entire purpose of this example is for learning the registry, not building perfect code to position forms.

Unstable operations

LoadKey and SaveKey methods of TRegistry provide ways to save a branch of the registry to a binary disk file and later import the information back into the registry. Between trying both methods myself and doing research over the web it is my conclusion that they do not work well. Using them resulted in unusual error messages with small parts my registry locking up until I restarted my computer.

The code below shows how to mess up a computer with Load/SaveKey

Make sure to add the registry unit to the uses clause

const SaveRestoreKey = 'JEDIRegistryDemo' ; SaveRestoreFile = 'JEDI_1' ;

.... procedure TForm1.LoadMeClick(Sender: TObject); var Reg: TRegistry ; begin if not FileExists(SaveRestoreFile) then begin ShowMessage('Restore file (' + SaveRestoreFile + ')not found') ; exit ; end ; Reg := TRegistry.Create ; try Reg.RootKey := HKEY_LOCAL_MACHINE; if Reg.LoadKey(SaveRestoreKey, SaveRestoreFile) then ShowMessage('Key has been restored') else ShowMessage('Restorekey failed with the following error' + #13 + SysErrorMessage( GetLastError ) ); finally Reg.CloseKey ; Reg.Free ; end ; end;

procedure TForm1.Button4Click(Sender: TObject); var Reg: TRegistry ; begin Reg := TRegistry.Create ; try Reg.RootKey := HKEY_LOCAL_MACHINE ; if not Reg.KeyExists(SaveRestoreKey) then begin ShowMessage('HKEY_LOCAL_MACHINE' + SaveRestoreKey + 'does not exist') ; end ; if Reg.SaveKey(SaveRestoreKey,SaveRestoreFile) then begin ShowMessage(SaveRestoreKey + ' was saved') ; Application.ProcessMessages ;

{if Reg.KeyExists(SaveRestoreKey) then if Reg.DeleteKey(SaveRestoreKey) then begin ShowMessage(SaveRestoreKey + ' deleted') ; Button5.Enabled := True ; end else begin ShowMessage('delete failed with the following error' + #13 + SysErrorMessage( GetLastError ) ); end ;} end else ShowMessage('SaveKey failed with the following error' + #13 + SysErrorMessage( GetLastError ) ); finally Reg.CloseKey ; Reg.Free ; end ; Reg := TRegistry.Create ; try Reg.RootKey := HKEY_LOCAL_MACHINE ; if Reg.KeyExists('JEDIRegistryDemo') then if Reg.DeleteKey('JEDIRegistryDemo') then ListBox1.Items.Add('Worked') ; finally Reg.CloseKey ; Reg.Free ; end ; end;

Kevin Gallagher
July 2000


Download the Article and Demos (122Kb)