Wix How do Directories work? - wix

I'm trying to build a WIX installer, which allows the user to specify the install path through command line arguments.
<Property Id="IISROOTPATH">
<RegistrySearch Id="FindInetPubFolder" Root="HKLM" Key="SOFTWARE\Microsoft\InetStp" Name="PathWWWRoot" Type="directory" />
</Property>
...
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="IISROOTPATH">
<Directory Id="INSTALLPATH" Name="WebsiteFolder">
...
If the user doesn't pass in any arguments, it will look up registry for the iisroot folder, and install the files under a folder called "WebsiteFolder"
But in order for the user to change the install path, they must pass in an absolute path eg
INSTALLPATH="C:\InetPub\wwwroot\CustomWebsiteFolder"
How does this work? does it then ignore the other Directory elements TARGETDIR and IISROOTPATH?
How can I change INSTALLPATH to just WEBSITEFOLDERNAME so the user is only required to pass in the folder name rather than the absolute install path. Thus forcing the user to always install into inetpub?

You should understand the Directory table - the MSI concept behind the Directory element. The best explanation I've ever met is a set of Rob's articles (the link is to the part 1, there are 6 parts, if I remember correctly).

To answer your second question:
Create a custom dialog (Removing the normal folder chooser dialog) where they user can type in the desired folder saving to a property (i.e. [FOLDER_NAME]).
Have a Directory element that is just a placeholder for the folder name.
Attach a custom action to the "Next" button on the dialog created in 1. which sets the Directory from [FOLDER_NAME].
<Directory Id="IISROOTPATH">
<Directory Id="INSTALLPATH" Name="WebsiteFolder">
</Directory>
</Directory>
<CustomAction Id="SetFolderPath" Directory="INSTALLPATH" Value="[IISROOTPATH][FOLDER_NAME]" />
Obviously it'll be a good idea to add conditions to the Next button so that the user doesn't use the default or leave the folder name blank.
This will then allow you to keep the root dir as C:\Intetpub\wwwroot and allow them to specify a custom folder.

Related

WiX: Change .msi Directory and CustomAction table with changes to be used by .msp Patch file

with the Wix Toolset v3.11 I have created a .msi file for my application (let's say version 1.1) with a Directory element that targets the LocalAppDataFolder and puts a Temp folder underneath. This structure is used with a DirectoryRef element to put some files there that are accessed within a custom action on InstallFinalize. Now I generated a Patch (version 1.2) that adds some files in the Temp folder. But I want that folder to be in the TARGETDIR now. So I changed
<Directory Id="LocalAppDataFolder">
<Directory Id="APPDATA_TEMP" Name="Temp" />
</Directory>
to
<Directory Id="MY_TEMP_PATH" Name=".">
<Directory Id="APPDATA_TEMP" Name="Temp" />
</Directory>
underneath the
<Directory Id="TARGETDIR" Name="SourceDir">
structure. For the Patch to be applied correctly I opened the .msi file that's referenced in the registry (under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\...\Products{GUID}\InstallProperties\LocalPackage) with the Orca tool and edited the corresponding values in the Directory table. I also edited the CustomActions table to reference the new path as it is used as a parameter there. So, now when I install the Patch it puts all the files (changed and new ones) in the LocalAppDataFolder as it did with the initial install. The custom action is called with the correct parameter though.
Is there a way to change the "base" msi to target my new Temp folder or, alternatively, somehow tell the Patch to use the new folder regardless of the initial path?
The goal is to apply the patch without having to uninstall the application first.
Any help on how to achieve that is greatly appreciated.
Finally we found a solution. So here's what solved our problem, should anyone ever come across something similar:
We found the actual path of the LocalAppDataFolder in two locations in the registry: The first entry is located in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\Folders. The second one is HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData...\Components. The entry with the component ID that puts the files in the Temp folder holds exactly one value, which is the path to the first file in that component. We changed both paths to our new temp folder.
Now the patch extracts the files correctly to the new temp folder and the custom action can process them further.

How can I set the default install location of a WIX bundle to a registry string if one exists?

I have a WIX bundle that currently allows the user to change the default install directory. I would like to change the default install directory before the user has access to it to a string from the registry if it exists there.
Desired logic:
1. Set default directory (currently using '<Variable Name="InstallFolder"
Type="string" Value="C:\Users\Public"/>')
2. Get string from registry entry (currently using '<util:RegistrySearch
Id="InstallLocSearch" Variable="PrevInstallFolder" Root="HKLM"
Key="SOFTWARE\Company\App\InstallDir" Result="value"/>')
3. If the registry entry contains a non-empty string, override InstallFolder
with string, leave it alone otherwise (not sure what to do here)
4. Allow user to change install dir if desired (done in UI)
What I would like to do is use another Variable element to assign the PrevInstallFolder variable to InstallFolder after the registry search. But how would I make sure that that variable gets assigned after the registry search happens? Also, how can I assign it only if the registry string isn't empty?
There is another similar stack overflow entry that sort of addresses what I want. The difference is that it checks to see if a directory exists. I don't care about this, so the solution doesn't really apply.
You could code out that logic in a C# custom action.
In the function you could set your install folder property to whatever you want:
Session["INSTALLFOLDER"] = "C:\Yourdir\etc"
Then use the same property name in the Directory definition fragment.
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="DesktopFolder" Name="Desktop"/>
<Directory Id="Websites" Name ="Websites">
<Directory Id="INSTALLFOLDER" Name="It'll be as set in the CA">
<Directory Id="SUB_DIR" Name="sub_dir"> </Directory>
Schedule your CA to run before CostFinalize.

I have created an msi file which i ran from D drive, even though it is installing in the C drive only. how can i change the installation path?

As i have this in my wix application it should install the msi file from where it is running . like if i run from D drive it should take SourceDir as D , but it is taking as C only . how to change it to D drive ?
According to this, TARGETDIR will default to the commandline value (if specified), then the ROOTDRIVE (usually C:) then the drive with the largest amount of space available.
The directory you opened your msi from has absolutely no bearing on the value of TARGETDIR which roots your directory installation path. If you want to allow the user to change the installation path you can implement WixUI_InstallDir UI or, using it as an example, implement your own UI. You can also usually make the Install's root folder (Usually the name of the Company or Product itself) have a public ID so that it can be set in the command line. Alternatively you can create a custom action to read the value of SourceDir and force the TARGETDIR to use the root of the SourceDir path (where you launched the installer) however this is not recommended.
I did this by setting a parameter when I run the installation (My default installation drive was C but sometimes I want to install on D drive):
ReSecServer.msi /L*v log.log APPLICATIONROOTDIRECTORY="D:\Program Files (x86)\XServer"
Here is an example of my directories:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="APPLICATIONROOTDIRECTORY" Name="XServer">
</Directory>
</Directory>
</Directory>

Copy files to network share with WiX

As a part of my project, the installer has to copy files to network share(\system_name\Folder). I have tried it using the <Directory> element defining the path to the share (NetHoodFolder property) but it's throwing an error.
Error 5 ICE64: The directory SP9381 is in the user profile but is not listed in the RemoveFile table. C:\Wix\MainProject\MainProject\Product.wxs 25 1 MainProject
Can we do the same using element ? Is there any other way to copy files to network share?
You can copy files to a network share but there are a number of limitations. First, to get around the ICE issue, try structuring your directory tree like so:
<Directory Id='TARGETDIR' Source='SourceDir'>
<Directory Id='ProgramFilesFolder' Name='PFiles'>
<Directory Id='NETWORKSHAREFOLDER' Name='network'>
</Directory>
</Directory>
Then somehow you need to get the network share to be known. You could ask in the UI or take it from the command-line or whatever. I hardcoded the string in my test case (definitely not recommended for reals):
<Property Id='NETSHARE' Value='\\server\share\folder' />
<SetDirectory Id='NETWORKSHAREFOLDER' Value='[NETSHARE]' />
If you wanted to pass it on the command line, don't add any of the above, just do:
msiexec /i path\to\your.msi NETWORKSHAREFOLDER="\\server\share\folder"
Basically, just root your NETWORKSHAREFOLDER in a well known folder and then change it later as explained above.

How to give INSTALLDIR folder permission in WIX?

I am designing a WIX 3.6 installer project, during the installation we need to grand the user create file permission to the install folder(INSTALLDIR, especially with the default install folder, the Program Files, the user normally can't create file in the installation. We've experienced some failures). I guess it can be achieved by setting a Permission element, with CreateFile property. However, the INSTALLDIR is a directory, and only such elements as CreateFolder, File, FileShare, Registry, ServiceInstall can have permission element. So could anyone tell me how to do that? My directory declaration is something like this:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFiles64Folder">
<Directory Id='MANUFACTUREFOLDER' Name='$(var.ManufacturerName)'>
<Directory Id="INSTALLDIR" Name="$(var.ProductName)">
Thanks!
UPDATED:
Now I have managed to create a file (not the deployed file. But a generated file based on one deployed file, and it is generated in a custom action in commit phase). However, I now have a problem deleting the deployed file I just described (because there is no use of it after the other file is successfully generated). When the installation folder is Program Files, I can't delete it in the custom action. It says access denied.
Another thing is, I really don't understand the purpose of CreateFolder element. If its aim is to create a folder, as its name implies, don't nested Directory(s) do the same thing, as in my example code? And I think it is more clear to describe the folder structure there since it is very common to separate the Component elements (in which CreateFolder elements will reside) and the Directory elements. The Component will just use DirectoryReference to refer to the correct directory. Secondly, it is also common that multiple Components reside in the same Directory. So if I add a CreateFolder to one of those Components, with the default directory as the common parent directory of those Components, what does it even mean? It is really not intuitive of such a structure.
As you noted, CreateFolder elements may have Permission elements, and they default to the directory of the parent component if no directory is specified. So the structure would look something like the following.
<Directory Id="INSTALLDIR" Name="$(var.ProductName)">
<Component>
<CreateFolder>
<Permission>
</Permission>
</CreateFolder>
</Component>
</Directory>
However, based on Vista and Windows 7 restrictions and Windows Logo guidelines, I wouldn't be surprised if you still have issues with this, since the Program Files directory is pretty locked down. After install you should not be writing to this directory.