As the title says, I am have the following snippet of a code that installs a service using wix
<Directory Id="Test" Name="Test">
<Component Id="ConnectorMainService" Guid="{SOME-ID}">
<CreateFolder/>
<ServiceInstall Id="ServiceInstaller" Type="ownProcess" Name="$(var.PRODUCT_NAME)" DisplayName="$(var.PRODUCT_NAME)" Description="$(var.DESCRIPTION)" Start="auto" Account="NT AUTHORITY\LocalService" ErrorControl="normal" Interactive="no" Vital="yes"/>
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="$(var.PRODUCT_NAME)" Wait="yes"/>
</Component>
</Directory>
I had to put a <CreateFolder> in there, otherwise I have the same issues as described in this question (Why does my WiX installer need an empty CreateFolder to conditionally update an Xml file?).
The difference I have is even having the <CreateFolder> there, the installer exits without creating the service, it just creates the Folder.
Replace the CreateFolder element with a File element for the service executable itself. The Windows Installer requires the service's executable File and ServiceInstall elements to be in the same Component (specifically, the service executable must be the KeyPath of the Component but WiX will take care of that for you if you put the executable File element first in the Component).
Related
I've struggled with WIX for some time now. I want my program to be installed at the location the user has defined, install a service and start a program after installation.
First my msi package doesn't ask for install path.
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="Test" />
</Directory>
</Directory>
</Fragment>
May someone tell me how to prompt a screen with change install path?
Second when my service will be installed there is an error which says I miss some permissions:
<File Id="FILE_Service" Source="$(var.Service.TargetPath)" />
<ServiceInstall Id="INSTALL_Service"
Name="Servcie"
Description=""
Start="auto"
ErrorControl="normal"
Type="ownProcess"/>
<ServiceControl Id="CONTROL_Service"
Name="Servcie"
Start="install"
Stop="both"
Remove="uninstall"
Wait="yes" />
May someone tell me how to start my service with admin access?
Third the installed package contains only one EXE file, no referenced assembly. May someone tell me how to tell WIX to search for references and install them?
WiX Tutorial: Quite a bit here. You should try a WiX tutorial: https://www.firegiant.com/wix/tutorial/
Links: Here is my WiX quick start tip answer - various resources and hints to deal with WiX and deployment in general.
Please note that there are alternative deployment and package creation tools that might help you make setups quicker and more reliably if you have little experience with MSI and setups.
Learning Advanced Installer - Resources
Direct link Advanced Installer Video Tutorials
Concrete Answer: Here are some attempted answers for your concrete questions:
Configurable installation directory (a bit down the page). You essentially set the ConfigurableDirectory attribute for a feature element to allow the user to select a custom installation directory (you get to the dialog where you can change the installation path by selecting "Custom" installation):
<Feature Id="FeatureDirectory" Title="FeatureDirectory" ConfigurableDirectory="MYCUSTOMDIR">
<!-- your stuff here -->
</Feature>
Major Upgrade Installation Directory: You need to read back the custom directory for major upgrades. Here is how: The WiX toolset's "Remember Property" pattern. Or it will revert to default during the major upgrade. This is because a major upgrade is an uninstall of the old version and a (re)-install of the new version.
Files: To install all required files you need to figure out by dependency scanning what files need to be deployed for your application to work, and then add them to your packages manually (or use heat.exe to auto-generate the files list to include). See the above quick start links for help, or see this hello wix style article: https://www.codeproject.com/Tips/105638/A-quick-introduction-Create-an-MSI-installer-with
Service Permissions: Services should be installed with admin rights if you install the setup after a UAC elevation prompt. Most likely it does not start because there are missing files and hence broken dependencies. What credentials does the service use to run? LocalSystem?
Mock-Up: Here is a quick mock-up of something along the lines of what you need. You need to add all files and dependencies and insert the Service constructs among other things:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="WiXSample" Language="1033" Version="1.0.0.0"
Manufacturer="Someone" UpgradeCode="cb24bedf-e361-4f25-9a06-ac84ce5d6f5c">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<!--Default GUI - add reference to WixUIExtension.dll -->
<UIRef Id="WixUI_Mondo" />
<Feature Id="Core" Title="Core" Level="1" ConfigurableDirectory="INSTALLFOLDER" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="WiXSample">
<Component Feature="Core">
<File Source="D:\MyBinary.exe" />
</Component>
</Directory>
</Directory>
</Directory>
</Product>
</Wix>
I've created a very simple MSI which copies some files to the ProgramFiles directory and while installing calling to custom actions found in a binary written in C#.
While installing, I can easily call any custom action I want. For example I've created an installation step where the user should enter a license, and after confirming the license it is checked against a server using logic written inside C# custom action.
But, when uninstalling, every time I add a custom action (even if it does nothing but returning Success), I get error that the installation failed.
This is how I use the uninstalling step:
<InstallExecuteSequence>
<Custom Action='TestUninstallation' After='MsiUnpublishAssemblies'>REMOVE="ALL"</Custom>
</InstallExecuteSequence>
where TestUninstallation is defined as following:
<CustomAction Id="TestUninstallation" Return="check" Execute="deferred" Impersonate="no" BinaryKey="TestCustomAction" DllEntry="Uninstall" />
The property DllEntry equals Uninstall which is a C# method which only returns Success.
After installation is completed, I'm trying to uninstall and I'm getting the UserExit dialog defined inside the AdminUISequence with the property OnExit.
Any idea what am I missing?
Debugging: Managed code is relatively easy to debug (native code is actually even easier). Here are some pointers:
Debug C# Custom Actions (Advanced Installer)
Different debugging methods / aspects
Suggestions: I think you just have a broken reference to the dll export function - in other words an erroneous dll function name / reference:
<CustomAction Id="TestUninstallation" Return="check" Execute="deferred" Impersonate="no"
BinaryKey="CustomActions" DllEntry="__ERRONEOUS FUNCTION REFERENCE__" />
Just check what the dll actually exports and match like this:
<CustomAction Id="CustomAction1" BinaryKey="CustomActions" DllEntry="CustomAction1"/>
As always the real McCoy is the check of the dll itself to see if you have the right function name (the below screen shot from this prior answer, recommended read).
This is a native code C++ dll:
This is a DTF-packaged managed code dll:
Notice that this is a native dll with the managed code stuff embedded. It yields a very different functions list, but you still have to find the function name in there that you refer to.
This is a straight-up managed code dll (no native wrapping):
And finally: this is the straight-up managed code DLL without being wrapped in a native dll shell.
Un-Uninstallable Setup: When a custom action crashes or fails during uninstallation, you will have problems getting rid of the installation (it just rolls-back and you are stuck with it installed). There are several fixes or workarounds.
The overall fix - in my view - is to not fail custom actions on uninstall, or at least condition them so you can force an uninstall by setting a property via the command line:
Set in MSI property table: SUPPRESSERROR = 0. Then - when needed - on the command line set:
msiexec.exe /x {PRODUCT-GUID} SUPPRESSERROR="1"
Inside the MSI you condition the uninstall custom action with:
REMOVE="ALL" AND SUPPRESSERROR="0"
Now the custom action will not run if SUPPRESSERROR is anything but 0.
There is an older answer with several further options: I screwed up, how can I uninstall my program? (courtesy of Wim Coenen, with me messing up his answer with more suggestions).
Boilerplate: For quick use, let me just dump a boilerplate ad-hoc custom action test project here. This assumes a C# managed code custom action project called "CustomAction1" in the same Visual Studio solution and a reference added to it in your WiX source - like you already have obviously (this is for later when we have all forgotten what the problem was and need to test again):
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="WiXCustomActionsTesting" Language="1033" Version="1.0.0.0"
Manufacturer="test" UpgradeCode="PUT-GUID-HERE">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<UIRef Id="WixUI_Mondo" />
<Property Id="SUPPRESSERROR" Value="0" Secure="yes" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<Feature Id="ProductFeature" Title="WiXCustomActionsTesting" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
<!--BEGIN CUSTOM ACTION SECTION-->
<Binary Id="CustomActions" SourceFile="$(var.CustomAction1.TargetDir)\$(var.CustomAction1.TargetName).CA.dll" />
<CustomAction Id="TestUninstallation" Return="check" Execute="deferred" Impersonate="no" BinaryKey="CustomActions" DllEntry="CustomAction1" />
<InstallUISequence></InstallUISequence>
<InstallExecuteSequence>
<Custom Action='TestUninstallation' After='InstallInitialize'></Custom>
</InstallExecuteSequence>
<!--END CUSTOM ACTION SECTION-->
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="WiXCustomActionsTesting" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component>
<File Source="C:\Projects\MySetup\MyApp.exe">
</File>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
Create WiX project
Copy paste the code, set a new Upgrade GUID
Create CustomAction project, default name
Add reference to custom action project from wix project
Add reference to WiXUIExtension.dll
Adjust path to file in component
Compile
I'm creating a MSI installer and need to add more than one entry to the PATH environment variable. Per the MSDN documentation:
Each row can contain only one value. For example, the entry Value;Value;[~] is more than one value and should not be used because it causes unpredictable results. The entry Value;[~] is just one value.
My installer source code looks like this currently (note, this is a per-machine installation), which is a violation of the above documentation:
<!-- NOTE: These two features are mutually exclusive -->
<Feature Id="Feature1" Level="1000" Absent="allow" AllowAdvertise="no" InstallDefault="local" TypicalDefault="install">
<Component Directory="INSTALLFOLDER">
<RegistryValue Action="write" Type="int" Root="HKLM" Key="SOFTWARE\MyProduct" Name="MyPathEntry1" Value="1" KeyPath="yes" />
<Environment Id="AddPathEntry1" Name="PATH" Value="[INSTALLFOLDER]SubDir1" Action="set" Permanent="yes" Part="last" System="yes" />
</Component>
</Feature>
<Feature Id="Feature2" Level="1000" Absent="allow" AllowAdvertise="no" InstallDefault="local" TypicalDefault="install">
<Component Directory="INSTALLFOLDER">
<RegistryValue Action="write" Type="int" Root="HKLM" Key="SOFTWARE\MyProduct" Name="MyPathEntry2" Value="1" KeyPath="yes" />
<Environment Id="AddPathEntry2" Name="PATH" Value="[INSTALLFOLDER]SubDir1;[INSTALLFOLDER]SubDir2;[INSTALLFOLDER]SubDir3" Action="set" Permanent="yes" Part="last" System="yes" />
</Component>
</Feature>
Now even though the above is "technically" a violation according to the MSDN documentation, it seems to work. I've tested fresh installations, modifying an installation, and upgrading. All seem to work with no hitch. But one thing I've learned with MSI is whenever possible, it's best to follow the rules to avoid messing up people's machines.
The natural solution of adding independent (i.e. not mutually exclusive) features containing only individual path components won't work because with MSI, you cannot guarantee the order in which features and/or components are installed. However, in this case, the order in which path components are added to the PATH environment variable is important due to how the PATH variable is used when finding unqualified executables.
The other question that may come to mind is, why am I using features? I want to give installers of the product the option to change their installation choice at a later date in time via the standard Add/Remove Programs or Program and Features Control Panel applet.
So, how can I add more than one path entry to the PATH environment variable in a deterministic order while following the recommended guidance from the MSDN? Or is the guidance from MSDN outdated and what I'm currently doing is perfectly fine?
I figured a way to do this which does not violate the guidance at MSDN for my particular situation.
The exact situation is I have a main program executable that installers may or may not want to be able to run from a Windows Command Shell. Additionally, there are other utilities that also installers may or may not wish to run from a Windows Command Shell.
The way this was originally modeled was as a set of 3 mutually exclusive features using radio buttons to decide how to update the system PATH environment variable. The 3 features boiled down to:
Don't modify the system's PATH environment variable (i.e. the program and optional tools can't be run from a Windows Command Shell by simply typing the name of the executable at any directory location).
Update the system's PATH environment variable so that only the main executable can be run from a Windows Command Shell at any given path.
Update the system's PATH environment variable to also add additional utility tool paths.
What I came to realize is that there are really only 2 features here, and they're not even mutually exclusive, but one is dependent on the other. The first radio button listed above is not really a feature--it's a no-op; it doesn't install anything! The second radio button represents the "main" feature in that the primary executable's directory is added to the system's PATH. The 3rd radio button depends upon the second: if the main executable is added to the PATH, then you can additionally add other utilities to the PATH as well.
This results in a much simpler UI. Instead of radio buttons, use checkboxes, and the second checkbox is disabled unless the first checkbox is checked. This also results in a much simpler implementaion for ensuring that the combination of features selected (say, for a command-line installation) is valid.
In a nutshell, here's what the WiX XML will boil down to in my particular case. I'm sure that this can be generalized (or modified) to suit other similar scenarios.
<Feature Id="AddExeToPath" Level="1" Absent="allow" AllowAdvertise="no" InstallDefault="local" TypicalDefault="install">
<Component Id="ExePath" Guid="{YOURGUID-HERE-0000-0000-00000000}" Directory="INSTALLFOLDER">
<CreateFolder />
<Environment Id="ExePath" Name="MY_PATHS" Value="[INSTALLFOLDER]SubDir1" Action="set" Part="first" System="yes" />
</Component>
<Component Directory="INSTALLFOLDER">
<RegistryValue Action="write" Type="integer" Root="HKLM" Key="SOFTWARE\MyProduct" Name="AddExeToPath" Value="1" KeyPath="yes" />
<Environment Id="AddToPath" Name="PATH" Value="%MY_PATHS%" Action="set" Part="last" System="yes" />
</Component>
<Component Id="RemoveExeFromPathOnUninstall" Guid="{YOURGUID-HERE-0000-0000-00000000}" Directory="INSTALLFOLDER">
<Condition>REMOVE><AddExeToPath OR REMOVE="ALL"</Condition>
<Environment Id="RemoveFromPath" Name="PATH" Value="%MY_PATHS%" Action="remove" />
</Component>
</Feature>
<Feature Id="AddToolsToPath" Level="1000" Absent="allow" AllowAdvertise="no" InstallDefaut="local" TypicalDefault="install">
<Component Id="SubDir2" Guid="{YOURGUID-HERE-0000-0000-00000000}" Directory="INSTALLFOLDER">
<CreateFolder />
<Environment Id="SubDir2" Name="MY_TOOLS" Value="[INSTALLFOLDER]SubDir2" Action="set" Part="first" System="yes" />
</Component>
<Component Id="SubDir3" Guid="{YOURGUID-HERE-0000-0000-00000000}" Directory="INSTALLFOLDER">
<CreateFolder />
<Environment Id="SubDir3" Name="MY_TOOLS" Value="[INSTALLFOLDER]SubDir3" Action="set" Part="last" System="yes" />
</Component>
<Component Directory="INSTALLFOLDER">
<RegistryValue Action="write" Type="integer" Root="HKLM" Key="SOFTWARE\MyProduct" Name="AddToolsToPath" Value="1" KeyPath="yes" />
<Environment Id="AddToolsToPath" Name="MY_PATHS" Value="%MY_TOOLS%" Action="set" Part="last" System="yes" />
</Component>
</Feature>
<CustomAction Id="InvalidPathFeatureSelection" Error="25000" Execute="firstSequence" />
<InstallExecuteSequence>
<Custom Action="InvalidPathFeatureSelection" Before="InstallValidate">
<![CDATA[NOT (REMOVE="ALL" OR (&AddToolsToPath >= 3 IMP &AddExeToPath >= 3))]]>
</Custom>
</InstallExecuteSequence>
This set of features and components results in the following:
If the feature AddExeToPath is selected for installation, the following components of the feature result in:
An environment variable named MY_PATHS is created and contains the path to the main executable.
The environment variable PATH is updated, placing %MY_PATHS% at the end its current value.
If the feature AddToolsToPath is selected, 3 components are installed:
The environment variable MY_TOOLS is created/set, and [INSTALLFOLDER]SubDir2 is put at the front of the variable's existing value (if any).
The environment variable MY_TOOLS is created/set and [INSTALLFOLDER]SubDir3 is put at the end of the variable's existing value (if any).
Note
These two components defined the way that they are ensure that the paths added to MY_TOOLS are added in the appropriate order.
The environment variable MY_PATHS is updated, placing %MY_TOOLS% at the end of the exisiting variable; again, this preserves the correct ordering of paths.
And so what you end up with is:
MY_TOOLS = [INSTALLFOLDER]SubDir2;[INSTALLFOLDER]SubDir3
MY_PATHS = [INSTALLFOLDER]SubDir1;%MY_TOOLS%
PATH = <existing_PATH_value>;%MY_PATHS%
I'm using WiX XML to create an install package.
One of the things I'd like the user/installer to be able to do is select from one (and only one) of several config files (i.e., config1.txt, config2.txt, config3.txt) that would wind up in the same location after the install is done (i.e., the final file is config.txt). How do I do this in WiX?
The manual install has the user copying the desired config file to the final location/file. i.e., if the user wants to use config2.txt, he copies config2.txt to config.txt. He runs the program. Then later, if he needs to use config1.txt, he copies config1.txt to config.txt and runs the program.
Any idea how to do this in WiX?
Thanks in advance!
-Adeena
I solve a similar problem using a CopyFile tag;
Somewhere I have a <Directory> containing the following components:
<Component Id="Config_6.txt" Guid="{}">
<File Id="Config_6.txt" Name="Config_6.txt" KeyPath="yes" Source="..\..\..\bin\Config_6.txt" />
</Component>
<Component Id="Config.txt" Guid="{}">
<File Id="Config.txt" Name="Config_8.txt" KeyPath="yes" Source="..\..\..\bin\Config.txt" />
</Component>
<Component Id="Config_7.txt" Guid="{}">
<File Id="Config_7.txt" Name="Config_7.txt" KeyPath="yes" Source="..\..\..\bin\Config_7.txt" />
</Component>
<Component Id="R8_Config.txt" Guid="{}">
<CreateFolder/>
<CopyFile Id="R8_Config.txt" FileId="Config.txt" DestinationName="Config.txt"/>
</Component>
<Component Id="R7_Config.txt" Guid="{}">
<CreateFolder/>
<CopyFile Id="R7_Config.txt" FileId="Config_7.txt" DestinationName="Config.txt"/>
</Component>
<Component Id="R6_Config.txt" Guid="{}">
<CreateFolder/>
<CopyFile Id="R6_Config.txt" FileId="Config_6.txt" DestinationName="Config.txt"/>
</Component>
And in the Features part something like this:
<Feature Id="Config" Title="Config directory" Display="expand" Level="1" ConfigurableDirectory="Config">
<Feature Id="Config8" Title="Config 8" Level="1" Description="Select only one version!">
<ComponentRef Id="R8_Config.txt"/>
</Feature>
<Feature Id="Config7" Title="Config 7" Level="1002" Description="Select only one version!">
<ComponentRef Id="R7_Config.txt"/>
</Feature>
<Feature Id="Config6" Title="Config 6" Level="1004" Description="...">
<ComponentRef Id="R6_Config.txt"/>
</Feature>
</Feature>
To display the features, add a UIRef that contains CustomizeDlg, for example:
<UIRef Id="WixUI_FeatureTree" />
A disadvantage of this solution is that the user can select multiple config files. That will probably cause the last one to become the active one.
Is this a web app? Are these ini files or xml files or something else? I prefer application configuration to be done by the application itself after installation during first launch (if it is an exe file). An installation is about getting default settings and files in place, any custom configuration is beyond the scope of initial deployment in my opinion.
Wix provides a feature to update an xml file during installation, here is a sample: http://www.tramontana.co.hu/wix/lesson6.php#6.10 . This would allow you to write a specific config setting to the file if it is an XML file.
There is nothing stopping you from installing several flavors of the same config file in the same folder and allowing the user to manually switch between them. What values typically change if you switch the base file? One or many settings?
Problem: After uninstalling a replacement GINA I get logged off immediately after logging on if I use the WIX 3.0 installer.
I have a replacement login process (GINA) for windows XP.
It consists of a single file placed in the system directory
C:\windows\system32\NewGina.dll
and a registry entry
(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Winlogon\GinaDLL=NewGina.dll)
and I have no trouble manually installing it, running it, manually uninstalling it and logging in normally.
I can also create an installer using the Microsoft installer package in VS2008 and install, login, uninstall, login still works properly.
The problem I have is when I use the Wix installer, and I install, login, uninstall, and login, I get logged out immediately after login. After immediate logout, I was able to connect a remote regedit and dump the registry. I tried diffing before and after registries and I tried process monitor hoping to discover what the Wix installer was doing but the actions and changes (about 35,000) were a bit extensive to analyze. The registry line (listed above) was gone and windows should revert to the original msgina.dll
Since the rest of the project uses the Wix Installer, I'm hoping to use it.
Any ideas on how to get this to work and avoid the auto logoff?
Thanks
APB
My Wix script looks like
<Package InstallerVersion="200" Compressed="yes" />
<Condition Message="This application is only supported on Windows XP">
<![CDATA[(VersionNT = 501)]]>
</Condition>
<InstallExecuteSequence>
<ScheduleReboot After="InstallFinalize"/>
</InstallExecuteSequence>
<Media Id="1" Cabinet="NewGina.cab" EmbedCab="yes" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="SystemFolder">
<Component Id="NewGina" Guid="cdbdfbe9-8137-4305-98cb-a05618ea0ade" >
<File Source="..\NewGina\Release\NewGina.dll" Checksum="yes" />
</Component>
<Component Id="RegistryEntries" Guid="cdbdfbe9-8137-4305-98cb-a05618ea0adf" >
<RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Name="GinaDLL" Value="NewGina.dll" />
</RegistryKey>
</Component>
</Directory>
</Directory>
<Feature Id="NewGina" Title="NewGina" Level="1" >
<ComponentRef Id="NewGina" />
<ComponentRef Id="RegistryEntries" />
</Feature>
This line is a little disturbing:
<RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" Action="createAndRemoveOnUninstall">
If my memory serves correctly that says create the Winlogon key during install (probably a noop) then remove the entire Winlogon key during uninstall. In you dump can you see if that registry key exists any longer? If my memory is correct, it might be all gone.
The correct authoring in any case, would be to just remove the RegistryKey/#Action attribute. You just want the RegistryValue installed and uninstalled. No special actions necessary.