![]() |
|
Published 1999-01-01 Printer-friendly version
With the introduction of Windows 95 and Windows NT, Microsoft introduced a new concept for the storing of system and application configuration data - the registry.
The registry was meant to do away with all those little pesky .INI files that were laying around your hard disk. Instead, all of the settings would be conveniently stored in one file - convenient, that is, until it became corrupted and you were unable to boot your computer.
However, thats another story - Im sure weve all been there, so it needs little explanation from me. This month Im going to explain how to access the registry from within your Clarion programs
Before I do, let me try to explain a little how the registry is organised. There are plenty of third party books out there which go into great detail, but a quick grounding seems appropriate.
The registry stores data in a hierarchically structured tree. Each node in the tree is called a key. Each key can contain both subkeys and data entries called values.
If you compare this to a typical hard disk, you can see that a key is similar to a directory, and a value is a file name. Keeping with this analogy, the contents of the value are the contents of the data file.
Values can be of basically two different types (strings or numbers), with variations thereof (Unicode, null-terminated, big-endian, little-endian etc). It is recommended that if you have a large amount of data (>2K) then the data should be stored as a file and referred to by using a key in the registry rather than being stored as a value. Executable binary code should never be stored in the registry.
There are a number of root keys in the registry which all keys are subkeys of. These main keys are:
| HKEY_CLASSES_ROOT | Registry entries subordinate to this key define types (or classes) of documents and the properties associated with those types. Data stored under this key is used by Windows shell applications and by object linking and embedding (OLE) applications. |
| HKEY_CURRENT_USER | Registry entries subordinate to this key define the preferences of the current user. These preferences include the settings of environment variables, data about program groups, colors, printers, network connections, and application preferences. |
| HKEY_LOCAL_MACHINE | Registry entries subordinate to this key define the physical state of the computer, including data about the bus type, system memory, and installed hardware and software. |
| HKEY_USERS | Registry entries subordinate to this key define the default user configuration for new users on the local computer and the user configuration for the current user |
You may well find other keys in the root, depending on your OS. Win95/98 and Win2000 both have OS-specific keys which Ill not bother covering here - Ill leave that as an exercise for you to complete. Most of the information were going to be reading, and all of that which youll ever need to write, is in one of the 4 base keys mentioned here.
Now that we have a vague understanding of how the registry is organised, lets examine some data. As is the case with lots of things, reading data is a damn sight easier than writing it, so for this example Im going to write a small Clarion application that displays a list of all the installed software on a given computer. Obviously were going to need to know the name of the key that stores this, and because were clever programmers we know its at HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall
Now, this list is also available in the Add-Remove Programs control panel, so at first glance this seems a pointless exercise - until I point out that there are normally lots more packages listed, and that the control panel doesnt display them all, just filters out those that dont have a DisplayName value.
Our first task is to prototype the registry functions we intend to use. Using our trusty MSDN we know that there are 38 functions concerning the registry - luckily for us, we only need 4 of them:
RegOpenKeyEx(Long,Long,Long,Long,Long),Long,Pascal,Raw,Name('RegOpenKeyExA')
RegCloseKey(Long),Long,Pascal,Raw
RegEnumKey(Long,Long,Long,Long),Long,Pascal,Raw,Name('RegEnumKeyA')
RegEnumValue(Long,Long,Ulong,Ulong,Ulong,Ulong,Ulong,Ulong),Long,Pascal,Raw,Name('RegEnumValueA')
As you can see from the prototypes, Ive decided to use the ANSI equivalents, but if you really want the extra hassle youre quite welcome to use the Unicode version instead.
One thing to be aware of is that although the procedures are prototyped exactly the same under Win95/98 and Win2000, they can behave differently, especially when deleting keys. The REGDELETEKEY() function (which we wont actually be using in this article) automatically deletes all sub-keys under Win95/98 - it doesnt under Win2000.
The first thing we need to do in our sample application is open the base key that we already know. We do this by using the REGOPENKEYEX() function:
Loc:KeyName = 'Software\Microsoft\Windows\CurrentVersion\UnInstall'
If RegOpenKeyEx(HKEY_LOCAL_MACHINE,|
Address(Loc:KeyName),|
0,|
Key_Query_Value,|
Address(Loc:KeyHandle)) = ERROR_SUCCESS
! OK
End
When dealing with the registry, get used to passing the base key as the one parameter and the rest of the key as another (it makes it easy to change base keys without any string manipulation). What were doing here is opening the key with sufficient access rights to enumerate all sub-keys and values. We pass in the address of the string containing the key to open, and the address of the variable thats going to hold the handle of the open key. We need this handle when we work our way through all the sub-keys.
Now that weve opened the base key, we have to enumerate this key to find all its sub-keys (each application that is installed has its own sub-key). Once we have that key-name, we have to open the sub-key and enumerate all the values in that key. As you can probably guess, for complex operations you will quickly end up with keys, sub-keys, sub-sub-keys and so on.
To enumerate all the sub-keys we use the RegEnumKey function.
RegEnumKey(Loc:KeyHandle,Loc:SubKeyLoop,Address(Loc:SubKeyName),100)
It takes the handle of the base key weve just opened, the number of the key that we want, a string to hold the name of the key, and an integer specifying the length of the string. To find all the sub-keys for a key, simply place a call to this function inside a loop and increase the number of the key until the return value is ERROR_NO_MORE_ITEMS (a constant of 259).
If the call succeeds, it returns 0 and the string contains the name of the sub-key. We have to concatenate this string with the original to find the new fully-qualifed name of the sub-key, which we then pass to another call to REGOPENKEYEX().
Once weve opened the sub-key successfully, we need to enumerate all the values within the key. For this we use the REGENUMVALUE() function:
Loc:ValueNameStrSize = 50
Loc:ValueDataStrSize = 500
Loc:ValueDataType = Reg_SZ
x# = RegEnumValue( Loc:SubKeyHandle,|
Loc:ValueLoop,|
Address(Loc:ValueNameStr),|
Address(Loc:ValueNameStrSize),|
NULL,|
Address(Loc:ValueDataType),|
Address(Loc:ValueDataStr),|
Address(Loc:ValueDataStrSize))
This is similar to using REGENUMKEY() - it takes the handle to the sub-key we just opened, and uses a numeric counter to work through all the values in the sub-key. Just like REGENUMKEY() it returns ERROR_NO_MORE_ITEMS when we run out of values. It also takes as parameters the address of the string to put the value name in, the length of this string, the address of the variable which holds the data type, and the address of the string containing the data itself, and the length of this second string.
So what do we end up with? Something similar to this:
Loc:KeyName = 'Software\Microsoft\Windows\CurrentVersion\UnInstall'
If RegOpenKeyEx(HKEY_LOCAL_MACHINE,|
Address(Loc:KeyName),|
0,|
Key_Query_Value,|
Address(Loc:KeyHandle)) <> ERROR_SUCCESS Then
Message('Unable to open registry key','User Notification')
Else
Loc:SubKeyLoop = 1
Loop
Case RegEnumKey(Loc:KeyHandle,Loc:SubKeyLoop,Address(Loc:SubKeyName),100)
Of Error_Success
Loc:SubKeyLoop += 1
Loc:Keyname = 'Software\Microsoft\Windows\CurrentVersion\UnInstall\' & Clip(Loc:SubKeyName)
Case RegOpenKeyEx(HKEY_LOCAL_MACHINE,|
Address(Loc:KeyName),|
0,|
Key_Query_Value,|
Address(Loc:SubKeyHandle))
Of Error_Success
Rq:Value=-1;RQ:DataString = Clip(Loc:SubKeyName);Add(RegistryQ)
Loc:ValueLoop = 0
Loop
Loc:ValueNameStrSize = 500
Loc:ValueDataStrSize = 500
Loc:ValueDataType = Reg_SZ
x# = RegEnumValue( Loc:SubKeyHandle,|
Loc:ValueLoop,|
Address(Loc:ValueNameStr),|
Address(Loc:ValueNameStrSize),|
NULL,|
Address(Loc:ValueDataType),|
Address(Loc:ValueDataStr),|
Address(Loc:ValueDataStrSize))
Case x#
Of Error_Success
Loc:ValueLoop += 1
If Clip(Loc:ValueNameStr) = '' Then
Loc:ValueNameStr = '{{Default}'
End
If Clip(Loc:ValueDataStr) = '' Then
Loc:ValueDataStr = '<<NONE>'
End
Rq:Value=2;RQ:DataString = Clip(Loc:ValueNameStr);Add(RegistryQ)
Rq:Value=3;RQ:DataString = Clip(Loc:ValueDataStr);Add(RegistryQ)
Of Error_No_More_Items
Break
Else
Message('Unable to enumerate value for key "' & Clip(Loc:SubKeyName) |
& '"','User Notification')
Break
End
End
If RegCloseKey(Loc:SubKeyHandle) <> ERROR_SUCCESS then
Message('Unable to close registry key','User Notification')
End
Else
Message('Unable to open sub-key ' & Clip(Loc:SubKeyName),'User Notification')
End
Of Error_No_More_Items
Break
Else
Message('Unable to enumerate key','User Notification')
Break
End
End
If RegCloseKey(Loc:KeyHandle) <> ERROR_SUCCESS then
Message('Unable to close registry key','User Notification')
End
End
This code should read the registry and build a queue of all programs installed on your machine, and all of their respective values. If you compare the list this code builds to that of the control panel mentioned earlier you can see that the registry contains a lot more information than you would normally see.
As I mentioned at the beginning, reading the registry is a lot easier than writing to it, mainly because you have to create the key if doesnt exist before you can open it. Thats not actually that difficult to do, just another thing to slow you down in coding the boring stuff and taking up valuable development time. So, in addition to this months download of the source code mentioned this month, theres also an added bonus . . .
This months download includes some code I wrote to access the registry. Its hand-coded source for a DLL called REGLIB that contains 2 registry versions of GetIni and PutIni, called GetRegistry and PutRegistry.
They both take the same number of parameters as their INI file equivalents, but do work slightly differently. GetRegistry takes 4 parameters - 2 strings, an optional string, and a long, and returns a string containing the required values:
String1 The name of the key the value is in.
String2 The name of the value we are looking for.
String3 The default value to return if the required value is not found.
Long1 The base key to use
PutRegistry also accepts 4 paramters - a string, 2 optional strings, and a long:
String1 The name of the key the value is in.
String2 The name of the value we are looking for.
String3 The data to put into the value
Long1 The base key to use
If the 3rd parameter (the data) is omitted or is an empty string, the value will be deleted from the key.
If the 2nd parameter (the value) is omitted or is an empty string, the entire key will be deleted (with the understanding that all sub-keys must have been deleted first when running under Windows NT).
Hopefully these should be of some use in cutting down development time, and should make it easier for Clarion developers to use the registry to store their configuration settings.
Using the registry neednt be as daunting as it first appears. With a little background, its as easy as using INI files, and just because of the complexity you can be happy in the knowledge that this time 99% of your users wont be hacking away and changing values they arent meant to!
Using Simple MAPI to email-enable your Clarion application.
As always, the best place for information is MSDN. If you don't suscribe, you can still get to the information via the web at http://msdn.microsoft.com/
Copyright © 1999-2008 by CoveComm Inc. All Rights Reserved. Reproduction in any form without the express written consent of CoveComm Inc., except as described in the subscription agreement, is prohibited.
Clarion Magazine ISSN 1718-9942
One year: $184
(includes all back issues since '99)
Renewals from $134
Two years: $274
Renewals from $224