Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1303
  • Last Modified:

C++ - Calling 'RegDeleteValue' in between 'RegEnumValue' causes 'RegEnumValue' to fail

Hi,
I need to do the following. Enumerate the value in a ragistry key and get the values one at a time. Based on some comparison results, I may have to delete a value, after it is read. I've done the code as below:
My problem is that after a call to 'RegDeleteValue' the next call to 'RegEnumValue'  returns 'ERROR_NO_MORE_ITEMS ' although there are other items remaining to be read.
Is it wrong to call the delete function in between the enumerate function?
If so, is there another way I can do this?

Regards,
hj
//reg_key = HKLM\SOFTWARE\KEYNAME
if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE ,
		                             reg_key , 0 ,
									 KEY_QUERY_VALUE|KEY_SET_VALUE , &hkey) )
	{
		TCHAR    achClass[MAX_PATH] = TEXT("");  // 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; 
	  
		TCHAR  achValue[MAX_VALUE_NAME]; //MAX_VALUE_NAME = 5
		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 key values. 

		if (cValues)
		{
			num_vals = cValues;
			if(reg_vals == NULL)
			{
				reg_vals = new char[num_vals * 5];
				SecureZeroMemory(reg_vals, ( (num_vals * 5) * sizeof(char) ) );
			}

			for (i=0, retCode=ERROR_SUCCESS; i < cValues; i++) 
			{ 
				cchValue = MAX_VALUE_NAME; 
				achValue[0] = '\0'; 
				retCode = RegEnumValue(hkey, i, 
					achValue, 
					&cchValue, 
					NULL, 
					NULL,
					NULL,
					NULL);
	 
				if (retCode == ERROR_SUCCESS ) 
				{ 
					//Check for incorrect values
					if( strlen(achValue) != (val_size - 1) )
					{
						//Delete the errenous value
						RegDeleteValue(hkey,achValue);
						achValue[0] = '\0';
						num_vals--;
					}
					else
					{
						//Get the registry values into the 'reg_vals' array
						strncpy_s((reg_vals + (i * val_size ) ), 5, achValue, strlen(achValue));
						reg_vals[(i * val_size )+ strlen(achValue) ] = ',';
					}
				} 
			}
				reg_vals[( (i-1) * val_size )+ strlen(achValue) ] = '\0';		}
		RegCloseKey(hkey);
	}

Open in new window

0
harsha_james
Asked:
harsha_james
  • 14
  • 11
1 Solution
 
pgnatyukCommented:
http://www.experts-exchange.com/Programming/Languages/C/Q_24932851.html
That was a long thread but in the end you can find the solution that does work and I think will work for you.
 
0
 
pgnatyukCommented:
Try this code (it's from the EE thread I posted above).
It deletes the values. RegEnumValue does not work exactly as you explained.
MSND: http://msdn.microsoft.com/en-us/library/ms724865(VS.85).aspx
To enumerate values, an application should initially call the RegEnumValue function with the dwIndex parameter set to zero.

#include <Windows.h>
#include <wchar.h>
 
static const LPCWSTR s_szKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist";
static const LPCWSTR s_szToBeDeleted = L"to_delete";
 
BOOL DeleteValues(LPCWSTR lpszKey, LPCWSTR lpszSubString);
 
int main()
{
	DeleteValues(s_szKey, s_szToBeDeleted);
	return 0;
}
 
BOOL DeleteValues(LPCWSTR lpszKey, LPCWSTR lpszSubString)
{
	HKEY hKey = NULL;
	DWORD nIndex = 0;
	HKEY hSubKey = NULL;
	DWORD nSubKey = 1024;
	WCHAR szSubKey[1024] = { 0 };
	DWORD nCnt = 0;
	WCHAR szValue[1024] = { 0 };
	DWORD nValue = 1024;
	LONG nRet = RegOpenKeyEx(HKEY_CURRENT_USER, s_szKey, 
		0, KEY_READ | KEY_WRITE, &hKey);
 
	if (hKey == NULL)
		return 0;
 
	while (RegEnumKeyEx(hKey, nIndex, szSubKey, &nSubKey, NULL,
		NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS)
	{
		wprintf(L"%d. %s\n", nIndex, szSubKey);
 
		nRet = RegOpenKeyEx(hKey, szSubKey, 0, KEY_READ | KEY_WRITE, &hSubKey);
		if (nRet == ERROR_SUCCESS)
		{
			nCnt = 0;
			do 
			{
				nRet = RegEnumValue(hSubKey, nCnt, szValue, &nValue, NULL, NULL, NULL, NULL);
				if (nRet == ERROR_SUCCESS)
				{
					wprintf(L"\t %d. name = %s\n", nCnt, szValue);
					if (wcsstr(szValue, s_szToBeDeleted) != 0)
					{
						RegDeleteValue(hSubKey, szValue);
						wprintf(L"\t %d. name = %s. Deleted.\n", nCnt, szValue);
					}
					ZeroMemory(szValue, 1024 * sizeof(WCHAR));
					nValue = 1024;
				}
				nCnt++;
			} while(nRet == ERROR_SUCCESS);
			RegCloseKey(hSubKey);
		}
 
		nIndex++;
		ZeroMemory(szSubKey, 1024 * sizeof(WCHAR));
		nSubKey = 1024;
	}
 
	RegCloseKey(hKey);
	return TRUE;
}

Open in new window

0
 
harsha_jamesAuthor Commented:
Hi pgnatyuk,
Thanks for the reply. But I cannot understand what's different in your code. Even in my code, for the first call to RegEnumValue, the dwIndex parameter is zero.
Also, before calling RegDeleteValue, ir has read many values successfully. Only when a value requires to be deleted, the next call to RegEnumValue does not work. RegDeleteValue works fine, though.
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
pgnatyukCommented:
Have you tried to work with the code I posted?
There is just a mistake in your code: num_vals--;
Comment this line.
0
 
harsha_jamesAuthor Commented:
I haven't tried your code, as it seems to be enumerating the subkeys under a key, and then for each subkey, enumerate the values.
I only have one subkey, whose values need to be enumerated.
I tried commenting that line, but it doesn't solve the problem. Why do you feel that's wrong?
0
 
harsha_jamesAuthor Commented:
Please help! I need to complete this today!

Thanks again
0
 
pgnatyukCommented:
I don't feel. I know.
I can repeat - comment the line and test yourself.
One day I also did the same mistake.
If you insist and will post now the exact key name, a condition to delete the value, I will post the working code for you.
0
 
pgnatyukCommented:
I added Test key to the local machine and 5 string values there: test1, test2,...
The attached app deteled "Test2" and "Test4".
 

#include <Windows.h>
#include <wchar.h>

static const LPCWSTR s_szKey = L"SOFTWARE\\Test";

int main()
{
	HKEY hKey = NULL;
	DWORD nIndex = 0;
	DWORD nValue = 1024;
	WCHAR szValue[1024] = { 0 };
	LONG nRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, s_szKey, 
		0, KEY_READ | KEY_WRITE, &hKey);

	if (hKey == NULL)
		return 0;

	do  
	{ 
		nRet = RegEnumValue(hKey, nIndex, szValue, &nValue, NULL, NULL, NULL, NULL); 
		if (nRet == ERROR_SUCCESS) 
		{ 
			wprintf(L"\t %d. name = %s\n", nIndex, szValue); 
			if ((wcsstr(szValue, L"2") != 0) || (wcsstr(szValue, L"4") != 0))
			{ 
				RegDeleteValue(hKey, szValue); 
				wprintf(L"\t %d. name = %s. Deleted.\n", nIndex, szValue); 
			} 
			ZeroMemory(szValue, 1024 * sizeof(WCHAR)); 
			nValue = 1024; 
		} 
		nIndex++; 
	} while(nRet == ERROR_SUCCESS); 

	RegCloseKey(hKey);
	return 0;
}

Open in new window

0
 
harsha_jamesAuthor Commented:
>comment the line and test yourself.

But as I said, I did try commenting the line and tested it myself. It still did not work.
You meant the line 'num_vals--;' didn't you?
I cannot figure out whats wrong in this statement. Can you please explain?

The key is one that's created for my local application. It can be anything . But I don't know if you can test the exact key, as I have a lot of application specific strings and values in the key.
Maybe you can try using a key in your registry with several strings of the same length. As you can see in my code, the condition to delete the value is if it does not have the same length that the strings are supposed to have. (In my case, val_size has value 3 - thats including the space for '\0').

I hope I'm clear. Please let me know if I should explain more.

Thanks
0
 
harsha_jamesAuthor Commented:
I'll try the above code and let you know
0
 
pgnatyukCommented:
Your code may not delete because of strlen(achValue). Check in the debugger that the condition is true and you call RegDeleteValue.
I didn't read the code you posted carefully - sorry. num_val is not the index for RegEnumValue. But you didn't posted the entire loop, so I do not know what happens with the counter i in your app - I mean this counter - it should be only i++ even when you delete a value.
0
 
pgnatyukCommented:
Yes, I understand you. I work with the Unicode projects only. I can check the multi-byte. But the registry likes Unicode. So in order to avoid bugs, I use only Unicode.
0
 
harsha_jamesAuthor Commented:
Yes. i++ should happen even if we delete a value.


>Your code may not delete because of strlen(achValue)

The delete works fine. Even the regenumvalue works fine. My problem is that once the delete function is called (If a length mismatch is found), regenumvalue starts  returning ERROR_NO_MORE_ITEMS even if there are more strings to be read.
0
 
harsha_jamesAuthor Commented:
I tried the code you gave above, but regenumvalue still returns ERROR_NO_MORE_ITEMS  after regdeletevalue.
0
 
pgnatyukCommented:
Please try the attached code.
I'm not that sure now about this index to RegEnumValue - I just saw that the code I posted before didn't delete all values.
0
 
pgnatyukCommented:
Hre is the code. :)
#include <Windows.h>
#include <cstdio>

static const LPCSTR s_szKey = "SOFTWARE\\Test";

int main()
{
	HKEY hKey = NULL;
	DWORD nIndex = 0;
	DWORD nValue = 1024;
	CHAR szValue[1024] = { 0 };
	LONG nRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, s_szKey, 
		0, KEY_READ | KEY_WRITE, &hKey);

	if (hKey == NULL)
		return 0;

	while (RegEnumValue(hKey, nIndex, szValue, &nValue, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
	{
		printf("\t %d. name = %s\n", nIndex, szValue);
		if (strlen(szValue) < 5)
		{ 
			RegDeleteValue(hKey, szValue); 
			nIndex = 0;
			nRet = RegEnumValue(hKey, nIndex, szValue, &nValue, NULL, NULL, NULL, NULL); 
			nIndex = 0;
			printf("\t %d. name = %s. Deleted.\n", nIndex, szValue); 
		} 
		else
			nIndex++;
		ZeroMemory(szValue, 1024); 
		nValue = 1024; 
	}

	RegCloseKey(hKey);
	return 0;
}

Open in new window

0
 
pgnatyukCommented:
Sorry again. I did it too fast. The following code works for me. It uses Multi-Byte Character set now.
#include <Windows.h>
#include <cstdio>

static const LPCSTR s_szKey = "SOFTWARE\\Test";

int main()
{
	HKEY hKey = NULL;
	DWORD nIndex = 0;
	DWORD nValue = 1024;
	CHAR szValue[1024] = { 0 };
	LONG nRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, s_szKey, 
		0, KEY_READ | KEY_WRITE, &hKey);

	if (hKey == NULL)
		return 0;

	while (RegEnumValue(hKey, nIndex, szValue, &nValue, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
	{
		printf("\t %d. name = %s\n", nIndex, szValue);
		if (strlen(szValue) < 5)
		{ 
			RegDeleteValue(hKey, szValue); 
			nIndex = 0;
			printf("\t %d. name = %s. Deleted.\n", nIndex, szValue); 
		} 
		else
			nIndex++;
		ZeroMemory(szValue, 1024); 
		nValue = 1024; 
	}

	RegCloseKey(hKey);
	return 0;
}

Open in new window

0
 
harsha_jamesAuthor Commented:
Thanks, pgnatyu!
I didn't understand why you set the index to zero after deleting.
In my case, the delete function is called at index value 34. So if, after deleting the 34th registry entry, I set the index back to 0, regenumvalue again starts reading from the first value.

But this made me realise that regenumvalue was indeed working fine, even after the delete function. :)

So I did a little more testing, and found the cause of my problem.
When I delete the value at index 34, the next value gets that index. So, I have to decrement the index by 1 before reading. Else, I'll be trying to read the value at index 35, but since there are only 34 values in total now, the 35th index returns ERROR_NO_MORE_ITEMS.

Thanks a lot for your sincere help!

Regards,
hj
0
 
pgnatyukCommented:
You are welcome.
MSND says do not modify the registry after RegEnumValue. So I set the index to 0 - just in order to avoid even a theratical problem.
 
0
 
pgnatyukCommented:
I cannot agree just to close the question.
Comment 26070587 is the answer.
And in 26070690 the asker recognized it.
0
 
harsha_jamesAuthor Commented:
Hi pgnatyuk,
I'm sorry, but comment 26070587 does not give the actual solution to my problem. As I said in comment 26070690, your suggestion indeed gave me an IDEA how to solve the problem, not the actual solution.
I have rated comment 26070587 in the 'Was this comment helpful?' section.

If you still feel I should accept comment 26070587 as the solution, please let me know.
But I feel that in future, if someone refers the post for help, it might misguide them.

Thanks once again for your help,
HJ
0
 
pgnatyukCommented:
I've said my opinion. That was the answer - the code works exactly as requested. Accordingly to the posted code you found a mistake in your code and now your application works. So I really didn't expect that you will not take that comment as a solution.
0
 
harsha_jamesAuthor Commented:
Ok, I'm accepting the comment as solution.
0
 
harsha_jamesAuthor Commented:
The solution partially answered my question, thus helping me to solve the problem.
0
 
pgnatyukCommented:
Thanks.
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 14
  • 11
Tackle projects and never again get stuck behind a technical roadblock.
Join Now