Loading learning content...
The Windows Registry is a hierarchical database that stores configuration settings for the operating system, applications, hardware, and users. Unlike Unix-like systems that use text configuration files, Windows centralizes most configuration data in this structured database.
The Registry is one of the most distinctive features of Windows architecture. It affects:
Understanding the Registry is essential for Windows system administration, application development, and troubleshooting. This page provides comprehensive coverage of Registry architecture, the Registry API, and best practices for Registry operations.
By the end of this page, you will understand the Registry's hierarchical structure, the relationship between Registry hives and files, how to use the Registry API effectively, proper error handling and security considerations, and common patterns for storing application configuration.
The Registry presents a unified hierarchical namespace, but its implementation involves multiple files, in-memory caches, and a sophisticated access control system.
Hierarchical Structure:
The Registry is organized like a file system:
HKEY_LOCAL_MACHINE
├── SOFTWARE
│ ├── Microsoft
│ │ ├── Windows
│ │ │ ├── CurrentVersion
│ │ │ │ └── (values: ProgramFilesDir, CommonFilesDir, ...)
│ │ │ └── ...
│ │ └── ...
│ └── (other publishers)
├── SYSTEM
│ ├── CurrentControlSet
│ └── ...
└── HARDWARE
└── ...
Root Keys (Hives):
The Registry has several predefined root keys, each serving a specific purpose:
| Root Key | Abbreviation | Purpose |
|---|---|---|
| HKEY_LOCAL_MACHINE | HKLM | System-wide hardware and software settings |
| HKEY_CURRENT_USER | HKCU | Settings for the currently logged-in user |
| HKEY_USERS | HKU | All loaded user profiles |
| HKEY_CLASSES_ROOT | HKCR | Merged view of HKLM\SOFTWARE\Classes and HKCU\SOFTWARE\Classes |
| HKEY_CURRENT_CONFIG | HKCC | Current hardware profile (link to HKLM\SYSTEM\CurrentControlSet\Hardware Profiles\Current) |
HKEY_CLASSES_ROOT and HKEY_CURRENT_CONFIG are not real hives—they're virtual views that merge or redirect to other locations. HKCR merges HKLM\SOFTWARE\Classes (system-wide) with HKCU\SOFTWARE\Classes (user-specific, which takes precedence). For new applications, write directly to HKLM or HKCU\SOFTWARE\Classes, not HKCR.
Registry Hive Files:
Most Registry data is stored in files called hives. Key hive files include:
| Hive | File Location | Description |
|---|---|---|
| HKLM\SYSTEM | %SystemRoot%\System32\config\SYSTEM | Boot and driver configuration |
| HKLM\SOFTWARE | %SystemRoot%\System32\config\SOFTWARE | Application settings |
| HKLM\SAM | %SystemRoot%\System32\config\SAM | Security Account Manager (user accounts) |
| HKLM\SECURITY | %SystemRoot%\System32\config\SECURITY | Security policy database |
| HKCU (per-user) | %UserProfile%\NTUSER.DAT | User preferences and settings |
| HKCU\SOFTWARE\Classes | %LocalAppData%\Microsoft\Windows\UsrClass.dat | User-specific file associations |
1234567891011121314151617181920212223242526272829303132333435363738394041
// Registry key internal structure (simplified)// Keys are implemented as kernel objects of type "Key" /* * Each registry key is represented by a KEY_NODE structure * stored in the hive file: * * KEY_NODE: * Signature // 'kn' (0x6B6E) * Flags // KEY_HIVE_ENTRY, KEY_NO_DELETE, etc. * Timestamp // Last write time * ParentKeyOffset // Offset to parent key * NumberOfSubKeys // Count of child keys * NumberOfValues // Count of values * SubKeysListOffset // Offset to subkey list * ValuesListOffset // Offset to value list * SecurityDescriptor // ACL for this key * ClassName // Optional class name * KeyName // Name of this key */ // Registry value types#define REG_NONE 0 // No defined type#define REG_SZ 1 // Null-terminated Unicode string#define REG_EXPAND_SZ 2 // String with %environment% expansion#define REG_BINARY 3 // Binary data#define REG_DWORD 4 // 32-bit number#define REG_DWORD_LITTLE_ENDIAN 4 // Same as REG_DWORD#define REG_DWORD_BIG_ENDIAN 5 // 32-bit big-endian (rare)#define REG_LINK 6 // Symbolic link to another key#define REG_MULTI_SZ 7 // Array of null-terminated strings#define REG_RESOURCE_LIST 8 // Device driver resource list#define REG_FULL_RESOURCE_DESCRIPTOR 9 // Hardware resource descriptor#define REG_RESOURCE_REQUIREMENTS_LIST 10 // Hardware resource requirements#define REG_QWORD 11 // 64-bit number#define REG_QWORD_LITTLE_ENDIAN 11 // Same as REG_QWORD // Maximum sizes#define MAX_KEY_NAME_LENGTH 255 // Unicode characters#define MAX_VALUE_NAME_LENGTH 16383 // Unicode characters#define MAX_VALUE_DATA_SIZE 1048576 // 1 MB for most typesThe Windows Registry API is part of the Advanced Windows 32 API (advapi32.dll). Registry operations follow a pattern similar to file operations: open, read/write, close.
Opening and Creating Keys:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
#include <windows.h>#include <stdio.h> void OpenKeyExample() { HKEY hKey; LSTATUS status; // Open an existing key status = RegOpenKeyExW( HKEY_LOCAL_MACHINE, // Predefined root key L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", // Subkey path 0, // Reserved (must be 0) KEY_READ, // Desired access rights &hKey // Receives opened key handle ); if (status == ERROR_SUCCESS) { // Use the key... printf("Key opened successfully\n"); // MUST close the key when done RegCloseKey(hKey); } else if (status == ERROR_FILE_NOT_FOUND) { printf("Key does not exist\n"); } else if (status == ERROR_ACCESS_DENIED) { printf("Access denied - may need elevation\n"); } else { printf("Error: %ld\n", status); }} void CreateKeyExample() { HKEY hKey; DWORD disposition; LSTATUS status; // Create or open a key status = RegCreateKeyExW( HKEY_CURRENT_USER, // Usually safer than HKLM L"SOFTWARE\\MyCompany\\MyApp", // Key path to create 0, // Reserved NULL, // Optional class name REG_OPTION_NON_VOLATILE, // Key persists across reboots KEY_ALL_ACCESS, // Full access NULL, // Security attributes (inherit from parent) &hKey, // Receives key handle &disposition // Was it created or opened? ); if (status == ERROR_SUCCESS) { if (disposition == REG_CREATED_NEW_KEY) { printf("Created new key\n"); } else if (disposition == REG_OPENED_EXISTING_KEY) { printf("Opened existing key\n"); } RegCloseKey(hKey); }} // Key creation options/* * REG_OPTION_NON_VOLATILE (0x00000000) * - Default. Key is stored in the hive file and persists. * * REG_OPTION_VOLATILE (0x00000001) * - Key exists only in memory, lost on reboot. * - Useful for temporary runtime settings. * * REG_OPTION_CREATE_LINK (0x00000002) * - Create a symbolic link (rare, requires special privilege) * * REG_OPTION_BACKUP_RESTORE (0x00000004) * - Open with backup/restore privileges */Access Rights for Registry Keys:
| Right | Value | Description |
|---|---|---|
| KEY_QUERY_VALUE | 0x0001 | Query subkey data |
| KEY_SET_VALUE | 0x0002 | Set subkey data |
| KEY_CREATE_SUB_KEY | 0x0004 | Create subkeys |
| KEY_ENUMERATE_SUB_KEYS | 0x0008 | Enumerate subkeys |
| KEY_NOTIFY | 0x0010 | Request change notifications |
| KEY_CREATE_LINK | 0x0020 | Create symbolic link |
| KEY_READ | Combination | QUERY_VALUE + ENUMERATE + NOTIFY + READ_CONTROL |
| KEY_WRITE | Combination | SET_VALUE + CREATE_SUB_KEY |
| KEY_ALL_ACCESS | Combination | All key rights + standard rights |
On 64-bit Windows, there are separate registry views for 32-bit and 64-bit applications. The WOW64 redirector automatically redirects 32-bit apps accessing HKLM\SOFTWARE to HKLM\SOFTWARE\WOW6432Node. Use KEY_WOW64_64KEY or KEY_WOW64_32KEY flags to explicitly access a specific view when needed.
Registry values store the actual configuration data. Each value has a name, a type, and data. The API provides functions for reading and writing values of various types:
Reading Values:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
#include <windows.h>#include <stdio.h> // Read a string valueBOOL ReadStringValue(HKEY hKey, LPCWSTR valueName, LPWSTR buffer, DWORD bufferSize) { DWORD type; DWORD size = bufferSize; LSTATUS status = RegQueryValueExW( hKey, // Open key handle valueName, // Value name (NULL for default value) NULL, // Reserved &type, // Receives value type (LPBYTE)buffer, // Receives data &size // In: buffer size, Out: actual size ); if (status == ERROR_SUCCESS) { if (type == REG_SZ || type == REG_EXPAND_SZ) { return TRUE; } // Wrong type return FALSE; } else if (status == ERROR_MORE_DATA) { wprintf(L"Buffer too small. Need %lu bytes\n", size); return FALSE; } return FALSE;} // Read a DWORD valueBOOL ReadDwordValue(HKEY hKey, LPCWSTR valueName, PDWORD pValue) { DWORD type; DWORD size = sizeof(DWORD); LSTATUS status = RegQueryValueExW( hKey, valueName, NULL, &type, (LPBYTE)pValue, &size ); if (status == ERROR_SUCCESS && type == REG_DWORD) { return TRUE; } return FALSE;} // Modern simplified API (Windows Vista+)void ModernReadExample() { DWORD value; LSTATUS status; // RegGetValue combines open + query in one call status = RegGetValueW( HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", L"CurrentBuild", RRF_RT_REG_SZ, // Expected type: string NULL, // Actual type (optional) &value, // Buffer NULL // Buffer size (optional for known types) ); // RRF_RT flags for type restriction: // RRF_RT_REG_SZ - String // RRF_RT_REG_DWORD - 32-bit integer // RRF_RT_REG_QWORD - 64-bit integer // RRF_RT_REG_BINARY - Binary data // RRF_RT_ANY - Any type // RRF behavior flags: // RRF_NOEXPAND - Don't expand REG_EXPAND_SZ // RRF_ZEROONFAILURE - Zero output buffer on failure // RRF_SUBKEY_WOW6464KEY - Access 64-bit view // RRF_SUBKEY_WOW6432KEY - Access 32-bit view} // Query value size before reading (two-pass method)LPWSTR ReadDynamicString(HKEY hKey, LPCWSTR valueName) { DWORD type; DWORD size = 0; LPWSTR buffer = NULL; // First call: get required size LSTATUS status = RegQueryValueExW( hKey, valueName, NULL, &type, NULL, &size); if (status != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ)) { return NULL; } // Allocate buffer buffer = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, size); if (!buffer) return NULL; // Second call: get actual data status = RegQueryValueExW(hKey, valueName, NULL, &type, (LPBYTE)buffer, &size); if (status != ERROR_SUCCESS) { HeapFree(GetProcessHeap(), 0, buffer); return NULL; } // Expand environment variables if REG_EXPAND_SZ if (type == REG_EXPAND_SZ) { DWORD expandedSize = ExpandEnvironmentStringsW(buffer, NULL, 0); LPWSTR expanded = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, expandedSize * sizeof(WCHAR)); if (expanded) { ExpandEnvironmentStringsW(buffer, expanded, expandedSize); HeapFree(GetProcessHeap(), 0, buffer); return expanded; } } return buffer;}Writing Values:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
#include <windows.h>#include <strsafe.h> // Write a string valueBOOL WriteStringValue(HKEY hKey, LPCWSTR valueName, LPCWSTR data) { // Size includes null terminator, in bytes DWORD size = (DWORD)((wcslen(data) + 1) * sizeof(WCHAR)); LSTATUS status = RegSetValueExW( hKey, valueName, // Value name 0, // Reserved REG_SZ, // Value type (const BYTE*)data, // Pointer to data size // Size in bytes ); return (status == ERROR_SUCCESS);} // Write a DWORD valueBOOL WriteDwordValue(HKEY hKey, LPCWSTR valueName, DWORD value) { LSTATUS status = RegSetValueExW( hKey, valueName, 0, REG_DWORD, (const BYTE*)&value, sizeof(DWORD) ); return (status == ERROR_SUCCESS);} // Write a QWORD (64-bit) valueBOOL WriteQwordValue(HKEY hKey, LPCWSTR valueName, ULONGLONG value) { LSTATUS status = RegSetValueExW( hKey, valueName, 0, REG_QWORD, (const BYTE*)&value, sizeof(ULONGLONG) ); return (status == ERROR_SUCCESS);} // Write a multi-string value (array of strings)BOOL WriteMultiStringValue(HKEY hKey, LPCWSTR valueName, LPCWSTR* strings, DWORD count) { // Calculate total size DWORD totalSize = 0; for (DWORD i = 0; i < count; i++) { totalSize += (DWORD)(wcslen(strings[i]) + 1); } totalSize += 1; // Extra terminator totalSize *= sizeof(WCHAR); // Build multi-string buffer LPWSTR buffer = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, totalSize); if (!buffer) return FALSE; LPWSTR current = buffer; for (DWORD i = 0; i < count; i++) { StringCchCopyW(current, wcslen(strings[i]) + 1, strings[i]); current += wcslen(strings[i]) + 1; } *current = L'\0'; // Final terminator LSTATUS status = RegSetValueExW( hKey, valueName, 0, REG_MULTI_SZ, (const BYTE*)buffer, totalSize ); HeapFree(GetProcessHeap(), 0, buffer); return (status == ERROR_SUCCESS);} // Modern API for setting key values (Windows Vista+)void ModernWriteExample() { LSTATUS status; // RegSetKeyValue combines open + set in one call status = RegSetKeyValueW( HKEY_CURRENT_USER, L"SOFTWARE\\MyCompany\\MyApp", // Creates key if needed L"Setting", // Value name REG_DWORD, // Value type &(DWORD){42}, // Pointer to data sizeof(DWORD) // Data size );}For strings, the size parameter to RegSetValueEx is in BYTES, not characters, and should include the null terminator. This is a common source of bugs. For Unicode strings: size = (wcslen(str) + 1) * sizeof(WCHAR).
To discover what subkeys or values exist under a key, use enumeration functions:
Enumerating Subkeys:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
#include <windows.h>#include <stdio.h> void EnumerateSubkeys(HKEY hKey) { WCHAR keyName[256]; DWORD keyNameSize; DWORD index = 0; FILETIME lastWriteTime; LSTATUS status; wprintf(L"Subkeys:\n"); while (TRUE) { keyNameSize = 256; // Reset size for each call status = RegEnumKeyExW( hKey, // Open key to enumerate index, // Index of subkey (0, 1, 2, ...) keyName, // Buffer for subkey name &keyNameSize, // In: buffer size, Out: name length NULL, // Reserved NULL, // Class buffer (optional) NULL, // Class buffer size &lastWriteTime // Last write timestamp ); if (status == ERROR_SUCCESS) { wprintf(L" [%lu] %s\n", index, keyName); index++; } else if (status == ERROR_NO_MORE_ITEMS) { // End of enumeration break; } else { wprintf(L"Error: %ld\n", status); break; } } wprintf(L"Total: %lu subkeys\n", index);} void EnumerateValues(HKEY hKey) { WCHAR valueName[16384]; // MAX_VALUE_NAME_LENGTH DWORD valueNameSize; DWORD type; BYTE data[1024]; DWORD dataSize; DWORD index = 0; LSTATUS status; wprintf(L"Values:\n"); while (TRUE) { valueNameSize = ARRAYSIZE(valueName); dataSize = sizeof(data); status = RegEnumValueW( hKey, // Open key index, // Value index valueName, // Buffer for value name &valueNameSize, // Name buffer size NULL, // Reserved &type, // Receives value type data, // Receives value data &dataSize // Data buffer size ); if (status == ERROR_SUCCESS) { wprintf(L" [%lu] %s (type=%lu, size=%lu)\n", index, valueName, type, dataSize); // Display value based on type switch (type) { case REG_SZ: case REG_EXPAND_SZ: wprintf(L" = \"%s\"\n", (LPWSTR)data); break; case REG_DWORD: wprintf(L" = %lu (0x%08lX)\n", *(DWORD*)data, *(DWORD*)data); break; case REG_QWORD: wprintf(L" = %llu\n", *(ULONGLONG*)data); break; case REG_BINARY: wprintf(L" = [binary, %lu bytes]\n", dataSize); break; } index++; } else if (status == ERROR_NO_MORE_ITEMS) { break; } else if (status == ERROR_MORE_DATA) { // Data buffer too small - skip this value wprintf(L" [%lu] %s (data too large)\n", index, valueName); index++; } else { wprintf(L"Error: %ld\n", status); break; } } wprintf(L"Total: %lu values\n", index);} // Get key metadatavoid QueryKeyInfo(HKEY hKey) { DWORD subKeyCount; DWORD maxSubKeyLen; DWORD valueCount; DWORD maxValueNameLen; DWORD maxValueDataLen; FILETIME lastWriteTime; LSTATUS status = RegQueryInfoKeyW( hKey, NULL, NULL, // Class (optional) NULL, // Reserved &subKeyCount, // Number of subkeys &maxSubKeyLen, // Longest subkey name NULL, // Longest class string &valueCount, // Number of values &maxValueNameLen, // Longest value name &maxValueDataLen, // Largest value data NULL, // Security descriptor size &lastWriteTime // Last write time ); if (status == ERROR_SUCCESS) { wprintf(L"Key info:\n"); wprintf(L" Subkeys: %lu (max name: %lu chars)\n", subKeyCount, maxSubKeyLen); wprintf(L" Values: %lu (max name: %lu chars, max data: %lu bytes)\n", valueCount, maxValueNameLen, maxValueDataLen); }}Subkeys and values are enumerated in no guaranteed order. Don't assume alphabetical or insertion order. If order matters for your application, retrieve all items first and then sort them yourself.
Deleting Registry items requires care—especially for keys, which have specific rules about subkeys.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
#include <windows.h>#include <shlwapi.h> // For SHDeleteKeyW#pragma comment(lib, "shlwapi.lib") // Delete a single valueBOOL DeleteValueExample(HKEY hKey, LPCWSTR valueName) { LSTATUS status = RegDeleteValueW(hKey, valueName); if (status == ERROR_SUCCESS) { wprintf(L"Value deleted\n"); return TRUE; } else if (status == ERROR_FILE_NOT_FOUND) { wprintf(L"Value not found\n"); return FALSE; } else { wprintf(L"Error: %ld\n", status); return FALSE; }} // Delete an empty key (no subkeys!)BOOL DeleteEmptyKey(HKEY hParent, LPCWSTR subKey) { // RegDeleteKey only works if the key has NO subkeys LSTATUS status = RegDeleteKeyW(hParent, subKey); if (status == ERROR_SUCCESS) { return TRUE; } else if (status == ERROR_ACCESS_DENIED) { // Key might have subkeys, or genuinely access denied wprintf(L"Cannot delete - may have subkeys\n"); return FALSE; } return FALSE;} // Delete a key and all its subkeys recursivelyBOOL DeleteKeyRecursive(HKEY hParent, LPCWSTR subKey) { // Option 1: Use Shell API (easiest) LSTATUS status = SHDeleteKeyW(hParent, subKey); return (status == ERROR_SUCCESS);} // Manual recursive delete (for cases where Shell API isn't available)LSTATUS DeleteKeyTree(HKEY hParent, LPCWSTR keyName) { HKEY hKey; LSTATUS status; WCHAR subKeyName[256]; DWORD subKeyNameSize; // Open the key status = RegOpenKeyExW(hParent, keyName, 0, KEY_READ | KEY_WRITE | DELETE, &hKey); if (status != ERROR_SUCCESS) return status; // Delete all subkeys first (recursively) while (TRUE) { subKeyNameSize = 256; status = RegEnumKeyExW(hKey, 0, subKeyName, &subKeyNameSize, NULL, NULL, NULL, NULL); if (status == ERROR_NO_MORE_ITEMS) { break; } else if (status != ERROR_SUCCESS) { RegCloseKey(hKey); return status; } // Recursive call for each subkey status = DeleteKeyTree(hKey, subKeyName); if (status != ERROR_SUCCESS) { RegCloseKey(hKey); return status; } } RegCloseKey(hKey); // Now delete the (now empty) key itself return RegDeleteKeyW(hParent, keyName);} // Windows Vista+: Explicit 32/64-bit key deletionvoid DeleteKeyWithView() { // Delete specifically from 32-bit or 64-bit view LSTATUS status; // Delete from 64-bit view status = RegDeleteKeyExW( HKEY_LOCAL_MACHINE, L"SOFTWARE\\MyCompany\\MyApp", KEY_WOW64_64KEY, // 64-bit view 0 // Reserved ); // Delete from 32-bit view (WOW6432Node) status = RegDeleteKeyExW( HKEY_LOCAL_MACHINE, L"SOFTWARE\\MyCompany\\MyApp", KEY_WOW64_32KEY, // 32-bit view 0 ); // Windows 10 1607+: Delete tree with specified view // RegDeleteTreeW(hKey, L"SubKeyName");}Registry deletions are permanent and immediate. There is no undo, no recycle bin, and changes take effect immediately. Always backup or export the key tree before mass deletions. Use RegSaveKey to create backups, or manually export from regedit.exe.
Proper Registry usage requires following established conventions and avoiding common pitfalls.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
#include <windows.h>#include <strsafe.h> // Pattern: Application settings helpertypedef struct { HKEY hKey; BOOL readOnly;} AppSettings; BOOL OpenAppSettings(AppSettings* settings, BOOL forWriting) { settings->readOnly = !forWriting; REGSAM access = forWriting ? KEY_ALL_ACCESS : KEY_READ; LSTATUS status = RegOpenKeyExW( HKEY_CURRENT_USER, L"SOFTWARE\\MyCompany\\MyApp\\Settings", 0, access, &settings->hKey ); if (status == ERROR_FILE_NOT_FOUND && forWriting) { // Create the key hierarchy DWORD disposition; status = RegCreateKeyExW( HKEY_CURRENT_USER, L"SOFTWARE\\MyCompany\\MyApp\\Settings", 0, NULL, REG_OPTION_NON_VOLATILE, access, NULL, &settings->hKey, &disposition ); } return (status == ERROR_SUCCESS);} void CloseAppSettings(AppSettings* settings) { if (settings->hKey) { RegCloseKey(settings->hKey); settings->hKey = NULL; }} // Pattern: Monitor for changes (instead of polling)void WatchForChanges() { HKEY hKey; LSTATUS status = RegOpenKeyExW( HKEY_CURRENT_USER, L"SOFTWARE\\MyCompany\\MyApp\\Settings", 0, KEY_NOTIFY, // Need KEY_NOTIFY access &hKey ); if (status != ERROR_SUCCESS) return; // Set up change notification HANDLE hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); status = RegNotifyChangeKeyValue( hKey, TRUE, // Watch subtree REG_NOTIFY_CHANGE_NAME | // Subkey changes REG_NOTIFY_CHANGE_LAST_SET, // Value changes hEvent, // Event to signal TRUE // Asynchronous ); if (status == ERROR_SUCCESS) { // Wait for change (or timeout) DWORD result = WaitForSingleObject(hEvent, INFINITE); if (result == WAIT_OBJECT_0) { // Registry changed! Re-read settings OutputDebugStringW(L"Registry changed!\n"); } } CloseHandle(hEvent); RegCloseKey(hKey);}Registry keys have access control lists (ACLs) that determine who can read, write, and modify them. Understanding Registry security is essential for both protecting your application's settings and for security research.
| Location | Administrators | Users | SYSTEM |
|---|---|---|---|
| HKLM\SOFTWARE | Full Control | Read | Full Control |
| HKLM\SYSTEM | Full Control | Read | Full Control |
| HKCU (user's own) | Full Control | Full Control* | Full Control |
| HKLM\SAM | Limited | None | Full Control |
| HKLM\SECURITY | None | None | Full Control |
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
#include <windows.h>#include <aclapi.h>#include <sddl.h> // Create a key with custom securityBOOL CreateSecureKey(HKEY hParent, LPCWSTR subKey, HKEY* pNewKey) { SECURITY_ATTRIBUTES sa; PSECURITY_DESCRIPTOR pSD = NULL; LSTATUS status; HKEY hKey; // Create a security descriptor from SDDL string // This SDDL means: // - D: DACL follows // - (A;;GA;;;BA) - Allow full control to Built-in Administrators // - (A;;GR;;;AU) - Allow read to Authenticated Users if (!ConvertStringSecurityDescriptorToSecurityDescriptorW( L"D:(A;;GA;;;BA)(A;;GR;;;AU)", SDDL_REVISION_1, &pSD, NULL)) { return FALSE; } sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = pSD; sa.bInheritHandle = FALSE; DWORD disposition; status = RegCreateKeyExW( hParent, subKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, &sa, // Apply our security descriptor &hKey, &disposition ); LocalFree(pSD); if (status == ERROR_SUCCESS) { *pNewKey = hKey; return TRUE; } return FALSE;} // Query security on a keyvoid PrintKeySecurity(HKEY hKey) { PSECURITY_DESCRIPTOR pSD = NULL; DWORD size = 0; // Get required buffer size RegGetKeySecurity(hKey, DACL_SECURITY_INFORMATION, NULL, &size); pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED, size); if (!pSD) return; if (RegGetKeySecurity(hKey, DACL_SECURITY_INFORMATION, pSD, &size) == ERROR_SUCCESS) { // Convert to SDDL string for display LPWSTR sddlString = NULL; if (ConvertSecurityDescriptorToStringSecurityDescriptorW( pSD, SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &sddlString, NULL)) { wprintf(L"Key SDDL: %s\n", sddlString); LocalFree(sddlString); } } LocalFree(pSD);} // Common SDDL elements for Registry:// BA = Built-in Administrators// SY = Local System// AU = Authenticated Users// BU = Built-in Users// WD = Everyone (World)// GA = Generic All (Full Control)// GR = Generic Read// GW = Generic WriteNever store sensitive information (passwords, keys) in plaintext in the Registry. Even with restrictive ACLs, administrators can access any key. Use Windows Credential Manager or DPAPI for sensitive data. Also, be aware that Registry auditing can be enabled, logging access to sensitive keys.
The Windows Registry is a centralized, hierarchical database for system and application configuration. Mastering Registry operations is essential for Windows application development and system administration.
What's Next:
With understanding of the Windows Registry, we'll complete our exploration of the Windows API by comparing it with the POSIX interface. This comparison will illuminate the fundamental design philosophy differences between Windows and Unix-like operating systems.
You now understand the Windows Registry's architecture, how to use the Registry API effectively, proper patterns for reading and writing configuration data, and Registry security considerations. This knowledge enables you to build well-behaved Windows applications that properly manage their configuration.