My code is setting 1 char of the registry name and value.
bool setRegValue(std::wstring valueName, std::wstring valueToSet)
{
HKEY key=NULL;
if (get_HKEY_LOCAL_MACHINE(&key) && (key!=NULL))
{
if (RegSetValueEx((HKEY)key, (LPCSTR)valueName.c_str(), 0, REG_SZ, (const BYTE*)valueToSet.c_str(), (valueToSet.size() +1)*sizeof(wchar_t)) != ERROR_SUCCESS)
{
RegCloseKey((HKEY)key);
log.error("Failed to SET the registry value. Name : "+ std::string(valueName.begin(), valueName.end())+" Value : "+ std::string(valueToSet.begin(), valueToSet.end()));
RegCloseKey(key);
return false;
}
log.info("Successfully SET the registry value. Name : " + std::string(valueName.begin(), valueName.end()) + " Value : " + std::string(valueToSet.begin(), valueToSet.end()));
RegCloseKey(key);
return true;
}
return false;
}
And, this is the calling method.
bool setServiceAsClient()
{
if (setRegValue(L"ServiceType", L"Client"))
return true;
return false;
}
Its setting the registry name as S
And, value as C
When a wide-char string is seen as a 1-char string, that's a symptom that you're providing a wide-char string where a multi-byte string is expected.
Indeed we see the error here: (LPCSTR)valueName.c_str() (where valueName is a std::wstring).
LPCSTR is const char *, whereas wstring::c_str() returns const wchar_t *.
So L"ServiceType" is seen as "S\0e\0r\0v\0i\0c\0e\0T\0y\0p\0e\0", which becomes simply "S"
There are 2 solutions possible:
Use std::string instead of std::wstring (and remove the L from strings like L"ServiceType"). This solution is not recommended, since the Win32 API internally is Unicode.
Change project settings from Multi-byte to Unicode Character Set and remove the casting to LPCSTR (if you do need to cast, use LPTSTR instead, which always matches project character set settings).
See Working with Strings - Win32 API for more details.
Related
My code is
string str = "C:\\Users\\mardare\\Downloads\\config";
LPSTR lp = const_cast<char *>(str.c_str());
MessageBox ( NULL, "The selected folder is : "+lp, "Bye!", MB_OK );
And when I try to build it and run i get this error
invalid operands of types 'const char [26]' and 'LPSTR'
LPSTR represents a char *-type, and string literal "The ..." denotes a const char[26]. You get the error because in your expression "The selected folder is : "+lp, you try to "concatenate" them with operator +, which is not supported for members of type const char* or char *.
Operator + is, however, supported for members of type std::string; So you could work around this by, for example, the following code:
string folder = "The selected folder is : " + str;
MessageBox ( NULL, folder.c_str(), "Bye!", MB_OK );
You are trying to concatenate a char* pointer (obtained from your string variable) to a const char[] array (from your string literal). You can't do that concatenation.
Also, the const_cast is not necessary.
You will have to convert one of the pointers to a string, eg:
string str = "C:\\Users\\mardare\\Downloads\\config";
const char *lp = str.c_str();
MessageBox ( NULL, (string("The selected folder is : ") + lp).c_str(), "Bye!", MB_OK );
Or:
string str = "C:\\Users\\mardare\\Downloads\\config";
const char *lp = str.c_str();
MessageBox ( NULL, ("The selected folder is : " + string(lp)).c_str(), "Bye!", MB_OK );
Alternatively, simply get rid of the char* from the string, you don't actually need it:
string str = "C:\\Users\\mardare\\Downloads\\config";
MessageBox ( NULL, ("The selected folder is : " + str).c_str(), "Bye!", MB_OK );
Or:
string str = "C:\\Users\\mardare\\Downloads\\config";
string msg = "The selected folder is : " + str;
MessageBox ( NULL, msg.c_str(), "Bye!", MB_OK );
Or:
string str = "C:\\Users\\mardare\\Downloads\\config";
ostringstream msg;
msg << "The selected folder is : " << str;
MessageBox ( NULL, msg.str().c_str(), "Bye!", MB_OK );
If you consider the second parameter of MessageBox in this line of code:
MessageBox ( NULL, "The selected folder is : "+lp, "Bye!", MB_OK );
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can see a string literal, i.e. "The selected folder is : ", which is a NUL-terminated C-style raw char array (the const char [26] part in your error message); and you are trying to "concatenate" it using the operator + with lp, which you defined as LPSTR (i.e. char*) in your code.
Such operation is not defined, hence the error message.
It's clear that your intent is to concatenate two strings, and print the resulting string calling the MessageBox API.
To do so, consider using a C++ string class. If you are at the Win32 API boundary, I think ATL/MFC CString is very convenient. Or, you can use a standard string class like std::string.
std::string (and CString as well...) conveniently defines proper overloads of operator+(), which make it possible to concatenate strings with a simple syntax, e.g.:
string str = "C:\\Users\\mardare\\Downloads\\config";
string message = "The selected folder is : " + str;
At this point, if you want to pass your message std::string instance to the MessageBox API (which is a C-interface API), you can call the std::string::c_str() method:
MessageBox( NULL, message.c_str(), /* ... other params ... */ );
Note that this code will compile in ANSI/MBCS builds (which is an obsolete build setting; Unicode build has been the default since VS2005, more than 10 years ago).
In your case, the actual Win32 API that gets called is MessageBoxA (note the A suffix).
Your code will fail to compile in VS solutions that use Unicode builds; in those cases, you may want to use the explicit MessageBoxA() call.
EDIT Not directly related to your error message, but might be interesting for you to know: If you are using a recent Visual C++ compiler, you may want to replace NULL with nullptr in your C++ code, and you can use raw string literals to simplify the way you write your paths, like this:
// Was: string str = "C:\\Users\\mardare\\Downloads\\config";
string str = R"(C:\Users\mardare\Downloads\config)";
You cannot add together a const char[26] (that is, your "The selected folder is : " literal) and a char* pointer (that is, your lp variable of type LPSTR, which is char*). Concatenate the strings first and then obtain a const char*, which you can pass to the function:
std::string str = R"(C:\Users\mardare\Downloads\config)"; //don't care about escape sequences, use raw string literals.
MessageBox(nullptr, ("The selected folder is : " + str).c_str(), "Bye!", MB_OK);
I am writing a program to edit the windows registry key by C++, but when I try to pass a string value to library function RegSetValueEx(), there is a file start with TEXT() which could only be hardcode value in it.
Parts of my code:
string region;
string excelserver_type;
string keyname = region + excelserver_type;
if (RegSetValueEx(key64, TEXT("XXXXXXXXX"), 0, REG_SZ, (LPBYTE)TEXT("XXXXXXXXXX"), 100) != ERROR_SUCCESS)
{
RegCloseKey(key);
cout << "Unable to set registry value in HKEY_LOCAL_MACHINE\\Software" << endl;
}
When I try to replace "XXXXXXXX" by keyname, it gives me an error. How do I pass value of keyname in RegSetValueEx()?
You need to use the std::wstring type instead. This will give you a wide (Unicode) string, based on the wchar_t type, which is how Windows stores strings internally.
Then, you can simply use the c_str() member function to retrieve a pointer to a C-style string, and pass this directly to the RegSetValueEx function. The size() member function gives you the length of the string, which you can pass as the cbData parameter, except for two caveats:
cbData expects the length of the string to include the terminating NUL character, so you will need to add 1 to the length returned by size().
cbData expects the size of the string in bytes, not the number of characters, so for a wide string, you will need to multiply the value returned by size() by the length of a wchar_t.
bool SetStringValue(HKEY hRegistryKey,
const std::wstring& valueName,
const std::wstring& data)
{
assert(hRegistryKey != nullptr);
return (RegSetValueExW(hRegistryKey,
valueName.c_str(),
0,
REG_SZ,
(LPBYTE)(data.c_str()),
(data.size() + 1) * sizeof(wchar_t)) == ERROR_SUCCESS);
}
If you absolutely have to use narrow (ANSI) strings (and you shouldn't, because you're interfacing directly with the operating system here, not working with user data), you can do the same thing but explicitly call the ANSI version of RegSetValueEx, which has an A suffix. Here, you still need to add 1 to the length, but the size in bytes is equivalent to the number of characters, so no scaling is necessary.
bool SetStringValue_ANSI(HKEY hRegistryKey,
const std::string& valueName,
const std::string& data)
{
assert(hRegistryKey != nullptr);
return (RegSetValueExA(hRegistryKey,
valueName.c_str(),
0,
REG_SZ,
(LPBYTE)(data.c_str()),
data.size() + 1) == ERROR_SUCCESS);
}
Internally, RegSetValueExA will convert the string to Unicode and then perform the same task as RegSetValueExW.
My application properly reads and writes to the registry. Now, I need to read a registry value from:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineGuid
Here is my code:
bool GetWindowsID(string &winID)
{
HKEY hKey = 0, hKeyType = HKEY_LOCAL_MACHINE;
bool status = false;
DWORD dwType = 0;
DWORD dwBufSize = 256;
char value[256] = "\0";
if (RegOpenKeyEx(hKeyType, L"SOFTWARE\\Microsoft\\Cryptography", NULL, KEY_QUERY_VALUE|KEY_WOW64_64KEY, &hKey) == ERROR_SUCCESS)
{
dwType = REG_SZ;
if (RegQueryValueEx(hKey, L"MachineGuid", NULL, &dwType, (LPBYTE)value, &dwBufSize) == ERROR_SUCCESS)
status = true;
RegCloseKey(hKey);
}
winID.assign(value);
return status;
}
I get the guid but in value array after each character their is a "\0" value due to which only first character of array gets assigned to string. This is wierd!
You have built targeting Unicode and so the registry API is returning UTF-16 Unicode text. Instead of char use wchar_t and remember that each wchar_t element is 2 bytes wide.
Do also make sure that you account for the returned string not being null-terminated, as described in the documentation. You must take account of the value returned in dwBufSize.
I get the guid but in value array after each character their is a "\0"
value due to which only first character of array gets assigned to
string. This is wierd!
This is because you are calling the Unicode version of RegQueryValueEx(), so the string is returned in Unicode (UTF-16).
You will have to use wide character parameters to get the value.
Change this line:
char value[256] = "\0";
To use wchar_t instead.
I created a simple function:
std::wstring GetRegKey(const std::string& location, const std::string& name){
const int valueLength = 10240;
auto platformFlag = KEY_WOW64_64KEY;
HKEY key;
TCHAR value[valueLength];
DWORD bufLen = valueLength*sizeof(TCHAR);
long ret;
ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, location.c_str(), 0, KEY_READ | platformFlag, &key);
if( ret != ERROR_SUCCESS ){
return std::wstring();
}
ret = RegQueryValueExA(key, name.c_str(), NULL, NULL, (LPBYTE) value, &bufLen);
RegCloseKey(key);
if ( (ret != ERROR_SUCCESS) || (bufLen > valueLength*sizeof(TCHAR)) ){
return std::wstring();
}
std::wstring stringValue(value, (size_t)bufLen - 1);
size_t i = stringValue.length();
while( i > 0 && stringValue[i-1] == '\0' ){
--i;
}
return stringValue;
}
And I call it like auto result = GetRegKey("SOFTWARE\\Microsoft\\Cryptography", "MachineGuid");
yet string looks like
㤴ㄷ㤵戰㌭㉣ⴱ㔴㍥㤭慣ⴹ㍥摢㘵〴㉡ㄵ\0009ca9-e3bd5640a251
not like RegEdit
4971590b-3c21-45e3-9ca9-e3bd5640a251
So I wonder what shall be done to get a correct representation of MachineGuid in C++?
RegQueryValueExA is an ANSI wrapper around the Unicode version since Windows NT. When building on a Unicode version of Windows, it not only converts the the lpValueName to a LPCWSTR, but it will also convert the lpData retrieved from the registry to an LPWSTR before returning.
MSDN has the following to say:
If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, and
the ANSI version of this function is used (either by explicitly
calling RegQueryValueExA or by not defining UNICODE before including
the Windows.h file), this function converts the stored Unicode string
to an ANSI string before copying it to the buffer pointed to by
lpData.
Your problem is that you are populating the lpData, which holds TCHARs (WCHAR on Unicode versions of Windows) with an ANSI string.
The garbled string that you see is a result of 2 ANSI chars being used to populate a single wchar_t. That explains the Asian characters. The portion that looks like the end of the GUID is because the print function blew past the terminating null since it was only one byte and began printing what is probably a portion of the buffer that was used by RegQueryValueExA before converting to ANSI.
To solve the problem, either stick entirely to Unicode, or to ANSI (if you are brave enough to continue using ANSI in the year 2014), or be very careful about your conversions. I would change GetRegKey to accept wstrings and use RegQueryValueExW instead, but that is a matter of preference and what sort of code you plan on using this in.
(Also, I would recommend you have someone review this code since there are a number of oddities in the error checking, and a hard coded buffer size.)
I have a function that gets a std::string. That function calls
RegSetValueEx
the 5th parameter is the value of the registry value and expects a variable of type const BYTE*.
So I have to convert the std::string to const BYTE* and also give the length of the resulting array as the 6th parameter.
I have found a way to do it, but it feels ugly and I don't really understand what is going on. Here is a slimmed down version of that function:
void function(const std::string& newValue)
{
HKEY keyHandle;
if(RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("some key"),0,KEY_ALL_ACCESS,&keyHandle) == ERROR_SUCCESS)
{
std::wstring wNewValue;
wNewValue.assign(newValue.begin(),newValue.end());
if (RegSetValueEx(keyHandle, TEXT("some value"), NULL, REG_SZ, (const BYTE*)(LPCTSTR)(wNewValue.c_str()), wNewValue.size()*2)==ERROR_SUCCESS)
{
//do something
}
RegCloseKey(keyHandle);
}
}
As you can see, i first make a wide string (UNICODE is defined), then use a double cast, and for the length i have to do *2, else it will only set half of the input string.
Is this form of cast the normal/best way to do it?
Why the * 2, what would be a better way?
void function(const std::string& newValue)
{
HKEY keyHandle;
if(RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("some key"),0,KEY_ALL_ACCESS,&keyHandle) == ERROR_SUCCESS)
{
if (RegSetValueExA(keyHandle, "some value", NULL, REG_SZ, (const BYTE*)newValue.c_str(), newValue.size() + 1)==ERROR_SUCCESS)
{
//do something
}
RegCloseKey(keyHandle);
}
}
I removed the part where you convert your string to a wstring, instead you'll be using the ANSI version of RegSetValueEx explicitly.
quote from RegSetValueEx remarks in MSDN:
If dwType is the REG_SZ, REG_MULTI_SZ,
or REG_EXPAND_SZ type and the ANSI
version of this function is used
(either by explicitly calling
RegSetValueExA or by not defining
UNICODE before including the Windows.h
file), the data pointed to by the
lpData parameter must be an ANSI
character string. The string is
converted to Unicode before it is
stored in the registry.
Also note that the cbData parameter should include the size of the null termination aswell.
The * 2 is because RegSetValueEx wants to know the number of bytes to write und each char (wchar_t) in a wstring is two bytes wide. So the resulting byte-array has twice the size!
Shouldn't it be wNewValue.size()*2+2 ? The +2 for the null character?
MSDN says: The size of the information pointed to by the lpData parameter, in bytes. If the data is of type REG_SZ, REG_EXPAND_SZ, or REG_MULTI_SZ, cbData must include the size of the terminating null character or characters.
You could also copy the unicode string into a byte array:
LPWSTR pData = L"SampleGrabber";
int dwSize = wcslen(pData)*sizeof(TCHAR);
BYTE slump[256];
memset((void*) slump, 0, 256*sizeof(BYTE));
memcpy((void*) slump, (const void *) pData, dwSize*sizeof(BYTE));
globit = RegSetValueEx(hKey, NULL, 0, REG_SZ, (const BYTE*) slump, dwSize);
If you had the misfortune to be writing code for a WINCE 5.0 device and it lacked part of the regedit API.