(WiX) Program files shortcut for per-machine install - wix

Following the example here, I added a shortcut to the ProgramMenuFolder that launches my application. (My code is actually simpler because I don't need the extra folder.)
<DirectoryRef Id='ProgramMenuFolder'>
<Component Id='cmpStartMenuShortcut'
Guid='MY GUID HERE'>
<Shortcut Id='StartMenuShortcut'
Name='$(var.ProductName)'
Icon='MainIcon.ico'
Description='$(var.ProductName)'
Target='[ClientDir]myapp.exe'
WorkingDirectory='ClientDir'/>
<RegistryValue Action='write' Type='integer' Root='HKCU'
Key='Software\Company\Product Name'
Name='installed' Value='1' KeyPath='yes'/>
</Component>
</DirectoryRef>
Since my installation is per machine (ALLUSERS=1, Package/#InstallPrivileges='elevated', and #InstallScope='perMachine') the ProgramMenuFolder is the folder for all users on the machine.
My question has to do with the registry value. My understanding is that it's needed simply to provide a KeyPath for the component that contains the shortcut. The sample uses HKCU, which is a per-user location.
Isn't it a mistake to use a per-user value as a KeyPath for a per-machine component?
If a machine has two admins, and admin #1 installs the product, and admin #2 attempts a repair, then Windows Installer won't see the registry value and think that the shortcut is missing and it will install a duplicate, right?
So I tried changing the RegistryValue/#Root to HKLM, but then WiX complains:
error LGHT0204 : ICE38: Component cmpStartMenuShortcut installs to user profile. It's[sic] KeyPath registry key must fall under HKCU.
error LGHT0204 : ICE43: Component cmpStartMenuShortcut has non-advertised shortcuts. It's[sic] KeyPath registry key should fall under HKCU.
error LGHT0204 : ICE57: Component 'cmpStartMenuShortcut' has both per-user and per-machine data with a per-machine KeyPath.
I don't understand why the key must be under HKCU.

That style of shortcut is for a target that might not be installed now nor at the time it is invoked. It creates the classic .lnk shortcut file. It is useful for shortcuts to targets that your installer is not responsible for but might be useful for users of your product to use (e.g. cmd.exe).
Alternatively, a shortcut for a target you are installing or advertising will be uninstalled when the target is unadvertised (product is uninstalled). For example, WiX installs a shortcut to wix.chm called WiX Documentation. The Shortcut element for an advertised shortcut can be made a child of the File element.
Here is a hand-written example:
<Component Id="ProductComponent">
<File Source="$(var.ConsoleApplication1.TargetPath)" KeyPath="yes">
<Shortcut Id="$(var.ConsoleApplication1.TargetName)Shortcut"
Name="$(var.ConsoleApplication1.TargetName)"
Advertise="yes"
Description="Starts $(var.ConsoleApplication1.TargetName)"
Directory="ProgramMenuFolder" />
</File>
</Component>
To insert the Shortcut element into heat's output, pass it the path to an XSL transform. Snippet:
<xsl:template match="wix:File[contains(#Source,'\myapp.exe')]">
<xsl:copy-of select="." />
<Shortcut Id='StartMenuShortcut'
Advertise="yes"
Name='$(var.ProductName)'
Icon='MainIcon.ico'
Description='$(var.ProductName)'
WorkingDirectory='ClientDir'/>
</xsl:template>

What I would recommend doing is simply making the folder as you said, but not placing the shortcut directly under it. Instead make a shortcut element under the component holding the file. You can use the Directory attribute to specify where you want the shortcut to appear.

Related

Hot ti Install File just in the first time with WIX

I have a component
<Component Id="ProductComponent" Guid="7935315f-4242-4c7a-a02c-6fd256805356">
<CreateFolder/>
<File
Id="propFile"
Name="aaa.properties"
DiskId="1"
Source="$(var.Project.TargetDir)"
Vital="yes"
KeyPath="yes" ></File>
<?endif?>
</Component>
I want to copy the file just on install , not upgrade.
But I can't find how to do it.
Any idea?
Have you tried using Condition element. I think you can provide a Condition inside Component element to check whether product is already installed or not. If not installed, then create file.
<Component Id="ProductComponent" Guid="7935315f-4242-4c7a-a02c-6fd256805356">
<Condition> NOT Installed </Condition>
<CreateFolder/>
<File
Id="propFile"
Name="aaa.properties"
DiskId="1"
Source="$(var.Project.TargetDir)"
Vital="yes"
KeyPath="yes" ></File>
</Component>
This is a weak spot of MSI (which WiX uses).
MSI installs a file
User modifies the file
MSI goes to install the file. Should it:
a) overwrite and lose user data
b) not overwrite and lose new applciation data
c) merge --- MSI doesn't support this.
If the user data is only one or few attributes there are tricks with custom actions to harvest the user data and reapply it but this is very tricky stuff.
IMO, the best way to approach this is never keep user data in a file installed by the installer. Take app.config appSettings element as an example. It was an atttribute that allows you to extend the file with another file that overrides the settings in the first file. Using this pattern the installer can lay down the app config and the application can create the override file and everything just works because MSI doesn't have to deal with the problem at all.

WIX does not remove shortcuts in the INSTALLDIR if not default

Why WIX does not remove a shortcut in the INSTALLDIR if it is not the default install directory is used? My WIX code look like?
<DirectoryRef Id="INSTALLDIR">
<Component Guid="..." Id="shortcuts_INSTALLDIR">
<RegistryKey ForceDeleteOnUninstall="yes" Id="shortcuts_reg_INSTALLDIR" Key="Software\MyCompany\MyProduct" Root="HKCU">
<RegistryValue KeyPath="yes" Name="shortcut_INSTALLDIR" Type="string" Value=""/>
</RegistryKey>
<Shortcut Arguments="my args " Description="my description" Id="InstallDir_my_name" Name="my name" Target="[INSTALLDIR]mydir\my.exe" WorkingDirectory="INSTALLDIR"/>
</Component>
</DirectoryRef>
It look like that the uninstaller does not know the new value of INSTALLDIR. Any idea?
Windows Installer is a bit of an odd beast here. It doesn't record the operations it performs; instead it tries to record the information necessary to reverse them. In this case it appears you're falling into a gap in that implementation.
Windows Installer notes that it has installed component shortcuts_INSTALLDIR. When a file is installed to a specific directory, it records the directory's location. Then during maintenance it restores all the directories it recorded. But it does not record (and thus does not restore) the directory for just a shortcut. Typically shortcuts are installed to predefined paths under the ProgramMenuFolder. Since such locations are not affected by changes to INSTALLDIR, this is usually not a problem.
To solve this you have to ensure the alternate INSTALLDIR is restored during maintenance. You can convince Windows Installer to do so automatically by installing any file directly to INSTALLDIR (if the extra file is not a problem, this is my preferred option). Alternately you can do so manually through the remember property pattern, possibly leveraging ARPINSTALLLOCATION and its saved value in the Uninstall key.

WiX common component as merge module and INSTALLDIR from registry

I want to have 2 installers with common part. So I used merge module, and created 2 wix installers.
Here is what I want to achive with more details (problem described there was solved): wix installers with common component
I am using WixUI_InstallDir so user is able to choose directory where application will be installed.
When second installer is launched I want to load previously choosen installation directory.
When user does not change default path all works fine. But if INSTALLDIR was changed in first installation, then second installer adds plugin to right path but also extracts core to default path - which is wrong.
However right path (from previous installation) is shown on "Destination Folder" dialog.
Here is significant code:
<Property Id="CORE_INSTALLATION_PATH">
<RegistrySearch Id="InstallFolderRegistrySearch" Type="raw" Root="HKLM" Key="SOFTWARE\PluginCompany\Plugins" Name="InstallFolder"/>
</Property>
<SetDirectory Id="INSTALLDIR" Value="[CORE_INSTALLATION_PATH]">CORE_INSTALLATION_PATH</SetDirectory>
<DirectoryRef Id="TARGETDIR">
<Component Id="CoreRegistryEntries" Guid="{C1701385-12CA-47EF-9FB2-884139B56390}">
<RegistryKey Root="HKLM" Key="SOFTWARE\PluginCompany\Plugins" Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Name="InstallFolder" Value="[INSTALLDIR]" KeyPath="yes"/>
</RegistryKey>
</Component>
</DirectoryRef>
You can download and run full sample solution from https://github.com/bwojdyla/wixplugins/tree/04f61b89b0465311818bec1cc06371b3dced5671
In logs I found this:
MSI (c) (38:CC) [08:54:03:324]: Dir (target): Key: INSTALLDIR , Object: C:\Program Files (x86)\PluginCompanyFolder\PluginInstaller2\
MSI (c) (38:CC) [08:54:03:324]: Dir (target): Key: INSTALLDIR.751E70EB_CF76_413B_B8C8_231A31F9C946 , Object: C:\Program Files (x86)\PluginCompanyFolder\PluginInstaller\
So there are two properties INSTALLDIR and INSTALLDIR with guid.
This second property is added by merge module and updating INSTALLDIR does not change second property. That is why core components were extracted to custom location by second installer.
To disable mudularization I used SuppressModularization attribute:
<Property Id="INSTALLDIR" SuppressModularization="yes"/>
Notice SuppressModularization description at:
http://wixtoolset.org/documentation/manual/v3/xsd/wix/property.html
Use to suppress modularization of this property identifier in merge
modules. Using this functionality is strongly discouraged; it should
only be necessary as a workaround of last resort in rare scenarios.
A couple or three things:
Make sure you have the correct registry, maybe you need a Win64 search, maybe not, depends if the data is in the native registry or the x86 WOW one.
If it's not too late, it's typically better to install shared components to a common location (such as the common files folder for your Company Name and Product Name) to avoid this kind of thing. It's perfectly ok if not all the files are in the main application folder, as long as the app doesn't care.
Do the install with a verbose log and see what the AppSearch is doing - that's where it will set (or not) that property value. That's where you'll see if the property is being set or not, and therefore whether something else is going wrong later on.

Is it possible to have advertised shortcuts and ProgId for a per user MSI?

I have a permachine MSI installer that I'm converting to a per user installer that is installing files to the LocalAppDataFolder.
To get rid of ICE38 warnings I added a registry as the keypath. The problem is in one of my components I have a advertised shortcut and ProgId.
I'm now getting:
ICE19 - 'settings' advertises component: 'CMP_Rapid'. This component cannot be advertised because the KeyPath type disallows it
ICE19 - Shortcut: 'SHC_RunConfigExe' advertises component: 'CMP_Rapid'. This component cannot be advertised because the KeyPath type disallows it.
ICE50 - Component 'CMP_Rapid' has an advertised shortcut, but the KeyPath cannot be found.
Is there anyway to advertise in per user installs when installing to the LocalAppDataFolder? I'm not that familiar with advertising. On the shortcut I use it because I like the repair functionality it offers. On the progId I use it because it automatically updates the appropriate icons after an install.
I need to install this on XP and Vista so I can use the Windows 7 solution: ProgramFilesFolder redirection.
Below is the component that generates the errors:
<DirectoryRef Id="INSTALL_FOLDER">
<Component Id="CMP_Rapid"
Guid="{9373A11C-5A3C-49E3-963D-C19B765A4285}">
<File Id="FILE_Rapid"
Source="$(var.FilePath)\Dynagen Configurator.exe">
</File>
<Shortcut Id="SHC_RunConfigExe"
Name="DYNAGEN Configurator"
Description="Opens DYNAGEN Configurator application."
Directory="ConfigShortCutDir"
WorkingDirectory="INSTALL_FOLDER"
Icon="ICO_RunConfigExe.exe"
Advertise="yes"/>
<ProgId Id="Rapid.drcS" Icon="ICO_drcS.ico" Advertise="yes">
<Extension Id="settings">
<Verb Id="Open" Command="Open" Argument="/so "%1"" />
<Verb Id="Edit" Command="Edit" Argument="/edit "%1""/>
<Verb Id="Program" Command="Program" Argument="/program "%1""/>
</Extension>
</ProgId>
<RegistryValue Root="HKCU"
Key="Software\Dynagen\DynagenConfigurator"
Name="CMP_Rapid"
Type="integer"
Value="1"
KeyPath="yes"
/>
</Component>
</DirectoryRef>
Turns out satisfying ICE38 by making a registry key the keypath is wrong. The proper thing to do is to ignore the ICE38 error but going going into the wix properties page and add ICE38 and ICE91 beside "Suppress specific ICE validation.
Microsoft or Wix doesn't have ideal support for pure per-user installs. I couldn't even find any good examples online.
Bryan at WiX-users#lists.sourceforge.net helped me out on this:
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.
Once I ignored that the above worked.
[Edit]
See my answer here as well. Turns out there is some edge cases you need to be aware of.

Wix: Preventing a file from restoring by Windows Installer Service

We have a little situation here. We are writing an ini file at install-time using following code segment:
<Component Id="_CFG" Guid="{CADE766F-3AF0-40A6-9D35-12AC4FD5B278}" Feature="DefaultFeature" KeyPath="yes" Location="either" NeverOverwrite="yes">
<CreateFolder Directory="CFG" />
<Environment Id="SharedAppend" Name="Path" Value="[CommonFilesFolder]Company Shared\MyDir" Separator=";" Action="set" Part="last" Permanent="yes" System="yes" />
<IniFile Id="MyCFG.ini1" Action="addLine" Directory="CFG" Key="LOCAL_ROOT" Name="ata.ini" Section="ALIAS" Value="[CommonAppDataFolder]Company\MyDir" />
<IniFile Id="MyCFG.ini73" Action="addLine" Directory="CFG" Key="APPLICATIONS" Name="ata.ini" Section="GENERAL" Value="Product1;Product2;Product3;Product4;" />
<RegistryValue Id="Registry47asdf" Root="HKLM" Key="SOFTWARE\Company\MyProd" Name="LocalRoot" Value="[CommonAppDataFolder]Company\MyDir\" Type="string" />
</Component>
This installation is conducted by an Admin user. Now a 2nd user (Standard) modify this file via some application. After that when a 3rd user would logged in and launch the application, Windows Installer Progress Dialog appear and that would restore the file to original one.
I thought, "NeverOverwrite" would prevent this, but it didn’t worked.
I’m assuming that "NeverOverwrite" attribute may not be applicable on element.
Anyone have any idea how to prevent this file from restoring by Windows installer service?
Thanks a bunch..
Modification of an ini file should not trigger Windows Installer Resiliency. What happens is that a component will be reinstalled when its keypath (i.e. a certain file or registry entry) disappears.
So you need to figure out these things:
Which component installs the INI file? (I suppose it is not the component you show in your question, because that one only modifies INI files.)
What is the keypath of that component? (If it is not marked explicitly, wix will take the first file or registry entry in that component.)
Why is the keypath file or registry entry disappearing, thus triggering the reinstallation of that component?
Also, you might want to consider putting the ini file in its own component. This way, it will be its own keypath and it will only be reinstalled by the windows installer resiliency mechanism when it actually disappeared (and not when some other file or registry entry disappears.)