Click here to Skip to main content
Click here to Skip to main content

Registry Class

By , 17 Nov 1999
 

OK, there are many registry classes out there, but this is the one I wrote. I have used it for years in many commercial applications, so it is quite well tested in terms of reading and writing values. The only weakness of the class is that it does not have extensive support for deleting or copying keys.

There are a few useful features in the class worth noting:

  • When reading a value, you can provide a default value in case the operation fails.
  • You can read and write objects such as CFonts, CPoints, etc. from/to the registry.

How to Use the Class

  1. Declare a CRegistry object local to your function.
  2. Call CRegistry::SetRootKey(HKEY hRootKey) to set the root key.
  3. Call CRegistry::SetKey(CString strKey, BOOL bCanCreate). Pass bCanCreate=TRUE to write to the registry. Pass bCanCreate=FALSE to read from the registry.

Here is an example...

void CMyApp::ReadRegistry()
{
	CRegistry Reg();
	Reg.SetRootKey(HKEY_LOCAL_MACHINE);
	if (Reg.SetKey("Software\\MyApp\\Settings", FALSE))
	{
		m_nData = Reg.ReadInt("Data1", 0);
		pi = Reg.ReadFloat("Pi", 3.14159);
		m_strUserName = Reg.ReadString("UserName", "Default User Name");
		Reg.ReadFont(&m_font);
	}
	else
	{
		TRACE("Failed to open key\n");
	}

}

It's just that simple!

I have included the class interface below to illustrate the variety of member functions.

#ifndef __REGISTRY_H__
#define __REGISTRY_H__

class CRegistry
{
public:
	CRegistry();
	~CRegistry();

int m_nLastError;

// CRegistry properties	
protected:
	HKEY m_hRootKey;
	BOOL m_bLazyWrite;
	CString m_strCurrentPath;

public:
	inline BOOL PathIsValid() {
		return (m_strCurrentPath.GetLength() > 0); }
	inline CString GetCurrentPath() {
		return m_strCurrentPath; }
	inline HKEY GetRootKey() {
		return m_hRootKey; }


//CRegistry	methods
public:
	BOOL ClearKey();
	BOOL SetRootKey(HKEY hRootKey);
	BOOL CreateKey(CString strKey);
	BOOL DeleteKey(CString strKey);
	BOOL DeleteValue(CString strName);
	int GetDataSize(CString strValueName);
	DWORD GetDataType(CString strValueName);
	int GetSubKeyCount();
	int GetValueCount();
	BOOL KeyExists(CString strKey, HKEY hRootKey = NULL);
	BOOL SetKey(CString strKey, BOOL bCanCreate);
	BOOL ValueExists(CString strName);
	void RenameValue(CString strOldName, CString strNewName);

	// data reading functions
	COleDateTime ReadDateTime(CString strName, COleDateTime dtDefault);
	double ReadFloat(CString strName, double fDefault);
	CString ReadString(CString strName, CString strDefault);
	int ReadInt(CString strName, int nDefault);
	BOOL ReadBool(CString strName, BOOL bDefault);
	COLORREF ReadColor(CString strName, COLORREF rgbDefault);
	BOOL ReadFont(CString strName, CFont* pFont);
	BOOL ReadPoint(CString strName, CPoint* pPoint);
	BOOL ReadSize(CString strName, CSize* pSize);
	BOOL ReadRect(CString strName, CRect* pRect);
	DWORD ReadDword(CString strName, DWORD dwDefault);

	// data writing functions
	BOOL WriteBool(CString strName, BOOL bValue);
	BOOL WriteDateTime(CString strName, COleDateTime dtValue);
	BOOL WriteString(CString strName, CString strValue);
	BOOL WriteFloat(CString strName, double fValue);
	BOOL WriteInt(CString strName, int nValue);
	BOOL WriteColor(CString strName, COLORREF rgbValue);
	BOOL WriteFont(CString strName, CFont* pFont);
	BOOL WritePoint(CString strName, CPoint* pPoint);
	BOOL WriteSize(CString strName, CSize* pSize);
	BOOL WriteRect(CString strName, CRect* pRect);
	BOOL WriteDword(CString strName, DWORD dwValue);

};// end of CRegistry class definition


#endif

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Robert Pittenger, MCPD-EAD
President Starpoint Software Inc.
United States United States
Member
Bob Pittenger is founder and President of Starpoint Software Inc. He holds a B.A. degree from Miami University, M.S. and Ph.D. degrees from Purdue University, and an MBA from Xavier University. He has been programming since 1993, starting with Windows application development in C++/MFC and moving to C# and .NET around 2005 and is a .NET Microsoft Certified Professional Developer.
 
Bob is the author of two books:
Billionaire: How the Ultra-Rich Built Their Fortunes Through Good and Evil and What You Can Learn from Them
and
Wealthonomics: The Most Important Economic and Financial Concepts that Can Make You Rich Fast.
Visit http://www.billionairebook.net for more information.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionIs there any license limitation ?memberMember 449889214 Jun '09 - 22:46 
Is there any license limitation ?
GeneralQuerySubKeysmemberVider1 Aug '07 - 2:31 
Dear Robert, first thank's very much for share your code, nice Job,
 
I start to use it, and I need another functionaly, navigate trough subkeys, I add this fuctionaly to your class and I thing it's a good function to include in the article,
 

BOOL CRegistry::QuerySubKey(int index, CString& subkeyname)
{
TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name
DWORD cbName; // size of name string
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;
 
HKEY hKey;
ASSERT(m_strCurrentPath.GetLength() > 0);
 
if (::RegOpenKeyEx(m_hRootKey, LPCTSTR(m_strCurrentPath), 0,
KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS) return FALSE;
 
TCHAR achValue[MAX_VALUE_NAME];
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 subkeys, until RegEnumKeyEx fails.
 
if (cSubKeys)
{
if ((index < 0) || (index >= cSubKeys))
return FALSE;
 
cbName = MAX_KEY_LENGTH;
retCode = RegEnumKeyEx(hKey, index,
achKey,
&cbName,
NULL,
NULL,
NULL,
&ftLastWriteTime);
if (retCode == ERROR_SUCCESS)
{
subkeyname = achKey;
}
else
return FALSE;
}
 
::RegCloseKey(hKey);
 
return TRUE;
}

 
And a little example of how to use it:
 

m_reg.SetRootKey(HKEY_LOCAL_MACHINE);
if (m_reg.SetKey("Software\\NFFormDestiller\\ExportDrivers", FALSE)) {
for (int x = 0; x < m_reg.GetSubKeyCount(); x++) {
CString subkey;
if (m_reg.QuerySubKey(x, subkey)) {
CRegistry reg;
reg.SetRootKey(HKEY_LOCAL_MACHINE);
if (reg.SetKey("Software\\NFFormDestiller\\ExportDrivers\\"+subkey, FALSE)) {
CString desc = reg.ReadString("","");
int index = m_lstcfg.InsertItem(x,desc,0);
m_lstcfg.SetItemText(index,1,subkey);
}
}
}
}

 
Best Regards
Rey
GeneralRe: QuerySubKeysmemberkid_sputnik7 Oct '07 - 5:27 
i cant believe noone has relied to this... vider, thank you so much for this addon (i couldnt use this class for what i wanted without it)! and of course, thank your Robert for the class/code. Excellent stuff.im coming from a C# background (im young, i know!), and code like this is making me realize how usable MFC can be, to the point where now i prefer it.
 
just wanted to again say thanks!
 
-daniel b
GeneralRegistry problem when UNICODE is definedmemberrajan40083 Aug '06 - 5:09 
I tried to write Systems IP address into registry. When UNICODE is not defined then i could see the value from registry. And if Unicode is defined then i'm seeing 4 square boxed in the place of IP address..
 
How to show the IP address in the registry though UNICODE is defined?
 
Code snippet:
int main(void)
{
HKEY hKey;
char name[] = "199.68.56.33";

RegOpenKeyExW(HKEY_LOCAL_MACHINE,L"SOFTWARE\\MyCompany\\\\InstallInfo", 0, KEY_ALL_ACCESS, &hKey);
 
RegSetValueEx(hKey, L"FTEBaseIPAddress", 0, REG_SZ, name, ((strlen(name)+1)*sizeof(TCHAR)) );

return 1;
}
 
ssssssssssssssssssssssss
ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss

AnswerRe: Registry problem when UNICODE is definedmemberchsml7 Oct '06 - 9:50 
I'm sorry about my poor english, but u shoud use _T("") rathor than L""
GeneralRegnum buffermemberJRaiden31 Jul '06 - 0:40 
when enumerating the registry or passing across buffers is there a need to null terminate the buffer as shown below:
 
DWORD dwSize = MAX_PATH;
TCHAR szSubkeyName[MAX_PATH+1];
memset(szSubkeyName,0x00,sizeof(TCHAR)*(MAX_PATH+1));
 
while( RegEnumKeyEx(hKey,
dwIndex,
szSubkeyName,
&dwSize,
NULL,
NULL,
NULL,
&ftLastWriteTime) == ERROR_SUCCESS)
{

NewsNT Native Registry API version of this here!memberDan Madden19 Jun '06 - 18:29 
Hi Robert,
 
I hope you don't mind (and you are mentioned in it), but I took your excellent format of this class (CRegistry) and created a class that uses the nt.dll Native functions (e.g. NtCreateKey, NtDeleteKey, etc...). I did this because I have enjoyed you class for a long time and always use in apps that I write. I especially like the simplicity of it and that is why I adopted it for this new class I wrote "CNtRegistry" - link is below. Please take a look and let me know what you think?
 
There is a lot of added stuff in it like "Hiding Keys, Copying Keys/Values, Searching, Deleting recursively, etc...". I did this because I saw a lot of folks here asking for it.
 
http://www.codeproject.com/system/NtRegistry.asp[^]
 
Again, let me know what you think.

 
Regards,
 
Dan
GeneralRead type BinarymemberDRLaverdure16 May '06 - 7:37 
I am trying to read a key of the type Binary, I do not see any function for this, The data is actually a person's name so I need to read a type Binary into a CSrting
 
Example: 44 00 6F 00 6E 00 20 00 4C 00 61 00 76 00 65 00 72 00 64 00 75 00 72 00 65
 
Converted=Don Laverdure
 
Any suggestions?Big Grin | :-D
 

 
Cheers,
Don
NewsRe: Read type BinarymemberDan Madden19 Jun '06 - 18:32 
Check out my new posting. It has this functionality...
 
http://www.codeproject.com/system/NtRegistry.asp[^]
 
Regards,
 
Dan
GeneralWriteDwordmemberJason Sundram16 Oct '05 - 19:47 
Thanks for a useful class. It would be nice if WriteDword() specified REG_DWORD instead of REG_BINARY.
 
Jason
 
The question of whether computers can think is like the question of whether submarines can swim (Dijkstra)

NewsRe: WriteDwordmemberDan Madden19 Jun '06 - 18:39 
Check out my new posting. It has this functionality...you could also use to get an idea how to modify his code (or whatever you want).
 
http://www.codeproject.com/system/NtRegistry.asp[^]
 
Regards,
 
Dan
GeneralUNICODE bugmemberWarren Stevens11 Aug '05 - 6:17 
Please note that there is a UNICODE bug in CRegistry::WriteString
the last parameter for RegSetValueEx should be number of bytes, NOT the existing number of characters, so it must be multiplied by sizeof(TCHAR) to fix it. It must also be cast to a DWORD to get rid of level 4 warnings.
 
Warren
GeneralRe: UNICODE bugmembersGrabert11 Sep '07 - 21:26 
There is a lot more to be fixed:
 
- change all LPSTR to LPCTSTR
- change strlen() to _tcslen()
- cast file.GetLength() to (DWORD)file.GetLength();
- change all char to TCHAR
 
This has to be done for all string handling routines, like ReadString, too. Roll eyes | :rolleyes:
Hope this will help..
QuestionKEY_ALL_ACCESS for reading ?membercross bones style23 Mar '05 - 3:20 
I've noted that if you are a non-priviliged user and you want to read the Registry, you may encounter problem when you use CRegistry::KeyExists and CRegistry::SetKey.
 
This seems due to the fact that these methods call ::RegOpenKeyEx with KEY_ALL_ACCESS parameter. I've seen that if you use KEY_READ everything is ok.
 
I'm not familiar with Microsoft security access masks, MSDN says that KEY_ALL_ACCESS is a combination of KEY_READ, KEY_WRITE and KEY_CREATE_LINK but it is not clear whether the combination is AND or OR.
From this, it seems to me that KEY_ALL_ACCESS requires both read and write rights, the latter not being available to a non-priviliged user.
 
So, why KEY_ALL_ACCESS has been used for reading ? Isn't KEY_READ more appropriate ?
AnswerRe: KEY_ALL_ACCESS for reading ?memberkid_sputnik7 Oct '07 - 5:59 
i agree. i would perhaps add a method for determining the current access level? i added this hack for my own program using this class.
GeneralRe: KEY_ALL_ACCESS for reading ?memberkid_sputnik7 Oct '07 - 6:15 
if anyone is interested, here is my hack:
 
in protected section, i added
 
int m_Access;
 

i set this to KEY_ALL_ACCESS is the constructor.
 
then, i added these methods to the header file:
 

// changes current access level to KEY_READ.
void SetToReadAccess() { m_Access = KEY_READ; }
 
// changes current access level to KEY_WRITE.
void SetToWriteAccess() { m_Access = KEY_WRITE; }
 
// changes current access level to KEY_EXECUTE.
void SetToExecuteAccess() { m_Access = KEY_EXECUTE; }
 
// changes current access level to KEY_ALL_ACCESS. This is the default.
void SetToAllAccess() { m_Access = KEY_ALL_ACCESS; }
 
i thought this method, of the access being set by the last SetToAccess... call fit in with the way the rest of the class works (like, main and sub keys being remembered from the last set calls), and this avoids the user having to dig out the access constants in the winnt.h file. i excluded the specific access constants, and just went with these 4 that are combos of the original ones.
 
with this i am finally able to use this class, i am reading values set by the VCP FTDI USB-to-ComPort driver, and it wouldn't read with KEY_ALL_ACCESS privelages.
 
- daniel b
GeneralAnother Thanks from me!memberhazem-mk22 May '04 - 3:43 
Thanks alot, thats exactly what I was looking for;)
GeneralExcellentmemberBalkrishna Talele11 May '04 - 21:08 
Mr Robert, you made me ROBOT!!! No more words to utter!!!
cheers
Balkrishna Talele
GeneralAn Excellent Piece of CodememberKaren030227 Nov '03 - 19:31 
This is an excellent piece of code Robert. It turned what was becoming a nightmarish task into a breeze! Thanks so much for sharing it!!
 
Karen
GeneralBugfix: Reading/writing long stringsmemberPEK21 Jan '03 - 2:46 
The original version limited textstrings to 255 characters, I needed more. I changed WriteString() and ReadString() to get what I wanted. The following doesn't have any limit and works well in Unicode too. This includes the code from my previous bugfix message.
 
BOOL CRegistry::WriteString(CString strName, CString strValue)
{
	ASSERT(m_strCurrentPath.GetLength() > 0);
	BOOL bSuccess = TRUE;
	HKEY hKey;
 
	if (::RegOpenKeyEx(m_hRootKey, LPCTSTR(m_strCurrentPath), 0,
		KEY_WRITE, &hKey) != ERROR_SUCCESS) return FALSE;
 
	if (::RegSetValueEx(	hKey, 
				LPCTSTR(strName),
				NULL,
				REG_SZ,
				(LPBYTE) ( (const TCHAR*) strValue),
				(strValue.GetLength()+1)*sizeof(TCHAR)
				)
		 != ERROR_SUCCESS) bSuccess = FALSE;
 
	if (!m_bLazyWrite) ::RegFlushKey(hKey);
	::RegCloseKey(hKey);
	return bSuccess;
}
 
CString CRegistry::ReadString(CString strName, CString strDefault)
{
	DWORD dwType = REG_SZ;
	DWORD dwSize = 0;
	BOOL bSuccess = TRUE;
	HKEY hKey;
							 
	ASSERT(m_strCurrentPath.GetLength() > 0);
 
	// make sure it is the proper type
	dwType = GetDataType(strName);
	
	if (dwType != REG_SZ && dwType != REG_EXPAND_SZ)
	{
		return strDefault;
	}
 
	m_nLastError = ::RegOpenKeyEx(m_hRootKey, LPCTSTR(m_strCurrentPath), 0,
		KEY_READ, &hKey);
	if (m_nLastError != ERROR_SUCCESS) return strDefault;
 
	//First, get size of string
	::RegQueryValueEx(	hKey,
			LPCTSTR(strName), 
			NULL,
			&dwType, 
			NULL,	//Don't require any data
			&dwSize);
 
	//Add one extra character
	dwSize += sizeof(TCHAR);
 
	//Allcolate memory
	TCHAR* sz = new TCHAR[ dwSize/sizeof(TCHAR) ];
 
	//Do the actual reading
	m_nLastError = ::RegQueryValueEx(	hKey, 
					LPCTSTR(strName), 
					NULL,
					&dwType, 
					(LPBYTE)sz, 
					&dwSize);
 
	//Save what we got, if we got anything
	CString retValue;
	if(bSuccess)
		retValue = sz;
 
	//Clean up
	delete [] sz;
 
	if (m_nLastError != ERROR_SUCCESS) bSuccess = FALSE;
	::RegCloseKey(hKey);	
	
	if (!bSuccess) return strDefault;
	return retValue;
}

GeneralRe: Bugfix: Reading/writing long stringsmemberHamed Mosavi12 May '08 - 20:51 
PEK wrote:
The original version limited textstrings to 255 characters, I needed more. I changed WriteString() and ReadString()

 
Thank you so much. I had the same issue that solved by reading your post.Rose | [Rose]
 
// "In the end it's a little boy expressing himself." Yanni
while (I_am_alive)
{
    cout<<"I love to do more than just programming.";
}

GeneralKey/Value EnumerationmemberGermanTux22 Dec '02 - 2:24 
Is there anyway to enumerate Key and Value entries?
GeneralRe: Key/Value Enumerationmemberlzandman24 Aug '04 - 2:26 
I've added these two member functions to the class so I could enumerate keys/values. Maybe they are of use to you...
 
Don't forget to include <vector>!
 
BOOL CRegistry::EnumKeys(std::vector<CString> *sv)
{
     BOOL bSuccess = TRUE;
     _TCHAR sz[255];
      DWORD dwSize;
     HKEY hKey;
 
      ASSERT(m_strCurrentPath.GetLength() > 0);
 
      sv->clear();
 
     m_nLastError = ::RegOpenKeyEx(m_hRootKey, LPCTSTR(m_strCurrentPath), 0,
          KEY_READ, &hKey);
     if (m_nLastError != ERROR_SUCCESS) return FALSE;
 
      DWORD idx = 0;
      DWORD err;
 
      do
      {
            dwSize = 255;
            err = ::RegEnumKeyEx(hKey,idx,(LPTSTR)sz,&dwSize,NULL,NULL,NULL,NULL);
 
            if (ERROR_SUCCESS == err)
            {
                  sv->push_back(CString((LPCTSTR)sz));
                  ++idx;
            }
      } while (ERROR_SUCCESS == err);
 
      ::RegCloseKey(hKey);    
 
      m_nLastError = err;
 
      if (m_nLastError != ERROR_NO_MORE_ITEMS) bSuccess = FALSE;
    
     if (!bSuccess) return FALSE;
 
     return TRUE;
}
 
BOOL CRegistry::EnumValues(std::vector<CString> *sv)
{
     BOOL bSuccess = TRUE;
     _TCHAR sz[16383];
      DWORD dwSize;
     HKEY hKey;
 
      ASSERT(m_strCurrentPath.GetLength() > 0);
 
      sv->clear();
 
     m_nLastError = ::RegOpenKeyEx(m_hRootKey, LPCTSTR(m_strCurrentPath), 0,
          KEY_READ, &hKey);
     if (m_nLastError != ERROR_SUCCESS) return FALSE;
 
      DWORD idx = 0;
      DWORD err;
 
      do
      {
            dwSize = 16383;
            err = ::RegEnumValue(hKey,idx,(LPTSTR)sz,&dwSize,NULL,NULL,NULL,NULL);
 
            if (ERROR_SUCCESS == err)
            {
                  sv->push_back(CString((LPCTSTR)sz));
                  ++idx;
            }
      } while (ERROR_SUCCESS == err);
 
      ::RegCloseKey(hKey);    
 
      m_nLastError = err;
 
      if (m_nLastError != ERROR_NO_MORE_ITEMS) bSuccess = FALSE;
    
     if (!bSuccess) return FALSE;
 
     return TRUE;
}

GeneralLinker warning in Release Buildsmemberkarl_w28 Nov '02 - 22:52 
I get the warning
"LINK : warning LNK4089: all references to "GDI32.dll" discarded by
/OPT:REF"
when building in Release Mode.
 
For testing I created a new dialogbased project and built it in Release Mode without any errors/warnings. After adding CRegistry class and rebuilding in Release Mode I got this warning. I only use the methods "SetRootKey", "SetKey", "WriteString", "ReadString", "ClearKey"
 
It builds without warnings in Debug Mode.
 
I'm using VC++6 (german version, latest SP) on Win2k.

 
--
 
Karl
GeneralRe: Linker warning in Release Buildsmemberkarl_w1 Dec '02 - 23:10 
Problem solved,
 
the references to GDI32.dll were caused by "ReadPoint()" and "WritePoint()". I took those out. Now it's linking without any warnings.
 
--
 
Karl

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 18 Nov 1999
Article Copyright 1999 by Robert Pittenger, MCPD-EAD
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid