The registry is used for storing application settings and preferences, together with device and other system settings. The registry is organized as a hierarchy of keys, and each key can contain zero, one or more values, and zero, one ormore keys. Each key can have one un-named (or default) value, and many named values. Each value has a data type, such as DWORD or Unicode string. The Windows CE registry is accessed using the same functions as Windows NT/98/2000. However, unlike Windows 2000 and NT, the Windows CE registry does not support security on keys, and so security-related parameters in the registry access functions are ignored. You can use the Remote Registry Editor to view a device or emulator's registry from your desktop PC, or use one of the many third-party Windows CE registry editors that run on the Windows CE device itself. The Windows CE registry has three primary, or root keys:
Generally, you should place your own data in a key under the "Software" key either in HKEY_CLASSES_MACHINE or HKEY_CURRENT_USER, depending on whether the data applies generally to the application or to a specific user running the application. The distinction between HKEY_CLASSES_MACHINE and HKEY_CURRENT_USER is not as significant as on Windows 2000 or NT, since Windows CE does not support the concept of different logged-on users. In Windows CE the registry key names are limited to 255 characters and cannot be nested more than 16 keys deep. Further, deeply nested keys in Windows CE can affect performance. The following sections show how to perform basic operations on registry keys, including adding keys and values, reading key values, deleting keys and values, and finally, enumerating all sub-keys and values for a particular key. Adding and Updating Registry Keys and ValuesFirst, let's look at creating a key in the registry, and then adding values to the key. Each key can have a default string value; zero, one, or more sub-keys; and zero, one, or more named values. Named values are typed and can be any one of the types listed in Table 4.24. Note that other data types are available but are not commonly used by applications.
The registry access code in Listing 4.20 does the following:
Listing 4.20 Adds or updates registry valuesvoid Listing4_20() { HKEY hKey; DWORD dwDisp; TCHAR szStr[200]; DWORD dwVal; if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\MyCompany\\MyApplication"), 0, NULL, 0, 0, NULL, &hKey, &dwDisp) != ERROR_SUCCESS) { // Warning! Key could have been created, // but not opened. cout _T("Could not open/create key:"); return; } if(dwDisp == REG_CREATED_NEW_KEY) cout _T("Created new key") endl; else if(dwDisp == REG_OPENED_EXISTING_KEY) cout _T("Opened existing key") endl; wcscpy(szStr, _T("Contents of Key 'String'")); if(RegSetValueEx(hKey, _T("StringTest"), NULL, REG_SZ, (LPBYTE)szStr, (wcslen(szStr) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) cout _T("Could not save key"); dwVal = 456; if(RegSetValueEx(hKey, _T("DWORDTest"), NULL, REG_DWORD, (LPBYTE)&dwVal, sizeof(DWORD)) != ERROR_SUCCESS) cout _T("Could not save key"); wcscpy(szStr, _T("Default Value")); if(RegSetValueEx(hKey, NULL, NULL, // default value REG_SZ, (LPBYTE)szStr, (wcslen(szStr) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) cout _T("Could not save key"); RegCloseKey(hKey); return; } RegCreateKeyEx (Table 4.25) creates a new named key or opens an existing key. The advantage of using RegCreateKeyEx is that the function will create the key if it does not exist, or open the key if it does exist. Thus the same code can be used when the application first runs and the key does not exist, and for subsequent calls after the key has been created. Note that keys can be created under HKEY_CLASSES_MACHINE\MyCompany\MyApplication by calling RegCreateKeyEx again, passing hKey as the first parameter rather than HKEY_CLASSES_MACHINE. Don't include leading or trailing backslash characters in the key name. If you do, the key may be created but the call to RegCreateKeyEx will fail. The function RegSetValueEx is used to add a new value to a key or, if the value already exists, to update the value. A key can have a default value which always has the REG_SZ data type. Any number of named values can be added, and these can have any of the data types described in Table 4.24. In Listing 4.20, RegSetValueEx is called three times.
Finally, the code calls RegCloseKey to close the key. This function is passed a single parameter, the handle of the key to close. Querying a Registry ValueWhen querying registry values, the key must first be opened using, for example, RegCreateKeyEx, and then the function RegQueryValueEx, called to read a specified key value. This is illustrated in Listing 4.21. The call to RegCreateKeyEx is identical to that made when creating the values in Listing 4.20. Listing 4.21 Queries a registry valuevoid Listing4_21() { HKEY hKey; DWORD dwDisp, dwcbData, dwType; TCHAR szStr[200]; DWORD dwVal; if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\MyCompany\\MyApplication"), 0, NULL, 0, 0, NULL, &hKey, &dwDisp) != ERROR_SUCCESS) { cout _T("Could not open/create key:"); return; } dwcbData = sizeof(szStr) * sizeof(TCHAR); if(RegQueryValueEx(hKey, _T("StringTest"), NULL, &dwType, (LPBYTE)szStr, &dwcbData) != 0) cout _T("Could not open key") endl; else cout _T("StringTest:") szStr endl; dwcbData = sizeof(DWORD); if(RegQueryValueEx(hKey, _T("DWORDTest"), NULL, &dwType, (LPBYTE)&dwVal, &dwcbData) != 0) cout _T("Could not open key") endl; else cout _T("DWORDTest:") dwVal endl; dwcbData = sizeof(szStr) * sizeof(TCHAR); if(RegQueryValueEx(hKey, NULL, // default value NULL, &dwType, (LPBYTE)szStr, &dwcbData) != 0) cout _T("Could not open key") endl; else cout _T("(default):") szStr endl; RegCloseKey(hKey); } The function RegQueryValueEx (Table 4.27) is passed a pointer to a buffer and the length of the buffer. The value is copied into this buffer, and the data type of the value is returned. Further, the actual number of bytes of data read from the value is returned.
Obviously, you need to know the names of the values to read before calling RegQueryValueEx. If you need to enumerate all the values in a key, you can RegQueryInfoKey, which is described later in the section "Enumerating a Registry Key." As with any open key, in Listing 4.21 RegCloseKey is called to close the key. Deleting a Registry ValueThe function RegDeleteValue can be used to delete individual values in a key, or the RegDeleteKey (described in the next section) can delete a key and all the values in that key. The code in Listing 4.22 deletes the keys created in Listing 4.20. The function RegDeleteValue is passed a handle to an open key and the name of the value to delete. If this second parameter is NULL, the default value is deleted. Note that the key itself is not deleted if all the values themselves are deleted. Listing 4.22 Deletes a registry valuevoid Listing4_22() { HKEY hKey; DWORD dwDisp; if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\MyCompany\\MyApplication"), 0, NULL, 0, 0, NULL, &hKey, &dwDisp) != ERROR_SUCCESS) { cout _T("Could not open/create key:"); return; } if(RegDeleteValue(hKey, _T("StringTest")) != ERROR_SUCCESS) cout _T("Could not delete key") endl; if(RegDeleteValue(hKey, _T("DWORDTest")) != ERROR_SUCCESS) cout _T("Could not delete key") endl; if(RegDeleteValue(hKey, NULL) != ERROR_SUCCESS) cout _T("Could not delete key") endl; RegCloseKey(hKey); } Deleting a Registry KeyListing 4.23 calls the function RegDeleteKey to delete a key, and all the values and sub-keys associated with the key. The function takes the parent key handle (or one of the standard root key values, such as HKEY_LOCAL_MACHINE), and the name of the key. Note that the key being deleted is not opened first. The second parameter cannot be NULL, so the standard root keys (such as HKEY_LOCAL_MACHINE) cannot be deleted. Listing 4.23 Deletes a registry keyvoid Listing4_23() { if(RegDeleteKey(HKEY_LOCAL_MACHINE, _T("Software\\MyCompany")) != ERROR_SUCCESS) cout _T("Could not delete key"); else cout _T("Key deleted"); } Enumerating a Registry KeyThe function RegQueryInfoKey can be used to determine information about a key, such as the number of sub-keys and values. Once this information has been determined, the function RegEnumValue can be used to enumerate the values associated with a particular key. Additionally, the function RegEnumKeyEx (not shown here) can be used to enumerate the keys associated with a key. The code in Listing 4.24 enumerates the values in the key HKEY_LOCAL_MACHINE\Platform This contains information on the manufacturer and other device information. This key has no sub-keys. As you would expect, calling RegOpenKeyEx first opens the key being enumerated. Listing 4.24 Enumerates a registry keyvoid Listing4_24() { HKEY hKey; DWORD dwSubKeys, dwValues, dwIndex, dwValueNameLen; DWORD dwValueType, dwValueLen; TCHAR szValueName[255]; TCHAR szValue[255]; DWORD dwValue; cout _T("Opening key HKEY_LOCAL_MACHINE\\Platform") endl; if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Platform"), 0, 0, &hKey) != ERROR_SUCCESS) cout _T("Could not open key") endl; else if (RegQueryInfoKey(hKey, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, &dwValues, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) { cout _T("Could not query info") endl; RegCloseKey(hKey); } else { cout _T("Key has ") dwSubKeys _T(" subkeys and ") dwValues _T(" values") endl; // now read each value for(dwIndex = 0; dwIndex dwValues; dwIndex++) { // First determine name and data type. dwValueNameLen = sizeof(szValueName); RegEnumValue(hKey, dwIndex, szValueName, &dwValueNameLen, NULL, &dwValueType, NULL, NULL); cout _T("Value Name: ") szValueName; switch (dwValueType) { case REG_SZ: cout _T(" String:"); dwValueLen = sizeof(szValue); dwValueNameLen = sizeof(szValueName); RegEnumValue(hKey, dwIndex, szValueName, &dwValueNameLen, NULL, &dwValueType, (LPBYTE)szValue, &dwValueLen); cout szValue; break; case REG_DWORD: cout _T(" DWORD:"); dwValueLen = sizeof(DWORD); dwValueNameLen = sizeof(szValueName); RegEnumValue(hKey, dwIndex, szValueName, &dwValueNameLen, NULL, &dwValueType, (LPBYTE)&dwValue, &dwValueLen); cout dwValue; break; default: cout _T(" Other"); break; } cout endl; } RegCloseKey(hKey); } } In Listing 4.24, the function RegQueryInfoKey (Table 4.28) is used to determine the number of sub-keys and values contained in the open key. Then, a "for" loop is executed, calling the function RegEnumValue for each value to determine the name of the value and its data type. This name is then passed again to RegEnumValue to obtain the data associated with the value. Two calls to RegEnumValue are made, so that the second call can pass the correct data pointer for the value's data type. The code contains a switch that makes a call to RegEnumValue passing a pointer to the appropriate data type. It is possible to do this with a single call to RegEnumValue with suitable casting of the lpData parameter.
The first call to RegEnumValue (Table 4.29) passes in the handle to the key and the index number. The name of the value is returned in szValueName.
Note how dwValueNameLen is initialized with the length of the szValueName buffer for each iteration, since the function RegEnumValue overwrites the value in dwValueNameLen with the number of bytes copied into szValueName. The value type constant (such as REG_SZ) is returned in the variable dwValueType. Note that you should not add or otherwise change values during an enumeration of values. Values have no particular order in the registry, so the index value used when calling RegEnumValue has no particular significance. // First determine name and data type. dwValueNameLen = sizeof(szValueName); RegEnumValue(hKey, dwIndex, szValueName, &dwValueNameLen, NULL, &dwValueType, NULL, NULL); cout _T("Value Name: ") szValueName; Implementing a Record Counter using the RegistryMany database designs rely on the database providing a counter field type, the value of which is set by the database when records are added and is auto-incremented. Windows CE property databases do not provide such a field type, but the same functionality can be implemented using the registry. The registry needs a value in a key for each table in the database that requires a counter field. The code in Listing 4.25 maintains a single counter in the registry key Software\MyCompany\MyApplication\ with the value name "Counter". The data is stored as a DWORD. The registry access code is very straightforward:
However, in a multitasking or multithreading environment, we need to protect against two applications or threads attempting to increment the counter value at the same time. For this reason, a mutex is used to ensure that only one application or thread increments the counter at a time. In Listing 4.25, a named mutex is created by calling CreateMutex. This function will open an existing mutex with the name "CounterMutex" if another application has already created the mutex (which will occur when two applications attempt to execute this code simultaneously). The mutex is initially not owned and is therefore signaled. The code then calls WaitForSingleObject on the mutex. On return from WaitForSingleObject the thread will own the mutex, and the mutex will be nonsignaled. This means that any other threads calling CreateMutex will block in the WaitForSingleObject function call. The function ReleaseMutex relinquishes the ownership on the mutex and changes it to signaled. You can find out more information about mutexes and thread synchronization in Chapter 6. Listing 4.25 Creates a counter value using the registryLONG GetNextCounterValue() { LONG dwCounter; HKEY hKey; DWORD dwDisp; HANDLE hMutex; hMutex = CreateMutex(NULL, FALSE, _T("CounterMutex")); if(hMutex == NULL) { cout _T("Could not create mutex"); return -1; } else WaitForSingleObject(hMutex, INFINITE); if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\MyCompany\\MyApplication"), 0, NULL, 0, 0, NULL, &hKey, &dwDisp) != 0) { cout _T("Could not open registry key"); ReleaseMutex(hMutex); CloseHandle(hMutex); return -1; } DWORD cbData, cbType; cbData = sizeof(DWORD); if(RegQueryValueEx(hKey, _T("Counter"), NULL, &cbType, (LPBYTE)&dwCounter, &cbData) != 0) { dwCounter = 0; } dwCounter++; if(RegSetValueEx(hKey, _T("Counter"), NULL, REG_DWORD, (LPBYTE)&dwCounter, sizeof(DWORD)) != 0) { cout _T("Could not save Server key"); ReleaseMutex(hMutex); CloseHandle(hMutex); return 0; } RegCloseKey(hKey); ReleaseMutex(hMutex); CloseHandle(hMutex); return dwCounter 1; } void Listing4_25() { cout _T("Next counter value:") GetNextCounterValue() endl; }
|