Microsoft & .NETVisual BasicThe Registry - Now I've got it what can I do?

The Registry – Now I’ve got it what can I do?

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories