Hackers’ Corner: Dyalog APL and the Windows 95 Registry
Vector 14:3, page 98
Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 98
Why Use the Windows Registry?¶
Windows 3.x used initialization files, which had the extension .ini, to hold the settings for Windows itself and for applications written to run under Windows. There were two types of initialization file, system and private, although in many cases programmers were lazy and wrote information that rightly belonged in a private initialization file into a system file, such as system.ini or win.ini. Since the largest size .ini file that Windows 3.x could handle was 64k, this caused problems whenever users installed "too many" of these applications. On the other hand, some programs used private initialization files to store system information; for example Microsoft Publisher stored its font information in a private file. Make the font unavailable and the program crashes!
With Windows 95 the Registry was introduced in order to keep all initialization data together, rather than in a succession of fragmented .ini files. Although support for .ini files is still maintained (and has even been enhanced) in Windows 95, because, obviously, programs written for Windows 3.x will require access to .ini files when run under Windows 95, performance when accessing the Windows registry is significantly faster than when accessing .ini files in almost all circumstances. There are no restrictions to the size of the registry, whereas the functions that write to .ini files fail when the file approaches its size limit of 64k.
Since Windows 95 applications are supposed to write to the registry rather than to initialization files, developers who fail to use the registry can produce applications that are not completely compliant with the Win95 software design guidelines.
The Functions¶
There are six of these:
Open, which opens a path in the registry and returns a handle
GetInt, which gets an integer value from the registry
GetString, which gets a character string from the registry
PutInt, which puts an integer on the registry
PutString, which puts a character string on the registry
Close, which closes the registry when you’ve finished.
Dyadic Systems have already provided some functions to perform registry operations: these are in the Workspace Documentation namespace in the session. We can reduce our workload by making use of them, giving them new names and putting them in our own namespace for completeness. I put these functions in a namespace called Reg to keep them together and out of the way. The functions we reuse (thanks to Dyadic for permission to list these) are:
Open←⎕SE.WSDoc.GetRegKeyHandle
GetInt←⎕SE.WSDoc.GetRegKeyValue
PutInt←⎕SE.WSDoc.PutRegKeyValue
∇ HANDLE←GetRegKeyHandle KEY;CURRENT_USER;ALL;RegCreateKeyExA
[1] ⍝ Get a handle to a Registry key as a subkey of HKEY_CURRENT_USER
[2] ⍝ The key is created if it doesn't already exist
[3]
[4] CURRENT_USER←2147483649 ⍝ HEX 0x80000001
[5] ALL←2035711 ⍝ HEX 0x1F0FFF
[6]
[7] ⎕NA'I ADVAPI32.dll.C32|RegCreateKeyExA U <0T I <0T I I I >U >U'
[8] HANDLE←⊃2⊃RegCreateKeyExA CURRENT_USER KEY 0 '' 0 ALL 0 0 0
∇
∇ VALUE←KEY GetRegKeyValue NV;SUBKEY;RegQueryValueExA;RC;VAL
[1] ⍝ Gets the value of a Registry SUBKEY or default DEFAULT
[2] ⍝ KEY is the handle for an existing Registry Key
[3] SUBKEY VALUE←NV
[4] ⎕NA'I ADVAPI32.dll.C32|RegQueryValueExA U <0T I I >I4 =I4'
[5] RC VAL←2↑RegQueryValueExA KEY SUBKEY 0 0 0 4
[6] →(RC≠0)/0
[7] VALUE←VAL
∇
∇ HANDLE PutRegKeyValue NV;SUBKEY;VALUE;RegSetValueExA
[1] ⍝ Stores the value of a Registry SUBKEY
[2] SUBKEY VALUE←NV
[3] ⎕NA'I ADVAPI32.dll.C32|RegSetValueExA U <0T I I <I4 I4'
[4] VALUE←RegSetValueExA HANDLE SUBKEY 0 4 VALUE 4
∇
The syntax of these functions is as follows, where key is a subkey of HKEY_CURRENT_USER, and that part of the key does not need to be given.
It is important to close all open registry handles as soon as you’ve finished with them, in order to maintain data integrity (what do you do if your system crashes between updating the registry itself and the time when Windows actually gets around to writing the updates to disk?) and also to free up Windows resources.
The other functions are easily written, copying the style of the Dyadic-supplied functions:
∇ VALUE←KEY GetString NV;LEN;SUBKEY;DEFAULT;RegQueryValueExA;RC;VAL
[1] ⍝ Gets the value of a Registry SUBKEY or default DEFAULT
[2] ⍝ Key is the handle for an existing Registry Key
[3] ⍝ Function by JS based on ⎕SE.WSDoc.GetRegKeyValue
[4] SUBKEY VALUE←NV
[5] ⎕NA'I ADVAPI32.dll.C32|RegQueryValueExA U <0T I I =0T =I4'
[6] RC VAL LEN←RegQueryValueExA KEY SUBKEY 0 0(256⍴' ')256
[7] :If RC=234 ⍝ More data (i.e. buffer is not big enough)
[8] RC VAL LEN←RegQueryValueExA KEY SUBKEY 0 0(LEN⍴' ')LEN
[9] :EndIf
[10] →(RC≠0)/0
[11] VALUE←VAL
∇
∇ {RC}←HANDLE PutString NV;SUBKEY;VALUE;RegSetValueExA
[1] ⍝ Stores the value of a Registry SUBKEY
[2] ⍝ HANDLE is the handle for an existing Registry Key
[3] ⍝ Function by JS based on ⎕SE.WSDoc.GetRegKeyValue
[4] SUBKEY VALUE←NV
[5] ⎕NA'I ADVAPI32.dll.C32|RegSetValueExA U <0T I I <0T I4'
[6] RC←RegSetValueExA HANDLE SUBKEY 0 1 VALUE(1+⍴VALUE)
∇
∇ {RC}←Close HANDLE;RegCloseKey
[1] ⍝ Close an open registry key
[2] ⎕NA'I ADVAPI32.dll.C32|RegCloseKey U'
[3] RC←RegCloseKey HANDLE
∇
Example¶
If you want to change the value of your application’s printer top margin to 36 points, saving the old value, you would key something like the following:
handle←Reg.Open 'Software\APLapps\application\Print'
old←handle Reg.GetInt 'TopMargin' 0
handle Reg.PutInt 'TopMargin' 36
Reg.Close handle
Other Considerations¶
If you try to open a non-existent subkey it (and all non-existent intermediate subkeys) will be created. This is a function of the registry interface. Similarly, you do not need to create a bottom-level container for your data: in the example above, if TopMargin did not already exist, the default value of 0 would be put into old, and the result of the PutInt call would be to create TopMargin and store its value.
Other registry functions are available, for example functions to delete entries. I have not coded those because up to now I have not needed them, being content to use the registry editor to delete unwanted entries, but those who do need them should have no trouble coding them yourself. (You would need deletion functions for an uninstall program, for instance.)
References¶
Any good book on the Windows 95 registry, or the Microsoft Developer Network CD-ROMs.