Attempted registry write to HKLM writes to HKCR instead - c++

I've been tasked with updating an old Visual Studio C++ COM executable to work with Visual Studio 2010 and run on 64bit Windows 7. (The program itself is 32bit.)
Part of that process involves making sure registry reads/writes/creates still work and/or updating them to use HKEY_CURRENT_USER rather than HKEY_LOCAL_MACHINE. However, when testing the registry functions which use RegCreateKeyEx, RegQueryValue, RegSetValue, etc. and using ProcessMonitor to watch the registry accesses I noticed some (potentially?) odd behaviour.
When the root is specified as HKEY_CURRENT_USER everything seems to work as expected. For example, when trying to create the key
HKCU\Software\Classes\CLSID\[Guid]
ProcessMonitor will show the following key being created,
HKCU\Software\Classes\Wow6432Node\CLSID\[Guid]
Which is expected (32bit application on 64bit machine).
However, if the root is HKEY_LOCAL_MACHINE when trying to create the key
HKLM\Software\Classes\CLSID\[Guid]
ProcessMonitor will show the following key being created,
HKCR\Wow6432Node\CLSID\[Guid]
Is this expected behaviour? Does it have anything to do with restrictions to writing to HKEY_LOCAL_MACHINE? This is my first time really dealing with registry access, so I'm not familair with any nitty-gritty details. Through some research I've learned that HKEY_CLASSES_ROOT is some kind of amalgemation of HKCU and HKLM, though I'm not sure if that is relevant.
The program is running as Administrator with UAC disabled. The first registry access looks like this, with pszRootKey = 'Software/Classes', and everything else builds off m_hk
dwErr = RegCreateKeyEx(
HKEY_LOCAL_MACHINE, pszRootKey, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
&m_hk, NULL
);
Edit:
When stepping through the code the above call to RegCreateKeyEx() produces the following in ProcessMonitor:
Operation | Path | Result | Detail
---------------------------------------------------------------------------------
RegQueryKey | HKLM | SUCCESS | Query: HandleTags, HandleTags: 0x0
RegCreateKey | HKCR | SUCCESS | Desired Access: All Access, Disposition:REG_OPENED_EXISTING_KEY
RegSetInfoKey | HKCR | SUCCESS | KeySetInformationClass: KeySetHandleTagsInformation, Length: 0

If you run your program with Administrator rights with UAC disabled, this will show your problem is not related to the User Account Control. Instead of this, the problem is very likely to be caused by WOW64. You can find more information about this at these locations:
Registry Redirector
Registry Keys Affected by WOW64

Related

RegOpenKeyEx access denied reading HKEY_LOCAL_MACHINE

In Windows 7 (32 bit), I consistently get error 5 (access denied) when I call ::RegOpenKeyEx on HKEY_LOCAL_MACHINE if I am not running in Administrator mode.
This is my code:
result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\MyCompany\\MyApp"), 0, KEY_QUERY_VALUE, &keySoftware)
I am trying to build an app that can be installed for an entire machine as opposed to a specific user. Therefore my installer (which does run in Administrator mode) writes to HKEY_LOCAL_MACHINE, and the installer works fine.
I'd like my app to be able to read the data that my installer has put into the registry. I don't want to change any of the registry data. I have also tried using KEY_READ and KEY_EXECUTE instead of KEY_QUERY_VALUE. But it seems that no matter what I do, I cannot read from HKEY_LOCAL_MACHINE without using an elevated status. What am I missing here?
Your installer needs to adjust the security permissions on your Registry key so that non-admin users are allowed to access it. Have a look at RegSetKeySecurity(), or your installer's equivalent, or any number of command-line tools that are available. You can create a DACL that enables read-only access for the Everyone user group, or for specific user accounts.
Update: since you are using Inno Setup, try this in your Registry setup:
[Registry]
Root: HKLM; Subkey: "Software\MyCompany\MyApp"; Permissions: everyone-read
Or:
[Registry]
Root: HKLM; Subkey: "Software\MyCompany\MyApp"; Permissions: users-read
Your code as I was writing this answer:
result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\MyCompany\\MyApp"), 0, KEY_QUERY_VALUE, &keySoftware)
RegOpenKeyEx doesn't support general paths, just direct key names.
To use it you would have to iterate your way down the hierarchy.
SHRegOpenUSKey is one alternative that does support general paths:
HUSKEY keySoftware;
LSTATUS const result = ::SHRegOpenUSKey(
L"Software\\Microsoft\\MediaPlayer", KEY_QUERY_VALUE, 0, &keySoftware, TRUE
);

Cannot read Registry entry in C++

I have a registry entry I can't seem to read in C++, but it shows up in Regedit.
Using the following C++ snippet:
openResult=RegOpenKeyEx( HKEY_LOCAL_MACHINE, _TEXT("SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full"), 0, KEY_READ, &root);
readResult1=RegQueryValueEx(root, _TEXT("InstallPath"), NULL, NULL, data1, &size);
readResult2=RegQueryValueEx(root, _TEXT("fake_entry"), NULL, NULL, data2, &size);
I get Error 2, ERROR_FILE_NOT_FOUND for my second RegQueryValueEx() call.
As you can see in the image below, my fake_entry exists.
I created this entry via Regedit.
Microsoft's Registry Keys Affected by WOW64 does not include the location I'm trying to read, and as you can see in the picture below, my fake_entry is not in the Wow6432Node location.
Yes, I understand this isn't a registry location I should be changing. I stumbled upon this as I was debugging my code and am curious why my added fake_entry doesn't work.
Yes, I've read about Registry Redirector.
Yes, I've read this question.
Yes, I tried reading fake_entry at SOFTWARE\\Wow6432Node\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full with the same error.
Running Windows 7, 64 bit, C++ in Visual Studio 2010, using ASCII character encoding.
Am I misunderstanding the Registry Redirector?
Is there a problem with my code?
Are there some sort of permission settings on certain portions of the Registry? I'm obviously missing something. Please point me in the right direction.
Try
openResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, _TEXT("SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full"), 0, KEY_READ|KEY_WOW64_64KEY, &root);
according to MSDN you should use either KEY_WOW64_64KEY or KEY_WOW64_32KEY for WOW64 access
You are misreading the table of keys affected by WOW64. From the very top of that page:
The following table lists registry keys that are redirected, shared by both 32-bit and 64-bit applications, or redirected and reflected on 64-bit Windows. Subkeys of the keys in this table inherit the parent key's behavior unless otherwise specified. If a key has no parent listed in this table, the key is shared.
The parent of your key is HKLM\Software which is redirected. So your key is also redirected. It inherits that from its parent, as the documentation that I quoted explains.
You'll need to read the 64 bit view using KEY_WOW64_64KEY.

Can't load 64-bit key using RegLoadKey in 32-bit service

I need to open up and modify a user's registry key from a 32-bit service (note that the user is not logged in at the time.) I do the following:
//For simplicity error checks are not shown
//I also made sure to enable the following privileges:
// SE_RESTORE_NAME, SE_BACKUP_NAME
//"ntuser.dat" = is the file OS uses to load user's profile
if(RegLoadKey(HKEY_LOCAL_MACHINE, L"Test123", L"C:\\Users\\UserA\\ntuser.dat") == ERROR_SUCCESS)
{
HKEY hKey;
DWORD dwRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
L"Test123\\Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\TrayNotify"),
NULL, KEY_READ | KEY_WOW64_64KEY, &hKey);
//'dwRes' = is returned as 2, or ERROR_FILE_NOT_FOUND
RegUnLoadKey(HKEY_LOCAL_MACHINE, L"Test123");
}
The problem is that the Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify key isn't loaded, even though I know that it exists in the actual user profile. I can verify that by loading the user account and by using 64-bit regedit.
I suspect that this has something to do with the Wow64 redirection but I can't seem to understand what am I doing wrong?
EDIT: Added error check for the first API.
I think I got it. Two corrections to my original code:
First off, since Vista I need to load Usrclass.dat file for the classes hive and not ntuser.dat. It kinda makes sense because ntuser.dat is a part of a user's roaming profile and Classes\Local Settings does not fit into the picture well. So here's the location of the Usrclass.dat file, which contains non-roaming user data (mostly COM stuff, but some other settings as well):
%LocalAppData%\Microsoft\Windows\Usrclass.dat
The key to open after the user hive loads is:
Test123\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify
that is because the original HKCU\Software\Classes is redirected to HKU\<UserSID>_Classes that is stored in the Usrclass.dat file.

Accessing 64 bit registry from 32 bit application

I need to open a registry entry "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{26A24AE4-039D-4CA4-87B4-2F86416024FF}" in c++. It contains the java 64 bit application. The full path of that registry entry is "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{26A24AE4-039D-4CA4-87B4-2F86416024FF}".
We can view this path through regedit. I use
returnStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{26A24AE4-039D-4CA4-87B4-2F86416024FF}"),
0, KEY_ALL_ACCESS, &hKey)
for open the registry; But it returns error value (2).
returnStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall")...
returns a success result. What can i do?
The registry keys for 32 bit and 64 applications are separated, you can't access (directly) the 64 bit registry from your 32 bit application. In your case the required hive doesn't exist in the 32 bit part of the registry then you can access the parent folder only.
From MSDN:
On 64-bit Windows, portions of the registry entries are stored separately for 32-bit application and 64-bit applications and mapped into separate logical registry views using the registry redirector and registry reflection, because the 64-bit version of an application may use different registry keys and values than the 32-bit version. There are also shared registry keys that are not redirected or reflected.
You can read the list on MSDN: Registry Keys Affected by WOW64. Unfortunately the SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall is not mentioned but it's affected too.
Solution
What you have to do is to explicitly ask RegOpenKeyEx to access the 64 bit part of the registry. This can be done adding the KEY_WOW64_64KEY flag to your call (you can access the 32 bit registry from a 64 bit application using KEY_WOW64_32KEY). Please note that this flag is not supported on Windows 2000 then if your application must be compatible with that (old) version you have to manage the case.
See this link on MSDN for further details: Accessing an Alternate Registry View.
To make it easy, simply change your call from:
returnStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{26A24AE4-039D-4CA4-87B4-2F86416024FF}"),
0, KEY_ALL_ACCESS, &hKey);
to:
returnStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{26A24AE4-039D-4CA4-87B4-2F86416024FF}"),
0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, &hKey);
Note
Note that you may access the key only via its path without any flags using this HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall.
Because the Wow6432 node is the virtualized node used by WOW64 but you shouldn't rely on this, it's stable but it should be considered an implementation detail subject to change.
References
- Registry Virtualization on MSDN.
- Readers my find interesting tips on this post: http://poshcode.org/2470, it's for the PowerShell but it explains how to access WMI data (relative to the 64 bit registry part) from a 32 bit application.
Error 2 means the registry path does not exist.
Make sure that the key {26A24AE4-039D-4CA4-87B4-2F86416024FF} actually exists under SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall

try to change ActivePowerScheme: RegOpenKeyEx failed with error 0

I need to set ActivePowerScheme by changing it in registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes.
So I try to do it with winapi functions RegOpenKeyEx and RegSetValueEx
wchar_t *PowerScheme=TEXT("8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c");
HKEY hRootKey = HKEY_LOCAL_MACHINE;
PWCHAR sKey = TEXT("SYSTEM\\CurrentControlSet\\Control\\Power\\User\\PowerSchemes");
PWCHAR sActivePowerS = TEXT("ActivePowerScheme");
HKEY hKeyResult = NULL;
//open
if (RegOpenKeyEx(hRootKey,sKey,0,KEY_ALL_ACCESS,&hKeyResult)!=ERROR_SUCCESS) {
//it is always failing with error 0 !
DWORD dw = GetLastError();
}
But RegOpenKeyEx() is always failing with error 0, that means "Operation completed successfully". And RegSetValueEx() returns same value.
if(RegSetValueEx(hKeyResult,sActivePowerS,0,REG_SZ,
(BYTE *)PowerScheme,wcslen(PowerScheme))!=ERROR_SUCCESS) {
//it is always failing with error 0
DWORD dw = GetLastError();
}
And of course current power scheme doesn't change value. But according to msdn:
"If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code".
I will be grateful to any your answers.
P.S. it compiled in Windows 7 and executed with rights of admin
You are going about this the wrong way. You RARELY need to change stuff in the registry yourself.
Read Power Scheme Management on the MSDN site for the proper way of doing it.
As documentation states, RegOpenKeyEx does not update GetLastError, and return value is the error code itself. Would you mind checking it?
I'd bet you have ERROR_ACCESS_DENIED error here.
UPD: While this perhaps answers your question, you should consider using API suggested by RedX in order to update power management settings. Permissions on this registry key are set (for a reason!) in a way that even Administrators have only read permissions, and not write.
In the comments you state that RegOpenKeyEx returns ERROR_ACCESS_DENIED. This is because you request write access to a key to which you do not have sufficient rights because of UAC. You will need to run your process elevated to write to this key.
As others have correctly pointed out, you should not call GetLastError since RegOpenKeyEx does not set the last error value and instead returns the error code directly. More importantly you should be using the power management API rather than hacking the registry.
Even when you switch to the power management API you will still require administrator rights. You can arrange this by setting requestedExecutionLevel to requireAdministrator in your application manifest.
In Visual Studio you can make this change in the project configuration under Linker | Manifest File | UAC Execution Level.