WiX GAC install and regasm for the same dll doesn't work - wix

I am creating an installer using WiX which contains few dlls. In the original script first gacutil.exe and then regasm for each dll is called. I am having a problem when transfering this idea into WiX. Here is the code fragment for installing into GAC :
<Component Id="GMAG.Core.Serialization.dll" Directory="_2.2.8.0" Guid="{my_guid}">
<File Id="my.dll" Source="my_src" Assembly=".net" KeyPath="yes" Checksum="yes"/>
</Component>
Now the question is how WiX will perform the assembly registration? My problem is:
I can't create another <File Id="my.dll" Source="my_src" KeyPath="yes"/> in the same component cause there must be only one keyPath="yes" attribute/component.
I can't put keyPath="yes" in component level, as it breaks component reference counting system.
I can't create <File Id="my.dll" Source="my_src"/> without keyPath="Yes", as it generates compilation error.
I can't create a seperate component as two components will try to install same file.
I know I don't need to call regasm or regsvr32 when using heat. In the component code :<File Id="my.dll" Source="my_src" KeyPath="yes"/> should be enough for the registration.
I'm using heat and now I'm stuck as I have to do assembly registration also.

Assembly=".net" will put you assembly in the GAC, you will then need registry keys for the COM registration. If you run Heat against your assembly it should generate the code fragment you require.

That's how we register DLL in GAC via WIX 3.5:
<Component Id="Level0GAC" Guid="21735A8C-DD0C-4f4e-8AB5-B5BB8C55726B" DiskId='1'>
<File Id='Level0' Name='DLLFileName.dll' DiskId='1' KeyPath="yes"
Source='DLLFileName.dll'
Checksum="yes" Assembly=".net" AssemblyManifest="Level0">
</File>
</Component>
Indeed, there can be only one file inside a Component with a KeyPath set ot "yes", that's why for several DLLs you should create several Components.

Related

WIX — Add files to both GAC and INSTALLFOLDER without changing the name

Despite the different Ids and GUIDs the following code refused to compile because the following components have the same name. Setting the Name attribute does the trick, but I don't want libraries to have different names in GAC and install folder.
So far I have circumvented the issue by creating a CustomAction that renames one of the components on install, but this clearly isn't optimal. Is there an out of the box solution?
<ComponentGroup Id="HistoryGroup" Directory="INSTALLFOLDER">
<Component Id="History" Guid="*">
<File Source="$(var.ProjectName.TargetPath)" KeyPath="yes"/>
</Component>
<Component Id="HistoryGAC" Guid="*">
<File Source="$(var.ProjectName.TargetPath)" KeyPath="yes" Assembly=".net"/>
</Component>
</ComponentGroup>
Give both file elements explicit unique Id attributes. For the one going to the GAC install it to another dummy folder that already exists. (Don't worry it won't get installed there, it'll go to the GAC.)

The target file 'XXXX' is installed in 'YYYY' by two different components on an LFN system

We recently upgraded DevExpress. Since we have a custom theme, we had to upgrade the custom theme too.
That was the easy part. Now I'm trying to upgrade the setup to match the new file.
So basically, I'm changing the <File .../> of one <Component .../>:
From
<Component Id="Lib_Various_Files" Guid="9C621EB0-12E6-4D1D-8B5B-4150A76E33AA" KeyPath="yes" SharedDllRefCount="yes">
...
<File Id="DevExpress.Xpf.Themes.PreviousTheme.v17.1.dll" Name="DevExpress.Xpf.Themes.PreviousTheme.v17.1.dll" ReadOnly="yes" Vital="no" Compressed="default" DiskId="1" Source="$(var.DirLib)\PreviousTheme\DevExpress.Xpf.Themes.PreviousTheme.v17.1.dll" />
</Component>
To:
<Component Id="Lib_Various_Files" Guid="9C621EB0-12E6-4D1D-8B5B-4150A76E33AA" KeyPath="yes" SharedDllRefCount="yes">
...
<File Id="DevExpress.Xpf.Themes.OurTheme.v17.2.dll" Name="DevExpress.Xpf.Themes.OurTheme.v17.2.dll" ReadOnly="yes" Vital="no" Compressed="default" DiskId="1" Source="$(var.DirLib)\OurTheme\.td\Publish\DevExpress.Xpf.Themes.OurTheme.v17.2.dll" />
</Component>
Now I've an error in the setup, which seems to have no links to this line:
error LGHT0204: ICE30: The target file
'qgikh9i6.dll|System.Windows.Interactivity.dll' is installed in
'[TARGETDIR]\OurProduct\Bin\' by two different components on an LFN
system: 'cmpF5730C92213BA3272DDA3A5657DFF782' and 'Lib_Prism'. This
breaks component reference counting.
[D:\ws\OurProduct-Nightly\SetupWix\SetupWix\SetupWix.wixproj]
We do reference this library, in the Lib_Prism component(which is then in another Lib_Various component, that reference Lib_Prism and Lib_Various_Files, but nowhere else.
Any idea what could be the issue?
So here is the complete components list of this file:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<?include ..\Common.wxi?>
<DirectoryRef Id="BIN">
<Component Id="Lib_Various_Files" Guid="9C621EB0-12E6-4D1D-8B5B-4150A76E33AA" KeyPath="yes" SharedDllRefCount="yes">
...
<File Id="DevExpress.Xpf.Themes.OurTheme.v17.2.dll" Name="DevExpress.Xpf.Themes.OurTheme.v17.2.dll" ReadOnly="yes" Vital="no" Compressed="default" DiskId="1" Source="$(var.DirLib)\OurTheme\.td\Publish\DevExpress.Xpf.Themes.OurTheme.v17.2.dll" />
</Component>
<Component Id="Lib_MicrosoftPractices" Guid="780097FD-40C9-417A-A2C3-7C2B44567BEC" KeyPath="yes" SharedDllRefCount="yes">
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll" />
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\Unity.4.0.1\lib\net45\Microsoft.Practices.Unity.Configuration.dll" />
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\Unity.4.0.1\lib\net45\Microsoft.Practices.Unity.RegistrationByConvention.dll" />
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\Unity.4.0.1\lib\net45\Microsoft.Practices.Unity.dll" />
</Component>
<Component Id="Lib_Prism" Guid="0F937515-2248-4CD2-B2E9-3E121FA9D743" KeyPath="yes" SharedDllRefCount="yes">
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\Prism.Core.6.3.0\lib\net45\Prism.dll" />
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\Prism.Unity.6.3.0\lib\net45\Prism.Unity.Wpf.dll" />
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\Prism.Wpf.6.3.0\lib\net45\Prism.Wpf.dll" />
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\Prism.Wpf.6.3.0\lib\net45\System.Windows.Interactivity.dll" />
</Component>
<Component Id="Lib_MvvmValidation" Guid="8681DBA1-F83D-475B-BCB8-A54A1F05FF0A" KeyPath="yes" SharedDllRefCount="yes">
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\MvvmValidation.3.1.0\lib\netstandard1.0\MvvmValidation.dll" />
</Component>
<Component Id="Lib_Protobuf_Net" Guid="AEE6F4EB-78E3-4EC5-AA88-D5CC29D683D0" KeyPath="yes" SharedDllRefCount="yes">
<File ReadOnly="yes" DiskId="1" Source="$(var.DirDotfuscated)\ProtobufNet.dll" />
</Component>
</DirectoryRef>
<ComponentGroup Id="Lib_Various" >
<ComponentRef Id="Lib_MicrosoftPractices" />
<ComponentRef Id="Lib_Prism" />
<ComponentRef Id="Lib_Various_Files" />
<ComponentRef Id="Lib_MvvmValidation" />
<ComponentRef Id="Lib_Protobuf_Net" />
</ComponentGroup>
</Fragment>
</Wix>
Looking at your source file there are several problems with your component reference counting outright. You should never install several binaries with one component - it is a direct violation of the component rules. This causes exactly the kind of problems the error message indicates.
I recommend using a single file per component because that solves a plethora of possible reference count issues and upgrade problems. The shared-dll ref counters can also cause some blues I think. Do you have a legacy installer that you are trying to be compatible with? If not, then there is no reason to enable this component option - it increments the legacy SharedDLL ref-counter used by older, non-MSI installer technologies.
Now, for the issue where you change a file name in an existing component. This is also a violation of the component rules. You can not change the absolute file name of a component's key path and keep the same component GUID - this breaks component referencing. There must be a 1-to-1 correspondence between an absolute installation path and a component GUID.
The component GUID doesn't follow the file around if it moves, and the file "moves" when you change its file name (its absolute installation path has changed). There is an explanation here with an example: Change my component GUID in wix? (recommended read - decode this MSI peculiarity and things will be clearer going forward).
If you change a file name you can either:
Set your component GUIDs to auto-generate by deleting the whole GUID section in your source. The GUID will then be generated to be stable as long as the installation target path remains the same, and when you change the file name - for example - a new GUID will be generated for you auto-magically by WiX. See this answer for sample: Syntax for guids in WIX?
Set a new, hard-coded GUID yourself for the components where you change the file name that is being installed. This can be easy to forget - hence the recommended auto-magic described in point 1.
What you should actually do when file names change is to remove the old component and add a new one with the new file name. However, changing the GUID of an existing component and changing the file name has the same effect (same as deleting the old component and adding a new one).
With that said, there are bigger problems with this source as explained above. For future reliability you must split these components into one file per component. This causes interference between your old and new version and in order to clean this up, you can:
Set a totally new installation path for your project and use a single component per file from now on and you can use WiX's auto-magic component generation feature as explained above. This will work. Setting a new main installation folder "breaks the link" to "past component referencing sins".
Or you can uninstall the existing installation early during your major upgrade by moving RemoveExistingProducts early in the InstallExecuteSequence of your newest MSI version. This also wipes the slate clean of any component referencing issues and you can change your source to use one file per component going forward. If you use the MajorUpgrade element this change is easy - just set Schedule="afterInstallValidate". That should work (no time to test).
That should be it - if I have understood your scenario correctly.
Sample WiX extract for the proposed, new version:
<DirectoryRef Id="BIN">
<Component Feature="Product">
<File Source="$(var.DirLib)\OurTheme\.td\Publish\DevExpress.Xpf.Themes.OurTheme.v17.2.dll" />
</Component>
<Component Feature="Product">
<File Source="$(var.DirPackages)\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll" />
</Component>
<Component Feature="Product">
<File Source="$(var.DirPackages)\Unity.4.0.1\lib\net45\Microsoft.Practices.Unity.Configuration.dll" />
</Component>
<Component Feature="Product">
<File Source="$(var.DirPackages)\Unity.4.0.1\lib\net45\Microsoft.Practices.Unity.RegistrationByConvention.dll" />
</Component>
<...>
</DirectoryRef>
Notice the tersified source with all attributes that can be auto-generated left out and all components now containing a single file. There is also direct specification of what feature each component belongs to as an attribute of the Component element. I find that this yields the least complicated and most flexible WiX source files. Preferences vary - obviously.
I would not roll with your current "multiple binaries per component" setup going forward. There will be more trouble if you do - almost guaranteed. MSI bites back - sorry to say - there are many bear traps. MSI has aspects that border on anti-patterns. The problems are faced by almost everyone. There is a section towards the bottom here on potential anti-patterns and also on the great benefits MSI yields for corporate deployment (just for reference): How to make better use of MSI files.
I am not particularly keen on this chaotic write-up of common MSI problems, but here it is: How do I avoid common design flaws in my WiX / MSI deployment solution? Maybe it can help to avoid some very common problems.
I finally found the issue:
It appears that DevExpress bin directory packs the System.Windows.Interactivity.dll library. So before we were not copying it and we didn't had it in our Lib\DevExpress folder.
It appears that we generate a componet with all Dll contained in the Lib\DevExpress folder, and therefore the System.WIndows.Interactivity.dll was contained in 2 differents packages.
I removed it from the DevExpress folder and now everything works fine. Sorry for the trouble.

How to include shared references in WiX project

I have a Visual Studio solution with three projects: Core, Application, Setup (WiX). In the setup project I have a separate ComponentGroup for each VS project. These ComponentGroups contain Component for the project output and Components for all its references. For example:
<ComponentGroup Id="ApplicationComponents" Directory="INSTALLFOLDER">
<Component Id="Application" Guid="SOME-GUID-1">
<File Id="ApplicationExe"
Name="$(var.Application.TargetFileName)"
Source="$(var.Application.TargetPath)"
KeyPath="yes" />
</Component>
<Component Id="ApplicationCommonLogging" Guid="SOME-GUID-2">
<File Id="ApplicationCommonLoggingDll"
Name="Common.Logging.dll"
Source="$(var.Application.TargetDir)Common.Logging.dll"
KeyPath="yes" />
</Component>
...
</ComponentGroup>
However, references to Common.Logging.dll exist in both Core and Application projects, so I get an error like this:
ICE30: The target file '138eezlo.dll|Common.Logging.dll' is installed in '[ProgramFilesFolder]\MyApp\' by two different components on an LFN system: 'CoreCommonLogging' and 'ApplicationCommonLogging'. This breaks component reference counting.
Is there something wrong with the structure of components in my setup project? How and where I should declare Components for Common.Logging and other shared references?

Add a new CLSID to windows registry using WiX

I am trying to register a new filter with Windows Desktop Search. Ideal way to achieve do this would be registering new filter with existing persistent handler CLSID. But this cannot be done since .html PersistentHandler CLSID, {eec97550-47a9-11cf-b952-00aa0051fe20}, is protected under WRP (More about the problem).
As a workaround, I am trying to create a different CLSID that does the same job as {eec97550-47a9-11cf-b952-00aa0051fe20}.
This is the sample code I am following. I am quite new to WiX and editing Windows registry.
<File Id="HTMLfilter.DLL">
<Class Id="$(var.CLSID_HtmlIFilter)" Context="InprocServer32" ThreadingModel="both" Description="Html Filter" />
</File>
Could someone help me regarding these;
How to create a CLSID that is not affiliated to any file? Since my
new CLSID is doing the work of above mentioned CLSID, I think this is
how it should be.
How to create a a sub-directory named PersistentAddinsRegistered
instead of InprocSever32
Thanks
Have a look at this page here shows how to add COM objects to installers
I suggest export selected branch to registry file (Export all or part of the registry to a text file).
Then using Heat.exe harvest registry file and include its output in your project.
This is an example
<Fragment>
<DirectoryRef Id="TARGETDIR">
<Component Id="cmp6E2CE62C9ADECD355465514E3C8F354E" Guid="PUT-GUID-HERE" KeyPath="yes">
<RegistryKey Key=".ascx\PersistentHandler" Root="HKCR">
<RegistryValue Value="{eec97550-47a9-11cf-b952-00aa0051fe20}" Type="string" />
</RegistryKey>
</Component>
</DirectoryRef>
</Fragment>

Is it possible to have a merge module targetting the GAC?

I'm trying to figure out the best way to create a merge module that installs .NET assemblies. I'd like to allow the user of the module to specify a folder or the GAC.
I found that to allow targeting of a folder I should use and to install in the GAC I have to set Assembly=".net" and KeyPath="yes" in the File tags.
It it possible to merge these two or do I have to create two different merge modules?
As far as I know, this is roughly how you'll have to do it:
<ComponentGroup Id="Assembly1">
<Component Id="gac_Assembly1" Guid="*">
<Condition><[CDATA[INSTALLGAC]]></Condition>
<File Id="fil_Assembly1" . . . />
</Component>
<Component Id="file_Assembly1" Guid="*">
<Condition><[CDATA[Not INSTALLGAC]]></Condition>
<File Id="fil_Assembly1" . . . />
</Component>
</ComponentGroup>
You should always KeyPath the one file under your component. INSTALLGAC is a public property that the MSM-includer can set.