I need to open a html help file from within a legacy windows application written in old version of C++ Builder. HtmlHelp is loaded via HtmlHelp.ocx, which I am loading via LoadLibrary.
This has worked fine for years, but it does not work anymore in Windows 7 x64. It might also fail under Windows7 x86, but I don't have any computer with this OS, so I can't try it out at the moment.
I am loading hhctrl.ocx dynamically as follows:
#define HHPathRegKey "CLSID\\{adb880a6-d8ff-11cf-9377-00aa003b7a11}\\InprocServer32"
bool THTMLHelper::LoadHtmlHelp()
{
HKEY HHKey;
DWORD PathSize = 255;
char Path[255];
bool R = false;
if (::RegOpenKeyExA(HKEY_CLASSES_ROOT, HHPathRegKey, 0, KEY_QUERY_VALUE, (void **)&HHKey) == ERROR_SUCCESS)
{
if (::RegQueryValueExA(HHKey, "", NULL, NULL, (LPBYTE)Path, &PathSize) == ERROR_SUCCESS)
{
//*****************************************
//LOADING FAILS HERE
//PATH IS %SystemRoot%\System32\hhctrl.ocx
//*****************************************
HHLibrary = ::LoadLibrary(Path);
if (HHLibrary != 0)
{
__HtmlHelp = (HTML_HELP_PROC) ::GetProcAddress(HHLibrary, "HtmlHelpA");
R = (__HtmlHelp != NULL);
if (!R)
{
::FreeLibrary(HHLibrary);
HHLibrary = 0;
}
}
}
::RegCloseKey(HHKey);
}
return R;
}
I checked if %SystemRoot%\System32\hhctrl.ocx exists on the Windows 7 system and it does.
Why does loading it via LoadLibrary fail? How can I work around this problem?
EDIT: GetLastError says (in German, so I am just translating): "Could not find file." But I debugged the function and the path is "%SystemRoot%\System32\hhctrl.ocx" and the file does exist.
Also, since two answers point in the direction of 64-bit vs 32-bit problems: My application is a 32 bit executable compiled in C++ Builder 5, so it should be a 32 bit process if I'm not mistaken. Or am I wrong to assume that?
Use ExpandEnvironmentStrings function to expand %SystemRoot%\System32\hhctrl.ocx to real path on user's intallation. 64bit OS will redirect expanded path to 32bit dll correctly.
You can't load 32bit dlls in a 64bit process, and visa versa. ActiveX controls are, of course, Dlls.
You can sometimes work around this by getting the 32bit ActiveX to load as an out-of-process server - its then hosted in a seperate 32bit (or 64bit) process as appropriate. This requires that the ActiveX onlyuses interfaces the system already knows how to marshal, and/or the project built 64bit AND 32bit versions of the proxy stub dll.
Depends is a tool that is very useful when you need to figure out why Dlls wont load. Of course, as a 32 bit application on a 64bit OS you need to know that 32 bit applications do NOT get access to %SYSTEMROOT%\System32 and, also do NOT read and write from HKCR directly. System32 actually contains the 64bit OS binaries, and HKCR contains the registry entries for 64bit apps.
A kernel process called 'reflection' redirects 32bit apps completely transparently to from System32 to %SYSTEMROOT%\SysWow64.
Likewise, registry access to HKEY_CLASSES_ROOT is redirected to `HKEY_CLASSES_ROOT\Wow6432Node'.
You need to know this of course, because explorer and regedit are 64bit processes and will happily show you the 64bit contents of System32 and HKCR. You need to explicitly navigate to the 32bit nodes to double check the view your 32bit process is going to get.
I have the exact same problem right now running W7 (x64).
I got it to work when I changed the "%SystemRoot%\System32\hhctrl.ocx" to "c:\windows\System32\hhctrl.ocx", but I guess I need to figure out why %SystemRoot% resolves wrong.
btw: I'm building a 32bit app on BCB2007.
Related
I have created a very small win32 console application (AddPrintMonitor.exe) that does nothing more than makes a call to AddMonitor. Here is a small snippet:
MONITOR_INFO_2 m_MonitorInfo2;
m_MonitorInfo2.pName = lpMonitorName;
TCHAR env[12] = TEXT("Windows x64");
m_MonitorInfo2.pEnvironment = env;
m_MonitorInfo2.pDLLName = lpDllName;
if ( !AddMonitor(NULL, 2, (LPBYTE) &m_MonitorInfo2) )
{
DWORD error = GetLastError();
std::cout << "Last error = " << error << "\n";
return PRINTER_ERR_API;
}
When I run this as a member of the Administrators group is fails. GetLastError() returns 5. When I run it as "The Administrator" it succeeds with no issues. I am running on Windows 7 x64. I am trying to install the redmonnt.dll and I do have the 64 bit version of that dll. This task is part of a larger install for a PostScript driver. I've only isolated out the AddMonitor portion to eliminate other external issues.
Error 5 is an access violation or security issue. My first question is why can't a member of the administrators group carry out this function call? What's the difference between the actual administrator and a member of the administrators group given this context?
Other details to note. I am using InstallShield 12 (old) for my Printer installation (addmonitor is only part). I am adding a monitor, port, driver and printer all through win32 function calls. Prior to running my AddPrinterMonitor.exe I made sure that the redmonnt.dll does exist in the system32 directory. Actually, whether the dll is present there or not makes no difference for the error I see. I did see posts about setting SeLoadDriverPrivilege. This is a dead end for me as I checked and the administrators group can Load/Unload device drivers.
Also, I am using win32 calls to get this done. This worked (I mean all of it worked) without any problems on Win2K, WinXP and Windows Server 2003. With the newer OS's like Windows 7 there are several difficulties. Is there a better way that I've overlooked? I been very frustrated trying to get this to work, so I've begun to question the approach for Vista and higher.
More initialization code:
#define MONITOR_NAME "My Redirected Port"
#define MONITOR_FILE "redmonnt.dll"
MONITOR_NAME is passed to lpMonitorName and MONITOR_FILE is passed to lpDllName
thanks
I'm running a C++ program (which works great on 32 bit Win XP) on Windows 7 64 bit in debugger under Visual Studio 2010 and I am unable to open an existing registry key with the following code:
#define ACCESS (KEY_WRITE | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS)
HKEY hKey;
long dwErrorCode = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\MYTHING", 0, ACCESS|KEY_WOW64_64KEY, &hKey);
if (dwErrorCode != ERROR_SUCCESS)
{
// display error message
}
The error code returned is: 5 with the system message being "Access is Denied".
This does work if I run the Visual Studio 2010 as Admin, however I'd rather not have to do that. Can anybody offer a suggestion?
Update: I forgot to mention, what I am doing here is porting legacy code from Windows XP. As such, I don't have the option of changing the fundamental structure of how this software was written. Since the legacy code uses the registry, that is what the ported code must do also.
Also, I would rather not make changes to my specific computer -- since that means I would have to change every computer that I want to run this on. This could get messy as there are a lot of machines affected. For example, I don't want to turn off UAC for the entire machine.
Further update: I haven't found a solution that I'm happy with. Have decided to ignore error code 5 for purposes of debugging and that seems to be working well enough for now. I'm trying to understand how standard apps like Word, Firefox, etc appear to be using the registry for all kinds of settings and yet are not elevated nor do I have to give them special permissions to make changes to the registry?
Ok, I found the answer to my question so I'll post it here in case anybody else needs it for future reference. This thread turned out to be helpful even though it is actually on the topic of C#:
http://social.msdn.microsoft.com/Forums/da-DK/netfx64bit/thread/92f962d6-7f5e-4e62-ac0a-b8b0c9f552a3
Basically, I needed to change my permission to read the 32 bit registry instead of the 64 bit one like so:
HKEY hKey;
long dwErrorCode = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\MYTHING", 0, ACCESS|KEY_WOW64_32KEY, &hKey);
if (dwErrorCode != ERROR_SUCCESS)
{
// display error message
}
Voila, everything works now! Thanks for your efforts everyone.
Update: it turns out this didn't work as well on my other machine, which led me to discover that someone must have changed access rights to the 32 bit registry on one of my machines. So it is still necessary to give the User access rights to the registry key you want to work with.
You need to run the process in elevated mode, or turn off UAC. You can of course assign access rights to your specific registry key that allows you access.
I have faced same issue. I have solved using following:
LPCTSTR subKey = TEXT("Software\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\App Paths");
LONG openRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey, 0, KEY_WOW64_32KEY && KEY_ALL_ACCESS, &hKey);
I try to create a COM object from my JS script like this:
function main()
{
var MyApplication = new ActiveXObject("Base.Application");
}
main();
I am getting error: "Automation server can't create object". This error occurs on Windows 2003 64 bit. The dll is 32 bit and it works fine on 32 bit systems.
I've tried both versions of Regsvr32.exe on the 64 bit system and both versions told me that dll registered succesfully.
Unfortunatelly the error message does not tell me why it can not create object. The reason is unknown, it might be that it can't create object because it is still not registered or it might be something totally different...
I've also add full permisions to this dll.
I don't know what else I can do, do you have any ideas?
After edit
Ok, I know that this DLL is registered under system (found it at the registry), so why I can't call any COB objects from it?
Maybe because your process is 64 bits and you try to call in a 32 bit dll? (There are some tricks though, in this case like the one described here).
Trying to find third issue in the database with no luck.
So, I'm developing on a 64bit system (windows seven).
I'm making a simple console programme that check if a dll is present on the windows system. in that case I check in the system32 folder and then, in the sysWOW64 folder.
The pro gramme is 32bit application.
On a 32bit target platform, I can check normally the win32dll, as the SysWOW64 directory doesn't exist, no problem.
Bit when it execute on a 64bit system I can check the win32 directory, but the sysWOW64 directory always me point to the system32 directory.
It seems that on 64bit system there is a kind of redirection.
I'm trying to use the "Wow64DisableWow64FsRedirection" but I have "error C3861: 'Wow64DisableWow64FsRedirection': identifier not found" when I compile.
So there are two questions:
In winbase.h this function is disabled, how to get it work ?
How to determine if I'm currently on a 32 or 64 bit system as programme is running?
You don't have to search for DLLs. LoadLibrary() and LoadLibraryEx() will automatically search all relevant folders for you.
The search order is as follows:
The directory from which the application loaded.
The system directory.
The 16-bit system directory.
The Windows directory
The current directory.
The directories that are listed in the PATH environment variable.
If you're sure that you want to disable the redirection you can do so with Wow64DisableWow64FsRedirection as you mentioned. To make it "work", you have to set
#define _WIN32_WINNT 0x0501 (or higher)
before you include windows.h
About How to determine if i'm currently on a 32 or 64 bit system, I think that you could check the size of an int pointer...
int bits = IntPtr.Size * 8;
Console.WriteLine( "{0}-bit", bits );
While not perhaps what you're looking for, slightly wasteful if you know those are the only two places the file could be located, and possibly wouldn't work if the user has modified them (Although a whole host of other things will have broken for the user too), you could simply use the %Path% environmental variable.
My boss just got Windows 7 and he tried running one of our installers which runs perfectly fine under XP. On Windows 7, the installer runs without giving any errors. However, it does not create registry keys under HKEY_LOCAL_MACHINE\SOFTWARE{Company}{product}. These keys get created correctly under XP.
Has anyone encountered this issue? I suspect it is a rights/security issue but I'm not sure and I don't have Windows 7 to experiment with.
EDIT
The computer in question is a 64-bit machine running 64-bit Windows. It turns out Windows 7 redirects 32-bit applications to HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node{Company}{product}. The problem is my application code tries to access the registry using a hardcoded value like this:
var t = Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\..., "ValueName", DefaultValue);
So, my new question is how do I access the registry such that the Windows 9 registry redirection will just work?
If you're using .NET 4, you can specifically request that your 32-bit (or 64-bit) process access the 64-bit view of the registry using the RegistryKey.OpenBaseKey method.
Cf. http://msdn.microsoft.com/en-us/library/microsoft.win32.registrykey.openbasekey.aspx
Here's an example that reads a value from the 64-bit view of the registry, even if it runs as part of a 32-bit process:
var hklm64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
var key = hklm.OpenSubKey(#"SOFTWARE\AcmeSoft\AnvilMaker 1.0");
var value = (string) key.GetValue("Blacksmith Name");
The RegistryKey.OpenBaseKey method also allows you to explicitly open a 32-bit view of the registry. This is useful if you're trying to go the other way around and access the 32-bit view of the registry from a 64-bit process and you don't want to explicitly add "Wow6432Node" to the registry path.
For example, today I needed to delete a sub-key tree in both the 32-bit and 64-bit views of the registry. Doing this in .NET 4 with a single registry path was easy:
foreach(var view in new[] {RegistryView.Registry32, RegistryView.Registry64})
{
var hklm = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view);
hklm.DeleteSubKeyTree(#"SOFTWARE\AcmeSoft\SomeKeyWeNoLongerWant", throwOnMissingSubKey: false);
}
On a 64-bit version of windows, the above code will remove the following sub-key trees from the registry:
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\AcmeSoft\SomeKeyWeNoLongerWant
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\AcmeSoft\SomeKeyWeNoLongerWant
-Adam
After more digging I came across this link which describes the registry access rules for .NET applications. My program had initially been targeted for "AnyCpu" which causes the app to target the 64-bit registry even though Windows installed it under the Wow6432Node. By setting the target to "x86" my program "magically" started accessing the registry under the Wow6432Node. Go figure!
In the C Windows API this is done by setting the samDesired parameter in the RegOpenKeyEx call to KEY_WOW64_64KEY. This means that the lookup of the registry value will map onto the regular 64-bit entry, rather than the WOW32Node one. I can't see how you would achieve this in .Net though as the Registry class doesn't appear to support these operations, but maybe it's provided through a newer class?
Registry Reflection
Code Sample