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

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.

Related

Is there a way to get an application's path to add it to the registry automatically and run alongside Windows startup in C++?

I'm developing an application and would like to know if there's a way to get it's executable path automatically and run alongside Windows startup by adding it to the registry.
This is my function so far:
void Open(){
HKEY hKey;
WCHAR path[MAX_PATH]; //to store the directory
DWORD size = GetModuleFileNameW(NULL, path, MAX_PATH);
const char* StartName = "MyApplication";
LONG lnRes = RegOpenKeyEx( HKEY_CURRENT_USER,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
0 , KEY_WRITE,
&hKey);
if( ERROR_SUCCESS == lnRes )
{
lnRes = RegSetValueEx( hKey,
StartName,
0,
REG_SZ,
(LPBYTE)path,
size );
}
RegCloseKey(hKey);
}
I'm using GetModuleFileName to get the path, but it returns me the path with a single backslash and in the registry it only recognizes the "D" drive. For example: D:\Usuario\Desktop\log\mariobros.exe
https://prnt.sc/vondsi (Here's a print from my registry)
I suspect that the problem is that for the code to be recognized as a single backslash it needs to have a double backslash. This is how I think it should've need to be: D:\\Usuario\\Desktop\\log\\mariobros.exe
Does anyone know what could I do here?
Thanks in advance.
You are clearly compiling with UNICODE undefined in your project, which means RegOpenKeyEx() and RegSetValueEx() are actually calling the ANSI functions RegOpenKeyExA() and RegSetValueExA(), respectively (as evident by you being able to pass char* strings to them without compiler errors).
But, you are retrieving the file path as a Unicode UTF-16 string and passing it as-is to RegSetValueExA(), so you end up with embedded nul characters written to the Registry when RegSetValueExA() misinterprets your UTF-16 string as an ANSI string and re-encodes each of its bytes individually to Unicode characters. Unicode characters in the ASCII range have nul bytes in them.
Since you are using a Unicode function to retrieve the file path, and because the Registry internally stores strings in Unicode form only, you should use the Registry's Unicode functions to match that same encoding.
Also, note that the return value of GetModuleFileName(A|W) does not include the null terminator in the output string's length, but RegSetValueEx(A|W) expects the cbSize parameter to include enough bytes for a null terminator for REG_(EXPAND_|MULTI_)SZ value types.
Try this:
void Open()
{
WCHAR path[MAX_PATH]; //to store the directory
DWORD size = GetModuleFileNameW(NULL, path, MAX_PATH);
if ((size > 0) && (size < MAX_PATH))
{
HKEY hKey;
LONG lnRes = RegOpenKeyExW(HKEY_CURRENT_USER,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
0, KEY_SET_VALUE,
&hKey);
if( ERROR_SUCCESS == lnRes )
{
lnRes = RegSetValueExW(hKey,
L"MyApplication",
0,
REG_SZ,
(LPBYTE)path,
(size + 1) * sizeof(WCHAR) );
RegCloseKey(hKey);
}
}
}
This looks like you are passing a wide string, while promising that it is a narrow string (evident by your C-style cast).
The second byte in a wide string is 0, and this terminates your narrow string.
Suggestion: use wide strings only while dealing with Win API.

Issue with converting a filesystem path to a const BYTE*

What I'm trying to achieve is getting my program executed on Windows startup using the Registry. When I try to put the file location, the compiler complains it cannot convert from filesystem::path to const BYTE*. I have no idea how to fix this, since I'm a beginner when it comes to C++. I have provided the code below:
HKEY newValue;
RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", &newValue);
RegSetValueEx(newValue, "myprogram", 0, REG_SZ, fs::temp_directory_path().append(filename), sizeof("tes3t")); // This line is the issue. fs::temp_directory_path().append(filename)
RegCloseKey(newValue);
return 0;
EXCEPTION: No suitable conversion function from "std:filesystem::path" to "const BYTE *" exists
Per the RegSetValueExA() documentation, the function does not accept a std::filesystem::path object. That is what the error message is complaining about.
LSTATUS RegSetValueExA(
HKEY hKey,
LPCSTR lpValueName,
DWORD Reserved,
DWORD dwType,
const BYTE *lpData, // <-- here
DWORD cbData
);
The 5th parameter takes a const BYTE* pointer to a null-terminated C-style string. The 6th parameter takes the number of characters in the string, including the null terminator:
lpData
The data to be stored.
For string-based types, such as REG_SZ, the string must be null-terminated. With the REG_MULTI_SZ data type, the string must be terminated with two null characters.
Note lpData indicating a null value is valid, however, if this is the case, cbData must be set to '0'.
cbData
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.
std::filesystem::path does not have an implicit conversion to const BYTE*, hence the compiler error. You need to explicitly convert the path to a std::string or std::wstring first (prefer the latter, since the Registry stores strings as Unicode internally), before you can then save that string value to the Registry, eg:
// using std::string...
HKEY newValue;
// don't use RegOpenKey()! It is provided only for backwards compatibility with 16bit apps...
if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_SET_VALUE, &newValue) == 0)
{
// this may lose data for non-ASCII characters!
std::string s = fs::temp_directory_path().append(filename).string();
// this will convert the ANSI string to Unicode for you...
RegSetValueExA(newValue, "myprogram", 0, REG_SZ, reinterpret_cast<LPCBYTE>(s.c_str()), s.size()+1);
RegCloseKey(newValue);
}
return 0;
// using std::wstring...
HKEY newValue;
// don't use RegOpenKey()! It is provided only for backwards compatibility with 16bit apps...
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_SET_VALUE, &newValue) == 0)
{
// no data loss here!
std::wstring s = fs::temp_directory_path().append(filename).wstring();
// no ANSI->Unicode conversion is performed here...
RegSetValueExW(newValue, L"myprogram", 0, REG_SZ, reinterpret_cast<LPCBYTE>(s.c_str()), (s.size()+1) * sizeof(WCHAR));
RegCloseKey(newValue);
}
return 0;
The WinAPI functions do not take arguments of std::filesystem::path type so you need to convert it to a const BYTE* somehow.
This is one example:
std::string fullpath = (fs::temp_directory_path() / filename).string();
RegSetValueEx(
newValue,
"myprogram",
0,
REG_SZ,
reinterpret_cast<LPCBYTE>(fullpath.c_str()), // returns a "const char*" then cast
fullpath.size() + 1 // + 1 for null terminator
);

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

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

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;
}

convert std::string to const BYTE* for RegSetValueEx()

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.