My Training Period: xx hours. This is a continuation from previousWindows Registry Tutorial 1. Before you begin, read someinstruction here.
The expected abilities:
Registry Functions
The following are the registry functions.
|
The following shell functions can be used with the registry.
AssocCreate()
AssocQueryKey()
AssocQueryString()
AssocQueryStringByKey()
SHCopyKey()
SHDeleteEmptyKey()
SHDeleteKey()
SHDeleteValue()
SHEnumKeyEx()
SHEnumValue()
SHGetValue()
SHQueryInfoKey()
SHQueryValueEx()
SHRegCloseUSKey()
SHRegCreateUSKey()
SHRegDeleteEmptyUSKey()
SHRegDeleteUSValue()
SHRegDuplicateHKey()
SHRegEnumUSKey()
SHRegEnumUSValue()
SHRegGetBoolUSValue()
SHRegGetIntW()
SHRegGetPath()
SHRegGetUSValue()
SHRegOpenUSKey()
SHRegQueryInfoUSKey()
SHRegQueryUSValue()
SHRegSetPath()
SHRegSetUSValue()
SHRegWriteUSValue() and
SHSetValue().
The following are the initialization-file functions. They retrieve information from and copy information to a system- or application-defined initialization file. These functions are provided only for compatibility with 16-bit versions of Windows. New applications should use the registry.
Function | Description |
GetPrivateProfileInt() | Retrieves an integer associated with a key in the specified section of an initialization file. |
GetPrivateProfileSection() | Retrieves all the keys and values for the specified section of an initialization file. |
GetPrivateProfileSectionNames() | Retrieves the names of all sections in an initialization file. |
GetPrivateProfileString() | Retrieves a string from the specified section in an initialization file. |
GetPrivateProfileStruct() | Retrieves the data associated with a key in the specified section of an initialization file. |
GetProfileInt() | Retrieves an integer from a key in the specified section of the Win.ini file. |
GetProfileSection() | Retrieves all the keys and values for the specified section of the Win.ini file. |
GetProfileString() | Retrieves the string associated with a key in the specified section of theWin.ini file. |
WritePrivateProfileSection() | Replaces the keys and values for the specified section in an initialization file. |
WritePrivateProfileString() | Copies a string into the specified section of an initialization file. |
WritePrivateProfileStruct() | Copies data into a key in the specified section of an initialization file. |
WriteProfileSection() | Replaces the contents of the specified section in the Win.ini file with specified keys and values. |
WriteProfileString() | Copies a string into the specified section of the Win.ini file. |
Table 7 |
These functions are provided only for compatibility with 16-bit versions of Windows.
RegCreateKey(), RegEnumKey(), RegOpenKey(), RegQueryValue() andRegSetValue().
The structure used with the registry is VALENT
The following are the size limits for the various registry elements.
Registry Element | Size Limit |
Key name | 255 characters. |
Value name | 16,383 characters. Windows 2000: 260 ANSI characters or 16,383 Unicode characters. Windows Me/98/95: 255 characters. |
Value | Available memory. Windows Me/98/95: 16,300 bytes. There is a 64K limit for the total size of all values of a key. |
Table 8. |
Long values (more than 2,048 bytes) should be stored as files with the file names stored in the registry. This helps the registry perform efficiently.
A registry value can store data in various formats. When you store data under a registry value, for instance by calling theRegSetValueEx() function, you can specify one of the following values to indicate the type of data being stored. When you retrieve a registry value, functions such asRegQueryValueEx() use these values to indicate the type of data retrieved. The following registry value types are defined in WinNT.h.
Value | Meaning |
REG_BINARY | Binary data in any form. |
REG_DWORD | A 32-bit number. |
REG_DWORD_LITTLE_ENDIAN | A 32-bit number in little-endian format. Microsoft® Windows® is designed to run onlittle-endian computer architectures (Intel processor family). Therefore, this value is defined asREG_DWORD in the Windows header files. |
REG_DWORD_BIG_ENDIAN | A 32-bit number in big-endian format. Some UNIX systems support big-endian architectures such as a system that use Sun SPARC processor. |
REG_EXPAND_SZ | Null-terminated string that contains unexpanded references to environment variables (for example, "%PATH%"). It will be a Unicode or ANSI string depending on whether you use the Unicode or ANSI functions. To expand the environment variable references, use theExpandEnvironmentStrings() function. |
REG_LINK | Reserved for system use. |
REG_MULTI_SZ | Array of null-terminated strings, terminated by two null characters. |
REG_NONE | No defined value type. |
REG_QWORD | A 64-bit number. |
REG_QWORD_LITTLE_ENDIAN | A 64-bit number in little-endian format. Windows is designed to run on little-endian computer architectures. Therefore, this value is defined asREG_QWORD in the Windows header files. |
REG_SZ | Null-terminated string. It will be a Unicode or ANSI string, depending on whether you use the Unicode or ANSI functions. |
Table 9. |
When writing a string to the registry, you must specify the length of the string, including the terminating null character (\0). A common error is to use thestrlen() function to determine the length of the string, but to forget thatstrlen() returns only the number of characters in the string, not including the terminating null. Therefore, the length of the string should be calculated as:
strlen(string) + 1
Note that aREG_MULTI_SZ string, which contains multiple null-terminated strings, ends with two null characters, which must be factored into the length of the string. For example, aREG_MULTI_SZ string can be represented in memory as follows:
string1\0string2\0string3\0laststring\0\0
When calculating the length of aREG_MULTI_SZ string, add the length of each of the component strings, as above, and add one for the final terminating null.
Byte Formats
In little-endian format, a multi-byte value is stored in memory from the lowest byte (the "little end") to the highest byte. For example, the value 0x12345678 is stored as (0x78 0x56 0x34 0x12) in little-endian format. In big-endian format, a multi-byte value is stored in memory from the highest byte (the "big end") to the lowest byte. For example, the value0x12345678 is stored as (0x12 0x34 0x56 0x78) in big-endian format.
----------------------------Program examples-------------------------------
Creating a Security Descriptor from Scratch for a New Object, a Registry key
The following example (taken from theAccess Control Module) creates a security descriptor for a new registry key. Similar code can be used to create a security descriptor for other object types as well. The steps:
|
// Creating a security descriptor for a new object,
// a registry key and then delete the key...
// #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>
#include <aclapi.h>
// Buffer clean up routine
void Cleanup(PSID pEveryoneSID, PSID pAdminSID, PACL pACL, PSECURITY_DESCRIPTOR pSD, HKEY hkSub)
{
if(pEveryoneSID)
FreeSid(pEveryoneSID);
if(pAdminSID)
FreeSid(pAdminSID);
if(pACL)
LocalFree(pACL);
if(pSD)
LocalFree(pSD);
if(hkSub)
RegCloseKey(hkSub);
}
int main(int argc,char *argv[])
{
DWORD dwRes, dwDisposition;
PSID pEveryoneSID = NULL, pAdminSID = NULL;
PACL pACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
// An array of EXPLICIT_ACCESS structure
EXPLICIT_ACCESS ea[2];
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
SECURITY_ATTRIBUTES sa;
LONG lRes;
HKEY hkSub = NULL;
// Create a well-known SID for the Everyone group.
if(!AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0, &pEveryoneSID))
{
printf("AllocateAndInitializeSid() error %u\n", GetLastError());
Cleanup(pEveryoneSID, pAdminSID, pACL, pSD, hkSub);
}
else
printf("AllocateAndInitializeSid() for the Everyone group is OK\n");
// Initialize an EXPLICIT_ACCESS structure for an ACE. The ACE will allow Everyone read access to the key.
ZeroMemory(&ea, 2 *sizeof(EXPLICIT_ACCESS));
ea[0].grfAccessPermissions = KEY_READ;
ea[0].grfAccessMode = SET_ACCESS;
ea[0].grfInheritance= NO_INHERITANCE;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea[0].Trustee.ptstrName = (LPTSTR) pEveryoneSID;
// Create a SID for the BUILTIN\Administrators group.
if(!AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSID))
{
printf("AllocateAndInitializeSid() error %u\n", GetLastError());
Cleanup(pEveryoneSID, pAdminSID, pACL, pSD, hkSub);
}
else
printf("AllocateAndInitializeSid() for the BUILTIN\\Administrators group is OK\n");
// Initialize an EXPLICIT_ACCESS structure for an ACE. The ACE will allow the Administrators group full access to the key.
ea[1].grfAccessPermissions = KEY_ALL_ACCESS;
ea[1].grfAccessMode = SET_ACCESS;
ea[1].grfInheritance= NO_INHERITANCE;
ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
ea[1].Trustee.ptstrName = (LPTSTR) pAdminSID;
// Create a new ACL that contains the new ACEs.
dwRes = SetEntriesInAcl(2, ea, NULL, &pACL);
if(dwRes != ERROR_SUCCESS)
{
printf("SetEntriesInAcl() error %u\n", GetLastError());
Cleanup(pEveryoneSID, pAdminSID, pACL, pSD, hkSub);
}
else
printf("SetEntriesInAcl() for the Administrators group is OK\n");
// Initialize a security descriptor.
pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if(pSD == NULL)
{
printf("LocalAlloc() error %u\n", GetLastError());
Cleanup(pEveryoneSID, pAdminSID, pACL, pSD, hkSub);
}
else
printf("LocalAlloc() is OK\n");
if(!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
{
printf("InitializeSecurityDescriptor Error %u\n", GetLastError());
Cleanup(pEveryoneSID, pAdminSID, pACL, pSD, hkSub);
}
else
printf("InitializeSecurityDescriptor() is OK\n");
// Add the ACL to the security descriptor.
if(!SetSecurityDescriptorDacl(pSD,
TRUE, // bDaclPresent flag
pACL,
FALSE)) // not a default DACL
{
printf("SetSecurityDescriptorDacl() Error %u\n", GetLastError());
Cleanup(pEveryoneSID, pAdminSID, pACL, pSD, hkSub);
}
else
printf("SetSecurityDescriptorDacl() is OK\n");
// Initialize a security attributes structure.
sa.nLength =sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;
//******************* Registry key **********************
// Use the security attributes to set the security descriptor
// when you create a registry key.
// make the subkey as char...
#define MAX_KEY_NAME 255
char cName[MAX_KEY_NAME] = "AnotherTestKey"; // Change accordingly...
HKEY hKey = HKEY_CURRENT_USER;// Change to other key accordingly...
lRes = RegCreateKeyEx(hKey,// handle to an open key
cName, // name of the subkey
0, // Reserved, must be 0
"", // class or object type of this key, may be ignored
0, // Options
KEY_ALL_ACCESS, // Access right for the key
&sa, // Pointer to security attribute structure, can be inherited or not. NULL is not inherited
&hkSub, // variable that receives a handle to the opened or created key
&dwDisposition); // variable that receives:
// REG_CREATED_NEW_KEY - create new key (non-exist)
// REG_OPENED_EXISTING_KEY - just open the existing key (already exist)
// If successful
if(lRes == 0)
{
printf("The value of the \'&dwDisposition\': %u\n", dwDisposition);
printf("HKEY_CURRENT_USER\\%s successfully created.\n", cName);
}
else
printf("Creating and opening HKEY_CURRENT_USER\\%s is failed.\n", cName);
// TODO: Call other functions such as setting the key values...
// Just to see the key has been created before it is deleted...
// You can verify through the regedit/regedt32...
system("pause");
// Then delete the subkey...
LONG res = RegDeleteKey(
hKey, // The key
cName // The subkey
);
if(res == ERROR_SUCCESS)
printf("HKEY_CURRENT_USER\\%s successfully deleted.\n", cName);
RegCloseKey(hKey);
return 0;
}
A sample output:
AllocateAndInitializeSid() for the Everyone group is OK
AllocateAndInitializeSid() for the BUILTIN\Administrators group is OK
SetEntriesInAcl() for the Administrators group is OK
LocalAlloc() is OK
InitializeSecurityDescriptor() is OK
SetSecurityDescriptorDacl() is OK
The value of the '&dwDisposition': 1
HKEY_CURRENT_USER\AnotherTestKey successfully created.
Press any key to continue . . .
HKEY_CURRENT_USER\AnotherTestKey successfully deleted.
Press any key to continue
The following screenshot is the verification through theregedt32/regedit before the created key was deleted.
Figure 11: A new registry key was created. |
You can use the default Application log without adding an event source to the registry. However, Event Viewer will not be able to map your event identifier codes to message strings unless you register your event source and provide a message file.
You can add a new source name to the registry by opening a new registry subkey under the Application key or a custom log using the RegCreateKey() function, and adding registry values to the new subkey using theRegSetValueEx() function. The following example opens a new source and adds a message-file name and a bitmask of supported types.
// WARNING!!!
// If you don't know what you are doing, please don't try
// this code...and don't forget to delete the key or use
// RegDeleteKey()...
// #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>
#include <aclapi.h>
BOOL AddMyEventSource(
LPTSTR pszLogName,// Application log or a custom log
LPTSTR pszSrcName,// event source name
LPTSTR pszMsgDLL, // path for message DLL
DWORD dwNum) // number of categories
{
HKEY hk;
DWORD dwData;
TCHAR szBuf[MAX_PATH];
// Create the event source as a subkey of the log.
wsprintf(szBuf, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\%s\\%s", pszLogName, pszSrcName);
//********************************************
// Create the registry key
if(RegCreateKey(HKEY_LOCAL_MACHINE, szBuf, &hk))
{
printf("Could not create the registry key.");
return FALSE;
}
else
printf("SYSTEM\\CurrentControlSet\\Services\\EventLog\\%s\\%s created successfully.\n", pszLogName, pszSrcName);
//********************************************
// Set the name of the message file.
if(RegSetValueEx(hk, // subkey handle
"EventMessageFile", // value name
0, // must be zero
REG_EXPAND_SZ, // value type
(LPBYTE) pszMsgDLL, // pointer to value data
(DWORD) lstrlen(szBuf)+1)) // length of value data
{
printf("Could not set the event message file.");
return FALSE;
}
else
printf("The event message file has been set successfully.\n");
// Set the supported event types.
dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
//********************************************
if(RegSetValueEx(hk, // subkey handle
"TypesSupported", // value name
0, // must be zero
REG_DWORD, // value type
(LPBYTE) &dwData, // pointer to value data
sizeof(DWORD))) // length of value data
{
printf("Could not set the supported types.");
return FALSE;
}
else
printf("The supported types have been set successfully.\n");
//********************************************************
// Set the category message file and number of categories.
if(RegSetValueEx(hk, // subkey handle
"CategoryMessageFile", // value name
0, // must be zero
REG_EXPAND_SZ, // value type
(LPBYTE) pszMsgDLL, // pointer to value data here we set same as "EventMessageFile"
(DWORD) lstrlen(szBuf)+1)) // length of value data
{
printf("Could not set the category message file.");
return FALSE;
}
else
printf("The category message file has been set successfully.\n");
//********************************************
if(RegSetValueEx(hk, // subkey handle
"CategoryCount", // value name
0, // must be zero
REG_DWORD, // value type
(LPBYTE) &dwNum, // pointer to value data
sizeof(DWORD))) // length of value data
{
printf("Could not set the category count.");
return FALSE;
}
else
printf("The category count has been set successfully.\n");
// Close the key
RegCloseKey(hk);
return TRUE;
}
int main(int argc,wchar_t *argv[ ])
{
// Application log or a custom log. Here we put a custom log just for learning!
LPTSTR pszLogName = "MyCustLogTest";
// The event source name
LPTSTR pszSrcName = "MyEventSrcName";
// The path for message dll, this dll or other executable file must exist lol!
// here, mytestdll.dll just a dummy. You will know it when you restart
// your computer if the created key does not deleted...:o)
LPTSTR pszMsgDLL = "%SystemRoot%\\System32\\mytestdll.dll";
// number of categories.
DWORD dwNum = 0x00000003;
BOOL test = AddMyEventSource(
pszLogName, // Application log or a custom log. Custom log here...
pszSrcName, // event source name.
pszMsgDLL, // path for message DLL.
dwNum // number of categories.
);
// Just to check the return value...
printf("The AddMyEventSource() return value is: %u\n", test);
return 0;
}
A sample output:
SYSTEM\CurrentControlSet\Services\EventLog\MyCustLogTest\MyEventSrcName created successfully.
The event message file has been set successfully.
The supported types have been set successfully.
The category message file has been set successfully.
The category count has been set successfully.
The AddMyEventSource() return value is: 1
Press any key to continue
Figure 12: Registry key, subkey and values have been created.
---------------------------Windows Registry: Story and Program Examples, Part I-----------------------
Further reading and digging:
For Multi bytes, Unicode characters and Localization please refer to Locale, wide characters & Unicode (Story) and Windows users & groups programming tutorials (Implementation).
Structure, enum, union and typedef story can be foundC/C++ struct, enum, union & typedef.
Check the best selling C, C++ and Windows books at Amazon.com.
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.
Notation used in MSDN is Hungarian Notation instead of CamelCase and is discussedWindows programming notations.
Windows data type information is inWindows data types used in Win32 programming.