MORE INFORMATION
Starting at the particular key specified, each key is traversed by using
RegEnumKeyEx, which determines if there are any subkeys. If so, the
subkey's name is passed to the recursive delete function in order to
traverse to the next subkey. This process is repeated for all subkey
descendants. When RegEnumKeyEx reports that there are no more subkeys (that
is, ERROR_NO_MORE_ITEMS) for the current key, a subkey leaf has been
reached.
Once the subkey leaf is deleted using RegDeleteKey, the recursive delete
function re-examines the parent key for any remaining subkeys. If a subkey
does exist, it is also traversed until a subkey leaf is reached and deleted
allowing the recursive delete function to re-examine the parent key. The
process is repeated for each subkey branch until no subkey branches remain.
Then the particular key specified may itself be deleted.
A point to remember when enumerating and deleting subkeys is to always
enumerate subkey index zero (that is, DWORD iSubkey = 0). Because keys are
re-indexed after each key is deleted, the use of a non-zero subkey index
would result in keys not being deleted. This in turn would result in the
failure of the RegDeleteKey function when an attempt is made to delete the
subkey's parent key.
Partial Deletions
Failure to fully delete the particular key specified can be the result of
'partial deletions' (the failure to delete all available subkeys). Although
partial deletions can result from several situations, one possible cause is
individual key protection.
To protect against partial deletions caused by protected keys, you should
test each individual key to ensure that it is not protected from deletion.
To test if the current user has deletion rights on all the keys to be
deleted, you must traverse, enumerate, and open all subkeys with DELETE
privilege requested:
RegOpenKeyEx(
hStartKey,pKeyName, 0,
KEY_ENUMERATE_SUB_KEYS | DELETE,
&hKey ))
If, however, between the time of the delete privilege test and the actual
attempt to delete, the key protection is altered, the recursive delete
function will still fail.
Note that the DELETE privilege is not explicitly defined in the
RegOpenKeyEx() documentation under the "samDesired" parameter. But most
securable objects under Windows NT, including registry keys, have a set of
standard access rights that correspond to operations specific to that type
of object. And, DELETE is one of these standard access rights that applies
to all registry keys, in addition to READ_CONTROL, RIGHT_DAC, and
RIGHT_OWNER rights. So, using the DELETE constant in the RegOpenKeyEx()
call above will work correctly.
To truly protect the registry against partial deletion, you need to follow
a two-step process. First, prior to the deletion attempt, save the initial
state of the registry path to be deleted. Then, to recover from a partial
deletion, you could restore the registry to its former state using the
information already saved. If partial deletions are acceptable, however,
failure to delete a key could trigger the recursive delete function to fail
or the key to be skipped.
Sample Code
// The sample code makes no attempt to check or recover from partial
// deletions.
//
// A registry key that is opened by an application can be deleted
// without error by another application in both Windows 95 and
// Windows NT. This is by design.
DWORD RegDeleteKeyNT(HKEY hStartKey , LPTSTR pKeyName )
{
DWORD dwRtn, dwSubKeyLength;
LPTSTR pSubKey = NULL;
TCHAR szSubKey[MAX_KEY_LENGTH]; // (256) this should be dynamic.
HKEY hKey;
// Do not allow NULL or empty key name
if ( pKeyName && lstrlen(pKeyName))
{
if( (dwRtn=RegOpenKeyEx(hStartKey,pKeyName,
0, KEY_ENUMERATE_SUB_KEYS | DELETE, &hKey )) == ERROR_SUCCESS)
{
while (dwRtn == ERROR_SUCCESS )
{
dwSubKeyLength = MAX_KEY_LENGTH;
dwRtn=RegEnumKeyEx(
hKey,
0, // always index zero
szSubKey,
&dwSubKeyLength,
NULL,
NULL,
NULL,
NULL
);
if(dwRtn == ERROR_NO_MORE_ITEMS)
{
dwRtn = RegDeleteKey(hStartKey, pKeyName);
break;
}
else if(dwRtn == ERROR_SUCCESS)
dwRtn=RegDeleteKeyNT(hKey, szSubKey);
}
RegCloseKey(hKey);
// Do not save return code because error
// has already occurred
}
}
else
dwRtn = ERROR_BADKEY;
return dwRtn;
}
On Windows 98 and Windows 2000, it would probably be easier to use SHDeleteEmptyKey and SHDeleteKey, as these have clearly defined behavior on all platforms. However, there are limitations about the use of these APIs on Windows 95 and Windows NT 4.0, clarified in the documentation for these APIs.