Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Heat-ing an ActiveX control #5673
Not certian if this is a bug, but the following wxs code is the result of heat on an ActiveX Dll which was made with Visual Studio C++ 6.0 Enterprise Edition with heat.exe in the WIX Toolset 3.11 (I get an identical result with 3.10). In fact there are several controls in a directory and they are all the same as this one
I'm not sure that this is correct. It seems to just register a typelib. When I install the resulting .msi file on Windows 7 or Windows 10, the Control cannot be created. One gets an error message "Module not found" or "Invalid Class string". If I deregister the control using regsvr32 /u aturis.dll and reregister with regsvr32 aturis.dll everything works fine.
The heat options are : "c:\program files\wix toolset v3.11\heat.exe" dir controls -ag -cg controls -indent 3 -dr INSTALLDIR -var var.controlsDir -out controls.wxs
Short version: The WiX code produced matches exactly what the control writes to the registry when called by Heat. This is neither a regression nor an unknown scenario. Recommended resolution: External
Long version: When heat extracts registry values, it does so by creating an environment with an (initially empty) virtual registry, then (for this binary) calls the DllRegisterServer export followed by retrieving all values in the virtual registry and transforming them into WiX code. What aturis.dll does in this environment is write the equivalent of the attached reg file (extension changed in order to attach. I hope I don't have three copies of it attached, if I do, they are all the same). As you can see, it does declare the following (connected) entities:
If that is the complete and expected footprint, then everything "looks fine", without knowing the actual internals. However, if some other CLSID/ProgId/Interface is needed, or if the control itself internally uses others that didn't surface, then it would be expected to fail.
Because some implementations of self registration make use of preexisting values in the registry, they don't register correctly in this kind of environment. If you have access to the source code, you may wish to verify if additional registration values are required (based on OS version or some other criteria that an empty registry prevents correctly calculating). You could then manually add the required information to the created controls.wxs file.
Windows Registry Editor Version 5.00
Thank you for your analysis, which unfortunately I don't understand. I do have access to the source code and if necessary I'll post parts of it. It is a very simple ATL control which in fact wraps another DLL by making the latter's procedure calls into methods and the latter's variables into properties. This DLL is dynamically loaded via a method, so there is, afaik, no additional registry values necessary.
What I do not understand is why registering with regsvr32 causes no problems whatsoever whereas via the installer I get "invalid class string"? If something is missing, what is it?
I couldn't tell you without seeing the source code, but I've seen this situation multiple times throughout my career. All too often registration code makes assumptions that don't apply in the wild because "it always worked" on the developer's test and dev boxes.
The registration code is running in a different environment in the harvester than it does in the already installed product. Are you using the ATL Registrar code/templates? Any custom hooks? Something completely custom? Also, RegSvr32 uses two different entry points covering three different methodologies because of at least that many different prescribed methods for self registration emitted over the years of the evolution of COM/OLE. Our harvester only attempts two, and only one of those overlaps with RegSvr32.
I did notice that the registration method appeared to be using an incorrect calling convention, because the debugger warned me about a stack misalignment when the call returned. Both RegSvr32 and our harvester silently ignore that, because it's exceedingly all too common, but it can be a sign of stack corruption and could possibly indicate that the code partially failed (possibly due to some assumption).
Like I said, all of this is made automatically using the relevant tools in VC++ 6.0 Enterprise Edition. I have all the files here, and you can have any of them except perhaps the actual C++ class implementation which contains the proprietary code (which I suspect has nothing to do with registration). The file extensions are Aturis and then
AturisReader.cpp (proprietary), AturisReader.h, AturisReader.rgs, dlldata.c resource,h, StdAfx.cpp,
There are no custom hooks or anything else strange. It is a simple C++ class which exports mostly methods to Automation and which loads a DLL and has methods which just simply call the corresponding (by name0 in the loaded DLL. Nothing could be simpler. It is just that VC++ 6.0 is VERY old.
VC++ 6 did not create 64-bit code, unless I don't remember my history very well. However, registering using the 32-bit regsvr32 on a 64-bit test box wrote the registration to both the 32- and 64-bit parts of the registry in the test box I tried using, while MSI will only write to one (32-bit). That violates what little I remember about COM/ActiveX registration using InprocServer32.
Are you somehow using a 64-bit host to try to load your ActiveX?
I thought that that would have something to do with it. Using VC++ 6.0 can one only create 32 bit controls. I use the WIX Toolset on Windows XP 32 bit to make an MSI file which contains the control and a 32-bit program which calls it (made with Delphi 5.0). This MSI file gets installed on my Windows 7 and Windows 10 machines for testing, both of which are 64 bit machines. In fact the XP machine is a Virtual Box machine on 64 bit Windows 7. I do all my development in virtual machines and test on real machines.