What is the usage of TARGETDIR and INSTALLDIR in WiX? - wix

In legacy Visual Studio Deployment Project installers, passing a command-line parameter that specified a value for TARGETDIR allowed me to override the default installation location (most of my installations take place without user interaction, so command-line automation is used heavily). However, the impression I'm getting is that WiX (by default) uses TARGETDIR for something different. While I can (and will) update our command-line tools to change the parameter name, that still leaves all of our existing installations that would need to be touched manually (a non-trivial effort).
Is there any way to override the installation location in a WiX package by specifying TARGETDIR without breaking anything?

After doing more digging, it looks like my previous experience is a result of behavior specific to VSDPROJ's (and possibly InstallShield), wheras WiX is conforming to the Windows Installer.
As I discovered at this link, TARGETDIR is actually supposed to represent the root of the drive with the most free space available (assuming there's more than one). That's why WiX projects have directories nested under there for Program Files, etc. Visual Studio actually adds a custom action that overrides this property to the full installation path.
I was able to accomplish what I wanted by doing two things:
Change all of my components and component groups to install to TARGETDIR instead of INSTALLFOLDER (the default directory that WiX put in there)
Add a custom action that sets the value of the TARGETDIR property to the installation path, assuming one wasn't passed in from the command line.
To do that, I added this under the <Product> tag:
<CustomAction Id="SetTARGETDIR" Property="TARGETDIR"
Value="[ProgramFilesFolder][Manufacturer]\[ProductName]"
Execute="firstSequence" />
And this within the <InstallExecuteSequence> tag:
<Custom Action="SetTARGETDIR" Before="CostFinalize">TARGETDIR=""</Custom>

Related

Wix msi updated property value not working during installation

Working with Wix in Visual Studio. I have a public property defined in Product node of wxs file.
<Property Id="MYPROP" Value="123456789"/>
The property value is passed as commandline argument to a deferred custom action executable. I'm able to receive it in the exe as well. The problem is even if I update the Property using vbs (verified through vbs select as well), when I launch the msi, it still passes the default/original value (123456789) to the custom action executable.
Also tried msiexec.exe /i myinstaller.msi MYPROP=SomeOtherValue
I'm still seeing the original value. What's wrong?
Maybe try this simple thing first:
<Property Id="MYPROP" Secure="yes" Value="123456789"/>
Essentially you need to add a property to the SecureCustomProperties list to have them pass properly to deferred mode in secure desktop environments.
See more information on SecureCustomProperties here. The technical details here are a little bit in flux because of Windows changes, so please just try this first - there could be several other reasons.
How do you use this property? What does it do?
When an msi is run, windows caches the msi file in %windows%\Installer folder. When that msi is run again, windows checks if an msi with identical PackageCode exists in the cache, if so then it uses the cached msi file instead.
PackageCode: identifies each unqiue msi installer package - even if it only has different properties.
In short, when a property is updated using a vbscript etc, then the PackageCode has to be updated as well. This will ensure that after updating msi, the same msi can be used on the same system and windows will not use the cached msi.

WiX Burn: Create custom log file name for MsiPackage?

When Burn runs an MSI installer, using MsiPackage, I'd like the MSI's log file to have a custom name, like MyProductName.log. I'd also like to append to an existing log file (with same name).
In InstallShield's Basic MSI Project's Release view there is an entry "MSI Command-Line Arguments" where you can enter a custom log file name:
/l+* "%TEMP%\MyProductName.log"
The "+" will append the log to an existing file.
Burn can pass public properties to the MSI, but I don't see a way to do what I want.
It looks like I'll need to write code (custom Burn bootstrapper) to run after the MsiPackage is installed (or uninstalled) to copy the contents of the log file (in the Burn variable defined in LogPathVariable) to the file with the custom name.
You can use the LogPathVariable attribute of the MsiPackage element to provide a custom log file name ...
See: http://wixtoolset.org/documentation/manual/v3/xsd/wix/msipackage.html
Also: https://support.firegiant.com/hc/en-us/articles/230912207-Pass-Properties-to-MsiPackage-from-Bundle-
To do what I needed, I wrote a simple bootstrapper, which became more complicated as I addressed things like passing installer properties to the bootstrapper, giving warnings if installing an x86 installer on an x64 OS (we encourage customers to use and x64 installer), etc.

Wix Burn: How to store the custom InstallFolder for later modifications?

I'm trying to solve this for a while now. I've authored a custom UI for my Bootstrapper Application. Setting a default value for InstallFolder is not the problem, but when the user changes this path, how can I store this path for later changes in add/remove programms, e.g. when another Package in the bundle should be installed by modifying the Bundle?
To write in the Registry could be an option, but the Bootstrapper Application doesn't run elevated all the time, so that it can't write to HKLM. But there should be a way to do this, I saw similar things for Visual Studio...
You should be storing the InstallFolder value in the registry in one (or all depending on how it is authored) of your MSIs that are packaged with the bootstrapper application. On startup you can use a util:RegistrySearch to look for and set the InstallFolder in the bootstrapper.
<Fragment>
<util:RegistrySearch
Id="ServerInstalledCheck"
Root="HKLM"
Key="SOFTWARE\$(var.OEMRegistryRootKeyName)\v7"
Value="ServerPath"
Result="value"
Variable="ServerInstalled"/>
<util:DirectorySearch
Path='[ServerInstalled]'
Variable='InstallFolder'
After='ServerInstalledCheck'
Condition='ServerInstalled' />
</Fragment>
I think you can directly set the variable InstallFolder in the registry search itself and omit the DirectorySearch. The DirectorySearch approach was used just to ensure we only set the InstallFolder to a location that actually exists on the machine. There may be other advantages as well but I can't think of them at the moment.
This will retain your default InstallFolder location on a fresh install and 'remember' the selected install location when running to uninstall/modify/upgrade.
You are right that you cannot rely on writing any registry keys inside your bootstrapper application because it is not guaranteed (and really shouldn't be) run elevated.
This is basically following the 'remember property' pattern which is explained here. Whenever you want to remember a value set in a previous install during modify/upgrade/removal, this is generally the go to.

WiX repair from managed bootstrapper fails to fix some corrupted DLLs

I have a WiX Managed Bootstrapper Application that installs some MSIs. I also have a series of tests that exercise the various functions of the installer. The problem I'm having is with the repair test. The test purposely corrupts all the DLLs we install then calls the EXE installer with "/repair /passive" flags. Once completed around 80% of the DLLs are repaired but the remaining ones are untouched and therefore still corrupted after the repair.
If I manually run msiexec on an individual MSI with the command line args "/fa" which instructs the MSI to forcibly replace all the files it installed, it fixes 100% of the DLLs.
My question is how can I force the MBA to instruct each MSI to repair in this way? I've tried setting the REINSTALLMODE property to 'amus' on the individual MSIs in their .wxs files but the MBA overwrites them at run time as is evident in the log via this line:
PROPERTY CHANGE: Modifying REINSTALLMODE property. Its current value is 'amus'. Its new value: 'cmuse'.
I've also tried to set the properties in the MBA to pass through to each MSI but it doesn't appear to pass them and instead uses it's default values.
I see many similar questions here but none actually address this specific issue. Any help would be greatly appreciated. Rob save me!
Here's your problem right here:
[12:25:25:874]: File: C:****\estimator.dll; Won't Overwrite; Won't patch; Existing file is unversioned but modified
The installer doesn't want to overwrite a file that has changed since it was installed if it cannot verify the version or language (and maybe some other properties?). Without these properties, it decides to look at the modified date. If it is newer than when it was first installed then it won't touch it assuming instead that something changed for a reason and reverting it will cause something to fail. (You can read more here)
One thing you can do in this case is use a Companion File
Set this attribute to make this file a companion child of another file. The installation state of a companion file depends not on its own file versioning information, but on the versioning of its companion parent. A file that is the key path for its component can not be a companion file (that means this attribute cannot be set if KeyPath="yes" for this file). The Version attribute cannot be set along with this attribute since companion files are not installed based on their own version.
Basically you will set the logic for installing/uninstalling this component to be the same as the "FileID" of another component in the install. In the estimator.dll component, in the File tag, remove KeyPath="yes" and instead replace that with CompanionFile="<NameOfAnotherFileID>".
The issue with this approach is that you may have a corrupted DLL but the companionFile it was linked to was fine so it is not reinstalled.
If this is a dll you do own, I would highly suggest versioning the file! Give it any version you want and this issue should go away.
Another thing you can try, although I don't know how it works really, is giving the file a DefaultVersion
This is the default version of this file. The linker will replace this value from the value in the file if the suppress files option is not used.
This would be the quickest solution to verify. Just build a new installer with DefaultVersion="1.0" in the estimator.dll's <File> and see if it gets replaced. I think this will have the installer think the file is versioned 1.0 but the installed file is not versioned so it will replace it (see here)

How to work with HEAT.exe for dll registry

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.