| My Training Period: xx hours. Before you begin, read some instruction here. This is a continuation from previous Windows Registry Tutorial 2.
The expected abilities:
Enumerating The Registry Subkeys Program Example
The following example demonstrates the use of the RegQueryInfoKey(), RegEnumKeyEx(), and RegEnumValue() functions. The hKey parameter passed to each function is a handle to an open key. This key must be opened before the function call and closed afterward.
// QueryKey() - Enumerates the subkeys of key and its associated values. // hKey - Key whose subkeys and values are to be enumerated.
// #define _WIN32_WINNT 0x0502 // Windows Server 2003 family // For Win Xp, change accordingly... #define _WIN32_WINNT 0x0501 // #define _WIN32_WINNT 0x0500 // Windows 2000 // #define _WIN32_WINNT 0x0400 // Windows NT 4.0 // #define _WIN32_WINDOWS 0x0500 // Windows ME // #define _WIN32_WINDOWS 0x0410 // Windows 98 // #define _WIN32_WINDOWS 0x0400 // Windows 95
#include <windows.h> #include <stdio.h>
#define MAX_KEY_LENGTH 255 #define MAX_VALUE_NAME 16383 |
void QueryKey(HKEY hKey)
{
CHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name
DWORD cbName; // size of name string
CHAR achClass[MAX_PATH] = ""; // buffer for class name
DWORD cchClassName = MAX_PATH; // size of class string
DWORD cSubKeys=0; // number of subkeys
DWORD cbMaxSubKey; // longest subkey size
DWORD cchMaxClass; // longest class string
DWORD cValues; // number of values for key
DWORD cchMaxValue; // longest value name
DWORD cbMaxValueData; // longest value data
DWORD cbSecurityDescriptor; // size of security descriptor
FILETIME ftLastWriteTime; // last write time
DWORD i, retCode;
CHAR achValue[MAX_VALUE_NAME];
DWORD cchValue = MAX_VALUE_NAME;
// Get the class name and the value count.
retCode = RegQueryInfoKey(
hKey, // key handle
achClass, // buffer for class name
&cchClassName, // size of class string
NULL, // reserved
&cSubKeys, // number of subkeys
&cbMaxSubKey, // longest subkey size
&cchMaxClass, // longest class string
&cValues, // number of values for this key
&cchMaxValue, // longest value name
&cbMaxValueData, // longest value data
&cbSecurityDescriptor, // security descriptor
&ftLastWriteTime); // last write time
// Enumerate the subkeys, until RegEnumKeyEx() fails.
if(cSubKeys)
{
printf("Subkey Names:\n");
for(i=0; i<cSubKeys; i++)
{
cbName = MAX_KEY_LENGTH;
retCode = RegEnumKeyEx(
hKey, // Handle to an open/predefined key
i, // Index of the subkey to retrieve.
achKey, // buffer that receives the name of the subkey
&cbName, // size of the buffer specified by the achKey
NULL, // Reserved; must be NULL
NULL, // buffer that receives the class string of the enumerated subkey
NULL, // size of the buffer specified by the previous parameter
&ftLastWriteTime // variable that receives the time at which the enumerated subkey was last written
);
if(retCode == ERROR_SUCCESS)
{ printf("(%d) %s\n", i+1, achKey); }
}
printf("Number of subkeys: %d\n\n", cSubKeys);
}
else
printf("RegEnumKeyEx(), there is no subkey.\n");
// Enumerate the key values if any.
if(cValues)
{
for(i=0, retCode=ERROR_SUCCESS; i<cValues; i++)
{
cchValue = MAX_VALUE_NAME;
achValue[0] = '\0';
retCode = RegEnumValue(
hKey, // Handle to an open key
i, // Index of value
achValue, // Value name
&cchValue, // Buffer for value name
NULL, // Reserved
NULL, // Value type
NULL, // Value data
NULL); // Buffer for value data
if(retCode == ERROR_SUCCESS)
{ printf("(%d) Value Name: %s.\n", i+1, achValue); }
}
printf("Number of values: %d\n", cValues);
}
else
printf("No value under this key/subkey...\n");
}
int main(int argc, char *argv[ ])
{
// Change the key and subkey accordingly...
// Predefined registry keys:
// HKEY_CLASSES_ROOT
// HKEY_CURRENT_CONFIG
// HKEY_CURRENT_USER
// HKEY_LOCAL_MACHINE
// HKEY_PERFORMANCE_DATA
// HKEY_USERS
//*************** open key (and subkey) ******************
HKEY theKey = HKEY_LOCAL_MACHINE; // Key, change accordingly...
HKEY hKey; // Key and subkey
LONG lRet = RegOpenKeyEx(
theKey, // Key
"SYSTEM\\ControlSet001\\Control", // Subkey, change accordingly
0, // Reserved
KEY_ALL_ACCESS, // desired access rights to the key
&hKey); // variable that receives a handle to the opened key
if(lRet == ERROR_SUCCESS)
printf("RegOpenKeyEx() is OK.\n");
else
printf("RegOpenKeyEx() is not OK.\n");
QueryKey(hKey);
RegCloseKey(hKey);
return 0;
}
A sample output:
RegOpenKeyEx() is OK.
Subkey Names:
(1) AGP
(2) Arbiters
(3) BackupRestore
(4) Biosinfo
(5) BootVerificationProgram
(6) Class
(7) CoDeviceInstallers
...
[trimmed]
...
(29) NetworkProvider
(30) Nls
(31) NTMS
(32) PnP
...
[trimmed]
...
(53) Watchdog
(54) Windows
(55) WMI
(56) WOW
(57) hivelist
(58) ServiceCurrent
Number of subkeys: 58
(1) Value Name: CurrentUser.
(2) Value Name: WaitToKillServiceTimeout.
(3) Value Name: SystemStartOptions.
(4) Value Name: SystemBootDevice.
Number of values: 4
Press any key to continue
Figure 1: Enumerate Registry keys and values.
Determining the Registry Size Program Example
On Windows 2000 and earlier, it is common for an installation utility to check the current and maximum size of the registry to determine whether there is enough available space for the new data it will be adding. This sample demonstrates how to do this programmatically using the "% Registry Quota In Use" performance counter within the System object. The following sample uses Performance Data Helper (PDH) to obtain the counter value; it must be linked with pdh.lib. PDH is a high-level set of APIs used to obtain performance data. Note that it is not necessary to implement this registry size-check on Windows XP or Windows Me/98/95, because they do not have a registry quota limit. Don’t forget to include the pdh.lib into your project as shown below.
Figure 2: Adding pdh.lib library to the Visual C++ .Net project. |
// ************ regsize.cpp ******************
// Determines the current and maximum registry size
// For Win XP, run on Win Xp...Change accordingly
// but it is useless here, Win Xp don’t have quota limit :o)
// Just for fun...
// #define _WIN32_WINNT 0x0502 // Windows Server 2003 family
// For Win Xp, change accordingly...
#define _WIN32_WINNT 0x0501
// #define _WIN32_WINNT 0x0500 // Windows 2000
// #define _WIN32_WINNT 0x0400 // Windows NT 4.0
// #define _WIN32_WINDOWS 0x0500 // Windows ME
// #define _WIN32_WINDOWS 0x0410 // Windows 98
// #define _WIN32_WINDOWS 0x0400 // Windows 95
// This is wide character/UNICODE based program
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <stdio.h>
#include <pdh.h>
// And don't forget to include the pdh.lib into
// this project...
// Function prototype
PDH_STATUS GetRegistrySize(LPTSTR szMachineName, LPDWORD lpdwCurrentSize, LPDWORD lpdwMaximumSize);
// Entry point for the program. This function demonstrates how to
// use the GetRegistrySize() function implemented below.
// It will use the first argument, if present, as the name of the
// computer whose registry you wish to determine. If unspecified,
// it will use the local computer.
int wmain(int argc, TCHAR *argv[])
{
LPTSTR szMachineName = NULL;
PDH_STATUS pdhStatus = 0;
DWORD dwCurrent = 0;
DWORD dwMaximum = 0;
// Allow a computer name to be specified on the command line.
if(argc > 1)
szMachineName = argv[1];
wprintf(TEXT("Usage: %s <computer_name> else default to local computer.\n\n"), argv[0]);
// Get the registry size.
pdhStatus = GetRegistrySize(szMachineName, &dwCurrent, &dwMaximum);
// Print the results.
if(pdhStatus == ERROR_SUCCESS)
{
printf("GetRegistrySize() is OK.\n");
wprintf(TEXT("\nCurrent registry size: %ld bytes\n"), dwCurrent);
wprintf(TEXT("Maximum registry size: %ld bytes\n"), dwMaximum);
}
else
{
// If the operation failed, print the PDH error message.
LPTSTR szMessage = NULL;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE,
GetModuleHandle(TEXT("PDH.DLL")), pdhStatus, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), szMessage, 0, NULL);
wprintf(TEXT("GetRegistrySize() failed: %s"), szMessage);
LocalFree(szMessage);
}
return 0;
}
// Retrieves the current and maximum registry size. It gets this
// information from the raw counter values for the "% Registry Quota
// In Use" performance counter within the System object.
// PARAMETERS:
// szMachineName - Null-terminated string that specifies the
// name of the computer whose registry you wish to query.
// If this parameter is NULL, the local computer is used.
// lpdwCurrentSize - Receives the current registry size.
// lpdwMaximumSize - Receives the maximum registry size.
// RETURN VALUE:
// ERROR_SUCCESS if successful. Otherwise, the function
// returns a PDH error code. These error codes can be
// found in PDHMSG.H. A textual error message can be
// retrieved from PDH.DLL using the FormatMessage function.
//******************************************************************
PDH_STATUS GetRegistrySize(LPTSTR szMachineName, LPDWORD lpdwCurrentSize, LPDWORD lpdwMaximumSize)
{
PDH_STATUS pdhResult = 0;
TCHAR szCounterPath[1024];
DWORD dwPathSize = 1024;
PDH_COUNTER_PATH_ELEMENTS pe;
PDH_RAW_COUNTER pdhRawValues;
HQUERY hQuery = NULL;
HCOUNTER hCounter = NULL;
DWORD dwType = 0;
// Open PDH query
pdhResult = PdhOpenQuery(NULL, 0, &hQuery);
if(pdhResult != ERROR_SUCCESS)
// Just return the error...
return pdhResult;
else
printf("PdhOpenQuery() is OK.\n");
// Create counter path
pe.szMachineName = szMachineName;
pe.szObjectName = TEXT("System");
pe.szInstanceName = NULL;
pe.szParentInstance = NULL;
pe.dwInstanceIndex = 1;
pe.szCounterName = TEXT("% Registry Quota In Use");
pdhResult = PdhMakeCounterPath(&pe, szCounterPath, &dwPathSize, 0);
if(pdhResult != ERROR_SUCCESS)
{
printf("PdhMakeCounterPath() is not OK.\n");
exit(1);
}
else
printf("PdhMakeCounterPath() is OK.\n");
// Add the counter to the query
pdhResult = PdhAddCounter(hQuery, szCounterPath, 0, &hCounter);
if(pdhResult != ERROR_SUCCESS)
// Here we just exit, better to retrieve and return the error code...
// same for the following...
exit(1);
else
printf("PdhAddCounter() is OK.\n");
// Run the query to collect the performance data
pdhResult = PdhCollectQueryData(hQuery);
if(pdhResult != ERROR_SUCCESS)
exit(1);
else
printf("PdhCollectQueryData() is OK.\n");
// Retrieve the raw counter data:
// The dividend (FirstValue) is the current registry size
// The divisor (SecondValue) is the maximum registry size
ZeroMemory(&pdhRawValues, sizeof(pdhRawValues));
pdhResult = PdhGetRawCounterValue(hCounter, &dwType, &pdhRawValues);
if(pdhResult != ERROR_SUCCESS)
exit (1);
else
printf("PdhGetRawCounterValue() is OK\n");
// Store the sizes in variables.
if(lpdwCurrentSize)
*lpdwCurrentSize = (DWORD) pdhRawValues.FirstValue;
if(lpdwMaximumSize)
*lpdwMaximumSize = (DWORD) pdhRawValues.SecondValue;
// Close the query
PdhCloseQuery(hQuery);
return 0;
}
A sample output:
F:\myproject\win32prog\Debug>regsize
Usage: regsize <computer_name> else default to local computer.
PdhOpenQuery() is OK.
PdhMakeCounterPath() is OK.
PdhAddCounter() is OK.
PdhCollectQueryData() is OK.
PdhGetRawCounterValue() is OK
GetRegistrySize() is OK.
Current registry size: 3886596 bytes
Maximum registry size: 58720256 bytes
F:\myproject\win32prog\Debug>regsize mypersonal
Usage: regsize <computer_name> else default to local computer.
PdhOpenQuery() is OK.
PdhMakeCounterPath() is OK.
PdhAddCounter() is OK.
PdhCollectQueryData() is OK.
PdhGetRawCounterValue() is OK
GetRegistrySize() is OK.
Current registry size: 3886596 bytes
Maximum registry size: 58720256 bytes
F:\myproject\win32prog\Debug>
The following example uses the GetVersionEx() function to display the version of the currently running operating system. Relying on version information is not the best way to test for a feature. Instead, refer to the documentation for the feature of interest. If you must require a particular operating system, be sure to use it as a minimum supported version, rather than design the test for the one operating system. This way, your detection code will continue to work on future versions of Windows.
// #define _WIN32_WINNT 0x0502 // Windows Server 2003 family
// For Win Xp, change accordingly...
#define _WIN32_WINNT 0x0501
// #define _WIN32_WINNT 0x0500 // Windows 2000
// #define _WIN32_WINNT 0x0400 // Windows NT 4.0
// #define _WIN32_WINDOWS 0x0500 // Windows ME
// #define _WIN32_WINDOWS 0x0410 // Windows 98
// #define _WIN32_WINDOWS 0x0400 // Windows 95
#include <windows.h>
#include <stdio.h>
#define BUFSIZE 80
int main(int argc, char *argv[])
{
OSVERSIONINFOEX osver;
BOOL bOsVersionInfoEx;
// Try calling GetVersionEx() using the OSVERSIONINFOEX structure.
// If that fails, try using the OSVERSIONINFO structure.
ZeroMemory(&osver, sizeof(OSVERSIONINFOEX));
osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if(!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osver)))
{
osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if(!GetVersionEx((OSVERSIONINFO *) &osver))
return FALSE;
else
printf("Buffer for the structure size is OK.\n");
}
else
printf("GetVersionEx() is OK.\n");
switch (osver.dwPlatformId)
{
//Test for the Windows NT product family.
case VER_PLATFORM_WIN32_NT:
// Test for the specific product family.
if(osver.dwMajorVersion == 5 && osver.dwMinorVersion == 2)
printf("Microsoft Windows Server 2003 family, ");
if(osver.dwMajorVersion == 5 && osver.dwMinorVersion == 1)
printf("Microsoft Windows XP ");
if(osver.dwMajorVersion == 5 && osver.dwMinorVersion == 0)
printf("Microsoft Windows 2000 ");
if(osver.dwMajorVersion <= 4)
printf("Microsoft Windows NT ");
// Test for specific product on Windows NT 4.0 SP6 and later.
if(bOsVersionInfoEx)
{
// Test for the workstation type.
if(osver.wProductType == VER_NT_WORKSTATION)
{
if(osver.dwMajorVersion == 4)
printf("Workstation 4.0 ");
else if(osver.wSuiteMask & VER_SUITE_PERSONAL)
printf("Home Edition ");
else
printf("Professional ");
}
// Test for the server type.
else if(osver.wProductType == VER_NT_SERVER || osver.wProductType == VER_NT_DOMAIN_CONTROLLER)
{
if((osver.dwMajorVersion == 5) && (osver.dwMinorVersion == 2))
{
if(osver.wSuiteMask & VER_SUITE_DATACENTER)
printf("Datacenter Edition ");
else if(osver.wSuiteMask & VER_SUITE_ENTERPRISE)
printf("Enterprise Edition ");
else if(osver.wSuiteMask == VER_SUITE_BLADE)
printf("Web Edition ");
else
printf("Standard Edition ");
}
else if((osver.dwMajorVersion == 5) && (osver.dwMinorVersion == 0))
{
if(osver.wSuiteMask & VER_SUITE_DATACENTER)
printf("Datacenter Server ");
else if(osver.wSuiteMask & VER_SUITE_ENTERPRISE)
printf("Advanced Server ");
else
printf("Server ");
}
// Windows NT 4.0
else
{
if(osver.wSuiteMask & VER_SUITE_ENTERPRISE)
printf("Server 4.0, Enterprise Edition ");
else
printf("Server 4.0 ");
}
}
}
// Test for specific product on Windows NT 4.0 SP5 and earlier
else
{
HKEY hKey;
char szProductType[BUFSIZE];
DWORD dwBufLen=BUFSIZE;
LONG lRet;
lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\ProductOptions", 0, KEY_QUERY_VALUE, &hKey);
if(lRet != ERROR_SUCCESS)
return FALSE;
else
printf("RegOpenKeyEx() is OK.\n");
lRet = RegQueryValueEx(hKey, "ProductType", NULL, NULL, (LPBYTE)szProductType, &dwBufLen);
if((lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE))
return FALSE;
else
printf("RegQueryValueEx() is OK.\n");
RegCloseKey(hKey);
if(lstrcmpi("WINNT", szProductType) == 0)
printf("Workstation ");
if(lstrcmpi("LANMANNT", szProductType) == 0)
printf("Server ");
if(lstrcmpi("SERVERNT", szProductType) == 0)
printf("Advanced Server ");
printf("%d.%d ", osver.dwMajorVersion, osver.dwMinorVersion);
}
// Display service pack (if any) and build number.
if(osver.dwMajorVersion == 4 && lstrcmpi(osver.szCSDVersion, "Service Pack 6") == 0)
{
HKEY hKey;
LONG lRet;
// Test for SP6 versus SP6a.
lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows
NT\\CurrentVersion\\Hotfix\\Q246009", 0, KEY_QUERY_VALUE, &hKey);
if(lRet == ERROR_SUCCESS)
{
printf("RegOpenKeyEx() is OK.\n");
printf("Service Pack 6a (Build %d)\n", osver.dwBuildNumber & 0xFFFF);
}
// Windows NT 4.0 prior to SP6a
else
{
printf("%s (Build %d)\n", osver.szCSDVersion, osver.dwBuildNumber & 0xFFFF);
}
RegCloseKey(hKey);
}
// Windows NT 3.51 and earlier or Windows 2000 and later
else
{
printf("%s (Build %d)\n", osver.szCSDVersion, osver.dwBuildNumber & 0xFFFF);
}
break;
// Test for the Windows 95 product family.
case VER_PLATFORM_WIN32_WINDOWS:
if(osver.dwMajorVersion == 4 && osver.dwMinorVersion == 0)
{
printf("Microsoft Windows 95 ");
if(osver.szCSDVersion[1] == 'C' || osver.szCSDVersion[1] == 'B')
printf("OSR2 ");
}
if(osver.dwMajorVersion == 4 && osver.dwMinorVersion == 10)
{
printf("Microsoft Windows 98 ");
if(osver.szCSDVersion[1] == 'A')
printf("SE ");
}
if((osver.dwMajorVersion == 4) && (osver.dwMinorVersion == 90))
{
printf("Microsoft Windows Millennium Edition\n");
}
break;
case VER_PLATFORM_WIN32s:
printf("Microsoft Win32s\n");
break;
}
return TRUE;
}
A sample output:
GetVersionEx() is OK.
Microsoft Windows XP Professional Service Pack 2 (Build 2600)
Press any key to continue
Further reading and digging:
Microsoft C references, online MSDN.
Microsoft Visual C++, online MSDN.
ReactOS - Windows binary compatible OS - C/C++ source code repository, Doxygen.
Linux Access Control Lists (ACL) info can be found atAccess Control Lists.