|
** 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!
|
|
|
|
|
This is the most pain free registry class I've seen. My reaction to it was to slap my forehead at the simplicity of the idea. It's such an obviously good way to handle what is typically a dirty plumbing job.
Jim Howard
|
|
|
|
|
Instead of all these specialized ones?
|
|
|
|
|
1. the registry provides different kinds of storing information: REG_SZ, REG_DWORD, REG_BINARY, ...
2. the objects like DWORD, CString, CRect, CPoint have different overloaded operators
3. to use templates, you would need to serialize the object to the registry. but MS recommends to store as less information in the registry as possible and serializing is not always as effective as those spezialized classes.
|
|
|
|
|
Try to use some capitalization of your sentences though. Looks sloppy.
Jason Henderson quasi-homepage articles "Like it or not, I'm right!"
|
|
|
|
|
This is a very innovative class as far as features go. I must say it's better than any previous ones I've seen, again this is featurewise. I haven't used any class previously nor will I use them in future. I rarely do any registry manipulation and if I do, I prefer to call the API directly. But again, I am really impressed by your cool idea of overloading all the operators and all those type classes.
Regards
Nish
Author of the romantic comedy
Summer Love and Some more Cricket [New Win]
Review by Shog9
Click here for review[NW]
|
|
|
|
|