Wix: How to remove GAC files marked as Permanent - wix

A previous version of an MSI marks some GAC files as permanent, now I want to remove those files upon upgrading to a newer version of the same program. Is it possible to do this using Wix code or do I have to always manually delete the files ?

I don't know that you can ever fully delete the component since it's permeant but you can remove the file.
I tested this by creating a component called Client.exe and a component called Server.dll which was marked Permanent="yes" and Assembly=".net".
<DirectorySearch
Id="SERVERFOLDER"
Path="[WindowsFolder]Microsoft.NET\assembly\GAC_MSIL\Server\v4.0_1.0.0.0__bb771c317b736a07" AssignToProperty="yes">
</DirectorySearch>
<RemoveFile Id="oldfile" Property="SERVERFOLDER" Name="Server.dll" On="both"/>
<RemoveFolder Id ="old" Property="SERVERFOLDER" On="both"/>
The remove File/Folder elements are added to the Client.exe component.
This results in Server.dll being removed from the GAC. But I do have to ask, why bother? A file in the GAC has a strong name and shouldn't break anything that doesn't reference it. Also it's not something that takes up much disk space or anything the user really sees.

Related

Make Wix to not uninstall common dll

I have Wix project in which I need a common used dll library to be installed if it's absent.
If this dll exists I should not overwrite it.
So, when I set DefaultVersion="0.0.0.0" this dll is not overwritten if it exists, its ok. But when I delete app, the dll is beeing removed. How do I prevent removing dll in the case when it existed before installation?
I don't want to make it permanent because it should be removed if it didn't exist before installation.
<Component Id="myLib.dll" Permanent="no" Directory="Shared_Dir">
<File Name="myLib.dll" KeyPath="yes"
Source="mySource\myLib.dll"
DefaultVersion="0.0.0.0"
/>
Add reference to WixUtilExtension and xmlns:util="http://schemas.microsoft.com/wix/UtilExtension" attribute to Wix element in your code.
Define <Property Id="Dll_Installed" Value="false"/> in Product element.
Add child <Condition>NOT Dll_Installed</Condition> to component myLib.dll.
Add that somewhere in your code:
<Fragment>
<util:FileSearch
Id="Dll_Installed"
Variable="Dll_Installed"
Path="[Shared_Dir]myLib.dll"
Result="exists"/>
</Fragment>
DefaultVersion attribute is not necessary.
The feature you are describing is reference counting. The Windows Installer reference counts with Components. Components are identified by their GUID.
So the normal way to address this requirement is to put the File in a Component and make sure the GUID of the Component is stable. The WiX Toolset should do exactly that automatically if if you do not specify the Component/#Guid attribute.
So the default behavior should just work for you.
The only other piece of the puzzle is the Windows Installer will install the latest version of a file. If the file version is the same or less the file will not be installed but will be reference counted.
Based on the details in the question it seems like you should be just fine with:
<Component Directory="Shared_Dir">
<File Source="mySource\myLib.dll" />
</Component>
One might ask why the Windows Installer use Components to reference count files. We'll, it allows you to group other resources, like registry keys, together and control their install as a unit. Much more important if you are installing COM servers than plain old files.

How can I conditionally overwrite file during WIX install?

I have two work modes in my installer:
use config files left from previous installation
delete all existing configs and put default configs instead
The mode is determined by the checkbox in the WPF UI of the installer. If second mode is selected, then CustomAction is run, which manually deletes the configs folder from disk:
<InstallExecuteSequence>
<Custom Action="RemoveConfigsFolder" After="RemoveFolders" Overridable="yes">NOT Installed AND DELETESETTINGS=1</Custom>
</InstallExecuteSequence>
I'm using NeverOverwrite attribute:
<ComponentGroup Id="Configs" Directory="INSTALLDIR" >
<Component Id="Configs" Permanent="yes" NeverOverwrite="yes">
<File Id="main.config" Name="main.config" Source=".\Configs\main.config" KeyPath="yes" />
</Component>
</ComponentGroup>
The first mode works fine in this case, but when I try to use second mode it fails and all configs are just deleted and never created again during the installation.
During my research of the issue, I think I've found the reason why this happens: https://community.flexerasoftware.com/showthread.php?96157-The-truth-the-whole-truth-about-quot-Never-overwrite-quot-and-quot-Permanent-quot-files&p=156826#post156826
Actually this is a Windows Installer issue. If you log the uninstall
you will notice that very early in the installation the Installer
decides that the component containing this file will not be installed
because it is marked "Never Overwrite" and a copy of this file already
exists on the target machine. The uninstall happens after that which
removes the existing file. This is because the Installer decides this
when the "CostFinalize" action is launched. This action HAS to be run
before the "RemoveFiles" action.
But how do I fix it?
The problem with settings such as Never Overwrite or Permanent is they look like build settings, but they are not really - they stick to the system attached to the component id. So resetting in the project won't help because it's associated with that id. It's also not clear why setting Never Overwrite might have been a solution to some problem, because by definition patches and overwrite upgrades won't overwrite it, but overwriting it is a requirement of your setup.
Even if you had not set Never Overwrite the Windows Installer rules would not overwrite the file if it was modified after install. So if you had installed it, then it was altered, and then you did an upgrade, the file would not be overwritten (which is another reason why Never Overwrite does not seem needed).
Another issue is that your custom action RemoveConfigsFolder is not marked with an Execute enumeration value, therefore it is immediate, therefore it does not run elevated, therefore it might simply be failing, so without seeing the code it's impossible to say if reports an issue if it can't do the remove. It's also not possible to determine if it explicitly specifies the full path to the folder correctly. So the most likely quick fix to this issue is to mark the custom action as execute deferred, and the DELETESETTINGS value will need to be passed in via CustomActionData.
My initial thought is to remove the 'Never Overwrite' property. Then create a component condition that checks if the file exists. My thought is that your custom action has the condition to correctly remove the config files. If the files do not exist then the components will be selected for install.

WiX NeverOverwrite flag deletes file

I have a data file that is installed with my application. When it is installed fresh, the data file gets installed properly. When it is upgrading, it was always overwriting the existing instance of the file, which was not wanted. I added the NeverOverwrite="yes" flag, and now for a new install is still the same, but for an existing install, it is deleting the existing file, and not installing the new file either. Have I used the flag incorrectly? This is the snippet in question:
<Component Id="cmp8553E6DDC92DBCDC568FB76CA13E7AF2" Guid="{C94263EB-56AF-4B19-942F-C92998252932}" NeverOverwrite="yes">
<File Id="fil7C9456E83E3FB4618684509DEEF67EBD" KeyPath="yes" Source="$(var.CDWrapper.ProjectDir)..\foo\bar" />
</Component>
Turns out I didn't have a full understanding of the NeverOverwrite flag and our usage of it. I will have to find a different method to preserve the existing file instead of always installing a new copy, but that's a whole different issue that should have reasonable answers elsewhere.

Wix - how to prevent overwrite entire directory?

I have wix installer and it copies some files to some directories. Each file is declared as single component, with some path - if directory does not exist, installer will create it and place file there.
What I want to do: if directory already exists, installer should not copy any files there (even if file does not exists, it should not be copied to already existing directory).
But it's impossible to set "Never overwrite" to directory, so how can I prevent copy new files to already existing directory? Is there any condition (something like "is directory exist") that I can use here?
Is there any condition (something like "is directory exist") that I can use here?
Yes, you can use a Condition element like this:
<Directory Id="FooFolder" Name="Foo">
<Component Id="SomeId">
<File Source="..." />
<Condition>Not FOO_FOLDER_ALREADY_EXISTS</Condition>
</Component>
</Directory>
The kind of things that you can use as a condition are explained in the Conditional Statement Syntax documentation of windows installer.
In this case, I believe you can set the FOO_FOLDER_ALREADY_EXISTS property with a DirectorySearch like this:
<Property Id="FOO_FOLDER_ALREADY_EXISTS">
<DirectorySearch Id="FooFolderSearch" Path="[FooFolder]" />
</Property>
edit: apparently the directory search above doesn't work because the [FooFolder] property is only resolved during the CostFinalize action (see documentation). But the directory search already happens before that during the AppSearch action.
I am unsure how to work around that. It would probably involve setting the FOO_FOLDER_ALREADY_EXISTS property after CostFinalize with a custom action instead of a windows installer directory search.
In regards to how to avoid the timing issue with the property being set before cost finalize, another reliable way to deal with it is to write the properties to registry.
I believe this is the most common work around to the timing issues with properties getting set, and it allows the installer to keep track of those properties for uninstalls, etc... I've used it to great effect.
Here is a good article by Rob Mensching on how to do that to get you started.

Install a file regardless of version number with WiX

MyApp version 1.0 contained the file foo.dll. The version of this file was version 10.5.567. Now MyApp is version 2.0 and the version of foo.dll is 2.0.123. The version is LOWER than the old version. The vendor who wrote this dll decided to go backwards with the file version number. This is not a decision I can change.
How do I get WiX to always install this file?
The RemoveExistingProducts action is scheduled after the InstallFinalize action and also cannot be changed.
InstallShield had an 'always overwrite' flag but I don't think I can mimic this without changing the behavior of all files via a compiler switch. I do not want this. I just want to update THIS ONE file.
I have also tried
<RemoveFile Id="foo.dll" On="install" Name="foo.dll" />
But this did not work either. I end up getting 'Error 2753 The file foo.dll is not marked for installation' later.
It is really crazy this post is 10 years old and we also have this problem. The other approaches did not work for us.
How to solve it:
All files which should be copied regardless of their version, must be in their own component
Component element must have a unique Guid, not a *
Set KeyPath to no in the inner File element
Example:
<Component Id="cmpExample" Guid="{5ABE7229-C247-4CBA-B8DE-2C4968CD103E}" >
<File Id="fileExample" KeyPath="no" Source="$(var.TargetDir)\SomeExample.dll" />
</Component>
I would recommend to aggregate all those component elements in a component group. And furthermore you can use XML Transformation to get rid of those files if you use heat.exe to harvest all your files.
With this solution you can copy the file, even if the installed file version is higher. And it still works as expected when a higher file version will be installed with your upgrade.
This isn't easy because it is against the standard behaviour of MSI-packages. When compiling, you have to set supress-file-options with -sf in light.exe. Then there are no file-informations from your files read. You can set DefaultVersion this version will be used.
I have also used RemoveFile in a solution, but i can say that it works. I have add it in same componente where i add the file.
<Component>
<File DiskId="1" Id="fooDLL" Name="foo.dll" Vital="yes"
DefaultVersion="$(var.MAJORVERSION).$(var.MINORVERSION).$(var.BUILDVERSION)"
DefaultLanguage="0"></File>
<RemoveFile Id='RemoveOldDLL' On='install' Name='foo.dll'/>
</Componente>
This is not a clean way, but to remove an old version it works for me.
I answered a similar question a while back:
Forcing an upgrade of a file that is modified during its initial installation
I think I would use a companion file to solve this problem, but you might get validation errors since the file in question is a dll and hence considered an executable. I believe it would still work, and reliably so.
Annotation
Force always overwrite in Light.exe output with the "InstallShield trick":
XPath="/wixOutput/table[#name='File']/row/field[5]" InnerText=„65535.0.0.0“
One thing that could be worth trying is to set RemoveExistingProducts real early in the sequence. The MSI should then remove the old program first, then install the upgrade. This will solve your issue. e.g.
<InstallExecuteSequence>
<RemoveExistingProducts After="CostFinalize" />
</InstallExecuteSequence>