How to assign path value to Directory in WIX?

Solution 1:

I don't have time to test the options I list below, but they should be viable, technical possibilities. Hopefully one of the options will be satisfactory.

But, first of all: I dislike this approach of setting secondary, top-level directories - is it really needed? Can you explain the scenario? Maybe there is a more reliable way?

See the "Alternatives?" section towards the bottom for what I deem to be a better approach than to "fiddle around" with directory properties. If I were you I would read this "Alternatives?" section first - maybe it saves you a whole lot of trouble?

But the first section deals with various technical options to do what you ask:


Setting a Directory Property

There are many ways to set a directory property, off the top of my head. As I said, I haven't had time to test these options - and it is always mad to post untested advice, but you seem like someone who can help yourself with some pointers:

  1. AppSearch - you can search the system for a specific file or registry entry and set the directory property to the path found (if any).
    • Very often the path you need can be found this way - and reliably too in the sense that it is a built-in feature (and hence better tested than any custom construct from yourself).
    • You can add a custom action as well to verify that the directory found is valid - if need be. Such "verify data" custom actions can be made pretty safe since they don't change the system - but they can still cause unexpected runtime failures if you are not careful and allow them to return error codes or abort the setup.
  2. A Set Property Custom Action (Custom Action Type 51). Just set the path to something like [ROOTDRIVE]MyFolder.
    • If someone would do that I would fail the package for quality reasons (it is technically possible, but ROOTDRIVE may switch to point to a different path depending on disk space available on each drive - not good at all).
    • Know about the possibility, look for it, fix it if you find it. Don't use it. Seductive the dark side is.
    • Ironically, if I am honest, setting something to [WindowsVolume]MyFolder - may actually work, but I don't like it personally. I want nothing installed to a top-level folder on the system drive in the first place.
    • I believe WiX has the SetDirectory Element for this "set property" purpose. I think it does all the custom action work for you. Some auto-magic. I am however not sure if this feature allows PUBLIC PROPERTIES to be overridden from the command line?
    • If you set something to D:\MyFolder you have to be aware that this path could at some point be missing unexpectedly (drive removed from system, new drive added (?), new DVD drive added (?), drive letter manually changed, etc...).
      • This is beyond normal "set property" actions - it is essentially "hard-coding" (see issue 5 below). Hard coding is never acceptable - for any package with generalized distribution.
      • I would prefer to target PersonalFolder (My Documents) and then redirect this to D:\ in Windows itself. I believe Windows will then assign a new value to PersonalFolder if it can't be found on boot. Windows Installer will then still be able to enumerate the target folders and not just get stuck or crash.
  3. Set directory as a feature directory exposed in the custom dialog in the installer GUI.

    • Mad to suggest since I have never used it, but it is an viable option I think - but requires significant testing in all installation modes (install, repair, self-repair, uninstall, modify, patching, et...) to make sure it works as desired.
      • Quick update: I did a smoke test, it works, but it is not trivial to do I would say. See mock up GUI sample and some WiX snippets in next section below.
    • This involves adding a separate feature for the components set to this feature directory. Then you set the ConfigurableDirectory attribute of the FeatureElement in your WiX source equal to the PUBLICPROPERTY holding the directory path.
    • You should now be able to set this directory from the WiX GUI if you use the dialog set Mondo (<UIRef Id="WixUI_Mondo" />) - from the "Custom" dialog. There is a step-by-step sample using Visual Studio to update a WiX source file to use Mondo here: WiX installer msi not installing the Winform app created with Visual Studio 2017.
    • The GUI selection you make (directory) will be assigned to the property you specified as the ConfigurableDirectory for the feature.
    • Somehow you must set the directory property to something meaningful for a silent install - when the GUI is skipped - or you could abort installation if nothing is defined for the property on the command line.
    • I would check for a valid directory while at it and protect against generalized lunacy such as installing to the system folder, installing straight to C:\, installing into existing folders for other products, etc... - "...users are amazingly creative when it comes to exercising a system in unexpected ways" - Grady Booch - words to live by, and what a cool name - and hair - :-).
  4. A regular, immediate mode custom action (not a set property custom action) which sets a property with a call to Session.Property = "SomeValue" somewhere in the code. Each type of custom action type does this "property set" slightly differently (custom action types: VBScript, C++, DTF / C#, etc... See documentation from Advanced Installer for how this can be done).

    • I am always telling people to avoid custom actions, and it is definitely true for read-write custom actions: Why is it a good idea to limit the use of custom actions in my WiX / MSI setups?.
    • I do, however, use read-only custom actions set to suppress errors and not return errors that roll back the setup when needed. This allows you some power to inspect the system as you see fit to determine what to set to the property in question.
    • Crucially such immediate mode read-only custom actions require no rollback feature since they make no system changes, they don't run elevated either, and most of them "live in the GUI sequence" and are used to get data and settings from the user - or checking various conditions and states of the target system - just inspecting things (they are often also needed in the silent install sequence if the GUI is skipped).
    • Remember that the GUI sequence is skipped in silent installation mode, so your custom action must live in both sequences. You can condition it to run only once though.
  5. Some people even hard code paths in the property table, or using a set property custom action to assign C:\ to a property at runtime - not at all acceptable. This will break - it is merely a question of time. On computers without a C:\ drive it will not install at all - for starters.

    • Not sure if such a property, set in the property table would be overridden during directory resolution and costing - I have never bothered to try - it is not a solution and I just want to state that to prevent its use.

    • A set property custom action assigning C:\ or similar to a property will appear to work, but will blow up in unexpected ways. Guaranteed.

    • Again: if you can help it, don't deploy to a separate, top-level folder.

  6. I have heard some people - in corporate, standardized environments - use an environment variable to define such installation folders. I have never used it for production, never tried it for testing and don't like it as an option.

Keep in mind that public properties can be set at the command prompt - and hence potentially override whatever logic you add to set it yourself. Maybe add a custom action to check for values coming in from the command line, and either accept it or abort the setup if it is wrong.


WiX-installer With Configurable Feature Directory

Here is the GUI screenshot of a WiX installer with configurable feature directory:

enter image description here

The browse button seen above is only available for features that has the ConfigurableDirectory attribute specified pointing to a custom directory property (ignore the C:\ entry in the screenshot above - just a hiccup):

<!-- A standard feature -->
<Feature Id="ProductFeature" Title="MinimalShortcutTester" Level="1">
   <ComponentGroupRef Id="ProductComponents" />
</Feature>

<!-- A configurable directory feature -->
<Feature Id="FeatureDirectory" Title="FeatureDirectory" ConfigurableDirectory="MYCUSTOMDIR">
        <!-- your stuff here -->
</Feature>

And elsewhere in the WiX source, the actual configurable directory:

<Directory Id="MYCUSTOMDIR">
     <!-- Mock-up GUID that MUST be changed, custom target directories do not function with auto-GUIDs -->
     <Component Id="MyFile.exe" Guid="{00000000-0000-0000-0000-000000000000}" Feature="FeatureDirectory">
         <File Source="C:\SourceControl\MyFile.exe" />
     </Component>    
</Directory>

I would use an extra custom action to default this MYCUSTOMDIR directory well, or to inspect the choice made by the user for validity. This is not entirely straight forward, but must be dealt with in each case.

On second thought, I might use a set property custom action to default the MYCUSTOMDIR directory to a sub-folder of PersonalFolder, then allow the user to override it on install. Now you have to read back the user choice on modify and repair (and other installation modes), or else you will default to PersonalFolder and whatever sub-folder you specified in other installation modes. You can persist the user folder selection in the registry, and read it back using AppSearch (the preference of most of the pros I think) or do it all in a custom action. It can be challenging to make this work right in all installation modes. Do test well.

Technical challenge: if you persist your original folder selection for MYCUSTOMDIR in the registry and read it back with AppSearch, you must make sure to condition your set property custom action (used to set a default value to MYCUSTOMDIR if no value is set) so that it does not run if the property already has a value retrieve from the registry. Not rocket science, but can be fiddly to get right - and be nitpicking about testing in all installation modes. If you rely on standard constructs and not custom actions you generally benefit once you understand "the moving parts" involved - first you default on first install since no value is set yet (unless a value is set via the command line), then you allow it to be overridden in the GUI (or by command line), then you persist in the registry during installation, on next launch (repair, modify, self-repair, upgrade, patch) you read back the persisted value and don't let the default value be set via condition, etc...

Throwing in a link for safe-keeping: Wix : Disable control in built-in dialog.


Alternatives?

As stated above these secondary top-level directory hierarchies may often be avoided by a better understanding of alternatives.

Some questions to check for alternatives:

  • What files are going to this secondary top-level folder? Data files? Databases?
  • Often such folders are for data files, and sometimes the folder used is unique for each user (i.e. not a shared folder). For example "My Documents".
    • In these cases I like to have the application create the folder on launch (there will be write access to such a folder), and then copy whatever files are required to the folder for each user from read-only template copies installed somewhere under %ProgramFiles%.
    • MSI is not great for deployment of user-profile files. I wrote a summary of some of the issues here: Create folder and file on Current user profile, from Admin Profile. It is not generally clear how to reference count files deployed many times, once per user.
    • I repeat this advice too many times, but it really is a nightmare to deal with userprofile file deployment at times. Very common problems: (1) unexpected data overwrite / reset, (2) failing to copy the userprofile files in place, (3) unexpected uninstall of modified files (read: user data), (4) unable to overwrite existing files reliably - the same problems tend to repeat.
  • It is of course also possible for such a top-level folder to be needed for other reasons, but my bet is that it will be filled with user-data or user-modifiable files. Is this accurate?