|
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
|
|
|
|
|
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.
Hope this will help..
|
|
|
|
|
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 ?
|
|
|
|
|
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.
|
|
|
|
|
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
|
|
|
|
|
Thanks alot, thats exactly what I was looking for;)
|
|
|
|
|
Mr Robert, you made me ROBOT!!! No more words to utter!!!
cheers
Balkrishna Talele
|
|
|
|
|
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
|
|
|
|
|
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);
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;
::RegQueryValueEx( hKey,
LPCTSTR(strName),
NULL,
&dwType,
NULL,
&dwSize);
dwSize += sizeof(TCHAR);
TCHAR* sz = new TCHAR[ dwSize/sizeof(TCHAR) ];
m_nLastError = ::RegQueryValueEx( hKey,
LPCTSTR(strName),
NULL,
&dwType,
(LPBYTE)sz,
&dwSize);
CString retValue;
if(bSuccess)
retValue = sz;
delete [] sz;
if (m_nLastError != ERROR_SUCCESS) bSuccess = FALSE;
::RegCloseKey(hKey);
if (!bSuccess) return strDefault;
return retValue;
}
|
|
|
|
|
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.
// "In the end it's a little boy expressing himself." Yanni
while (I_am_alive) { cout<<"I love to do more than just programming."; }
|
|
|
|
|
Is there anyway to enumerate Key and Value entries?
|
|
|
|
|
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;
}
|
|
|
|
|
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
|
|
|
|
|
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
|
|
|
|
|
Obviously this warning is generated because the class is linking to functions in this DLL which your code is not calling. The compiler omits these references to optimize your code -- no sense in loading a bunch of code you don't need.
Of course, you could just simply ignore this warning, as it's basically just an FYI message.
If you ABSOLUTELY MUST achieve a warning-free compile, you may need to omit more than ReadPoint() and WritePoint(). I still received the warning after commenting out these two functions in VC6. In my case, I commented out the Read and Write functions for Color, Font, Point, Size and Rect.
To fully optimize your code, it's actually a good idea to comment out all functions that you aren't using. Of course, it goes without saying you should also comment out the corresponding declarations in the header file.
I LOVE this class! I use it all the time, and have NEVER had a problem with it.
|
|
|
|
|
I think this is an excellent class, but still it has a little problem .
If you working with Unicode and trying to write a string to the registry only the first half of the string is written. The reason: the length is calculated correctly but every character is 2 bytes. RegSetValueEx want's the size in bytes so it's necessary to multiply with 2. OK, no more details . Here is the fix:
Remove these lines:
#ifdef _UNICODE
if (::RegSetValueEx(hKey, LPCTSTR(strName), 0,
REG_SZ, (LPBYTE)sz, wcslen(sz) + 1)
!= ERROR_SUCCESS) bSuccess = FALSE;
#else
if (::RegSetValueEx(hKey, LPCTSTR(strName), 0,
REG_SZ, (LPBYTE)sz, strlen(sz) + 1)
!= ERROR_SUCCESS) bSuccess = FALSE;
#endif
…and replace width these:
if (::RegSetValueEx(hKey, LPCTSTR(strName), NULL,
REG_SZ, (LPBYTE)sz, (lstrlen(sz)+1)*sizeof(TCHAR))
!= ERROR_SUCCESS) bSuccess = FALSE;
Yes, these lines are enough to do the work in Unicode and none-Unicode versions.
|
|
|
|
|
another method could be
if (::RegSetValueEx(hKey, LPCTSTR(strName), 0,<br />
REG_SZ, (LPBYTE)sz, (DWORD)(_tcslen(sz) + 1))<br />
!= ERROR_SUCCESS) bSuccess = FALSE;
|
|
|
|
|
I am trying to use the simple class but I am problably stupid
because I can t found how to get the default data of a Key.
I mean the value to entrer in the function ReadString("???", "***");
My registry :
(par défaut) REG_SZ Winzip <- Who to get this value
Content Type REG_SZ application/mac-binhex40
Any idees ?
|
|
|
|
|
Sorry I am found the solution 10 secondes later LOL
Put empty CString to get the default value !!!!!!!
|
|
|
|
|
I'm not even sure that this is the problem, but when I run my application using your CRegistry class, it seems to have a problem writing the keys when I run as a user that does not have administrative privileges. Do you know why this might be?
Here's my code:
void CPreferences::InitializeRegistry()
{
CRegistry Reg;
Reg.SetRootKey(HKEY_LOCAL_MACHINE);
if( !Reg.KeyExists( "Software\\Compact Power" ) )
{
Reg.CreateKey( "Software\\Compact Power" );
}
if( !Reg.KeyExists( "Software\\Compact Power\\BMS Monitor" ) )
{
Reg.CreateKey( "Software\\Compact Power\\BMS Monitor" );
}
if( !Reg.KeyExists( "Software\\Compact Power\\BMS Monitor\\Settings" ) )
{
Reg.CreateKey( "Software\\Compact Power\\BMS Monitor\\Settings" );
}
Reg.SetKey( "Software\\Compact Power\\BMS Monitor\\Settings", FALSE );
if( !Reg.ValueExists( "COMPORT" ) )
Reg.WriteString( "COMPORT", "COM1" );
if( !Reg.ValueExists( "PARITY" ) )
Reg.WriteString( "PARITY", "NONE" );
if( !Reg.ValueExists( "DATABITS" ) )
Reg.WriteString( "DATABITS", "8" );
if( !Reg.ValueExists( "STOPBITS" ) )
Reg.WriteString( "STOPBITS", "1" );
if( !Reg.ValueExists( "BAUDRATE" ) )
Reg.WriteString( "BAUDRATE", "57600" );
if( !Reg.ValueExists( "HANDSHAKING" ) )
Reg.WriteString( "HANDSHAKING", "NONE" );
if( !Reg.ValueExists( "RUNMODE" ) )
Reg.WriteString( "RUNMODE", "1" );
if( !Reg.ValueExists( "LASTDB" ) )
Reg.WriteString( "LASTDB", "" );
if( !Reg.ValueExists( "BASEDBDIR" ) )
Reg.WriteString( "BASEDBDIR", "C:\\BMSMonitorData" );
if( !Reg.ValueExists( "AUTOSAVE" ) )
Reg.WriteString( "AUTOSAVE", "5" );
if( !Reg.ValueExists( "SAVEDATA" ) )
Reg.WriteString( "SAVEDATA", "0" );
if( !Reg.ValueExists( "LOGFILE" ) )
Reg.WriteString( "LOGFILE", "" );
}
Thanks for your help.
-Matt
------------------------------------------
The 3 great virtues of a programmer:
Laziness, Impatience, and Hubris.
--Larry Wall
|
|
|
|
|
If you are are running an NT based windows then the user will need to be given privleges access the registry. This can be done through a program called regedt32.exe. In there you can set the permissions on the registry keys the same way that you can set up with files and directories, ie. Full Control, Read, Write etc.
Cheers,
Clint
|
|
|
|
|
That is good to know, but how do I do this programatically? I need to ensure that *anyone* can have permission to read and write these settings. I want my app to be able to add the keys as necessary if they don't exist starting with a key just under the Software directory in HKEY_LOCAL_MACHINE regardless of the current user's permissions.
Thanks for your help.
-Matt
------------------------------------------
The 3 great virtues of a programmer:
Laziness, Impatience, and Hubris.
--Larry Wall
|
|
|
|
|
From what I could find out it seems that it can't be done programatically. Unless there is a way to run the program as a privledged user. Only one with administrator privleges can even make the changes to the registry keys to give lower users access. That means the lower privledged user is going a pretty hard time accessing the registry. If they did have access then that would be a potential security breach. In any case good luck with your endevour.
Cheers,
Clint
|
|
|
|
|
Duh! I'm an idiot. You might expect this kind of question from a long time Windows guy, but I started with Unix. If anyone should understand permissions, I should. If a user had the ability to give himself super-user rights, what's the point of security? Sorry for the stupid question. I guess I just have a hard time getting it through my head that Microsoft finally got around to adding security after those excuses for OSs Win95/98.
I think I'm just going to start looking through the registry for a spot that is not as protected.
Thanks for your help.
-Matt
------------------------------------------
The 3 great virtues of a programmer:
Laziness, Impatience, and Hubris.
--Larry Wall
|
|
|
|
|
That's what HKEY_CURRENT_USER\Software is for
These are not the droids you're looking for...
|
|
|
|
|