Wix Installer - Create Folder hierarchy based on Property - installation

I am using Wix 3.6 to create a setup. I am still learning as I go along. The information out there is still scattered around. I am just waiting for my Wix Developer Guide book arriving.
I currently have a custom UI dialog where the user enters some application configuration. Part of that configuration is to specify a log folder. This at present this just sets a property [LogFolder]. This is defaulted to something like D:\Logs.
I want the installer to create that directory when the setup is run. I have the following to try and do this but it just created a folder named [LOGFOLDER] on the D: drive when I run the setup.
<Product ...
<Directory Id="TARGETDIR" Name="SourceDir" >
<Directory Id="LogFolderDir" Name="[LOGFOLDER]" >
<Component Id="LogFolderComponent" Guid="{7E7D6916-B321-40D6-ABAD-696B57A6E5FB}" KeyPath="yes">
<CreateFolder />
</Component>
</Directory>
</Directory>
...
</Product>
How can I do this with Wix?

The first step is create a property set to the value you want:
<Product>
<Property Id="LOGFOLDER" Value="D:\Logs" />
</Product>
The second step is to create a dialog where you set this property (or another thing to change its value):
<Dialog>
<Control Id="Edit_LogFolder" Type="Edit" Property="LOGFOLDER" />
</Dialog>
Then you need to change your directories structure to create this folder in a default location:
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="MyApp">
<Directory Id="LOGFOLDER" Name="Logs" />
</Directory>
</Directory>
The last step is to create a Component that will create the directory, like this:
<ComponentGroup Id="ComponentGroup_LogFolder">
<Component Id="Component_LogFolder" Guid="" Directory="LOGFOLDER">
<CreateFolder Directory="LOGFOLDER" />
</Component>
</ComponentGroup>
Remark:
If D:\ is a disc drive and you have a disc inserted, the installation will fail because it will try to create the folder and it won't succeed.

The Name attribute isn't formattable so you can use properties in it. The Id 'LogFolderDir' doesn't have a parent such as "ProgramFilesFolder' so it's defaulting to the volume with the largest amount of disk space. In this case D but YMMV.
It's dangerous to default to D: because D: might not exist. How I'd set this directory up is Id="LOGDIR" Name="Logs" and make it a child of the INSTALLDIR/INSTALLLOCATION directory element. Then in your custom UI, wire up another BrowseFolder dialog to give the user the ability to override it. Or, make it associated with a required Logs feature so that the stock feature selection dialog can be used to select the feature and browse the destination folder.
If you still want it to "default" to D:\Logs what I would do is have a custom action that checks to see if D: exists and is a fixed disk. If so, set the LOGDIR=D:\Logs

There is a simpler solution by using the same ID for the property and the directory (without naming the directory). But you must use the full path of the folder in the property.
Let's say the log directory is C: \ ProgramDirectory \ Data \ Log and you want to set Data with a property (usually if the value of Data is different and conditionally set).
<Property Id="PR_DATA_DIRECTORY" Value="C:\ProgramDirectory\Data" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="DIR_PROGRAM" Name="C:\ProgramDirectory" >
<Directory Id="PR_DATA_DIRECTORY">
<Directory Id="DIR_LOG" Name="Log" />
<Directory/>
</Directory>
</Directory>

Related

WIX Toolset - application version in MSI name and installation folders

In my WIX project i do have BuildVersion variable read from .exe file of my application. Lets say currently its 1.0.0.0. General idea is to allow users to install multiple version of application on single pc. There are 2 problems:
How to create .msi output files with version information in its name ? For example Application_v1.0.msi. To be honest i have no idea where to even start with that.
How to create separate folders for each version in Start menu, and Program files ? Idea is to have structure like Application/v1-0 (v2-0, v3-1 ..generaly v[major]-[minor]) in both - start menu and Program files.
According to point 2 so far i tried something "simple", like this in localisation file:
<WixLocalization Culture="en-us" xmlns="http://schemas.microsoft.com/wix/2006/localization">
<String Id="ProductNameFolder">MyApp\[BuildVersion]</String>
And then in file where i do have "directory" section:
<Directory Id="TARGETDIR" Name="SourceDir">
<!-- Shortcut folder is a Start Menu Folder-->
<Directory Id="ProgramMenuFolder">
<Directory Id="InstallProgramMenuFolder" Name="!(loc.ProductNameFolder)" />
</Directory>
<?if $(var.Platform)=x86 ?>
<!-- Program Files folder-->
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="!(loc.ProductNameFolder)" />
</Directory>
<?else?>
<!-- Program Files (64 bit) folder-->
<Directory Id="ProgramFiles64Folder">
<Directory Id="INSTALLFOLDER" Name="!(loc.ProductNameFolder)" />
</Directory>
<?endif?>
</Directory>
But when im trying to build im getting errors Invalid DefaultDir string everywhere where im using loc.ProductNameFolder.
I have tried also something like that in Directory section:
<Directory Id="InstallProgramMenuFolder" Name="!(loc.ProductNameFolder)\$(var.BuildVersion)" />
But that does give me errors like:
The Directory/#Name attribute's value '!(loc.PoductNameFolder)\1.0.0.0', is not a valid long name because it contains illegal characters
General idea is to have folder name like v[major]-[minor] but at start im trying simple version=folder name concept. No idea how i could convert Version to v[major]-[minor] so far as well.
Im using WIX Toolset 3.11.2, and Visual Studio 2019 Extension.
My own "partial" solution for point 1 - instalation file name with version:
Unload project, add something like this in BeforeBuild section :
get buildversion:
<GetAssemblyIdentity AssemblyFiles="..\src\MyApp\bin\Debug\MyApp.exe">
<Output TaskParameter="Assemblies" ItemName="AssemblyVersion" />
</GetAssemblyIdentity>
Create custom .msi name with version:
<CreateProperty Value="MyAppInstaller_v%(AssemblyVersion.Version)">
<Output TaskParameter="Value" PropertyName="TargetName" />
</CreateProperty>
In result you will get something like MyAppInstaller_v1.0.0.0. Still i would need to convert it to MyAppInstaller_v1.0

Wix add entered path to registry

I met a problem while adding entered by user path to registry.
Here are important parts of wix code.
Property declaration:
<Property Id="PathProp">C:\</Property>
Directory declaration:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="PathProp" Name="Name">
</Directory>
</Directory>
Registry block:
<RegistryKey Root="HKLM" Key="SOFTWARE\Path\To\Key" Action="createAndRemoveOnUninstall">
<RegistryValue Name="UserSetPath" Value="[PathProp]" Type="string" />
</RegistryKey>
I have the dialog which pulls PathProp from user and I want entered value be in registry. But I see the default value ("C:\"). Is it possible to register exactly what user entered?
I had a experience in past, where installer properties set in UI mode were destroyed in Execute mode.
Reason:
Properties were not set "secure".
Wix properties have attribute secure="true". Use It.
Also if possible share the installer log with us,so we can have a look at it.
You can get log by using this command:
<InstallerPath>/<InstallerName>.msi /l*v <LogPath>/InstallLog.txt

How can I automatically enable a VSIX extension, when installing?

I've created a VSIX package that I install via a WIX-generated MSI.
However, when I install it, and look at it in VS2010, in the Tools > Extension Manager menu, it is [Disabled] and I need to enable it manually.
How can I avoid this?
** EDIT **
Here's what I did:
I tried adding capturing the VSInstallDir from the registry like this:
<Property Id="VSINSTALLER">
<RegistrySearch Id="VSInstallRegistry" Root="HKLM" Key="SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0" Name="InstallDir" Type="directory" />
I added the directory structure under target-dir like this:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="VSINSTALLER">
<Directory Id="Extensions" Name="Extensions">
<Directory Id="Copy_CompanyFolder" Name="my company">
<Directory Id="INSTALLVSIX" Name="app name">
</Directory>
</Directory>
</Directory>
</Directory>
And I added a CopyFile element to the component in the installation folder, like this:
<Component Id="VsPackage" Guid="00000000-0000-some-guid-00000000">
<File Id="VsPackageInstaller" Source="$(folder.prop)\extensionName.vsix"
KeyPath="yes" DiskId="1">
<CopyFile Id="Copy_InstallVsix"
DestinationDirectory="INSTALLVSIX" />
</File>
</Component>
And I added the true element to the manifest.
When I do this, the extension is not installed.
Any ideas why?
If you install your extension files to a directory you create under %VSInstallDir%\Common7\IDE\Extensions, it will be enabled automatically for all users. This is the recommendation for MSI-installed extensions.
Also, please be sure to add <InstalledByMsi>true</InstalledByMsi> to your vsixmanifest.
There is no need to run VSIXInstaller.exe or write registry keys to enable your extension (In fact, you really shouldn't do this.).
Use "VSIXInstaller.exe" tool from VS2010\Common7\IDE.
Also you can manually enable your extension by adding registry value to
HKCU\Software\Microsoft\VisualStudio\10.0Exp\ExtensionManager\EnabledExtensions

Running copied files in WiX using a custom action

I'm creating an MSI installer using WiX and I have, say, a *.bat file that I'm copying to SomeFolder2 under %temp% (something like the code snippet below...)
...
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFilesFolder' Name='PFiles'>
<Directory Id='MyDir' Name='SomeFolder'>
<!-- %TEMP -->
<Directory Id="TempFolder" Name="TmpFolder">
<Directory Id='MyDir2' Name='SomeFolder2'>
<!-- CREATE THE %TEMP%\SomeFolder2 FOLDER -->
<Component Id='FolderComponent' Guid='{GUID}'>
<CreateFolder />
</Component>
<Component Id='CheckComponent' Guid='{GUID}'>
<File Id='mybat' Name='mybat.bat' DiskId='1' Source='.\mybat.bat' KeyPath="yes">
<Shortcut Id="mybatShcut"
Directory="ProgramMenuDir"
Name="{name}"
WorkingDirectory='INSTALLDIR'
Advertise="yes" />
</File>
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
</Directory>
...
Now, to run this, I have two custom actions (DESTDIR is %TEMP%\SomeFolder2):
<CustomAction Id="SetPath" Property="ThePath" Value="[DESTDIR]\mybat.bat" />
<CustomAction Id="StartAction" Property="ThePath" ExeCommand="" Return="asyncNoWait" />
Then in the install sequence:
<InstallExecuteSequence>
<Custom Action="SetPath" After="{some standard MS action}">NOT REMOVE="ALL"</Custom>
<Custom Action="StartAction" Before="{some other action}">NOT REMOVE="ALL"</Custom>
...
</InstallExecuteSequence>
I've put SetPath to run after a variety of standard actions (for example, PublishProduct) while StartAction will come before another custom action.
When I run the MSI file, I look in the log and ThePath does get set with the correct path. However, when StartAction is run, I get this error:
Return value 1631.
which, according to the documentation, translate to "ERROR_CREATE_FAILED" (the Windows Installer service failed to start. Contact your support personnel). The thing is, the file did get copied to %TEMP%\SomeFolder2 (before the setting of the path and the actual exection, might I add...), but for some reason, it doesn't execute at all (if you do execute it manually or via the command prompt or whatnot, it does execute normally).
I tried putting the same file under ProgramFiles\Some_Directory_For_The_Program. The same thing happens; it gets copied there, but it doesn't execute. Why is this happening?
First off, as long as you'd like to use a file installed by your package in a custom action, you should make it deferred. That is, StartAction CA in your example must be deferred. Also, I try to use QtExec standard CA when I need to run executables from CA.
I hope this helps.

Wix Conditionally Install Component if Registry Key Exists

I have a component I need to install only if a registry key exists which means an application has been installed.
I need to assign the value of the registry key (it is a directory) to a property then use this property to copy files from.
I have the following script so far but get an error "The system cannot find the file '[MYTESTDIR]fileToCopy.dat'."
Any help would really be appreciated.
<Property Id="MYTESTDIR">
<RegistrySearch Id="NetFramework20"
Root="HKLM"
Key="SOFTWARE\TEST\VALUE\1.00"
Name="MyName"
Type="directory" />
</Property>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="TEST" Name="Test">
<Component Id="MyComponent" Guid="E5FF53DE-1739-42c4-BE37-60F810C9CD69">
<Condition>MYTESTDIR</Condition>
<File Id="fileToCopy.dat" Name="fileToCopy.dat" Source="[MYTESTDIR]fileToCopy.dat">
<CopyFile Id="fileToCopy.datCopy" DestinationProperty="WEBSERVICEBINFOLDER" />
</File>
</Directory>
</Directory>
</Directory>
<Feature Id="MyFeature" Title="MyFeature" Level="1">
<ComponentRef Id="MyComponent" />
</Feature>
Based on my reading of the Wix Schema documentation, your problem is that you have the CopyFile element nested under a File element. Ditch the File element and just have the CopyFile sit under the Component:
<Component Id="MyComponent" Guid="E5FF53DE-1739-42c4-BE37-60F810C9CD69">
<Condition>MYTESTDIR</Condition>
<CopyFile Id="fileToCopy.datCopy" SourceName="[MYTESTDIR]fileToCopy.dat" DestinationProperty="WEBSERVICEBINFOLDER" />
</Component>
The way you had it, nested under the File, Wix was looking for the file on your system during the build - rather than setting up a copy command to be run at install time.
MYTESTDIR is a windows installer property which will get its value on the target system when the package is being installed.
However, you are trying to use this property in a Source attribute, which is used to point to files on the system where the setup package is being build.
Obviously that is not going to work. Windows installer properties don't even exist while the Source attribute is being evaluated, so Source can definitely not support such use.
Bryan's answer is the correct solution for what you're trying to do here. Using CopyFile under a File element is not illegal, but it is intended for copying files which you also install. In this case, you want to copy a file which is already on the target system, so the File element is not appropriate.

Resources