I am new to Wix installation toolkit and have been trying to create an installer. I am using it as a plugin to VS 2017. This is so far as I have got.
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util=" http://schemas.microsoft.com/wix/UtilExtension">
<Directory Id="CommonAppDataFolder">
<Directory Id="ProgramDataVendorFolder" Name="ABC">
<!--This will create the \ProgramData\MyVendor\MyProductName\ folder. -->
<Directory Id="ProgramDataAppFolder" Name="DEF" />
</Directory>
</Directory>
</Directory>
<DirectoryRef Id="ProgramDataAppFolder">
<Component Id="CmpCreateCommonAppDataFolderWithPermissions" Guid="*" Permanent="yes">
<CreateFolder>
<!--This will ensure that everyone gets full permissions to the folder that we create in the ProgramData folder.-->
<util:PermissionEx User="Everyone" GenericAll="yes" />
</CreateFolder>
</Component>
</DirectoryRef>
Added a reference to "WixUtilExtension.dll" in the project.
But I get the error
Error : The CreateFolder element contains an unhandled extension
element 'util:PermissionEx'. Please ensure that the extension for
elements in the ' http://schemas.microsoft.com/wix/UtilExtension'
namespace has been provided.
In addition I have also tried using the http://wixtoolset.org/schemas/v4/wxs/util instead of http://schemas.microsoft.com/wix/UtilExtension
I tried to comment out the code above and then get the error
The extension
'Microsoft.Tools.WindowsInstallerXml.Extensions.UtilExtension'
contains a defintion for table 'EventManifest' that collides with a
previously loaded table definition. Please remove one of the
conflicting extensions or rename one of the tables to avoid the
collision.
Could someone please help me out here.
Missing Reference: You must have forgotten to add a reference to the
WixUtilExtension.dll file in the WiX installation folder. Please follow the procedure below to add the reference.
In Visual Studio:
Open your WiX project.
Right click "References" in the right pane => Add References...
Browse to the file, or paste the following (if using standard installation paths)C:\Program Files (x86)\WiX Toolset v3.11\bin\WixUtilExtension.dll.
Click "Add" and then "OK".
WiX Sample Code: Some WiX sample code described here.
The most common namespaces to add to the WiX element:
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension" (Burn)
xmlns:netfx="http://schemas.microsoft.com/wix/NetFxExtension" (.NET)
xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension" (IIS)
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension" (Util)
xmlns:wixloc="http://schemas.microsoft.com/wix/2006/localization" (Localization)
IsWiX is nice for namespaces and more (screenshot below). It is a free WiX tool from Chris Painter:
You have a leading space in the xmlns:util namespace. Remove this and things should work OK.
Related
I'm trying to build an installer with Wix and i have to put a file in the Startup folder. I already found out how to build the path to the startup folder but i can't find any variabile that can identify the current user.
That's what i have done for now and it works but the part with the name of the user just creates a new directory with that name
There are a couple of things probably wrong with your idea:
You shouldn't need to build that entire directory tree to get to the StartupFolder because there is already a standard Windows Installer property named StartupFolder. This is already the path to the current user's startup folder so it's not clear why you need the value of LogonUser.
Properties are resolved by placing them in square brackets, so in the general case you'd use [LogonUser], but directory names in the Directory table are not marked as Formatted type, so calling a directory [LogonUser] won't work. You'd need to set another public property to the value of [LogonUser] and then use that property as a directory name. However, I think point 1. may be all you need, and your directory tree isn't clear about your intent.
Run on Startup
Normally you would put a shortcut to a file in the startup folder, and not an actual file. You do so by refering to the built-in Windows Installer Property StartupFolder as shown in the mockup-sample below (and as stated by Phil).
In the realm of alternatives, there are many ways to schedule something to start with Windows. What type of file is this and what does it do? In case you are interested, you can see a number of ways used to start something on login or boot by running AutoRuns (from SysInternals). There is a shocking array of possibilities (small digression).
Very often you can run things as services or scheduled tasks, rather than using other startup features. Generally services for features that need to run continuously, and scheduled tasks for stuff that needs to run every now and then. I think most people want to avoid too many things running on login - if they are not really necessary. I find the startup folder "clunky" - and also prone to user interference as well.
Self-Repair and the Startup Folder
This Experts-Exchange article describes a case when self-repair was triggered after deleting a startup folder entry (search for "startup" to find the section).
Frankly I am a bit surprised at the described scenario. When a shortcut is deleted, it should not come back automatically easily, since it is generally not the key path of its hosting component. Still, something to check when you test your MSI (delete the shortcut and then launch your app directly - if there is a shortcut to do so). If you see the problem, please let us know.
If I were to guess what really happened, they might have installed an actual file into the shortcut folder and set it as the key path (which is what it seems you are trying to do as well). Then they have put this in the same feature hierarchy as an advertised shortcut - the same feature or the top feature of the application, or a parent feature - causing self-repair to always be invoked when the advertised shortcut is invoked, and the missing file is detected in the Startup folder and self-repair ensues.
Digression: a sizeable digression, the important point is to please check this for your setup! This kind of problem really aggravate your users - the cause of it tends to elude their support guys.
Mockup WiX Sample
Here is one sample for how to install a shortcut to the Startup folder. Note that the Startup folder redirects depending on whether the setup is installed per-user or per-machine, as documented on MSDN: StartupFolder.
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="Startup Shortcut" Manufacturer="Someone" Version="0.0.1"
Language="1033" UpgradeCode="PUT-GUID-HERE">
<Package InstallScope="perMachine" Compressed="yes" />
<Media Id="1" Cabinet="my.cab" EmbedCab="yes" />
<UIRef Id="WixUI_Mondo" /> <!-- Just include a default setup GUI -->
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder" Name="PFiles">
<Directory Id="MyCompany" Name="Company">
<Directory Id="MyAPP" Name="MyApp">
<Component Feature="MyFeature">
<File Source="MyApp.exe" />
<!-- Set Advertise="no" to avoid advertised shortcut -->
<Shortcut Id="MyApp" Directory="StartupFolder" Name="MyApp"
Advertise="yes" />
</Component>
</Directory>
<Directory Id="StartupFolder" />
</Directory>
</Directory>
</Directory>
<Feature Id="MyFeature" Absent="disallow" />
<Property Id="MSIFASTINSTALL" Value="7" /> <!-- Tweak to install faster -->
</Product>
</Wix>
This should be a property set automatically in the installer at run time, LogonUser.
I built a simple installer in Wix which will place a couple of data files in a specific folder in a preexisting product installation so that the user doesn't need to know anything about the product's installation in order to update their data files. The product stores its installation path in an environment variable (ENVVAR) which I'm using here to calculate the path of its NewData subfolder.
When I double-click the .msi or run it from the command line (msiexec /i filename.msi) it works perfectly and the files show up in C:\ProductPath\NewData. However, if it's installed with elevated privileges (msiexec /a filename.msi) the files go to the root of D:\ (which isn't even the same drive the product is installed on.)
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"
xmlns:netfx="http://schemas.microsoft.com/wix/NetFxExtension">
<?include InstallVariables.wxi ?>
<Product Id="*"
Name="Product Name"
Manufacturer="My Company"
Version="$(var.Version)"
UpgradeCode="guidgoeshere"
Language="1033">
<Package Description="Description $(var.Version)" Comments="Install package for my product."
InstallerVersion="300" Compressed="yes" InstallScope="perMachine"/>
<Media Id="1" Cabinet="Cabname.cab" EmbedCab="yes" CompressionLevel="high"/>
<SetDirectory Id="PATHMAP" Value="[%ENVVAR]\NewData" Sequence="first" />
<!-- Describe the folder layout here. -->
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="PATHMAP" FileSource="..\New Files">
<Component Id="File1" Guid="guidgoeshere">
<RemoveFile Id="Remove_File1File" Name="$(var.File1Pattern)" On="both" />
<File Id="File1File" Name="$(var.File1)" Vital="yes" KeyPath="yes" />
</Component>
<Component Id="File2" Guid="guidgoeshere">
<RemoveFile Id="Remove_File2File" Name="$(var.File2Pattern)" On="both" />
<File Id="File2File" Name="$(var.File2)" Vital="yes" KeyPath="yes" />
</Component>
</Directory>
</Directory>
<Feature Id="FeatureId" Title="New Files for a Feature" Level="1" >
<ComponentRef Id="File1"/>
<ComponentRef Id="File2"/>
</Feature>
</Product>
</Wix>
Note that the file removal in the components is intentional; if there is an existing version of either file (which may have a slightly different file name -- not my choice) I want to remove and replace it. The patterns used to do so are in the include file and are working properly.
The command line: msiexec.exe /a filename.msi will not trigger installation with elevated privileges, but rather an administrative installation. Follow the link for a description - it is important that you do for a complete description. Essentially an administrative installation is just an extraction of embedded files in the MSI to make a network installation image from where people can run a regular installation of the MSI (better explained in the linked answer above) - administrative installations don't install anything at all - it is a mere extraction.
You should be able to control the output directory of the administrative installation by providing a TARGETDIR like this: msiexec.exe /a filename.msi TARGETDIR=C:\MyOutputFolder\. Your MSI is probably lacking a basic GUI to show the administrative installation's dialog sequence - which makes the extraction happen without any parameters specified (hence you output to the largest drive on the box by default). You might want to consider linking a standard dialog set such as <UIRef Id="WixUI_Mondo" /> for your MSI. You can see a step-by-step description of how to do this here: WiX installer msi not installing the Winform app created with Visual Studio 2017. This will give your setup basic, standard GUI for both regular installation and administrative installation. Very useful I think - you should also set your own license agreement - I have updated the linked answer to include that.
I think this is the end of the answer for you. Installing with /a isn't installation with elevated rights - essentially - it is just an extraction of files. But do link in that default GUI to make your MSI more standard and better overall.
A couple of comments on the environment variable approach. I have never stored anything like that in environment variables. I usually just write to my own location in HKLM and read back from there either via a custom action or using MSI's built in search feature (preferably the latter - it is much better to rely on built-in MSI features. I am a little sloppy with read-only custom actions at times, but very much against read-write custom actions. You can see why here: Why is it a good idea to limit the use of custom actions in my WiX / MSI setups? - a digression I guess). WiX can easily define these searches and set the search result to your property: Define Searches Using Variables.
Maybe a quick link to "The WiX toolset's "Remember Property" pattern" by Rob Mensching (WiX creator). This is quite old now, there may be a new, smarter way to do this that I am not aware of yet.
If I were you, I would rather download these updated files from the network rather than deploy them like this into a "data folder" using Windows Installer. Deployment of user data files has always been problematic with MSI with its complex file overwrite rules and "quirks". Though perhaps not entirely related to your use case, here is a description of other approaches you can use to deploy data files for your application - perhaps in a more reliable fashion: Create folder and file on Current user profile, from Admin Profile. Maybe have a quick skim at least.
There are two types of environment variables: user environment variables (set for each user) and system environment variables (set for everyone).
By default, a child process inherits the environment variables of its parent process. Programs started by the command processor inherit the command processor's environment variables.
https://msdn.microsoft.com/en-us/library/windows/desktop/ms682653(v=vs.85).aspx
Probably, you can check machine users and if product is installed for all users point to system environment variable in Local Machine.
Following directory setting works perfectly for me.
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id="ProgramFilesFolder">
<Directory Id='INSTALLDIR' Name='MyApp'/>
</Directory>
</Directory>
However, when I tried changing "ProgramFilesFolder" to "LocalAppDataFolder", I got lots of error when using light to link and generate my msi:
D:\runGroup.wxs(53) : error LGHT0204: ICE38: Component cmpA5561BE36D80EB58252E69DDA0C2FF8C installs to user profile. It must use a registry key under HKCU as its KeyPath, not a file.
D:\main.wxs(38) : error LGHT0204 : ICE64: The directory INSTALLDIR is in the user profile but is not listed in the Remove File table.
Looks like "LocalAppDataFolder" is not acceptable for WiX, while I believe it is one of the system folder properties which defined in here.
What am I supposed to use for LocalAppData folder?
I converted an application from being a perMachine install to be a perUser install. In order to properly convert the install I had to add a registry key for each of the components I have.
Originally I had the following:
<Component Id="C.MyExe">
<File Id="Fi.MyExe" Name="$(var.MyExe.TargetFileName)" Source="$(var.MyExe.TargetPath)" DiskId="1">
<Shortcut Id="SC.StartMenu"
Directory="D.ApplicationMenuDir"
Name="$(var.AppName)"
WorkingDirectory="INSTALLDIR"
Icon="MY_ICON.ico"
IconIndex="0"
Advertise="yes"
/>
...
When I moved the exe component to the user install I had to do something like this:
<Directory Id="LocalAppDataFolder" Name="AppData">
<Directory Id="MyAppDirectory" Name="$(var.AppName)">
<Component Id="C.MyExe" Guid="{MY_GUID}">
<CreateFolder />
<RemoveFolder Id="RemoveMyAppDirectory" On="uninstall" />
<RegistryKey Root="HKCU" Key="Software\MyCompany\MyApp">
<RegistryValue Name="MainExe" Value="1" KeyPath="yes" Type="integer" />
</RegistryKey>
<File Id="Fi.MyExe" Name="$(var.MyExe.TargetFileName)"
Source="$(var.MyExe.TargetPath)" DiskId="1" Checksum="yes">
</File>
</Component>
...
The most important part is that you will have to add a registry key which points to HKEY_CURRENT_USER. I added a registry value for each component which indicates that the component is installed.
I also had to remove the following: Advertise="yes".
I had this problem recently. I wanted to convert my installer from per-machine to a per-user but was getting ICE38. I asked on wix-users and one opinion was that you can ignore ICE38 because that was meant as a check for per-machine installs.
See the discussion at wix-users.
Since that is the case, ICE38 is (in my opinion) incorrect and you will want to ignore it. ICE38 implies you are installing per-user resources in the context of a per-machine installation but never verifies that this is so.
Actually authoring a per-user install requires that you ignore ICE38
because it won't ever be accurate for that world.
[Edit]
Looks like you got help here.
From Peter Shirtcliffe:
This is my own, admittedly inexpert, understanding of per-user installations:
Installing to subdirectory of LocalAppDataFolder is perfectly OK in a
per-user MSI. Because of certain scenarios relating to roaming users, you
need to add components containing elements for any
directories you create under LocalAppDataFolder. That's why ICE64 is
appearing.
The ICE38 error is slightly misleading: since you have a per-user
installation, it's safe to ignore as long as the user cannot pick an
alternative installation location that is common to all users. ICE38 is
checking for the situation where multiple users all install the same
component to the same path.
Just posting to help other people (like me).
Ok, just found that we can do it by overwriting "ProgramFilesFolder":
<SetProperty Id="ProgramFilesFolder" Value="[LocalAppDataFolder]" Before="CostFinalize"><![CDATA[NOT Privileged]]></SetProperty>
Another thing to do is, in <Package> we need to set InstallPrivileges to limited.
Well, I can see no reason why "ProgramFilesFolder" can be used directly while "LocalAppDataFolder" can't.
Are you installing per-user or per-machine? Also, what OS versions are you targetting? You might want to read:
Authoring a single package for Per-User or Per-Machine Installation context in Windows 7
I have created a silent installer Using WIX for my application. I want it to install my application to C:\MyApps folder but its Directory Id='TARGETDIR' Name='SourceDir' tag randomly picks C or D drive. I want to enforce my installation to C drive only. Also in case, I provide version number greater than 4.0.5, I am geting an error during installation saying "This installation package cannot be installed by the Windows Installer Service. You must install a newer version of the Window Installer service." I am having Windows XP professional SP3 Version 2002.
To begin with, I think you should start your WiX journey with the tutorial available here. It contains the answers to the most of basic questions you'll face with the first thing. You should also be aware that understanding WiX means understanding the concepts of Windows Installer first - otherwise some points will seem a weird magic to you.
When you create a new WiX setup project in Visual Studio, it generates a template with some placeholders. It is recommended to start modifying this template. For instance, the directory structure:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLLOCATION" Name="SetupProject1">
<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
<!-- <Component Id="ProductComponent" Guid="ba7d579f-5234-4448-b880-109f589d58e5"> -->
<!-- TODO: Insert files, registry keys, and other resources here. -->
<!-- </Component> -->
</Directory>
</Directory>
</Directory>
This snippet defines the INSTALLLOCATION folder under the ProgramFileFolder, and this is a better approach than to place it under the C:\ root. You can still change the installation location by modifying the INSTALLLOCATION property at install time (for instance, base on user's input).
The quick answer to your questions are:
...randomly picks C or D drive...
That's expected - it picks the drive with the most free space by the time of installation. If you stick to the way WiX template defines by default, it will fall under C: (actually, under Program Files folder).
...You must install a newer version of the Window Installer service...
Basically, it means what it says - the version Windows Installer on your machine is lower than the one you require in your package. If you try to solve the above problems with this change, then it has nothing to do with the Windows Installer version. You should require higher version than it is specified by default only in case you are going to use new features of Windows Installer.
Hope you'll make right conclusion out of this brief intro - start with the tutorial. :-)
The problem with your versions is you're changing the Windows Installer version when you change your Product version.
<Package
Id='*'
InstallerVersion='406'
Compressed='yes'
Description="Installer Number 406" />
The InstallerVersion attribute should be the minimum required version of Windows Installer required to install this package. You have Windows Installer v4.5 installed. When this is set to 406, it looks for Windows Installer v4.6 which frankly, doesn't exist. Setting this to 301 (version 3.1) is usually sufficient.
InstallerVersion='301'
While your description attribute is fine, I would find the following more meaningful:
Description="My Product v4.0.6 Installer"
Try this:
<Fragment>
<Property Id="_BrowseProperty" Value="INSTALLDIR" Secure="yes"/>
<CustomAction Id="SetDataLocationDefault" Property="INSTALLDIR" Value="[WindowsVolume]$(var.Title)\" />
<InstallUISequence>
<Custom Action="SetDataLocationDefault" After="CostFinalize" />
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="SetDataLocationDefault" After="CostFinalize" />
</InstallExecuteSequence>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLDIR" Name="$(var.Title)">
<!-- TODO: Insert your components here. -->
</Directory>
</Directory>
</Fragment>
I think this should work!
Do not rely on TARGETDIR, and use the custom property, like this:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLLOCATION" Name="SetupProject1">
<!-- TODO: Insert your components here. -->
</Directory>
</Directory>
The template is taken from Yan's answer. Set INSTALLLOCATION to the desired folder C:\MyApps, that should do the trick.
I'm using Wix 3.6 beta from the command line, not as VS projects. I have a web application that is harvested with heat as a directory. This works. I'm using web.config transforms to manage each of the target environment web.config files. These are output with msbuild, this works and keeps things visible in Visual Studio and source control.
I've hit a problem deploying one of the several web.config files which I am manually including in product.wxs as components with conditions. I was expecting to include all components as deployable features and let the conditions select just one as active. For example:
<DirectoryRef Id="wwwroot">
<Component Id="setup_a" Guid="some_guid" >
<File Source="$(var.ConfigSourceDir)\setup_a\web.config" />
<Condition>ENVIRON = setup_a</Condition>
</Component>
<Component Id="setup_b" Guid="some_guid" >
<File Source="$(var.ConfigSourceDir)\setup_b\web.config" />
<Condition>ENVIRON = setup_b</Condition>
</Component>
This didn't create any file renaming, moving or deleting issues, but has the very fundamental problem that multiple web.config files are mapped to the same destination and this gives me a light error of "Product.wxs(xxx) : error LGHT0091 : Duplicate symbol 'File:web.config' found. This typically means that an Id is duplicated. Check to make sure all your identifiers of a given type (File, Component, Feature) are unique."
An alternative approach was to use different named .config files and rename/move one to be the web.config, so something like:
<DirectoryRef Id="wwwroot">
<Component Id="setup_a" Guid="some_guid" >
<File Id="setup_a.config" Source="$(var.ConfigSourceDir)\setup_a.config" />
<CopyFile Id="moveit" SourceDirectory="wwwroot" SourceName="setup_a.config" DestinationDirectory="wwwroot" DestinationName="web.config" />
</Component>
This doesn't throw an error, bot the CopyFile command does nothing at all. I just get setup_a.config in the wwwroot folder.
If I nest the CopyFile inside the File, the copy action then works:
<DirectoryRef Id="wwwroot">
<Component Id="setup_a" Guid="some_guid" >
<File Id="setup_a.config" Source="$(var.ConfigSourceDir)\setup_a.config" >
<CopyFile Id="moveit" DestinationName="web.config"/>
</File>
</Component>
...but nested CopyFile means I can't add (it's disallowed) the Delete="yes" attribute to create a 'move' action. Instead I'm left with both setup_a.config and web.config in the wwwroot folder. Alternatively, if I add a seperate removefile within the same component element it also does nothing:
<RemoveFile Id="removefile" On="install" Directory="wwwroot" Name="setup_a.config"/>
</Component>
So, I'm hoping for a working example of how handle multiple web.config files in a conditional deployment, that doesn't leave files behind. the destination filename of web.config is fixed by the framework and can't be changed. The different configs are also pre-generated outside of wix using config transforms, this also can't be changed but the generated filenames could be anything.
cheers!
You complicate it too much. This should work:
<Component Id="setup_a" Guid="some_guid" >
<File Name="web.config" Id="config_a" Source="$(var.ConfigSourceDir)\setup_a\web.config" />
<Condition>ENVIRON = setup_a</Condition>
</Component>
<Component Id="setup_b" Guid="some_guid" >
<File Name="web.config" Id="config_b" Source="$(var.ConfigSourceDir)\setup_b\web.config" />
<Condition>ENVIRON = setup_b</Condition>
</Component>
Pay attention to a couple of things here:
the File/#Name is the same - that's the target file name you'd like to have (web.config)
the File/#Id is different for each File in order to avoid the light error you mentioned first
the File/#Source can be anything - it just describes what file to take as a source
In this sample light will still complain with warning LGHT1076, but that's just a warning - it pays your attention that conditions MUST be mutually exclusive to avoid problems.
I usually take a different approach. Rather then placing multiple mutually exclusive files into an installer that are tightly coupled to specific instances I put a generic file in the installer and use XML changes to transform the XML with the variation point data such as connection string and what not.
This allows me to make installers that can be deployed anywhere silently just by passing a few properties and the command line.