In WiX files, what does Name="SourceDir" refer to? - wix

WiX files always seem to include this line:
<Directory Id="TARGETDIR" Name="SourceDir">
What is "SourceDir"? What is it used for? It's not a real directory name. Is it some kind of magical value?

From: https://robmensching.com/blog/posts/2010/1/26/stackoverflow-what-does-namesourcedir-refer-to/
Honestly, it's something that we should have hidden from the developer but didn't. Sorry. The truth of the matter is that the Windows Installer expects the Directory tree to always be rooted in a Directory row where the primary key (Directory/#Id) is "TARGETDIR" and the DefaultDir column (Directory/#Name) is "SourceDir".
During an install, TARGETDIR will default to the largest drive on the machine. SourceDir will be set to the location where the MSI is being executed. Now, SourceDir is tricky after the initial install because it won't be set unless the ResolveSource action is called. However, you don't want to explicitly call the ResolveSource action because it is likely to prompt you to provide the original source media (aka: insert the CD, please).
What we should have done in the WiX toolset is remove the need to specify the TARGETDIR/SourceDir pair and say "Any Directory element that has no parent will automatically be parented to TARGETDIR because that's what the MSI SDK says to do." Instead, you have to do it yourself... and some devs wonder what it all means.

From the wix.chm documentation, topic "How To: Add a File To Your Installer":
The element with the id TARGETDIR is
required by the Windows Installer and
is the root of all directory
structures for your installation
According to the MSDN documentation TARGETDIR is
the root destination directory for the
installation
Also according to MSDN, SourceDir is
the root directory that contains the
source cabinet file or the source file
tree of the installation package
So the SourceDir property points to a real directory: the one where your MSI file sits. You can see this in the installer log when installing with msiexec /lvx* installer.log installer.msi.
However, for some reason SourceDir is completely ignored when resolving the TARGETDIR. The TARGETDIR must be either set explicitly (e.g. on the command line) or else it resolves to ROOTDRIVE. If ROOTDRIVE is not explicitly set then it is the root of the drive with the most free space.
A quick test shows that installing a component to TARGETDIR indeed puts the files at the root of my D:\ drive, instead of the folder where the MSI sits.

None of this was really helpful for me. I found this thread wondering how to make a Debug build, where my source files (the ones going in the installer) could be pulled from either the "Release" build dir or the "Debug" build dir of the project I am trying to make an installer for.
After some grepping, I found the actual path in the wixproj file, there SourceDir is defined as:
<SourceDir>$(SolutionDir)distribution\Release</SourceDir>
which has really nothing to do with installation files and project files. I was able to add another PropertyGroup that mirrored the release group which now pointed to my debug files:
<SourceDir>$(SolutionDir)distribution\Debug</SourceDir>
Hope this helps someone. I know it's a little off topic, but hopefully it helps someone in the future. Not sure why the project plug-in does not expose this value? Or am I missing that?

Related

Why msiexec TARGETDIR doesn't seem to work

I tried
msiexec /i "yarn-1.10.1.msi" TARGETDIR="C:\programs" /qb
The software wwas installed, but not into TARGETDIR. Did I miss something?
You can try the following command line instead:
msiexec.exe /i "yarn-1.10.1.msi" INSTALLDIR="C:\programs" /qb
Standards Compliance: Installing underneath %ProgramFiles% is always recommended. Creativity in deployment is generally always overrated and usually causes unnecessary problems. However, you could have an SOE setup that requires custom paths?
Path Update: A per-user path variable is added to a folder that is not created by default (%SystemDrive%\Users\[UserName]\AppData\Local\Yarn\bin). It is by design. The application probably creates the folder when run.
Administrative Installation: TARGETDIR is generally used during administrative installations (essentially a file extraction from the MSI) to specify the output directory for extracted files:
msiexec.exe /a yarn-1.10.1.msi TARGETDIR=D:\YarnExtractedFiles\
TARGETDIR: Without over-complicating too much, let me add two links with more information about TARGETDIR (this is to try to make the answer helpful also for others - more "general and generic" info - what you might not need, but that you just might want to know):
In WiX files, what does Name="SourceDir" refer to?
Why does the TARGETDIR directory need a name?

Add Zip Files to TARGETDIR in WiX

In my Visual Studio 2017 solution, I have a WiX 3 setup project that pulls in the output several other projects (libraries, executables, assets, content). Under the directory structure for the solution but not added to the solution as a project, I have a project that compiles some browser extensions using webpack. This webpack project outputs to an artifacts folder with subdirectories for each browser. Inside each subdirectory is the compiled extension with the version number included in the file name like:
artifacts
Chrome
myextension-0.1.0.0.zip
myextension-0.1.0.1.zip
myextension-0.1.0.2.zip
At compile time, ultimately I want to include the files matching the version number i.e. myextension-\$(var.VERSION).zip into the MSI package so it can then be placed into the application folder during installation. Even when I hard-code the version number i.e. myextension-0.1.0.2.zip into the component, I get an error from light:
LGHT0001: The system cannot find the path specified. (Exception from HRESULT: 0x80070003)
I'm getting the directory with a define like this:
<?define ChromeTargetDir=$(var.SolutionDir)Extensions\artifacts\chrome\?>
And then my component looks like this:
<Component Id="ChromeExt"
Location="local"
Guid="GUID_HERE">
<CreateFolder/>
<File Id="ChromeExtension"
Name="myextension-0.1.0.2.zip"
Source="$(var.ChromeTargetDir)myextension-0.1.0.2.zip"
KeyPath="yes"/>
</Component>
When I look in the wixobj created by candle, I see the full correct path replaced for the file where it resides on my system:
<field>C:\Users\me\source\repos\mysolution\Extensions\artifacts\chrome\myextension-0.1.0.2.zip</field>
So my question is, what is the correct way to include "arbitrary" files in my WiX project?
1) Solution vs Project Dir: The first thing I would try would be to replace $(var.SolutionDir) with $(var.ProjectDir) and try a recompile. I'll follow up if the problem is something else. Let's just rule that out first.
2) Quotes: I also use quotes around my paths:
<?define ChromeTargetDir="C:\Sources\Packages\MyChromeExtension\Files\" ?>
3) Project Variables: And finally you need a reference added to your project for project references and variables to work: WiX: How to use relative path to SolutionDir.
Maybe add: %ProgramFiles (x86)%\WiX Toolset v3.11\bin\WixUIExtension.dll
WiX Documentation: Using Project References and Variables
Obscure: More obscure causes could be lacking access rights (file not seen by the build process and light.exe - running impersonated?). Corrupted file or folder? (try to replace). And whatever else might conspire against you. Locked files?

How to edit a .NET config file with WiX AFTER its installation

I need to change an entry of a .NET config file.
The problem is that the App.config in the project folder needs to stay untouched, because otherwise the application won't work within the IDE anymore.
I need the installed .NET config file changed after its installation.
Whenever I try to reference that file and build my installer, i get an error that the file can't be found. Of course it can't be found there, because the installation folder doesn't exist, when I build the installer.
How can I achieve that goal?
You are not saying how you are editing it, but WIX has two facilities for doing this properly. You can use XmlFile or XmlConfig. These are scheduled at the proper time by WIX for you. You can set the path to the file a few ways as they accept a formatted string, so you can use an expected installed path or the file id.

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 do a DirectorySearch of a specific directory in WIX

I am trying to determine if a directory is installed to see if I should install a third party client or not.
<util:DirectorySearch Path="C:\Program Files (x86)\CenterSpace\NMath 5.0 and NMath Stats 3.3"
Variable="NmathInstalled"/>
When I do this candle.exe errors out and tells me that the directory "is not a valid relative long name because it contains illegal characters." and eludes to the fact that ':' is illegal. How can I search for a directory in c:\ without the ':'?
In your particular case, use the Burn Built-in Variable ProgramFilesFolder to have the C:\Program Files (x86)\ resolve to the correct location on the machine. That link to the documentaiton has lots of other folder options.