|
|
Comments and Discussions
|
|
 |
|

|
Hello,
I think there is a bug if you want to use this class in Unicode projects. Line 497 should read
if (RegSetValueEx(m_hKey, m_key.c_str(), 0, REG_SZ, (BYTE *)m_value.c_str(), (DWORD)(m_value.size()+1)*sizeof(TCHAR))==ERROR_SUCCESS)
The "sizeof(TCHAR)" is necessary if TCHAR is not 1 byte.
Am I correct?
|
|
|
|

|
As is, this class will only return the first null terminated string portion of a multi-string registry entry. I have modified the class so that the CRegString() method will automatically determine whether entry is regular string or multi-string and return it correctly. Multi-strings are returned as vertical bar or pipe-delimited strings (eg. "string-a|string-b"). Here is the code modification:
CString CRegString::read()
{
ASSERT(m_key != _T(""));
if (RegOpenKeyEx(m_base, m_path, 0, KEY_EXECUTE, &m_hKey)==ERROR_SUCCESS)
{
int size = 0;
DWORD type;
RegQueryValueEx(m_hKey, m_key, NULL, &type, NULL, (LPDWORD) &size);
TCHAR* pStr = new TCHAR[size];
if(type==REG_MULTI_SZ)
{
delete [] pStr;
RegCloseKey(m_hKey);
TCHAR szResult[512] = _T("\0");
if(RegMultiRead(m_base, m_path.GetBuffer(), m_key.GetBuffer(),szResult,512)==0)
{
m_read=TRUE;
m_value = CString(szResult);
}
else
{
m_read=FALSE;
m_value = m_defaultvalue;
}
m_path.ReleaseBuffer();
m_key.ReleaseBuffer();
return m_value;
}
if (RegQueryValueEx(m_hKey, m_key, NULL, &type, (BYTE*) pStr,(LPDWORD) &size)==ERROR_SUCCESS)
{
m_value = CString(pStr);
delete [] pStr;
ASSERT(type==REG_SZ);
m_read = TRUE;
RegCloseKey(m_hKey);
return m_value;
}
else
{
delete [] pStr;
RegCloseKey(m_hKey);
return m_defaultvalue;
}
}
return m_defaultvalue;
}
And then you must add the corresponding new method to the CRegString class:
DWORD CRegString::RegMultiRead(HKEY hHive, LPTSTR szKey, LPTSTR szValue, LPTSTR szResult, size_t iResultSize)
{
long lRet = 0;
HKEY hKey;
if (lRet = RegOpenKeyEx(hHive,szKey,0,KEY_EXECUTE,&hKey) == ERROR_SUCCESS)
{
DWORD dwType = 0;
DWORD dwSize = iResultSize;
DWORD dwCapacity = iResultSize;
LPTSTR szData = new TCHAR[iResultSize+4*sizeof(TCHAR)];
lRet = RegQueryValueEx(hKey,szValue,NULL,&dwType,(LPBYTE)szData,&dwSize);
if( (lRet != ERROR_SUCCESS) || (dwSize > dwCapacity) || dwType != REG_MULTI_SZ )
{
RegCloseKey(hHive);
delete[] szData;
return dwSize;
}
LPTSTR szToken = NULL;
LPTSTR szTmp = new TCHAR[dwCapacity];
_tcscpy (szTmp, _T("\0"));
szToken = szData;
while( szToken != NULL )
{
_tcscat (szTmp, szToken);
szToken = szToken + _tcslen (szToken) + sizeof(TCHAR);
if (_tcslen (szToken) > 0)
_tcscat (szTmp, _T("|"));
else
szToken = NULL;
}
_tcscpy(szResult, szTmp);
delete[] szData;
delete[] szTmp;
RegCloseKey(hKey);
RegCloseKey(hHive);
}
else
return lRet;
return 0;
}
In business, if two people always agree, one of them is unnecessary.
|
|
|
|

|
ok ive never used a tab control before i have no clue as to go about it. could someone give me a sample as to how to put a control on a tab all im going to use is check boxs and a few edits.
thanks in advance.
|
|
|
|

|
This is very useful. Very easy to use and powerful.
The only trouble I have with it is that if I try to read a non-existant key, it should either return null, or give an error indicating that there is no key. Returning the default value can be confusing as this is data that might then be mishandled.
Perhaps returning the default value on non-keys could be an option that could be turned on or off?
Anyway, this is very helpful.
Thanks!
|
|
|
|
|

|
As I'm learning, nothing in life is certain, esp. so with Windows. This class, since it writes to something VERY important should be throwing exceptions. Might just add those and send to the author.
later....
C. Gilley
Will program for food...
Whoever said children were cheaper by the dozen... lied.
|
|
|
|

|
Just wanted you to know that it is excellent class.
Sincerely,
Alex Chirokov
|
|
|
|

|
Hello,
I'm using you little registry class library and I must say that it does the job almost perfect! I found a little "feature" which I don't know if it's there on purpose or as a surprise..
This "feature" is in the constructor / read method of the CRegBase derived classes. This is what happens:
I call the constructor with a default value. The registry value / key don't exist yet. The read function returns the default value. The variable m_value doesn't change! So after the constructor finished the object has an invalid default value. (Not the one I specified.)
The code now looks like this:
// constructor
m_value = "";
m_defaultvalue = def;
m_force = force;
m_base = base;
m_read = FALSE;
key.TrimLeft(_T("\\"));
m_path = key.Left(key.ReverseFind(_T('\\')));
m_path.TrimRight(_T("\\"));
m_key = key.Right(key.GetLength() - key.ReverseFind(_T('\\')));
m_key.Trim(_T("\\"));
read();
// read method
ASSERT(m_key != _T(""));
if (RegOpenKeyEx(m_base, m_path, 0, KEY_EXECUTE, &m_hKey)==ERROR_SUCCESS)
{
int size = 0;
DWORD type;
RegQueryValueEx(m_hKey, m_key, NULL, &type, NULL, (LPDWORD) &size);
TCHAR* pStr = new TCHAR[size];
if (RegQueryValueEx(m_hKey, m_key, NULL, &type, (BYTE*) pStr,(LPDWORD) &size)==ERROR_SUCCESS)
{
m_value = CString(pStr);
delete [] pStr;
ASSERT(type==REG_SZ);
m_read = TRUE;
RegCloseKey(m_hKey);
return m_value;
}
else
{
delete [] pStr;
RegCloseKey(m_hKey);
return m_defaultvalue;
}
}
// Note: the variable m_value is not changed here!
return m_defaultvalue;
And I think that the code should look like this:
// constructor
m_value = "";
m_defaultvalue = def;
m_force = force;
m_base = base;
m_read = FALSE;
key.TrimLeft(_T("\\"));
m_path = key.Left(key.ReverseFind(_T('\\')));
m_path.TrimRight(_T("\\"));
m_key = key.Right(key.GetLength() - key.ReverseFind(_T('\\')));
m_key.Trim(_T("\\"));
// Option 1, change this line to m_value = read()
read();
// read method
ASSERT(m_key != _T(""));
if (RegOpenKeyEx(m_base, m_path, 0, KEY_EXECUTE, &m_hKey)==ERROR_SUCCESS)
{
int size = 0;
DWORD type;
RegQueryValueEx(m_hKey, m_key, NULL, &type, NULL, (LPDWORD) &size);
TCHAR* pStr = new TCHAR[size];
if (RegQueryValueEx(m_hKey, m_key, NULL, &type, (BYTE*) pStr,(LPDWORD) &size)==ERROR_SUCCESS)
{
m_value = CString(pStr);
delete [] pStr;
ASSERT(type==REG_SZ);
m_read = TRUE;
RegCloseKey(m_hKey);
return m_value;
}
else
{
delete [] pStr;
RegCloseKey(m_hKey);
// Option 2 modify the function as follows
// Note: no value returned here!
}
}
// and modify this line
return (m_value = m_defaultvalue);
I think that option 2 is the better option, because that way m_value is always changed, which I thinkt is the purpose of the read() method.
Besides that, it's a great class!
I also got the blogging virus..[^]
|
|
|
|

|
and sure Trim is not a member of CString, then...
|
|
|
|

|
I think that maybe it's a bug.
I think that it maybe should like this:
m_key.TrimLeft(_T("\\"));
And i believe it is right with trimleft().
|
|
|
|

|
Trim is now a member of the old MFC CString class, but here is what google found:
"The trimChars can be either a string of the characters or an integer array of characters that will be trimmed from both ends"
so instead of
string.Trim()
i used
string.TrimLeft();
string.TrimRight();
so far it works,
Sincerely,
Alex Chirokov
|
|
|
|

|
Yes, Trim is a valid CString function... BUT.. it returns the result rather than changing the object directly. So the line MyString.trim(), while being valid code, will have no effect as the result is lost... It needs to be written CString szTrimmed = MyString.Trim(), and now the result of the Trim() operation is captured in the szTrimmed variable.
Trim operates on both ends of the string as opposed to TrimLeft and TrimRight which operate on only one end -- so if MyString = "*****Important String!!!!!!", you could use szTrimmed = MyString.Trim("*!"); and the result would be "Important String".
The important point here is that the reason there is data loss in the example code, is because he didn't assign a variable to catch the returned result of the Trim operation.
In business, if two people always agree, one of them is unnecessary.
|
|
|
|

|
The .reg file saved by RegSaveKey() is a binary file.
How to export registry as an ASCII text file?
|
|
|
|

|
Hello,
No problem when I use removeValue(),
however; when I use removeKey() I get linker errors?
How do I fix the linker errors, or how can
I remove keys without using removeKey()?
Linking...
maindoc.obj : error LNK2001: unresolved external symbol __imp__SHDeleteKeyA@8
../../Apps/Debug/SPMNotify.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.
VC++ 6.0 SP5
Thanks,
Jacques
|
|
|
|

|
Just add Shlwapi.lib import library to your VS 5 project (Project/Settings/Settings for all configurations/Link/Object/library modules).
|
|
|
|

|
thanks for posting this outstanding code!
|
|
|
|

|
Is there any way to use this registry class in program that does not support MFC?
I would like to use this registry class in an existing DLL that does not support MFC. I think it is rather difficult to make that DLL to support MFC, so I would like to see if there is any way to use this class with no support of MFC.
I am definitely open to the suggestion on how to change a DLL to support MFC with minimum effort.
Thanks,
LT
|
|
|
|

|
This is one heck of a class library!
keep up the great work!
|
|
|
|

|
Hi,
Do you know how to load an entire .reg file programmatically?
I didn't see that anywhere ?
Thanks.
|
|
|
|

|
Thought this little bit may be of use for some.
The class declares CRegStdWORD instead of CRegStdDWORD as you (and I) would expect. Those programs referencing CRegStdDWORD will not compile. Since I did not want to change a lot of the code, adding this line to the end of registry.h provides a quick fix:
typedef CRegStdWORD CRegStdDWORD;
Thanks for the great class!
|
|
|
|

|
This was exactly what I was looking for. thanks a lot for writing it.
|
|
|
|

|
Congratulations ; is just what I was looking for, thanks a lot.
People say that I am a perverse person, but are not truth because I have the heart of a boy.... I have it in a bottle with formol in my writing-desk.
|
|
|
|

|
Hi, I am getting the following linker error: error LNK2001: unresolved external symbol "public: class CRegStdWORD & __thiscall CRegStdWORD::operator=(unsigned long)" (??4CRegStdWORD@@QAEAAV0@K@Z) Debug/StickyNotes.exe : fatal error LNK1120: 1 unresolved externals. Looks like CRegStdWORD::operator=(DWORD d) is not defined. Is it a bug or a feature? Or am I missing something? With respect, Igor.
|
|
|
|
|

|
You need to define CRegStdWORD::operator=(DWORD d) yourself. Looks like it's just missing. After I implemented it I was able to compile the project.
Igor.
|
|
|
|

|
Hello,
The class generated 3 compiler errors. All 3 compiler
errors were generated from the 3 occurrences of the
statement:
//generates compiler error.
m_key.Trim(_T("\\"));
Compiler error message:
Registry.cpp(135) : error C2039: 'Trim' : is not a member of 'CString'
However, I replaced all 3 statements with:
// m_key.Trim(_T("\\"));
m_key = m_key.Mid(1);
and all is well.
Regards,
Jacques
|
|
|
|

|
Yes, Trim is a new function in VC7 / .NET.
I think I just replaced that one with TrimLeft + TrimRight.
|
|
|
|

|
your code helped me a lot. many thx 4 that.
to get the value of registry's "(Default)" entries i suggest to not making any assertions on the given key in the read() methods.
|
|
|
|

|
This is a great idea for a regitry class. Thx for that pice off code.
|
|
|
|

|
** Original post 8/2003. Modified 12/2004 **
I had a need to read & write to the registry, and I found this article to be the best assistance to me. I did however eventually need to use it with REG_MULTI_SZ so I could it's capabilities for a project I needed to do.
Anyhow, here's the code. Add this to the source in this article:
Registry.h
class CRegMultiString : public CRegBase
{
public:
CRegMultiString();
/**
* Constructor.
* \param key the path to the key, including the key. example: "Software\\Company\\SubKey\\MyValue"
* \param def the default value used when the key does not exist or a read error occured
* \param force set to TRUE if no cache should be used, i.e. always read and write directly from/to registry
* \param base a predefined base key like HKEY_LOCAL_MACHINE. see the SDK documentation for more information.
*/
CRegMultiString(CString key, CString def = _T(""), BOOL force = FALSE, HKEY base = HKEY_CURRENT_USER);
~CRegMultiString(void);
CString read(); ///< reads the value from the registry
void write(); ///< writes the value to the registry
operator CString();
CRegMultiString& operator=(CString s);
CRegMultiString& operator+=(CString s) { return *this = (CString)*this + s; }
protected:
CString m_value; ////< the cached value of the registry
CString m_defaultvalue; ///< the default value to use
BOOL m_read; ///< indicates if the value has already been read from the registry
BOOL m_force; ///< indicates if no cache should be used, i.e. always read and write directly from registry
};
Registry.cpp
CRegMultiString::CRegMultiString(void)
{
m_value = _T("");
m_defaultvalue = _T("");
m_key = _T("");
m_base = HKEY_CURRENT_USER;
m_read = FALSE;
m_force = FALSE;
}
/**
* Constructor.
* @param key the path to the key, including the key. example: "Software\\Company\\SubKey\\MyValue"
* @param def the default value used when the key does not exist or a read error occured
* @param force set to TRUE if no cache should be used, i.e. always read and write directly from/to registry
* @param base a predefined base key like HKEY_LOCAL_MACHINE. see the SDK documentation for more information.
*/
CRegMultiString::CRegMultiString(CString key, CString def, BOOL force, HKEY base)
{
m_value = "";
m_defaultvalue = def;
m_force = force;
m_base = base;
m_read = FALSE;
key.TrimLeft(_T("\\"));
m_path = key.Left(key.ReverseFind(_T('\\')));
m_path.TrimRight(_T("\\"));
m_key = key.Right(key.GetLength() - key.ReverseFind(_T('\\')));
m_key.Trim(_T("\\"));
read();
}
CRegMultiString::~CRegMultiString(void)
{
//write();
}
// *** Modified 12/04 ***
CString CRegMultiString::read()
{
ASSERT(m_key != _T(""));
if (RegOpenKeyEx(m_base, m_path, 0, KEY_EXECUTE, &m_hKey)==ERROR_SUCCESS)
{
int size = 0;
DWORD type;
RegQueryValueEx(m_hKey, m_key, NULL, &type, NULL, (LPDWORD) &size);
TCHAR* pStr = new TCHAR[size];
if (RegQueryValueEx(m_hKey, m_key, NULL, &type, (BYTE*) pStr,(LPDWORD) &size)==ERROR_SUCCESS)
{
m_value = CString(pStr);
delete [] pStr;
ASSERT(type==REG_SZ);
m_read = TRUE;
RegCloseKey(m_hKey);
return m_value;
}
else
{
delete [] pStr;
RegCloseKey(m_hKey);
return m_defaultvalue;
}
}
return m_defaultvalue;
}
/// *** End modification
void CRegMultiString::write()
{
ASSERT(m_key != _T(""));
DWORD disp;
if (RegCreateKeyEx(m_base, m_path, 0, _T(""), REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &m_hKey, &disp)!=ERROR_SUCCESS)
{
return;
}
#ifdef _UNICODE
if (RegSetValueEx(m_hKey, m_key, 0, REG_MULTI_SZ, (BYTE *)(LPCTSTR)m_value, (m_value.GetLength()+1)*2)==ERROR_SUCCESS)
#else
if (RegSetValueEx(m_hKey, m_key, 0, REG_MULTI_SZ, (BYTE *)(LPCTSTR)m_value, m_value.GetLength()+1)==ERROR_SUCCESS)
#endif
{
m_read = TRUE;
}
RegCloseKey(m_hKey);
}
CRegMultiString::operator CString()
{
if ((m_read)&&(!m_force))
return m_value;
else
{
return read();
}
}
CRegMultiString& CRegMultiString::operator =(CString s)
{
if ((s==m_value)&&(!m_force))
{
//no write to the registry required, its the same value
return *this;
}
m_value = s;
write();
return *this;
}
|
|
|
|

|
I don't get it. Why are you calling RegQueryValueEx FOUR times for the same key in CRegMultiString::read() ??
The first one is to get the length of the value, which is used to allocate a buffer that you fill with the second call, but then there's a fixed 64000 byte buffer and two more calls, why?
Am I missing something?
|
|
|
|

|
Hi,
When I first modified the code to read multi-string I was just grabbing other pieces from this class and modifying it. I obviously didn't need all those calls to RegQueryValueEx. It does work however as I get the results I wanted. I wasn't really paying attention to the details when I posted the note and I'm a bit more experienced at coding now than I was back then. The following bit of code should get you the same results without all the extra coding. I honestly don't remember where I got the bit to read multistring reg values as I didn't come up with the code in it's entirety. I just applied it to this class because I found it useful and thought someone else may need to read/write multistring values like I did.
Here's the same code that's used by CRegString and also works for reading multistrings.
CString CRegMultiString::read()
{
ASSERT(m_key != _T(""));
if (RegOpenKeyEx(m_base, m_path, 0, KEY_EXECUTE, &m_hKey)==ERROR_SUCCESS)
{
int size = 0;
DWORD type;
RegQueryValueEx(m_hKey, m_key, NULL, &type, NULL, (LPDWORD) &size);
TCHAR* pStr = new TCHAR[size];
if (RegQueryValueEx(m_hKey, m_key, NULL, &type, (BYTE*) pStr,(LPDWORD) &size)==ERROR_SUCCESS)
{
m_value = CString(pStr);
delete [] pStr;
ASSERT(type==REG_SZ);
m_read = TRUE;
RegCloseKey(m_hKey);
return m_value;
}
else
{
delete [] pStr;
RegCloseKey(m_hKey);
return m_defaultvalue;
}
}
return m_defaultvalue;
}
|
|
|
|

|
this code simply sucks, there is a syntax error in a for loop...
I dont even bother trying to fix it...
|
|
|
|

|
You have to change the ASSERT from:
ASSERT(type==REG_SZ);
to
ASSERT(type==REG_MULTI_SZ);
|
|
|
|

|
Firstly, Great Stuff!
I have tried to implement this as member variables in my Options class, but of course the compiler thinks I am trying to define a function and spits the dummy!
All I can think of is to declare them as pointers, and use new in the constructor to initialise them.
I missing something very obvious here, or do I need to write Create functions?
Thanks for a great class,
Paul.
|
|
|
|

|
in your header:
CRegString m_regstrMyString
then in your class constructor or init routine:
m_regstrMyString = CRegString("Software\\something\\somethingelse\\somestring")
|
|
|
|

|
Another way to use it as a member is to do this:
class COptions
{
public:
protected:
CRegString c_sYourString;
};
COptions::COptions() :
c_sYourString( _T("Software\\Something\\SomethingElse\\SomeString") )
{
}
Now you won't have to call the constructor for the object 3 times, but rather, only once.
[edit]Err, I didn't mean "call the constructor for the object 3 times"; I really meant call the class constructor 3 times.[/edit]
Chris Richardson Terrain Software
|
|
|
|

|
This class, especially the idea behind it, is simply beautiful. Really, but I usually wanna be picky If any thing, you might want to make it unicode-compliant? Just a few "Replace" in your VC++ editor and it becomes even closer to perfection.
|
|
|
|

|
good job!!
I think it will be better if you make it working well with UNICODE
|
|
|
|

|
I'm surprised you said this worked w/ Unicode; had to make a couple of minor changes to CRegString::read and ::write to get it to work
read - changed the data type to TCHAR for allocating memory
write - when in UNICODE, doubled [GetLength() + 1]
Tom Wiseley
|
|
|
|

|
Only problem I had, well first problem i had was that I get Trim() as not being a function. TrimRight, TrimLeft, both there, but not Trim.
|
|
|
|

|
Trim() is a short for TrimRight() and TrimLeft() together (in MFC7).
|
|
|
|

|
Thanks for your code!. but i still wonder how i can read data as REG_MULTI_SZ in registry. May you give me an example of a piece of code to read and set them. Thanks for your help.
Tuan
|
|
|
|

|
Use the REG_MULTI_SZ like the normal REG_SZ type. The only difference is that instead of just one String (char[] with ending zero) you pass and receive multiple strings. The last String in the buffer must end with two instead of one zero char.
|
|
|
|

|
I'm also trying to use REG_MULTI_SZ but can only get the first line. Not sure what the solution to this based on Steve's suggestion. Any ideas or code would be appreciated. Thanks!!
|
|
|
|

|
Suppose you have this registry key: Software\My Company\Subkey1\mydword and you want to remove the "mydword" key. If you write CRegDWORD mydword("Software\My Company\Subkey1\mydword"); mydword.removeKey(); you will be removing Software\My Company\Subkey1 and all its subkeys. So wouldn't it be better to code removeKey like this: DWORD removeKey() { RegOpenKeyEx(m_base, m_path, 0, KEY_WRITE, &m_hKey); return SHDeleteKey(m_hKey, (LPCTSTR)m_key); } Now, "mydword.removeKey()" would only delete the mydword key, and not other subkeys of Subkey1. Best wishes, Hans
|
|
|
|

|
if you only want to remove the mydword, you should use removeValue().
If you want to make sure that only keys are removed when no subkeys are there then use RegRemoveKey() instead of SHDeleteKey.
|
|
|
|

|
In several place in your code you have: char *pStr = " "; RegQueryValueEx(m_hKey, m_key, NULL, &type, (BYTE*) pStr, (LPDWORD) &size); pStr = new char[size]; if (RegQueryValueEx(m_hKey, m_key, NULL, &type, (BYTE*) pStr,(LPDWORD) &size)==ERROR_SUCCESS) { m_value = CString(pStr); delete pStr; . . . This should be changed to:
RegQueryValueEx(m_hKey, m_key, NULL, &type, NULL, (LPDWORD) &size); char *pStr = new char[size]; if (RegQueryValueEx(m_hKey, m_key, NULL, &type, (BYTE*) pStr,(LPDWORD) &size)==ERROR_SUCCESS) { m_value = CString(pStr); delete [] pStr; . . . (note [] after delete). Best wishes, Hans
|
|
|
|

|
oops! thanks for the bugfix!
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.
|
A registry class with overloaded operators to use registry values like normal variables
| Type | Article |
| Licence | CPOL |
| First Posted | 30 Jun 2002 |
| Views | 140,145 |
| Bookmarked | 58 times |
|
|