So far in this series we have investigated the built
in Registry functions in Visual Basic, as well as investigating most of the API functions that allow you complete
access to all parts of the Registry.
In this article, I will show you how to get all the sub keys under a key (enumeration),
as well as investigating the practical uses of the Registry, such as saving settings and
changing the computer security settings. I recommend that you read the previous article in this series in order to fully
understand how the Registry is structured, as well as how the Windows API functions can be
used.
As ever, I warn you to be careful with changing the registry, as it can have disastrous
effects on the system. Make sure that you back up system.dat and user.dat in the windows
directory before you do too much more.
Public Function GetAllKeys( _ hKey As Long, _ strPath As String) As Variant Dim lRegResult As Long Dim lCounter As Long Dim hCurKey As Long Dim strBuffer As String Dim lDataBufferSize As Long Dim strNames() As String Dim intZeroPos As Integer lCounter = 0 lRegResult = RegOpenKey(hKey, strPath, hCurKey) Do 'initialise buffers (longest possible length=255) lDataBufferSize = 255 strBuffer = String(lDataBufferSize, " ") lRegResult = RegEnumKey(hCurKey, _ lCounter, strBuffer, lDataBufferSize) If lRegResult = ERROR_SUCCESS Then 'tidy up string and save it ReDim Preserve strNames(lCounter) As String intZeroPos = InStr(strBuffer, Chr$(0)) If intZeroPos > 0 Then strNames(UBound(strNames)) = Left$(strBuffer, intZeroPos - 1) Else strNames(UBound(strNames)) = strBuffer End If lCounter = lCounter + 1 Else Exit Do End If Loop GetAllKeys = strNames End Function
This function works using the RegEnumKey function. You pass a number to the function
and it returns the name of the sub key holding that position. These are numbered from 0,
starting with the oldest, so you can get some idea of the age of keys using this API call.
It initialises the buffer, and then makes the call. If the call returns ERROR_SUCCESS,
there was a key at that number, but if it returns something else, there was either a
problem, or all the keys have been retrieved. It adds the retrieved name to an
array, and continues. If there was an error, it exits the loop, otherwise it just keeps
going.
The function returns an array of names in a variant. You would use it as follows:
Dim SubKeys As Variant Dim KeyLoop As Integer SubKeys = GetAllKeys(HKEY_CURRENT_USER, vbNullString) If VarType(SubKeys) = vbArray + vbString Then For KeyLoop = 0 To UBound(SubKeys) Debug.Print SubKeys(KeyLoop) Next End If
Public Function GetAllValues( _ hKey As Long, _ strPath As String) As Variant ' Returns: a 2D array. ' (x,0) is value name ' (x,1) is value type (see constants) Dim lRegResult As Long Dim hCurKey As Long Dim lValueNameSize As Long Dim strValueName As String Dim lCounter As Long Dim byDataBuffer(4000) As Byte Dim lDataBufferSize As Long Dim lValueType As Long Dim strNames() As String Dim lTypes() As Long Dim intZeroPos As Integer lRegResult = RegOpenKey(hKey, _ strPath, hCurKey) Do ' Initialise bufffers lValueNameSize = 255 strValueName = String$(lValueNameSize, " ") lDataBufferSize = 4000 lRegResult = RegEnumValue(hCurKey, lCounter, _ strValueName, lValueNameSize, 0&, lValueType, _ byDataBuffer(0), lDataBufferSize) If lRegResult = ERROR_SUCCESS Then ' Save the type ReDim Preserve strNames(lCounter) As String ReDim Preserve lTypes(lCounter) As Long lTypes(UBound(lTypes)) = lValueType 'Tidy up string and save it intZeroPos = InStr(strValueName, Chr$(0)) If intZeroPos > 0 Then strNames(UBound(strNames)) = _ Left$(strValueName, intZeroPos - 1) Else strNames(UBound(strNames)) = strValueName End If lCounter = lCounter + 1 Else Exit Do End If Loop 'Move data into array Dim Finisheddata() As Variant ReDim Finisheddata(UBound(strNames), 0 To 1) As Variant For lCounter = 0 To UBound(strNames) Finisheddata(lCounter, 0) = strNames(lCounter) Finisheddata(lCounter, 1) = lTypes(lCounter) Next GetAllValues = Finisheddata End Function
This function is slightly more complicated because we must know what type of data the
value refers to. Because of this, this function returns a two dimensional array. The index
for the value is the first dimension, and the second dimension is either 0 or 1. In 0, the
name of the value is stored, and in 1, the type of data is stored. I decided to return the
data type rather than the data because it makes the function easier, and also means that
you can retrieve only the ones that you want, rather than eating up memory by retrieving
then all.
In all other respects, this function is similar to the other. It has a counter, which
counts from 0, retrieving the value names and data. The buffer length is set to 4000
because this is the largest binary type value. However, if you know that your largest
value will be less than this, you can reduce it to lower the memory requirements.
Once all the data has been retrieved from the registry, it is moved into the array,
which is passed from the function. This could not be done as we were going along because
only the last dimension of a dynamic array can be resized. Since I wanted to keep in sync
with the VB function, this seems to be the best way to do it.
Use the function in a similar way to the VB function. Although the line for printing
the first element of the binary key looks slightly wrong, it is correct. Since the
function returns an array, I access it outside the brackets of the function’s parameters.
Dim Values As Variant Dim KeyLoop As Integer Dim RegPath As String Dim HKCU As Long HKCU = HKEY_CURRENT_USER ' to save typing RegPath = "SoftwareMicrosoftWindowsCurrentVersionExplorer" Values = GetAllValues(HKCU, RegPath) If VarType(Values) = vbArray + vbVariant Then For KeyLoop = 0 To UBound(Values) Debug.Print Values(KeyLoop, 0) Select Case Values(KeyLoop, 1) Case REG_DWORD Debug.Print GetSettingLong(HKCU, RegPath, _ CStr(Values(KeyLoop, 0))) Case REG_BINARY Debug.Print GetSettingByte(HKCU, RegPath, _ Hex$(Values(KeyLoop, 0)))(0) Case REG_SZ Debug.Print GetSettingString(HKCU, RegPath, _ CStr(Values(KeyLoop, 0))) End Select Next End If
Public Sub SaveFormPos( _ frmSave As Form) Dim strRegPath As String strRegPath = "Software" & App.CompanyName _ & "" & App.Title & "" & frmSave.Name If frmSave.WindowState = vbMaximized Then SaveSettingLong HKEY_CURRENT_USER, strRegPath, "Maximised", 1 DeleteValue HKEY_CURRENT_USER, strRegPath, "Left" DeleteValue HKEY_CURRENT_USER, strRegPath, "Top" DeleteValue HKEY_CURRENT_USER, strRegPath, "Width" DeleteValue HKEY_CURRENT_USER, strRegPath, "Height" Else With frmSave SaveSettingLong HKEY_CURRENT_USER, strRegPath, "Maximised", 0 SaveSettingLong HKEY_CURRENT_USER, strRegPath, "Left", .Left SaveSettingLong HKEY_CURRENT_USER, strRegPath, "Top", .Top SaveSettingLong HKEY_CURRENT_USER, strRegPath, "Width", .Width SaveSettingLong HKEY_CURRENT_USER, strRegPath, "Height", .Height End With End If End Sub Public Sub LoadFormPos(frmLoad As Form) Dim strRegPath As String Dim IsMax As Long strRegPath = "Software" & App.CompanyName _ & "" & App.Title & "" & frmLoad.Name IsMax = GetSettingLong(HKEY_CURRENT_USER, strRegPath, "Maximised", 2) Select Case IsMax Case 0 With frmLoad .Move GetSettingLong(HKEY_CURRENT_USER, strRegPath, "Left", .Left), _ GetSettingLong(HKEY_CURRENT_USER, strRegPath, "Top", .Top), _ GetSettingLong(HKEY_CURRENT_USER, strRegPath, "Width", .Width), _ GetSettingLong(HKEY_CURRENT_USER, strRegPath, "Height", .Height) End With Case 1 frmLoad.WindowState = vbMaximized Case 2 MsgBox "There is no form data saved for this form" End Select End Sub
This useful pair of functions allow you to save the position of the form in the
registry, and load it at a later time. This is good when you have a small form that the
user can move around, and will expect it in the same place next time they start. How it
works is pretty self-explanatory. It uses the functions that I wrote last week in a
module. The path that the information is saved to is
HKEY_CURRENT_USERCompanyNameAppNameFormName, so don’t forget to set up the company name
in Project Options. It pops up a message box if there is no data saved, and you may
want to catch this error, and set up a default position.
This idea is mainly applicable to Windows 95, although most work in Windows 98. I have
not tried it under NT, but I doubt that it will work. They require a setting being set in
the registry, and then some take immediate effect, while some require a restart of Windows to take full effect.
Most of this can be done with the product poledit.exe, available on the internet, but
it has a very bad UI, and why not do it in your own applications anyway. The properties
that I can set are called policies, and are found in
HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrentVersionPolicies…
The four sub keys are Explorer, WinOldApp, Network and System.
i) Explorer
Disable Run Menu: "NoRun" – Prevents users running programs from the run
command on the Start Menu. If you restart the computer, the menu item will be invisible.
Disable Find Menu: "NoFind" – Prevents users from searching the computer for
files by disabling the find command.
Remove Shutdown Command: "NoClose" – This disables the Shut Down computer
command. This prevents users Restarting windows, Shutting Down windows or exiting to DOS.
Don’t save Settings: "NoSaveSettings" – This stops Windows saving the
settings when you shut down. If this option is off, all the Explorer Windows that Ire open
on shut down will reappear when you have restarted.
Hide all Items on the Desktop: "NoDesktop" – This option hides all the
shortcuts on the desktop. You must restart your computer for this to take effect.
Disable Details and General Pages: "NoPrinterTabs" – This stops Windows
showing the Details and General tabs in the properties of printers. On these tabs, you can
change which ports you print to, which driver you use, and several other sensitive
settings. Using this prevents users from tampering with these settings.
ii) System
Disable Display System Control Panel: "NoDispCPL" – This prevents the user
calling up the Display Control Panel page, preventing them from changing the wallpaper,
screensavers, colours and display settings.
Disable Configuration Page: "NoConfigPage" – This disables the hardware
profiles tab on the System Control Panel, preventing the user from adding or removing
system configurations.
Disable Device Manager Page: "NoDevMgrPage" – This disables the Device
Manager tab on the System Control Panel. This prevents users from adding, removing, or
tampering with devices installed on the system.
Disable File System Page: "NoFileSysPage" – This disables the File System
button on the performance tab on the System Control Panel. This stops users changing
caching and troubleshooting settings. The troubleshooting settings reduce Windows’
performance, if some of the features are not compatible.
Disable Virtual Memory Page: "NoVirtMemPage" – This disables the Virtual
Memory button on the performance tab on the system control panel. This prevents users
tampering with virtual memory settings. Virtual memory is a file on the hard disk, which
is being used to complement the system RAM.
Disable Remote Administration Page: "NoAdminPage" – Disables the Remote
Administration tab on the Password Control Panel.
Disable User Profiles Page: "NoProfilePage" – Disables the User Profiles tab
on the Password Control Panel. On this tab, you set whether all users use the same
settings, or different ones.
Disable Change Passwords Page: "NoPwdPage" – This disables the Change Passwords
tab on the Password Control Panel. This prevents users from changing their passwords.
Disable Password Control Panel: "NoSecCPL" – This disables the Password
Control Panel page, preventing users from altering any of the above settings.
iii) WinOldApp
Disable MS-DOS prompt: "Disabled" – This prevents the computer running any
MS-DOS based programs. The user cannot load the DOS prompt or any other program running in
DOS. If there are any DOS programs running when this option is applied, they will not be
affected.
iv) Network
Disable Network Control Panel: "NoNetSetup" – This prevents users from
changing the settings to do with the network. These include, the network components,
file/print sharing, and identification to other computers.
Disable Identification Page: "NoNetSetupIDPage" – This disables the
Identification tab on the Network Control Panel, where you can change the computer name
and workgroup name.
Disable Access Control Page: "NoNetSetupSecurityPage" – This disables the
Access Control tab on the Network Control Panel page. This is where you can set who has
what access to the shared resources on you computer.
Disable File and Print Sharing Controls: "NoFileSharingControl" – This
removes the File and Print sharing button on the Configuration tab of the Network Control
Panel. This button gives you control over whether users can access you files and printers.
Disable Password Caching: "DisablePwdCaching" – This stops the computer from
storing Dial-Up Networking Passwords in a cache on the computer. Caching speeds up
Windows, but is also a possible security risk.
I recommend that you set the value to 1 (long) if you want to enable it, and delete the
value if you want to disable it. We have already written an application, which encompasses
all of these features, and is available for download at http://www.jelsoft.com.
It is called Stop’Em, and is a quick solution to preventing tampering.
So, to disable MS-DOS prompt, use:
SaveSettingLong HKEY_CURRENT_USER,
"SoftwareMicrosoftWindowsCurrentVersionPoliciesWinOldApp",
"Disabled", 1
And to re-enable:
DeleteValue HKEY_CURRENT_USER,
"SoftwareMicrosoftWindowsCurrentVersionPoliciesWinOldApp",
"Disabled"
Be particularly careful with playing with these settings, because in some cases, you
may be able to lock yourself out of the computer altogether.
Have a look around the Internet for other tips and things that you can do with the
registry. One of the best places that I found is http://www.regedit.com.
This site includes things that you can do with windows (not necessarily programming
based), and is a very good resource.