I am trying to write a DWORD value to the registry programmatically in C++.
I've done a bit of searching and I have found that this question has been asked before. I've tried to follow their solution but have come up with a really frustrating issue which, as far as I know, have not been addressed by their solution.
This is my code:
HKEY hKey;
LPCWSTR sKeyPath;
int iResult;
sKeyPath = L"Software\\ABI\\";
iResult = RegOpenKeyEx(HKEY_CURRENT_USER, sKeyPath, NULL, KEY_ALL_ACCESS, &hKey);
DWORD value = 0x00000003;
iResult = RegSetValueEx(hKey, L"Test", NULL, REG_DWORD, (const BYTE*)value, sizeof(value));
RegCloseKey(hKey);
I've done some basic debugging and found that the value of iResult is 998 after I call RegSetValueEx. I am sure that this key is present in the windows registry because I created it manually with regedit.exe for testing purposes. The value of the DWORD "Test" is initially 0x00000009 and is unchanged after I run my program.
I am not sure where I am wrong.
Any help would be appreciated.
P.S. I've not managed to find any helpful site on the net for error 998. The only reference I found mentions that that's the worst error you can get when handling registry.
P.P.S. By the way, I'm running this program on Windows 8. I don't think that changes anything but I've had experiences with Windows 8 having some weird security issues before.
You need to pass the address of value:
iResult = RegSetValueEx(hKey,
L"Test",
NULL,
REG_DWORD,
(const BYTE*)&value, // Change made here.
sizeof(value));
The error code 998 means:
Invalid access to memory location.
When the address of value is not passed its actual value (3) is being used as a memory address, causing the failure.
Related
I'm trying reading the Registry data from a value named Scoop in HKLM\Software\Wow6432Node\Company\Software\Settings:
This key and value already exist prior to installing my software.
My software contains its own 32-bit service. So when my software is installed, the service (running under SYSTEM), attempts to read this value.
However, within the service, RegQueryValueEx returns "2" (ERROR_FILE_NOT_FOUND). Yet, when I run services.msc and restart my service, RegQueryValueEx returns "0" (ERROR_SUCCESS). RegOpenKeyEx always returns ERROR_SUCCESS, but RegQueryValueEx is returning 2 at installation.
My code:
HANDLE hKey = NULL;
WCHAR *sPath = L"SOFTWARE\\Company\\Software\\Settings";
RegOpenKeyExW(HKEY_LOCAL_MACHINE, sPath, 0, KEY_WOW64_32KEY | KEY_QUERY_VALUE, &hKey);
DWORD dwSize = 0;
DWORD dwType = 0;
WCHAR sZeroLengthString[4] = {0};
// get the buffer size needed, including null terminating characters.
dwRV = RegQueryValueExW(hKey, L"Scoop", NULL, &dwType, (LPBYTE) sZeroLengthString, &dwSize);
The instalation is being run under a domain admin account.
I can't for the life of me figure out why RegQueryValueEx is returning 2 at install, but is fine restarting with services.msc. The manifest of the service is set to highestAvailable, and there are no special permissions on the Registry key, just the standard...SYSTEM does have Full Control.
Do not access the Wow6432Node key directly at all, pretend it doesn't even exist. Access the non-redirected key normally, using the appropriate KEY_WOW64_(32|64)KEY flag to handle the redirection based on which view your want to access. This is even stated in the documentation:
Registry Redirector
Redirected keys are mapped to physical locations under Wow6432Node. For example, HKEY_LOCAL_MACHINE\Software is redirected to HKEY_LOCAL_MACHINE\Software\Wow6432Node. However, the physical location of redirected keys should be considered reserved by the system. Applications should not access a key's physical location directly, because this location may change. For more information, see Accessing an Alternate Registry View.
Wow6432Node is an implementation detail of the WOW64 emulator for 32bit processes. So, don't rely on it. Simply drop Wow6432Node from your sPath since you are explicitly accessing the 32bit Registry view:
HANDLE hKey = NULL;
LPCWSTR sPath = L"SOFTWARE\\Company\\Software\\Settings";
RegOpenKeyExW(HKEY_LOCAL_MACHINE, sPath, 0, KEY_WOW64_32KEY | KEY_QUERY_VALUE, &hKey);
DWORD dwSize = 0;
DWORD dwType = 0;
WCHAR sZeroLengthString[4] = {0};
// get the buffer size needed, including null terminating characters.
dwRV = RegQueryValueExW(hKey, L"Scoop", NULL, &dwType, (LPBYTE) sZeroLengthString, &dwSize);
I'm writing a programme that should return the Value of a registry subkey. I tried this code:
LPCTSTR sk = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\\");
HKEY regkey;
char out[255];
RegOpenKeyEx(HKEY_CURRENT_USER, sk, 0, KEY_SET_VALUE, ®key);
RegGetValue(regkey, L"test", NULL, RRF_RT_ANY, NULL, (PVOID)&out, (LPDWORD) strlen(out) +1);
RegCloseKey(regkey);
MessageBox(NULL, (LP) out, L"Output", MB_OK);
I wrote this in Visual Studio 2017 and it doesn't show any errors. But when I run it, it crashes on line 5.
Crash reason:
Exception Error on 0x7511C481 (KernelBase.dll) in reader.exe: 0xC0000005: Access Violation While Reading at Location 0x00000005. (Translated by Google Translate)
I have already checked if RegOpenKeyEx() works and yes it does work.
What am I doing wrong and how to fix it?
You should use sizeof(out) and not strlen(out)+1. That variable is uninitizlied and depending on how you build this can either be filled with zeros (in which case you're telling RegGetValue() it can write 1 bytes into it) or it can have random data (in which case you're telling RegGetValue() it can write a random number of bytes).
The second issue is that RegOpenKeyEx() is called with KEY_SET_VALUE so you don't even have permission to read. You need KEY_QUERY_VALUE.
The third issue, and the one probably causing the crash, is that you cast the result of strlen(out)+1 to a pointer. It's a number, not a pointer. The function is expecting a pointer so it can write the number of bytes it actually read. Use:
DWORD len = sizeof(out);
RegGetValue(regkey, L"test", NULL, RRF_RT_ANY, NULL, (PVOID)&out, &len);
And finally, as all the comments mention, you should check for errors on all functions and handle all of them.
I´m currently using the following C++ code to get the MachineGuid from the windows registry and use that information for my licensing algorithm:
std::wstring key = L"SOFTWARE\\Microsoft\\Cryptography";
std::wstring name = L"MachineGuid";
HKEY hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, key.c_str(), 0, KEY_READ, &hKey) != ERROR_SUCCESS)
throw std::runtime_error("Could not open registry key");
DWORD type;
DWORD cbData;
if (RegQueryValueEx(hKey, name.c_str(), NULL, &type, NULL, &cbData) != ERROR_SUCCESS)
{
RegCloseKey(hKey);
throw std::runtime_error("Could not read registry value");
}
if (type != REG_SZ)
{
RegCloseKey(hKey);
throw "Incorrect registry value type";
}
std::wstring value(cbData/sizeof(wchar_t), L'\0');
if (RegQueryValueEx(hKey, name.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(&value[0]), &cbData) != ERROR_SUCCESS)
{
RegCloseKey(hKey);
throw "Could not read registry value";
}
RegCloseKey(hKey);
This works pretty well on a x86 system (32 bit). Now I´ve migrated the whole code to x64 (64bit) Windows and the RegQueryValueEx call is returning error.
Among some other posts, this link explains very clearly why this does not work on 64bit machines, and offers an alternative for both 32bit and 64bit using the ManagementObject class from System.Management.dll. The problem is that this solution works on C#, not on C++. I can´t find out a C++ equivalent of the ManagementObject class.
So, what is the correct solution for the problem: Getting the window serial number (MachineGuid) on both x86 and x64 machines using C++.
Thanks for helping.
Add KEY_WOW64_64KEY bit to your RegOpenKeyEx argument. Like this:
RegOpenKeyEx( HKEY_LOCAL_MACHINE, key.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey )
The documentation says it’s ignored on 32-bit OSes, so you don’t even need to detect WOW64.
P.S. I don’t recommend WMI, it’s too slow. I currently have i5-4460 CPU, 16GB RAM, relatively fast SSD, and yet WMI takes 1-2 seconds to initialize and run even a simple query.
I am new to programming, I would like to know the number of entries in a registry key. I think entries are called subkeys but I am not sure. I am trying to use RegQueryInfoKey() but I don't fully understand the MSDN webpage since I am beginner.
HKEY hKey = HKEY_LOCAL_MACHINE;
char regpath[] = "SOFTWARE\\MyApplication\\"
LPDWORD numberofEntries;
RegOpenKeyEx(hKey, regpath, 0, KEY_READ, &hKey);
RegQueryInfoKey(hKey, NULL, NULL, NULL, numberofEntries, NULL);
then I would like to printf the number of entries in this key. The code above doesn't work, the app crash.
How is it done?
Thank you
RegQueryInfoKey has 12 parameters. You are only passing 6. I can't understand how that even compiles—perhaps you are providing your own definition of RegQueryInfoKey rather than the one from the windows header files.
Perhaps you are getting confused by the fact that many of the parameters to RegQueryInfoKey are marked as optional. This only means that you can pass NULL into the function and not that you can omit the parameters altogether.
We had a line of code
if( !CreateFile( m_hFile, szFile, GENERIC_READ|GENERIC_WRITE, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL ) )
{
DWORD dwErr = GetLastError();
CString czInfo;
czInfo.Format ("CMemoryMapFile::OpenAppend SetFilePointer call failed - GetLastError returned %d", dwErr);
LOG(czInfo);
return false;
}
This code worked great for many years. A few weeks ago, we had a customer with a problem. Turns out, the problem could be traced to this line of code, where the function would return a INVALID_HANDLE_VALUE handle and GetLastError() returned ERROR_FILE_NOT_FOUND(2).
Now, this is very confusing to us. OPEN_ALWAYS should direct the file to be created if it does not exist. So, why are we getting a ERROR_FILE_NOT_FOUND?
More confusion: For this customer, this only happened on one network share point (we were using a UNC path). Other UNC paths to other machines for this customer worked. Local paths worked. All our other customers (10000+ installs) have no problem at all.
The customer was using XP as the client OS, and the servers were running what appeared to be standard Windows Server 2003 (I think the Small Business Server version). We could not replicate their errors in our test lab using the same OS's. They could repeat the problem with several XP clients, but the problem was only on one server (other Server 2003 servers did not exhibit the problem).
We fixed the problem by nesting two CreateFile calls, the first with OPEN_EXISTING, and the second with CREATE_ALWAYS if OPEN_EXISTING failed. So, we have no immediate need for a fix.
My question: Does anyone have any idea why this API call would fail in this particular way? We are puzzled.
Addendum:
The CreateFile function above is a wrapper on the Windows API function. Here's the code:
bool CMemoryMapFile::CreateFile( HANDLE & hFile, LPCSTR szFile, DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes )
{
hFile = ::CreateFile (szFile, dwDesiredAccess, dwShareMode, NULL,
dwCreationDisposition, dwFlagsAndAttributes, NULL);
return (hFile != INVALID_HANDLE_VALUE)
}
First, you should always check for success of the CreateFile() API like this:
if (CreateFile(...) == INVALID_HANDLE_VALUE)
{
// handle the error
}
because CreateFile() doesn't return !=0 in case of success, but anything other than INVALID_HANDLE_VALUE (which is -1).
Then, the CreateFile() can fail in the situation you described if either the directory doesn't exist where you want to create/open the file in, or it can fail if the user has the rights to open and write files in that directory, but no rights to create new files.
Maybe the directory you wanted to create the file in did not exist?
Are you sure you fixed it by using 2 CreateFile calls? Or did you just not reproduce it?
We have also seen this problem when a file is accessed by a UNC share. None of the ideas and suggestions applied in our case - the directory always exists, the parameters to CreateFile are correct, we are checking the return correctly.
We believe this to be a shares issue. We solved the problem with a simple retry loop:
If a CreateFile with OPEN_ALWAYS fails with ERROR_FILE_NOT_FOUND, we simply sleep for a few ms and try again. It always works second time (if it failed first time).
If CreateFile fails, it will return INVALID_HANDLE_VALUE which is non-zero (I think it's negative 1). So CreateFile might be succeeding and returning zero as the handle.
It's only safe to check GetLastError() after a function fails but it looks like you might be checking the last error when CreateFile has succeeded (returned zero).
According to this article, some functions set the "last error" if they succeed:
My guesses would be something server related like the server not implementing support correctly for that filesystem operation. Maybe a linux server compared to a windows server?
Your arguments to create file are incorrect, so I assume that this is some sort of CreateFile helper function.
A call to CreateFile should look like:
m_hFile = CreateFile( szFile, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 );
if(m_hFile == INVALID_HANDLE_VALUE)