Write to XML file in maintenance mode with Wix - wix

I'm using Wix 3.10. I've got an installer that asks the user to enter a value when they install, which it saves in an XML file. This was done with XmlFile, as part of the component which installs the XML file, and this works fine. However now I want to be able to go to add/remove programs, click the Change button, so the installer is run in maintenance mode, the user can change the value they previously entered, and it will update the XML file, however I'm not sure how to do this, it doesn't work as it is being set in the component. Any pointers would be appreciated, thanks! Below is the component xml, SETTING is the property set from the UI text box which gets written to the XML file:
<Component Id="MyApp.exe.config" Directory="INSTALLFOLDER" Guid="my-guid">
<RegistryKey Root="HKMU" Key="Software\MyCompany\MyApp">
<RegistryValue Name="MyApp.exe.config" Value="1" KeyPath="yes" Type="integer"/>
</RegistryKey>
<File Source="MyApp.exe.config"/>
<util:XmlFile Id="Config1" File="[INSTALLFOLDER]MyApp.exe.config" Action="setValue" Name="value" Value="[SETTING]" ElementPath="//configuration//appSettings//add" Sequence="1"/>
</Component>

Related

The target file 'XXXX' is installed in 'YYYY' by two different components on an LFN system

We recently upgraded DevExpress. Since we have a custom theme, we had to upgrade the custom theme too.
That was the easy part. Now I'm trying to upgrade the setup to match the new file.
So basically, I'm changing the <File .../> of one <Component .../>:
From
<Component Id="Lib_Various_Files" Guid="9C621EB0-12E6-4D1D-8B5B-4150A76E33AA" KeyPath="yes" SharedDllRefCount="yes">
...
<File Id="DevExpress.Xpf.Themes.PreviousTheme.v17.1.dll" Name="DevExpress.Xpf.Themes.PreviousTheme.v17.1.dll" ReadOnly="yes" Vital="no" Compressed="default" DiskId="1" Source="$(var.DirLib)\PreviousTheme\DevExpress.Xpf.Themes.PreviousTheme.v17.1.dll" />
</Component>
To:
<Component Id="Lib_Various_Files" Guid="9C621EB0-12E6-4D1D-8B5B-4150A76E33AA" KeyPath="yes" SharedDllRefCount="yes">
...
<File Id="DevExpress.Xpf.Themes.OurTheme.v17.2.dll" Name="DevExpress.Xpf.Themes.OurTheme.v17.2.dll" ReadOnly="yes" Vital="no" Compressed="default" DiskId="1" Source="$(var.DirLib)\OurTheme\.td\Publish\DevExpress.Xpf.Themes.OurTheme.v17.2.dll" />
</Component>
Now I've an error in the setup, which seems to have no links to this line:
error LGHT0204: ICE30: The target file
'qgikh9i6.dll|System.Windows.Interactivity.dll' is installed in
'[TARGETDIR]\OurProduct\Bin\' by two different components on an LFN
system: 'cmpF5730C92213BA3272DDA3A5657DFF782' and 'Lib_Prism'. This
breaks component reference counting.
[D:\ws\OurProduct-Nightly\SetupWix\SetupWix\SetupWix.wixproj]
We do reference this library, in the Lib_Prism component(which is then in another Lib_Various component, that reference Lib_Prism and Lib_Various_Files, but nowhere else.
Any idea what could be the issue?
So here is the complete components list of this file:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<?include ..\Common.wxi?>
<DirectoryRef Id="BIN">
<Component Id="Lib_Various_Files" Guid="9C621EB0-12E6-4D1D-8B5B-4150A76E33AA" KeyPath="yes" SharedDllRefCount="yes">
...
<File Id="DevExpress.Xpf.Themes.OurTheme.v17.2.dll" Name="DevExpress.Xpf.Themes.OurTheme.v17.2.dll" ReadOnly="yes" Vital="no" Compressed="default" DiskId="1" Source="$(var.DirLib)\OurTheme\.td\Publish\DevExpress.Xpf.Themes.OurTheme.v17.2.dll" />
</Component>
<Component Id="Lib_MicrosoftPractices" Guid="780097FD-40C9-417A-A2C3-7C2B44567BEC" KeyPath="yes" SharedDllRefCount="yes">
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll" />
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\Unity.4.0.1\lib\net45\Microsoft.Practices.Unity.Configuration.dll" />
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\Unity.4.0.1\lib\net45\Microsoft.Practices.Unity.RegistrationByConvention.dll" />
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\Unity.4.0.1\lib\net45\Microsoft.Practices.Unity.dll" />
</Component>
<Component Id="Lib_Prism" Guid="0F937515-2248-4CD2-B2E9-3E121FA9D743" KeyPath="yes" SharedDllRefCount="yes">
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\Prism.Core.6.3.0\lib\net45\Prism.dll" />
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\Prism.Unity.6.3.0\lib\net45\Prism.Unity.Wpf.dll" />
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\Prism.Wpf.6.3.0\lib\net45\Prism.Wpf.dll" />
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\Prism.Wpf.6.3.0\lib\net45\System.Windows.Interactivity.dll" />
</Component>
<Component Id="Lib_MvvmValidation" Guid="8681DBA1-F83D-475B-BCB8-A54A1F05FF0A" KeyPath="yes" SharedDllRefCount="yes">
<File ReadOnly="yes" DiskId="1" Source="$(var.DirPackages)\MvvmValidation.3.1.0\lib\netstandard1.0\MvvmValidation.dll" />
</Component>
<Component Id="Lib_Protobuf_Net" Guid="AEE6F4EB-78E3-4EC5-AA88-D5CC29D683D0" KeyPath="yes" SharedDllRefCount="yes">
<File ReadOnly="yes" DiskId="1" Source="$(var.DirDotfuscated)\ProtobufNet.dll" />
</Component>
</DirectoryRef>
<ComponentGroup Id="Lib_Various" >
<ComponentRef Id="Lib_MicrosoftPractices" />
<ComponentRef Id="Lib_Prism" />
<ComponentRef Id="Lib_Various_Files" />
<ComponentRef Id="Lib_MvvmValidation" />
<ComponentRef Id="Lib_Protobuf_Net" />
</ComponentGroup>
</Fragment>
</Wix>
Looking at your source file there are several problems with your component reference counting outright. You should never install several binaries with one component - it is a direct violation of the component rules. This causes exactly the kind of problems the error message indicates.
I recommend using a single file per component because that solves a plethora of possible reference count issues and upgrade problems. The shared-dll ref counters can also cause some blues I think. Do you have a legacy installer that you are trying to be compatible with? If not, then there is no reason to enable this component option - it increments the legacy SharedDLL ref-counter used by older, non-MSI installer technologies.
Now, for the issue where you change a file name in an existing component. This is also a violation of the component rules. You can not change the absolute file name of a component's key path and keep the same component GUID - this breaks component referencing. There must be a 1-to-1 correspondence between an absolute installation path and a component GUID.
The component GUID doesn't follow the file around if it moves, and the file "moves" when you change its file name (its absolute installation path has changed). There is an explanation here with an example: Change my component GUID in wix? (recommended read - decode this MSI peculiarity and things will be clearer going forward).
If you change a file name you can either:
Set your component GUIDs to auto-generate by deleting the whole GUID section in your source. The GUID will then be generated to be stable as long as the installation target path remains the same, and when you change the file name - for example - a new GUID will be generated for you auto-magically by WiX. See this answer for sample: Syntax for guids in WIX?
Set a new, hard-coded GUID yourself for the components where you change the file name that is being installed. This can be easy to forget - hence the recommended auto-magic described in point 1.
What you should actually do when file names change is to remove the old component and add a new one with the new file name. However, changing the GUID of an existing component and changing the file name has the same effect (same as deleting the old component and adding a new one).
With that said, there are bigger problems with this source as explained above. For future reliability you must split these components into one file per component. This causes interference between your old and new version and in order to clean this up, you can:
Set a totally new installation path for your project and use a single component per file from now on and you can use WiX's auto-magic component generation feature as explained above. This will work. Setting a new main installation folder "breaks the link" to "past component referencing sins".
Or you can uninstall the existing installation early during your major upgrade by moving RemoveExistingProducts early in the InstallExecuteSequence of your newest MSI version. This also wipes the slate clean of any component referencing issues and you can change your source to use one file per component going forward. If you use the MajorUpgrade element this change is easy - just set Schedule="afterInstallValidate". That should work (no time to test).
That should be it - if I have understood your scenario correctly.
Sample WiX extract for the proposed, new version:
<DirectoryRef Id="BIN">
<Component Feature="Product">
<File Source="$(var.DirLib)\OurTheme\.td\Publish\DevExpress.Xpf.Themes.OurTheme.v17.2.dll" />
</Component>
<Component Feature="Product">
<File Source="$(var.DirPackages)\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll" />
</Component>
<Component Feature="Product">
<File Source="$(var.DirPackages)\Unity.4.0.1\lib\net45\Microsoft.Practices.Unity.Configuration.dll" />
</Component>
<Component Feature="Product">
<File Source="$(var.DirPackages)\Unity.4.0.1\lib\net45\Microsoft.Practices.Unity.RegistrationByConvention.dll" />
</Component>
<...>
</DirectoryRef>
Notice the tersified source with all attributes that can be auto-generated left out and all components now containing a single file. There is also direct specification of what feature each component belongs to as an attribute of the Component element. I find that this yields the least complicated and most flexible WiX source files. Preferences vary - obviously.
I would not roll with your current "multiple binaries per component" setup going forward. There will be more trouble if you do - almost guaranteed. MSI bites back - sorry to say - there are many bear traps. MSI has aspects that border on anti-patterns. The problems are faced by almost everyone. There is a section towards the bottom here on potential anti-patterns and also on the great benefits MSI yields for corporate deployment (just for reference): How to make better use of MSI files.
I am not particularly keen on this chaotic write-up of common MSI problems, but here it is: How do I avoid common design flaws in my WiX / MSI deployment solution? Maybe it can help to avoid some very common problems.
I finally found the issue:
It appears that DevExpress bin directory packs the System.Windows.Interactivity.dll library. So before we were not copying it and we didn't had it in our Lib\DevExpress folder.
It appears that we generate a componet with all Dll contained in the Lib\DevExpress folder, and therefore the System.WIndows.Interactivity.dll was contained in 2 differents packages.
I removed it from the DevExpress folder and now everything works fine. Sorry for the trouble.

Wix set program run as admistrator

I have already set the program run as the windows startup, however the program can only work when it is run as administrator in windows 10, can anybody tell me what I should add in the code to set that? Here is my Wix code:
In product modules there is
<ComponentRef Id="RegistryEntries"/>
and then
<Fragment>
<DirectoryRef Id="ProgramFilesFolder">
<Component Id="RegistryEntries" Guid="14fe9526-0da4-4761-ad27-8a77f145c6b5">
<RegistryKey Root="HKCU"
Key="Software\Microsoft\Windows\CurrentVersion\Run"
Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Name="March Networks Video Assistant" Value="[INSTALLFOLDER]March Networks Video Assistant.exe" KeyPath="yes"/>
</RegistryKey>
</Component>
</DirectoryRef>
</Fragment>
Thank you so much to everybody.
The problem is you are putting this registry key in the HKCU which will run things in the current user's context at startup.
If you want it to be run as administrator on startup it needs to go into HKLM.
Try the following:
<Fragment>
<DirectoryRef Id="ProgramFilesFolder">
<Component Id="RegistryEntries" Guid="14fe9526-0da4-4761-ad27-8a77f145c6b5">
<RegistryKey Root="HKLM"
Key="Software\Microsoft\Windows\CurrentVersion\Run"
Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Name="March Networks Video Assistant" Value=""[INSTALLFOLDER]March Networks Video Assistant.exe"" KeyPath="yes"/>
</RegistryKey>
</Component>
</DirectoryRef>
</Fragment>
So notice I changed HKCU to HKLM (this might cause ICE warnings during compilation but you can safely ignore them). If you are using a per user install, I don't know if this will be able to write the registry key though unless the install is run as administrator... Also I put " around your RegistryValue's value since it's always nice to wrap full paths in quotes because of spaces.
Another nice thing you can do is update the RegistryValue's value using the install path of the component which can be referenced using the following syntax:
[#IDOfFile]
So if your component defining the "March Networks Video Assistant.exe" <File> uses Id="MarchNetworksVideoAssistant.exe" you can update your RegistryValue's value to
value=""[#MarchNetworksVideoAssistant.exe]""
You can get a good explanation of this syntax here
If a substring of the form [#filekey] is found, it is replaced by the full path of the file, with the value filekey used as a key into the File table. The value of [#filekey] remains blank and is not replaced by a path until the installer runs the CostInitialize action, FileCost action, and CostFinalize action. The value of [#filekey] depends upon the installation state of the component to which the file belongs. If the component is run from the source, the value is the path to the source location of the file. If the component is run locally, the value is the path to the target location of the file after installation. If the component has an action state of absent, the installed state of the component is used to determine the [#filekey] value. If the installed state of the component is also absent or null, [#filekey] resolves to an empty string, otherwise it resolves to the value based upon the component's installed state. For more information about checking the installation state of components, see Checking the Installation of Features, Components, Files.

Wix install location

I have a WIX setup that allows the user to select the install location. When uninstalling, I need to run a custom action that should activate a file in the install location. I tried getting the install location from session["INSTALLDIR"] but it results in the default path and not the one given by the user.
How can I reach that location?
I've done this in my own installer - the following should work.
This adds a property to retrieve the install location value from the registry.
<Property Id="INSTALLDIR">
<RegistrySearch Id='Registry' Type='raw' Root='HKCU' Key='Software\$(var.Manufacturer)\$(var.ProductName)' Name='Location' />
</Property>
This sets the install location in the registry.
<Component Id="Registry" Guid="*">
<RegistryKey Root="HKCU" Key="Software\$(var.Manufacturer)\$(var.ProductName)">
<RegistryValue Name="Location"
Type="string"
Value="[INSTALLDIR]"
Action="write"
KeyPath="yes" />
</RegistryKey>
<util:RemoveFolderEx On="uninstall" Property="INSTALLDIR" />
If you want INSTALLDIR for a later time such as uninstallation you should use Remember property pattern described in link below.
"The root issue is that the Windows Installer does not save Property values for you. That means if the user enters values in the install UI or passes them on the command-line, those values will be not be present during repair, upgrade nor uninstall."
http://robmensching.com/blog/posts/2010/5/2/the-wix-toolsets-remember-property-pattern/

How to run .reg files when running the msi?

I am trying to understand how my setup.msi inserts registry values when installing it. In Orca editor I am seeing like this,
After installing the msi, in the log file I am seeing like this,
MSI (s) (A8:B4) [16:27:28:674]: Executing op: ComponentRegister(ComponentId={45667B7F-9DC7-43B7-BE9E-3215ED1B1985},KeyPath=02:\SOFTWARE\myCompany\MySolution\Plugins\MyProduct\ProductCode,State=3,,Disk=1,SharedDllRefCount=0,BinaryType=0)
I want to do the reverse engineering of this mechanism, can any one help me to understand this? I want to recreate the same using WIX, so I just tried like below
<Component Id="RegistryEntries" Guid="*">
<RegistryKey Root="HKLM"
Key="Software\Microsoft\MyCompany"
Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Name="MyApp" Value="[INSTALLLOCATION]" KeyPath="yes"/>
<RegistryValue Type="string" Name="Configuration Files" Value="[INSTALLLOCATIONCONFIG]"/>
<RegistryValue Type="string" Name="Configuration Files1" Value="[INSTALLLOCATIONCONFIG1]"/>
</RegistryKey>
</Component>
When I built the msi and if edit it Orca, I am seeing like below,
What should I do to get the msi as shown in the previous image?
MSI expresses registry data in the Registry table. During the installation, the system determines which features and components are being installed therefore which registry adds, updates and deletes to perform. The actual work is carried out by the WriteRegistryValues and RemoveRegistryValues actions.
It is not a best practice to "execute a reg file" during an installation because it's out of process and MSI can't manage the changes. Instead use the WiX Heat tool to harvest the contents of the registry file into wix xml source for inclusion in your installer.

different shortcut behavior during unattended installation

I am trying to create a Start Menu shortcut. This is the code I'm using:
<Fragment>
<SetProperty Id="MIFOLDER" Value="[INSTALLFOLDER]\MI" Before="CostFinalize"></SetProperty>
<!-- Start menu -->
<Icon Id="iconCMD" SourceFile="$(var.SharedComponents.ProjectDir)Images\Icons\cmd.ico" />
<DirectoryRef Id="STARTMENUFOLDER">
<Component Id="cmp211060161C737F50377C120FF39D7623" Guid="{E7B9FB15-4A1D-4E3E-BCDE-EB2E5638C452}" Win64="yes">
<Shortcut Id="shrtct211060161C737F50377C120FF39D7623" Name="Management Interface (MI)"
Target="[System64Folder]\cmd.exe" Icon="iconCMD" Arguments='/k "title Managment Command Line Interface (MI)"' WorkingDirectory="MIFOLDER" />
<RegistryValue Root="HKCU" Key="Software\$(var.Manufacturer)\VB\Installer\$(var.ProductName)\MI"
Name="MI" Type="string" Value="$(var.VersionNumber)" KeyPath="yes" />
<RemoveFolder Id="rem211060161C737F50377C120FF39D7623" On="uninstall"/>
</Component>
</DirectoryRef>
</Fragment>
I want this shortcut to run Command Line Interface and start it in installation folder.
When running the setup normally, it indeed works fine and all well.
When I'm installing with /q (unattended), and after installation I click the shortcut, and it leads to Windows\system32 directory and not working directory. When I look at shortcut properties, I see that it set to start at \MI as SetProperty action doesn't take place.
Any help and ideas why?
InstallUISequence will be skipped in silent installation. Use Sequenceattribute to run the SetProperty action as per your requirement(both or first).