C++ Read a Registry String Value in char* [duplicate] - c++

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to compare strings
I want to Compare to registry string values and if they were the same an messagebox appears
Currently I'm using this functions , It returns the value correctly but whenever I want to compare them, The compare result is always wrong
char* GetRegistry(char* StringName)
{
DWORD dwType = REG_SZ;
HKEY hKey = 0;
char value[1024];
DWORD value_length = 1024;
const char* subkey = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\MCI\\Player";
RegOpenKey(HKEY_LOCAL_MACHINE,subkey,&hKey);
RegQueryValueEx(hKey, StringName, NULL, &dwType, (LPBYTE)&value, &value_length);
return value;
}
I use this to compare them
if (GetRegistry("First") == GetRegistry("Second"))
{
MessageBox(NULL,":|",":|",1);
}
But the MessageBox appears how ever The values are different
Any help is appreciated.

By using std::string, comparison would behave as you expected. Also that would fix another bug that the function returns a pointer to a local buffer.
std::string GetRegistry(const char* StringName)
{
....
return std::string(value);
}

GetRegistry() returns a char*, so you are actually comparing pointers with operator==.
You should use strcmp() to do raw C-like char* string comparisons, or better use a robust C++ string class, like CString or std::[w]string.
Here is a possible rewrite of your function using ATL's CString:
#include <atlbase.h>
#include <atlstr.h>
CString GetRegistry(LPCTSTR pszValueName)
{
// Try open registry key
HKEY hKey = NULL;
LPCTSTR pszSubkey = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\MCI Extensions");
if ( RegOpenKey(HKEY_LOCAL_MACHINE, pszSubkey, &hKey) != ERROR_SUCCESS )
{
// Error:
// throw an exception or something...
//
// (In production code a custom C++ exception
// derived from std::runtime_error could be used)
AtlThrowLastWin32();
}
// Buffer to store string read from registry
TCHAR szValue[1024];
DWORD cbValueLength = sizeof(szValue);
// Query string value
if ( RegQueryValueEx(
hKey,
pszValueName,
NULL,
NULL,
reinterpret_cast<LPBYTE>(&szValue),
&cbValueLength)
!= ERROR_SUCCESS )
{
// Error
// throw an exception or something...
AtlThrowLastWin32();
}
// Create a CString from the value buffer
return CString(szValue);
}
And then you can call it like this:
if ( GetRegistry(_T("First")) == GetRegistry(_T("Second")) )
...
Note that this code will compile in both ANSI/MBCS and Unicode builds (it's based on Win32 TCHAR model).

You have a couple of problems with this source code.
First of all you have a function with a local variable, a variable on the stack, which is returning the address of that variable yet when the function returns, the variable disappears and the address is no longer valid.
The next problem is that you are not comparing the character strings. You are instead comparing the address returned by the function and if you get lucky, the address could be the same. Since you are calling the function twice in succession, you are getting lucky so the address is the same.
I suggest you do the following: (1) create two local character strings in your function which is calling the function GetRegistry() and (2) modify the GetRegistry() function so that it uses those buffers rather than its own. So the code would look something like:
char registryEntryOne[1024];
char registryEntryTwo[1024];
DWORD dwRegistryEntryOneLen;
DWORD dwRegistryEntryTwoLen;
registryEntryOne[0] = 0; // init the registry entry to zero length string
registryEntryTwo[0] = 0;
dwRegistryEntryOneLen = sizeof(registryEntryOne);
GetRegistry ("First", registryEntryOne, &dwRegistryEntryOneLen);
dwRegistryEntryTwoLen = sizeof(registryEntryTwo);
GetRegistry ("Second", registryEntryTwo, &dwRegistryEntryTwoLen);
// two strings are equal if:
// the lengths are the same
// at least one of the lengths is non-zero
// the bytes are the same in the same order
if (dwRegistryEntryOneLen && dwRegistryEntryOneLen == dwRegistryEntryTwoLen && memcmp (registryEntryOne, registryEntryTwo, dwRegistryEntryOneLen) == 0) {
// strings are equal
} else {
// strings are not equal
}
The GetRegistry() function would look something like:
char* GetRegistry(char* StringName, char *valueBuffer, DWORD *value_length)
{
DWORD dwType = REG_SZ;
HKEY hKey = 0;
const char* subkey = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\MCI\\Player";
RegOpenKey(HKEY_LOCAL_MACHINE,subkey,&hKey);
RegQueryValueEx(hKey, StringName, NULL, &dwType, (LPBYTE)valueBuffer, value_length);
return valueBuffer;
}

Related

VBA/Excel, and C++ DLL, specifically problems with strings

I am working on a project for serial communications between Excel and an Arduino. The following is what I have in VBA for the reading of data from the Arduino which is where my problem lies.
Private Declare Function readFromSerialPort Lib "C:PathToDll.dll"(ByRef Buffer As String) As String
And in a looping function within my VBA I have the following which sets a cell value to a string which is my buffer.
BigData.Range("Buf").Value = "B "
Another cell called dataWindow takes in Buf as an argument so it updates when this is called.
=readFromSerialPort(Buf)
And here is the C++ code on the other end in the DLL.
DLL_EXPORT BSTR WINAPI readFromSerialPort(LPBSTR bufferTemp) {
char errorMsg[] = "read failed";
char mbstring[MAX_STRING_SIZE];
BSTR wcstring = *bufferTemp;
int sLen = wcstombs(mbstring,wcstring,MAX_STRING_SIZE);
char charString[MAX_STRING_SIZE];
DWORD dwBytesRead = 0;
if (hSerial == INVALID_HANDLE_VALUE) {
ErrorExit("ReadFile (port not open)");
int wLen2 = mbstowcs(*bufferTemp,errorMsg,strlen(errorMsg));
return *bufferTemp;
}
if(!ReadFile(hSerial, charString, sLen, &dwBytesRead, NULL)) {
ErrorExit("ReadFile");
int wLen2 = mbstowcs(*bufferTemp,errorMsg,strlen(errorMsg));
return *bufferTemp;
}
int wLen2 = mbstowcs(*bufferTemp,charString,sLen);
return *bufferTemp;
}
The issue is that this works when called from a cell but not when I change it to declaring a string in VBA and calling the read from there.
GSerg has solved this issue. I had not understood that VBA automatically converts when passing as String, so that in the DLL I am not dealing with BSTRs but with ASCII char* or LPSTRs. Also that due to a bug in excel, calling functions from within a cell does not do this conversion.

C++ - getting null values in value read from registry

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.

Why obtained MachineGuid looks not alike a GUID but like Korean?

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.)

Comparison of process name

I write filtering system and use Winsock2 LSP.
In WSPConnect I need to compare executable filename of process with harcoded Unicode String.
I do:
LPWSTR moduleName = {0};
GetModuleFileNameEx (GetCurrentProcess(),0,moduleName,_MAX_FNAME );
LPWSTR mn = L"redirect.exe";
if (lstrcmp (moduleName, mn) == 0){ ...some code there...}
What I am doing wrong?
You should compare "case-insensitive": lstrcmpi
You need to pass a correct char array...
Also you should always check the result values of function calls!
Also you should not use the TCHAR version of GetModuleFileNameEx if you explicit use wchar_t => GetModuleFileNameExW!
Also you should use the method GetModuleFileNameW if you want to get the name of the current process! This is more reliable!
ALso you should use MAX_PATH instead of _MAX_FNAME, because the method might also return the full path!
Also be sure that your string is correctly NUL-terminated!
Also you must be aware that the returned path might contain the full path, so comparing with the process name does never match...
Also you must be aware that the path might contion the short file name! (not in your case, because the name is not longer than 8 characters; but if you compare it with "MyExecutable.exe" you also must compare with the short file name; see GetShortPathName
The code part should now look like:
WCHAR moduleName[MAX_PATH+1];
if (GetModuleFileNameW (NULL, moduleName, MAX_PATH) != 0)
{
moduleName[MAX_PATH] = 0;
LPWSTR mn = L"redirect.exe";
int len = lstrlen(moduleName);
int lenmn = lstrlen(mn);
if (len > lenmn)
{
if (lstrcmpi (&moduleName[len-lenmn], mn) == 0){ ...some code there...}
}
}
You need to declare storage space, just a pointer is not enough
LPWSTR moduleName = {0};
GetModuleFileNameEx (GetCurrentProcess(),0,moduleName,_MAX_FNAME );
should be
TCHAR moduleName[_MAX_PATH];
GetModuleFileNameEx(GetCurrentProcess(), 0, moduleName, _countof(moduleName));
and use case-insensitive lstrcmpi().

RegQueryValueExW only brings back one value from registry

I am querying the registry on Windows CE. I want to pull back the DhcpDNS value from the TcpIp area of the registry, which works.
What happens though, however, is if there is two values - displayed as "x.x.x.x" "x.x.x.x" in my CE registry editor - then it only brings back one of them. I am sure this is a silly mistake but I am unsure why it is happening.
Here is the code I am using
std::string ISAPIConfig::GetTcpIpRegSetting(const std::wstring &regEntryName)
{
HKEY hKey = 0;
HKEY root = HKEY_LOCAL_MACHINE;
LONG retVal = 0;
wchar_t buffer[3000];
DWORD bufferSize = 0;
DWORD dataType = 0;
std::string dataString = "";
//Open IP regkey
retVal = RegOpenKeyExW(root, L"Comm\\PCI\\FETCE6B1\\Parms\\TcpIp", 0, 0, &hKey);
//Pull out info we need
memset(buffer, 0, sizeof(buffer));
bufferSize = sizeof(buffer);
retVal = RegQueryValueExW(hKey, regEntryName.c_str(), 0, &dataType, reinterpret_cast<LPBYTE>(buffer), &bufferSize);
Unicode::UnicodeToAnsi(buffer, dataString);
return dataString;
}
void UnicodeToAnsi(const std::wstring &wideString, std::string &ansiString){
std::wostringstream converter;
std::ostringstream converted;
std::wstring::const_iterator loop;
for(loop = wideString.begin(); loop != wideString.end(); ++loop){
converted << converter.narrow((*loop));
}
ansiString = converted.str();
}
The value is a multi_sz, which is in the format:
{data}\0{data}\0\0
I don't know what the Unicode::UnicodeToAnsi does, but it's likely just looking for that first null terminator and stopping there. You have to parse past single nulls until you hit the double-null.
EDIT
You have to update your code - very likely your interfaces. Right now you're trying to returns a string for a multi_sz which, by definition, means multiple strings. you probably want to returns a string[] (though I'd probably opt to use a couple output parameters - one that's an array pointer and the other that is a element count).
You then need to loop through the data that came back from the RegQuery call, something maybe like this (off the top of my head, not tested or compiled):
TCHAR *p = buffer;
if(bufferSize > 0)
{
do
{
Unicode::UnicodeToAnsi(p, dataString);
// do something with dataString - store it in an array or whatever
p+= _tcslen(p);
} while((p-buffer) < bufferSize)
}