Wix/MSI and unique per-installation directory - wix

Is it possible to install files from an MSI in a unique per-installation directory? For example, I would like to do the equivalent of the following:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLDIR" Name="$(var.MyProductName)">
<Directory Id="SXSBASEDIR" Name="SxS">
<Directory Id="SXSDIR" Name="<<Unique-ID>>">
^
|
---------- Notice the <<Unique-ID>> here ----------+
<Directory Id="BINDIR" Name="bin" />
</Directory>
</Directory>
</Directory>
</Directory>
</Directory>
The <<Unique-ID>> must be different for every installation. For example, if one installs the MSI once, files end up installed in \Program Files\MyProductName\SxS\<<Unique-ID-1>>\bin. If then one uninstalls and then reinstalls the same MSI, files should end up installed in \Program Files\MyProductName\SxS\<<Unique-ID-2>>\bin.
I have looked at DATE and TIME variables as a means to generate the <<Unique-ID>> but unfortunately they contain characters that are not legal in file names (and their format varies according to locale). Any suggestions on how to approach this are appreciated. If the only answer is to use a custom action, where in the InstallExecuteSequence would you recommend that I place the custom action?
EXPLANATION
An explanation of why I need this follows.
I have a Windows file system driver (FSD) which is a kernel mode driver. Due to Windows file system design once an FSD is loaded it cannot be (easily) unloaded. This makes uninstallation and upgrades impossible without a system reboot.
As a workaround I have come up with a solution where the FSD is installed in a unique per-installation directory, for example: \Program Files\MyProductName\SxS\<<Unique-ID>>\bin. This should allow for multiple instances of the FSD to be loaded at the same time, the currently installed one and any previously uninstalled ones. (The FSD has already been modified to support such side-by-side operation.)
Consider, for example, the following scenario:
User installs MyProduceName.msi.
The FSD is installed in \Program Files\MyProductName\SxS\1\bin.
The FSD is loaded and is potentially unloadable (for reasons outside of the scope of this discussion).
User uninstalls MyProductName.msi.
The Windows installer infrastructure marks the \Program Files\MyProductName\SxS\1\bin as deleted but the directory remains.
User reinstalls the same MyProductName.msi.
The FSD is installed in \Program Files\MyProductName\SxS\2\bin.
The FSD is loaded again.
At this point two instances of the same FSD exist:
The originally installed FSD, which will go away after a reboot. This FSD location is in the directory \Program Files\MyProductName\SxS\1\bin which is marked as deleted and will go away after a reboot.
The currently installed FSD. This FSD location is in the directory \Program Files\MyProductName\SxS\2\bin and will remain after a reboot.

You need to write a custom action e.g a vbscript which will set the string using some random number and then INSTALLDIR could be be set.
dim r
randomize
r = int(rnd*10) + 1
session.TargetPath("INSTALLDIR") = session.property("ROOTDRIVE") + '\Program Files\SxS\' + r + '\bin'
This custom action should be sequenced after CostFinalize action.

Related

How to change directory in wxs file where all dll and other files will be installed?

I am quite new in creating setup project using .wxs file in .NET project. I knew that by default when you run MSI file it creates folder with this project and it's reference files (dll, exe, etc...) in C:\Program Files (x86). My question is, can I change this location in my .wxs file to another using XML.
ConfigurableDirectory: You can use the ConfigurableDirectory attribute of the Feature Element to set a configurable feature directory. See down the page in the screenshot section here: How to assign path value to Directory in WIX?.
Mock-up only:
Note: I am basing myself on the standard WiX Mondo dialog set. To hook up the Mondo dialogs, see this answer. Essentially, add reference to WixUIExtension.dll and insert the <UIRef Id="WixUI_Mondo" /> element. This will compile a default WiX dialog set into your MSI.
<Feature Id="MyFeature" Title="MyFeature" Level="1" ConfigurableDirectory="INSTALLFOLDER"></Feature>
<..>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="InstallDirConfigurableTesting">
Remember Property: Note that you must persist the custom directory location yourself to the registry and read back for major upgrades or else your whole product gets "moved" during upgrades (I know, it is weird). This persisting does not happen auto-magically in any way that I know about. You can persist the property and read it back using the "Remember Pattern" as described by WiX creator and benevolency Rob Mensching here: The WiX toolset's "Remember Property" pattern.
Implementation Tip: Testing the read-back of the directory property for major upgrade scenarios can be a bit fiddly. If you create a test project in Visual Studio using Votive you can use the trick to just compile version 1 of your MSI (now suffix your MSI file in the build output folder with _Version1.0.0.msi or similar), and then kick up one of the first three digits of the version number property and build an upgrade version (suffix with _Version2.0.0.msi or similar). Then you install in sequence selecting a custom installation directory and check whether your second setup correctly detects the modified path. Just use a mockup or test harness project with a single component in it to get this working, or else you could drive yourself mad if you have to compile your whole setup. Then just inject your finished markup into the main project. Obvious, yes - just mentioning.
Some Further Links:
How to use ProgramFiles64Folder in ConfigurableDirectory
What is the meaning of WiX configurable directory?

How does association between Directory and Property/Variable work in WiX?

In Wix (3.11) there seems to be an implicit link between a with the Id 'ProgramFilesFolder' and the 'ProgramFilesFolder' variable. That is, a directory with that id will be named according to the property value.
When I try replicating this on my own with a "Test" directory:
<Property Id="Test" Value="Test"/>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="Test"/>
...
I get a warning from WiX, and the installer actually hangs. I've also tried declaring "Test" as a WixVariable, but in that case it appeared the element was simply ignored - iow, no "Test" folder was created.
My question is how does this implicit link work for WiX variables, but not for my own?
ProgramFilesFolder isn't really a WiX variable or Id. It's a Windows Installer standard property that you can't change - it refers to the 32-bit Program Files path on the system that you're installing on. Your Test directory will be beneath the Program Files folder.
There isn't enough of your WiX source to see what's going on, but just declaring those locations and properties won't create the directories if nothing is installed there. A complete working example of the issue would be useful.
You don't need to define a separate property for your directory, the id of the directory doubles as a property because directories are properties. If the property (directory id) is all uppercase, it becomes a public property, and can be set from the command line.
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="TEST" Name="Test" />
...
Setting the install path of the TEST directory from the command line:
msiexec /i A:\Example.msi TEST=c:\Example\Test /qn
The directory id "ProgramFilesFolder" is a bit different because it's one of the System Folder Properties, pre-defined by Windows Installer.
You should probably specify a name for your TEST directory. See Directory Element:
Do not specify this attribute (or the LongName attribute) if this directory represents the same directory as the parent (see the Windows Installer SDK's Directory table topic for more information about the "." operator).

Merge module files into different locations

I'd like to parse merge module files into two different locations. Is it possible?
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="MergeRedirectFolder">
<Component Id="LoggerClient" Guid="*">
<File Id="log4net" Name="log4net.dll" Source="..\..\_Release\log4net.dll" KeyPath='yes' />
<File Id="LoggerLibrary" Name="LoggerLibrary.dll" Source="..\..\_$(var.Configuration)\LoggerLibrary.dll" />
<File Id="app.config" Name="app.config.xml" Source="..\..\_Release\app.config.xml" />
<File Id="msvcr110.dll" Name="msvcr110.dll" Source="c:\windows\sysWoW64\msvcr110.dll" />
</Component>
</Directory>
</Directory>
</Module>
<Merge Id ="MergeModule.msm" Language ="!(loc.Lang)" SourceFile ="_$(var.Configuration)\MergeModule.msm" DiskId ="1" />
I want the second file to copy to a different folder than the other files.
There is a concept for that, it is called a retargetable merge module. I have avoided used it - the concept doesn't seem right to me. I have not tried to make one with Wix.
I think you could combine a Wix include file (simple sample) with the new auto-generated component guids to deploy such duplicated files reliably by adding an Include statement where appropriate. You must not hard code the guids in this case, but let them be auto generated by the Wix compiler and linker.
Also have a read of WixLibs (Wix library files): http://robmensching.com/blog/posts/2008/10/10/what-are-.wixlibs-and-why-would-you-use-them/
Wix documentation; http://wixtoolset.org/documentation/manual/v3/overview/files.html
Merge modules are for installing common runtimes and genuinly shared files. Typically C and C++ runtimes and other, similar libraries that should be available in the latest version for all applications.
Your files look like they are part of your application folder, with the exception of msvcr110.dll which you should remove and allow to be loaded from the system folder.
If the remaining files have no per-machine registration (COM for example or COM Interop), you can duplicate them in several folders without interference, yes, but why not load them from a shared location inside your own application folder structure?
%ProgramFiles%\My Company\My Shared Runtimes
%ProgramFiles%\My Company\My Apps\My App 1\
%ProgramFiles%\My Company\My Apps\My App 2\
These sample folders you "own" and you can deploy things here however you like. Not so for shared, system folders. You could make your own merge module for shared components between your applications into "My Shared Runtimes" and make your applications aware of the shared location "....\MySharedRuntimes\"
It depends on what you mean by different locations. You can build a merge module with 4 files, each in their own component and directory. One could go to the CommonFilesFolder; another to the SystemFolder; another to...you get the idea. So it's potentially easy if you make each file its own component in its own directory. However you've got them all under TARGETDIR, so you're going the wrong direction. You just define the other directory and that other component and file and you might be done, unless there's more to the question than meets the eye.
I would suggest contacting me privately for a few 30-60 minute conversation on MSI, component rules, Merge Modules and file set theory. It's too much to write. In a nutshell I would advise more merge modules.

MSI installing file inside a folder which contains in its name the substring %Path%?

I've come across a project that builds an MSI package. One of the folders from that package has this name:
%P_%F_%Path%alfa
At install time, during InstallFiles standard action, Windows Installer will resolve the substring %Path% as an environment variable, thus the character ':' appears inside the folder name and the installation errors out (invalid char in folder name).
EDIT: The same error occurs for DuplicateFiles standard action too.
If I create an MSI that creates this folder empty (i.e. during CreateFolders standard action) Windows Installer does not try to resolve the substring %Path% to an environment variable and the installation succeeds, creating the folder with the name presented above.
I never met this situation before. Anybody else did? If yes, can you give more details about what is going on exactly and if there is a workaround available?
Note! I added all the tags of different MSI authoring tools because I suspect this to be a tool independent situation.
I can confirm, using WiX and IsWiX. IsWiX authors a folder with a file like this:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="MergeRedirectFolder">
<Directory Id="owd6248671CA393CCC018715A2FB53AD2D6" Name="%P_%F_%Path%alfa">
<Component Id="owcA59F51CBEAEE88B00B715AF4FEE6BF72" Guid="1619af96-1b2b-64ea-91f5-1a297c3c636a">
<File Id="owfA59F51CBEAEE88B00B715AF4FEE6BF72" Source="$(var.SourceDir)\test.txt" KeyPath="yes" />
</Component>
</Directory>
</Directory>
IsWiX authors an empty folder like this:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="MergeRedirectFolder">
<Directory Id="owd6248671CA393CCC018715A2FB53AD2D6" Name="%P_%F_%Path%alfa">
<Component Id="owc6248671CA393CCC018715A2FB53AD2D6" Guid="071c27cb-0566-40b1-9a50-5672b3fbd5e1">
<CreateFolder />
</Component>
</Directory>
</Directory>
</Directory>
Both create MSI's that compile and pass validation but the folder with a file gives the error you describe while the folder with the CreateFolder element works.
Forbidden Folder Names: Interesting, all sorts of pecularities. This is not as much an answer, as a couple of further pointers for MSI folder name peculiarities. Did you know that you can't create folder names with any of the following names in Windows Explorer? con, prn, nul, aux? The list goes on with lpt0 to lpt9 and com0 to com9.
Device References: This is all for legacy reasons. You can't make folders that have "System Action" or "Device" references. These names are old devices and legacy concepts. Device names were recognized before path names. CON was the console device, AUX was the auxiliary device, PRN was the printer, along with LPR<digit>. COM<digit> refers to a com port. There are others. Note that these names ignored the file extension such that CON.EXE or con.txt still meant the console.
Tool Handling: I believe you can still create such folders with Win32 API calls or even with a command prompt, but they are not valid Windows names allowed in Windows Explorer. Incidentally it seems Advanced Installer and Installshield allow them as MSI folder names, but you get runtime errors whilst installing - or you get a warning that a folder name is invalid on MSI launch. I understand the desire to not add code to disallow such folders in the tools - there are always new bugs possible when you start to protect against things like these - rather people should know about these folders I guess. Actually a compile time warning would be great - just a list of illegal folder names to check, and illegal character sequences. It sure is an odd problem to discover when you don't know what is causing it.
Some Links:
What characters are forbidden in Windows and Linux directory names?
Why can't we make CON, PRN,Null folder in windows?
Naming Files, Paths, and Namespaces
Sources (I copied so much from some of these, that they should be listed for reference):
Why can't we make CON, PRN, Null folder in windows? (SuperUser)
Why can't I save a folder named "con" in Windows? (Quora)
Q: why can't i create a folder with "con" name? (Microsoft Community)
Unable to rename a folder or a file as 'con' (SuperUser)

Patch (minor upgrade) creation issues with MSM (merge modules)

I am facing issues with patches (minor upgrade) installation (updates) with MSM (merge modules).
I am creating MSI (test.msi) with texst.wxs. And inside text.wxs referring to app.msm file (there is a folder app, which contains so many folders and files. And harvesting this folder and making app.msm file)
Below are steps for making app.msm file.
heat dir "app" -gg -sfrag -template:module -srd -ke -var var.source -out app.wxs
candle -dsource=app app.wxs
light app.wixobj
Below is snippet of test.wxs file
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFilesFolder' Name='PFiles'>
....
....
<Directory Id='Config' Name='Config'>
<Component Id='APP_CLIENT' Guid='*'>
<Component Id='Manual' Guid='*'>
<File Id='Manual' Name='Manual.pdf' DiskId='1' Source='Resources/Manual.pdf'
KeyPath='yes'>
<Shortcut Id="startmenuManual" Directory="ProgramMenuDir"
Name="Instruction Manual" Advertise="yes" />
</File>
</Component>
</Directory>
<Directory Id='exmp_REPO' Name='!(loc.Merge_FolderTitle)'>
<Merge Id="LocalRepository" Language="1033" SourceFile="app.msm" DiskId="1"/>
<Component Id='exmp_REPOSITORY' Guid='*'>
<CreateFolder/>
<RemoveFolder Id='exmp_REPO' On='uninstall' />
</Component>
</Directory>
....
<Feature Id='Complete' Display='expand' Level='1' ConfigurableDirectory='MYAPPPATH'>
<ComponentRef Id='Manual'/>
<ComponentRef Id='App_CLIENT'/>
<ComponentRef Id='exmp_REPOSITORY'/>
...
...
I am able to make major upgrade with my test.wxs by using app.msm (merge module). But not able to make patch with successful installation. Patch install (update) is reflecting in version change in the "Programs and Features" and showing in "View Installed updates". The manual changes also are reflecting with patch update. But whatever the changes in "app" (which are created app.msm and referred in test.wxs) folder are not reflecting.
I have used 2 approaches for making patch, which are mentioend in below urls
1) http://wixtoolset.org/documentation/manual/v3/patching/patch_building.html
2) http://wixtoolset.org/documentation/manual/v3/patching/wix_patching.html
Please help in this regards.
First, I would advise to find out, whether the built patch contains the correct files or not. If not, you have a build problem, that the msm is not updated. If yes, you have most likely a problem with the content of the msm which may be not consistent with it's predecessor (especially GUIDs, table primary keys, etc.).
You can find out and see the content of the patch without installing with tools like Orca and Insted which you can search and download.
Second, using merge modules is highly complicating things, especially for patches, and of limited usefulness, if they are your own and you use them only once. Msms are primarily made for situations, where you need the .msm at least for 2 different MSI packages. I have seen a handfull of problems using merge modules in patches with other tools, BTW. I have no special experience here with WiX+patches+MSMs, but, it's as I said.
Last, but not least, you will have to choose, if you really want to keep this complexity in the future. As I remember, there are other possibilities in WiX to modularize / encapsulate parts of your software.
You can check the versions of files in merge modules and MSI files by opening them with Orca and looking in the File table. Or open the MSI file with Orca and then Transform=>View patch to see changes.
It may be obvious, but a binary versioned file will be replaced by a file with a higher file version. I mention because there is a belief out there that somehow "new" files replace "old" files, and that's wrong. Versions matter.
Generally you need to install a patch with an msiexec command that specifies REINSTALL=ALL REINSTALLMODE=omus. Double-clicking an MSP file will not necessarily just work unless you've arranged for it to do this internally with a custom action that sets them when PATCH is set.
The patch will not work if you break component rules. A common mistake is to remove a component during a patch, and that will result in an "advertised" update that doesn't actually update anything. In a verbose log look for SELMGR entries and text about removal of components is not supported. Setting the MSIENFORCEUPGRADECOMPONENTRULES property to 1 will fail the patch if you've done this.
If a file has no version whether it's overwritten depends on the replacement rules here, and there's some difference if the file is hashed or not:
https://msdn.microsoft.com/en-us/library/aa370531(v=vs.85).aspx
Also: how do you know the patch isn't working? If you have no file versions then you can't know if a file has been replaced unless you look very carefully. You cannot trust the dates because Windows changes timestamps when a file is installed. You really need to build binary files with file versions because everything like patches, hotfixes, service packs etc will use them to replace binary files. Otherwise for data files use file hashing.
I see several potential problems here, maybe even in addition to the other answers:
when you heat up files for your merge modules -gg autogenerates new component guids. This will not work with patches, since it'll basically add a bunch of new components (new guids each time!!). Also, you'll remove components this way, which is something you shouldn't do and which cannot be done easily, unless you still include the original merge module. And then you'll end up with path problems.
The tutorial for wix patching uses wixpdb files for diffing the original and the updated installer. With wixpdb files, merge modules will not be patched, irrespective of whether files changed in them or not. You will need to do administrative installations and then diff on the msi itself. You'll still have problem #1.
your wxs snippet is bad. At least the xml element is never correctly closed. Your Feature also has a MergeRef somewhere?
Some tips:
you can view what your patches do with a program called Orca. Open the original msi, then just drag your msp patch file on top of it.
rather don't use merge modules, they complicate things. You can also use heat to generate a fragment which you then simply include into your wix project.
use the wix patching approach (Patch element, not the PatchCreation element). It's easier, but you have the same control
don't autogenerate guids if you plan to update those autogenerated components with a patch. It won't be a problem with major upgrades :)
Just one important issue of a number of potential reasons while files are not updated correctly: You write, in your .msm is a high number of unversioned files, like .xml, etc.
Important rule:
EVERY unversioned file which is changed on the PC AFTER the first MSI install not by the MSI engine itself (e.g. you edited a config.xml file or so), will be normally NEVER updated by MSI again, not by patch and not by Major Upgrade. (With normally I mean, you have to take special actions, like uninstalling or especially specifying those files as socalled companion files).