I have a .NET 4.0 assembly that needs to be made COM-visible during installation. I'm using WiX (version 3.5), so generating a WiX fragment with heat is the logical option. This works fine, except no matter how I twiddle the knobs on heat, the CodeBase RegistryValues default to "file:///C:/Windows/Microsoft.Net/assembly/GAC_32/..." while the assembly is most certainly not installed in that directory.
The registry entries are added correctly during installation, but point to a non-existent assembly. Sad day. I have this issue even in the simplest possible use case ("heat asm.dll -out asm.wxs").
The options seem to be:
Handle installation of the assembly elsewhere in the WiX project, then manually modify the CodeBase declarations in the fragment before passing it to the rest of the WiX toolchain.
Manually add an Assembly=".net" attribute to the file tag
(http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/net-COM-registration-with-heat-td3606182.html)
Transform the fragment with XSLT (2010-06-03 Accentient Blog “Adding HEAT output to the GAC”)
None of these options are particularly appealing. Is there an automated way to do this that doesn't involve fiddling with XSLT?
Does registration-free COM activation meet your requirements?
Registration-Free Activation of COM Components: A Walkthrough
http://msdn.microsoft.com/en-us/library/ms973913.aspx
Related
Background
I have complex C++ application which is installed using msi installer (defined with wix).
My application need MSVC run time libraries so Microsoft_VC141_CRT_x64.msm is merged to my installer.
<DirectoryRef Id="APPDIR">
<Merge Id="M.visualStudioRuntime" Language="0" SourceFile="$(var.DependenciesPath)/Microsoft_VC141_CRT_x64.msm"
DiskId="1" />
</DirectoryRef>
....
<FeatureRef Id="F.SomeComponent">
....
<MergeRef Id="M.visualStudioRuntime" />
</FeatureRef>
Now during installation process some data on remote server have to be verified. For that task, I'm extracting my application dll to temporary folder, loading library and execute some function with required parameters. Depending on outcome installation setup is allowed to continue or not.
This dll depends on third party (ok not fully third party it comes from other team) C++ library which also uses MSVC run-time so static linking is not acceptable solution.
Problem
When I'm loading my application dll from temporary folder it needs those MSVC run time libraries. To do that I need exact those dlls to this temporary folder.
Problem is observed only on Windows 2012, other supported Windows versions have this run time preinstalled.
Question
What is best way to extract those MSVC runtime libraries to temporary folder?
Notes
I've inherited this installer and current implementation manually embeds those dll (as duplicates) in installer extract them based on predefined id. This is bad solution and I wish need to fix it properly.
C++: Are you running a C++ custom action that needs those runtimes? Try to statically link as explained here - then there is no need for the VCRuntime files. If you are not running C++, please let us know.
Inlined Essence: Project settings => C/C++ => Code Generation => Change the runtime library setting to multithreaded instead of multithreaded dll.
Custom Action Tips: Here is an answer on common C++ custom action problems: Interrupt installation when custom action returns error.
Secondary Answer: To extract files from a merge module, the easier approach is to add it to a project, build an MSI and then run administrative installation on the MSI - which is a glorified file extraction from the MSI: What is the purpose of administrative installation initiated using msiexec /a?
Basic Command:
msiexec.exe /A MySetup.msi
I created a solution with two new projects: ATLProject1 which is a COM dll and ATLProject2 which is a COM EXE. To both projects added same simple class with single method.
Added both to WIX setup project (this is an existing setup project that already installs other COM dlls. I'm just using it for this test).
Added following lines to product.wxs:
<Component Id="ATLProject1.dll" Guid="{...}">
<File Id="ATLProject1.dll" Name="ATLProject1.dll" KeyPath="yes" SelfRegCost="0" Source="$(var.TargetDir)\ATLProject1.dll" />
</Component>
<Component Id="ATLProject2.exe" Guid="{...}">
<File Id="ATLProject2.exe" Name="ATLProject2.exe" KeyPath="yes" SelfRegCost="0" Source="$(var.TargetDir)\ATLProject2.exe" />
</Component>
and also
<ComponentRef Id="ATLProject1.dll" />
<ComponentRef Id="ATLProject2.exe" />
The file also has these lines:
<EnsureTable Id="PublishComponent"/>
<EnsureTable Id="Condition"/>
<EnsureTable Id="TypeLib"/>
<EnsureTable Id="Class"/>
<EnsureTable Id="Extension"/>
When running the setup I get error: "Module ATLProject2.exe failed to register. HRESULT -2147024769" (hex 0x8007007f the specified procedure cannot be found).
If I remove ATLProject2 from setup, it succeeds and ATLProject1 is correctly registered in registry (this is without generating registry information e.g. using heat, it just works).
Should exe component be treated differently?
I found this 10 year old post suggesting heat.exe does not treat COM exe as COM. If this is the problem, Not sure if this is still the case?
The Windows Installer does not recommend using SelfReg to register at install time. Instead, adding the registration to your .wxs code or capturing the registration at build time is highly recommended.
To add the registration manually, you don't use EnsureTable, you use the COM related elements (like Class, ProgId, TypeLib). It can be tedious but will be far more robust than trying to selfreg during installation.
Unfortunately, the alternative to capture the registration during build using heat.exe (provided in the WiX toolset) does not support capturing from executables. If you are open to a commercial solution, we (FireGiant) developed an alternative to heat.exe that can capture executable registration (and much more). That advanced harvesting solution has more documentation on the FireGiant site.
RegServer Switch: COM EXE files are normally self-registered via the /RegServer switch as in:
MyBinary.exe /RegServer
In other words EXE files are not registered via the normal regsvr32.exe mechanism. This is the tool used to register COM dll's and OCX files, but it does not handle EXE files. There is also an /UnRegServer switch to unregister EXE COM files - for the executables that support /RegServer (which is not all COM EXE files - it could be missing as a feature).
Self-Registration: Self-Registration is not ideal to register COM files, and here is a write-up of why this is the case: MSI register dll - Self-Registration considered harmful. In MSI one extracts the COM registry data and populates a number of special COM-tables to allow the registration of the COM server in a way that supports advanced features such as rollback. I don't like the COM extract either (risk of self-repair problems, more on self-repair problems), but it helps in most cases - especially when there are dependencies that can trigger registration problems. Moreover it is the way COM files are supposed to be registered in MSI. It is the standard. I should note that some COM settings go into the Registry table still - since there are no dedicated COM-related tables for them.
heat.exe: WiX's own heat.exe tool now can extract COM data from dll files and ocx files (32-bit). But it does not seem to work for EXE COM files - I am not sure why:
heat.exe file MyCOMDll.dll -out MyCOMDll.wxs
RegSpy2.exe: There is a tool you can use to extract COM registration information from both DLL, OCX and EXE files. It can be downloaded from here: http://www.installsite.org/files/iswi/RegSpy2.zip. Here is the main page listing numerous tools. The RegSpy tool is written by Phil Wilson - MSI Expert and author of The Definitive Guide to Windows Installer (APress).
Here is how to extract the COM data from a COM executable (if you get no data, try unregistering the file first and then running regspy.exe):
RegSpy.exe MyBinary.exe /RegServer >> RegistryOutput.reg
The exported *.reg file can then be converted to WiX elements. This is not an easy process. Using the WiX tool heat.exe does not populate the proper COM tables, but rather puts everything in the Registry table (which will work though, barring MSI validation errors):
heat.exe reg MyCOMRegistryData.reg -out MyWiXFile.wxs -sfrag -suid
There used to be a tool called Tallow that converted reg files to WiX COM registration, but this tool is no longer anywhere to be found. I am not aware of any other ways to generate it short of writing it yourself, or download another deployment tool and import the COM data or extract it and decompile the generated MSI with dark.exe and take out the WiX markup. Or figure out how heat.exe writes its WiX XML output with COM data and adapt that to process the output from RegSpy.exe.
UPDATE: Throwing in a link to Paraffin: https://github.com/Wintellect/Paraffin. This is supposedly a "better Tallow". I am not sure what it supports in terms of COM-extract. My quick test seemed to indicate it doesn't support COM extraction at all, but supports auto-generating WiX markup and add and remove files for updates.
Custom Actions: It is possible to register your COM EXE by means of a custom action that calls the /RegServer switch as well, though this is not recommended for all the reasons listed in the link above (self-registration considered harmful).
Some Links:
Adding a .reg file to registry WIX
How to generate WiX XML from a .reg file?
I am working on Wix and I have to place a new dll (Let's say New.dll) as a part of MSI installation into a specific path in GAC where some other dll already resides.(Let's say Exisiting.dll. Please note this dll exists before installing my MSI).
So is there a way I can achieve this? Please note New.dll contains native code and is not .net assembly. Thanks.
The GAC is only for files in strongly-named assemblies. An assembly is a versioned set of files, one of which has a manifest. Creating multi-file assemblies is easy but most tools neither help create nor respect that an assembly may have multiple files. The basic tool is the assembly linker. See How to: Build a Multifile Assembly.
A native DLL can be made part part of an assembly with the /linkresource switch for the C# compiler. If you want to fiddle with a C# project file, you can override the standard Compile target and Csc task with your own that will pass linkresources.
WiX does not handle multi-file assemblies. You have to create File elements for each file in the assembly. All such Files should be in the same Component. This is an instance where having a single component makes sense when Organizing Applications into Components.
If want want a different behavior (that is, replace one of the files), then you have to create a new version of the assembly/Component. Hopefully, that is how the previous native DLL got into the GAC in the first place.
I've only done this a little bit years ago. I'm 99% certain that the new.dll would be a companion file of the old.dll. In other words, the old.msi has a component with old.dll as the keyfile. The new.dll has to be added as an additional file to this component so that MsiPublishAssemblies / Fusion API can publish them together into the same storage directly.
You may be able to do this in a new.msi provided that you sync up the component ID (GUID) and mark it as a shared component. You would have to rebuild old.dll with the same strong name (and AssemblyVersion) but with a higher AssemblyFileVersion / Win32 File Version.
MSI component rules require newer DLL's to be backwards compatible. This goes double for the GAC. This seems to be a substantial change to old.dll. It might make sense to just rev the AssemblyVersion and define this as a whole new component and install side by side in the GAC.
The first thing you are going to want to do is search for the file on the system... You can do this with the FileSearch WiX statement:
<Property Id="PROPFILESEARCHEXISTINGDLL" Secure="yes">
<DirectorySearch Id="EXISTINGDLLFOLDERPATH" Path="[WindowsFolder]\assembly\" Depth="5">
<FileSearch Name="Existing.dll"/>
</DirectorySearch>
</Property>
DirectorySearch Reference: http://wixtoolset.org/documentation/manual/v3/xsd/wix/directorysearch.html
FileSearch Reference: http://wixtoolset.org/documentation/manual/v3/xsd/wix/filesearch.html
This will put the full path to the file in the PROPFILESEARCHEXISTINGDLL property. From here you can then reference this property when placing the file on disk. (Assuming of course your have permissions to write to that location...)
and i need to register a dll . Right now am registering the dll as below
<CustomAction Id='comReg' Directory='INSTALLLOCATION' Impersonate='no' Execute='deferred'
ExeCommand='"[NETFRAMEWORK40CLIENTINSTALLROOTDIR]regasm.exe" "[INSTALLLOCATION]myProduct.dll" /codebase' Return='check' />
but all are suggesting to use HEAT.exe in internet wherever and whenever i surf . I even have gone through this link. But there they have only the syntax etc. I really dont know how to work with it just by knowing those syntax.
I need some step by step procedure or some good blog which elaborately tell how how to harvest a dll and how to impllement it in to wix code and how the registry will be done , so that i can register my dll based on conditions also.Even i tried this link alse
Regards
Registering a COM component is done through standard Windows Installer actions and tables. Numerous table entries are required so WiX provides heat.exe to harvest COM files. It works by inspecting a file for type libraries and, if the DllRegisterServer entry point is present, running it in a Registry sandbox where changes are intercepted and captured. (In the era of Windows Installer [1999-present], DllRegisterServer is effectively deprecated for any other purpose.)
As you may know, a Feature is the smallest user-selectable unit of installation. A Component is a member of one or Features in one or more Products. Heat creates a component for each file it harvests and a ComponenentGroup for each harvest run. So, you have to pick a ComponentGroup Id and reference it in one or more Features. You also have to pick a Directory Id for the destination of the ComponentGroup.
Now, it is simply a matter of editing your project file to harvest the file. To edit a project file you could use a text editor but Visual Studio is all set up for it. See How to: Edit Project Files.
Add a HarvestFile element to a new or existing ItemGroup, entering the desired ComponentGroup Id and Directory Id
<ItemGroup>
<HarvestFile Include="comserver.dll">
<ComponentGroupName>COM</ComponentGroupName>
<DirectoryRefId>ServerDir</DirectoryRefId>
</HarvestFile>
</ItemGroup>
In your Product.wxs or elsewhere, add a ComponentGroupRef to one or more Features.
I am building a MSI installer with WiX for redistributing my MinGW64 application. This requires to ship MinGW dlls with the executable.
The libstdc6.dll file is not versionned. What is the cleanest approach for this?
I have tried:
Adding a companion file to the dll component, pointing to the main app exe, which is versionned. However, wix complains that I cannot set a companion to a dll
Putting the dll in the main app exe component, but this is discouraged in every wix faq, howto and forum...
Adding attribute DefaultVersion="!(bind.FileVersion.mainapp.exe)" to the dll file element for overriding the (missing) dll version. This seems to work at first glance, but wix is issuing warnings related to this:
C:\Users\Etienne\Documents\coding\ocean\ocean.wxs(116) : warning
LGHT1103 : The DefaultVersion '!(bind.FileVersion.mainapp.exe)' was used
for file 'libstdc6.dll' which has no version. No entry for this file
will be placed in the MsiFileHash table. For unversioned files,
specifying a version that is different from the actual file may result
in unexpected versioning behavior during a repair or while patching.
Version the resource to eliminate this warning.
What is the best way to solve this?
Thanks,
Etienne
IMHO, your best bet is option #2. All the rules assume that executable code would be properly versioned and thus are best deployed as KeyPaths of their respective Components. Since the .dll is not versioned, placing it as a data file with a versioned file is the next best thing.
Although, for the record, versioning the .dll is by far the most optimal solution. :)