How to create a multi-level subfolder in Start menu using WiX - installation

How do I create sub folders (several levels deep) in the Windows Start menu, using WiX?
Currently I am able to put my shortcut in the Start menu, but only in a folder immediately under Programs (Start / Programs / MyFolder), but I want to nest my shortcut deeper (Start / Programs / MyPlatform / MyProduct / etc.). I tried different combinations, but alas.
<DirectoryRef Id="StartMenuMyProduct">
<Component Id="ApplicationShortcut" Guid="{PUT-SOME-GUID-HERE}">
<Shortcut Id="ApplicationStartMenuShortcut"
Name="Configure My Product"
Description="Add or remove this and that"
Target="[MYPRODUCTDIR]ConfigureMyProduct.exe"
WorkingDirectory="MYPRODUCTDIR"/>
<RemoveFolder Id="StartMenuMyProduct"
On="uninstall"/>
<RemoveFolder Id="StartMenuMyPlatform"
On="uninstall"/>
<RegistryValue Root="HKCU"
Key="SOFTWARE\MyCompany\MyPlatform\My Product"
Name="Installed"
Type="integer"
Value="1"
KeyPath="yes"/>
</Component>
</DirectoryRef>
<!-- Shortcut to the configuration utility in the Windows Start menu -->
<Directory Id="ProgramMenuFolder">
<!--<Directory Id="StartMenuMyPlatform" Name="MyPlatform">-->
<Directory Id="StartMenuMyProduct" Name="My Product" />
<!--</Directory>-->
</Directory>

What makes things interesting is that MSI demands a registry value to be created as a way to detect whether the component has been installed. If we prefer to create only one such registry value for all shortcuts, then we'll have to put all our shortcuts in a single component.
Fortunately it is possible to create components which span multiple target directories by making use of the Directory attribute on the Shortcut element.
<!-- shortcuts to applications in the start menu -->
<DirectoryRef Id="ProgramMenuProductFolder">
<Component Id="ProgramMenuShortcutsComponent" Guid="PUT-GUID-HERE">
<!-- create folders -->
<CreateFolder Directory="ProgramMenuVendorFolder" />
<CreateFolder Directory="ProgramMenuProductFolder" />
<CreateFolder Directory="ProgramMenuSubFolder" />
<!-- remove folder -->
<RemoveFolder Id="RemoveProgramMenuVendorFolder"
Directory="ProgramMenuVendorFolder"
On="uninstall" />
<RemoveFolder Id="RemoveProgramMenuProductFolder"
Directory="ProgramMenuProductFolder"
On="uninstall" />
<RemoveFolder Id="RemoveProgramMenuProductSubFolder"
Directory="ProgramMenuProductSubFolder"
On="uninstall" />
<!-- main shortcut -->
<Shortcut
Id="MainShortcut"
Name="My Product"
Target="[SomeInstalledFolder]app1.exe" />
<!-- shortcut in subfolder -->
<Shortcut
Id="SubFolderShortcut"
Name="mySubFolderShortcut"
Target="[SomeInstalledFolder]app2.exe"
Directory="ProgramMenuProductSubFolder" />
<!--
RegistryValue whichs serves as KeyPath
-->
<RegistryValue
Root="HKCU"
Key="Software\MyVendor\MyProduct"
Name="InstalledStartMenuShortcuts"
Type="integer"
Value="1" />
</Component>
</DirectoryRef>
<!-- shortcut directories -->
<Directory Id="ProgramMenuFolder">
<Directory Id="ProgramMenuVendorFolder" Name="MyVendor">
<Directory Id="ProgramMenuProductFolder" Name="MyProduct">
<Directory Id="ProgramMenuProductSubFolder" Name="MySubFolder" />
</Directory>
</Directory>
</Directory>

Related

.wxs only two file not getting deleted on uninstall

I am using wix installer for msi.
i am using default UI
<UIRef Id="WixUI_Minimal"/>
There are somany dll i am using for msi package. on installation all dll will be installed at
C:\Program Files (x86)\MyApp
while on uninstall all dll uninstalled or deleted except two dll
libssl-1_1.dll
libcrypto-1_1.dll
I found that identifier '-' is making problem on uninstalling and not getting deleted.
I tried custom action like this for deleting that folder itself, but it didn't work.
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id="ALLUSERSPROFILE" SourceName="All Users" />
<Directory Id='ProgramFilesFolder' SourceName="program files">
<Directory Id="INSTALLDIR" Name="MyApp" >
<Component Id="First.dll" Guid="GUID">
<File Id="first.dll" Name="first.dll" KeyPath="yes" Vital="no" Source="$(var.PATH)\first.dll" />
</Component>
....... All dll component place like above ......
</Directory>
</Directory>
</Directory>
<Property Id="INSTALLDIR">
<RegistrySearch Root="HKLM" Key="SOFTWARE\MyProj\MyApp" Type="raw" Id="APPLICATIONFOLDER_REGSEARCH" Name="Path" />
</Property>
<DirectoryRef Id='INSTALLDIR'>
<Component Id="UninstallFolder" Guid="*">
<CreateFolder Directory="INSTALLDIR"/>
<util:RemoveFolderEx Property="INSTALLDIR" On="uninstall"/>
<RemoveFolder Id="INSTALLDIR" On="uninstall"/>
<RegistryValue Root="HKLM" Key="SOFTWARE\MyProj\MyApp" Name="path" Type="string" Value="[ProgramFilesFolder]MyApp " KeyPath="yes"/>
</Component>
</DirectoryRef>

ICE91 Wix in perMachine Installer

I am trying to make a perMachine Installer that is able to create files and folders in the PersonalFolder and LocalAppDataFolder of each user. I am getting always the warning message "warning LGHT1076: ICE91: ..." as listed below. When I install as an Administrator the file and folder is created despite the warning, but when I try the installer as standard user no file and no folder is created on the user profile. What could be the solution?
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="PFInstallDir" Name="Example">
<Component Id="ApplicationFiles" Guid="12345678-1234-1234-1234-222222222222">
<File Id="ApplicationFile1" Source="notepad.exe"/>
</Component>
</Directory>
</Directory>
<Directory Id="CommonFilesFolder">
<Directory Id="CFInstallDir" Name="Example">
<Component Id="CommonApplicationFiles" Guid="12345678-1234-1234-1234-222222222223">
<File Id="ApplicationFile2" Source="notepad.exe"/>
</Component>
</Directory>
</Directory>
<Directory Id="CommonAppDataFolder">
<Directory Id="CAInstallDir" Name="Example">
<Component Id="CommonAppDataApplicationFiles" Guid="12345678-1234-1234-1234-222222222224">
<File Id="ApplicationFile3" Source="notepad.exe"/>
</Component>
</Directory>
</Directory>
<Directory Id="PersonalFolder">
<Directory Id="InstallDirPersonal" Name="ExampleDocs">
<Component Id="ApplicationPersonalFiles" Guid="12345678-1234-1234-1234-222222222225">
<CreateFolder />
<RemoveFolder Id="RemoveMyExampleDir1" On="uninstall" Directory="InstallDirPersonal"/>
<RegistryKey Root="HKCU" Key="Software\MyCompany\Documents">
<RegistryValue Name="MainExe" Value="1" KeyPath="yes" Type="integer" />
</RegistryKey>
<File Id="Notepad.MyExe"
Source="notepad.exe" DiskId="1" Checksum="yes">
</File>
</Component>
</Directory>
</Directory>
<Directory Id="LocalAppDataFolder" Name="AppData">
<Directory Id="InstallDirAppDataPersonal" Name="ExampleLocals">
<Component Id="ApplicationPersonalDataFiles" Guid="12345678-1234-1234-1234-222222222226">
<CreateFolder />
<RemoveFolder Id="RemoveMyExampleDir" On="uninstall" Directory="InstallDirAppDataPersonal"/>
<RegistryKey Root="HKCU" Key="Software\MyCompany\Example">
<RegistryValue Name="MainExe" Value="1" KeyPath="yes" Type="integer" />
</RegistryKey>
<File Id="Notepad.MyExe1"
Source="notepad.exe" DiskId="1" Checksum="yes">
</File>
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id="DefaultFeature" Level="1">
<ComponentRef Id="ApplicationFiles"/>
<ComponentRef Id="CommonApplicationFiles"/>
<ComponentRef Id="CommonAppDataApplicationFiles"/>
<ComponentRef Id="ApplicationPersonalDataFiles"/>
<ComponentRef Id="ApplicationPersonalFiles"/>
</Feature>
</Product>
-- Build started: Project: SetupProjectPerUsrMachine, Configuration: Debug x86 --
warning LGHT1076: ICE91: The file 'Notepad.MyExe' will be installed to the per user directory 'InstallDirPersonal' that doesn't vary based on ALLUSERS value. This file won't be copied to each user's profile even if a per machine installation is desired.
warning LGHT1076: ICE91: The file 'Notepad.MyExe1' will be installed to the per user directory 'InstallDirAppDataPersonal' that doesn't vary based on ALLUSERS value. This file won't be copied to each user's profile even if a per machine installation is desired.
Problem solved!
http://www.installworld.com/index.php?option=com_content&do_pdf=1&id=146
"If the resource must be copied to each user’s profile, then add functionality to your package to do this. To achieve this, add a current user registry key (i.e., just a dummy registry key) and make this registry key the key path of the component which contains the resource. This means that the resource will be copied to the next user’s profile as part of the MSI self healing mechanism, provided that advertised entry points exist for the product."
http://blog.bittercoder.com/2007/02/28/wix-shortcuts/
<Component Id="StandAloneApplication" Guid="C8D5DB05-2D68-40e8-88D1-EF5BEA18DBE1">
<File Id="SomeCompanySomeProductHostApp"
Name="SomeCompany.SomeProduct.HostApp.exe"
DiskId="1"
Source="....buildSomeCompany.SomeProduct.HostApp.exe"
Vital="yes">
<Shortcut Advertise="yes"
Id="SomeCompanySomeProductHostAppShortcut"
Directory="ProgramMenuDir"
Name="My Product"
WorkingDirectory="INSTALLDIR"
Description="SomeProduct Application"
Icon="HostAppShortcutIcon.exe">
<Icon Id="HostAppShortcutIcon.exe"
SourceFile="....buildSomeCompany.SomeProduct.HostApp.exe" />
</Shortcut>
</File>
</Component>

How to add to PATH variable with a WIX for everyone: admin and standard users?

I'm trying to set a global PATH environment variable along with my custom variable using WIX installer. I do the following:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="COMPANYFOLDER" Name="my company">
<Directory Id="INSTALLFOLDER" Name="app name">
<!-- blah blah -->
</Directory>
</Directory>
</Directory>
</Directory>
<DirectoryRef Id="TARGETDIR">
<!-- Add environment variables -->
<Component Id="idPathEnvSys" Guid=".....">
<Environment Id="idEnvPathSys" Name="PATH" Action="set" Part="last" System="yes" Value="[INSTALLFOLDER]" Permanent="no"/>
</Component>
<Component Id="idPathEnvUsr" Guid=".....">
<Environment Id="idEnvPathUsr" Name="PATH" Action="set" Part="last" System="no" Value="[INSTALLFOLDER]" Permanent="no"/>
</Component>
<Component Id="EnvVarsSys" Guid=".....">
<Environment Id="idEnvMyPathSys" Name="MYPATH" Value="[INSTALLFOLDER]" Part="all" System="yes" Action="set" Permanent="no" />
</Component>
<Component Id="EnvVarsUsr" Guid="....">
<Environment Id="idEnvMyPathUsr" Name="MYPATH" Value="[INSTALLFOLDER]" Part="all" System="no" Action="set" Permanent="no" />
</Component>
</DirectoryRef>
<Feature Id="ProductFeature" Title="!(loc.ProductName)" Level="1">
<!-- main components first -->
<ComponentRef Id='idPathEnvSys' />
<ComponentRef Id='idPathEnvUsr' />
<ComponentRef Id='EnvVarsSys' />
<ComponentRef Id='EnvVarsUsr' />
</Feature>
It compiles just fine but when I install it, for some reason it only adds the system variable. So in other words, if I run a command prompt as admin and do set path it shows my PATH addition. But when I do the same from a regular user account, it is not there.
What am I missing?

Wix installed app (and shortcut) shows admin symbol

After successfully creating and testing an application I've also manually created the installer for this app using Wix instead the ClickOnce provided by VS.
Anyway, the installation is successful, places all the registry keys in correct locations, same for files where they need to be, and the shortcuts (and all is cleaned up afterwards).
The issue is not critical, I'm just really picky :D
On the main exe file that the Wix setup is installing, and on the shortcuts that points to this, they have the little blue and yellow admin shield on the bottom right of the icons. The application does not require admin permissions to work properly, nor does the application actually bring up the UAC or run as admin anyway (unless explicitly done through right-click > Run as admin).
The question is how do I prevent the shield from being applied to the application and shortcut icons?
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:netfx="http://schemas.microsoft.com/wix/NetFxExtension">
<?include "Macros.wxi" ?>
<!-- Installation Settings -->
<Product Id="*"
Name="$(var.NameApp)"
Language="1033"
Version="1.0.0.0"
Manufacturer="$(var.NameCompany)"
UpgradeCode="$(var.GUID_Upgrade)">
<Package InstallerVersion="200"
Compressed="yes"
InstallScope="perMachine"
Comments="Windows Installer Package"/>
<Media Id="1"
Cabinet="product.cab"
EmbedCab="yes"/>
<MajorUpgrade DowngradeErrorMessage="A newer version of this software is already installed" />
<!-- .NET Framework Check -->
<PropertyRef Id="NETFRAMEWORK40CLIENT" />
<Condition Message="This application requires .NET Framework 4.0. Please install the .NET Framework then try again">
<![CDATA[Installed OR NETFRAMEWORK40CLIENT]]>
</Condition>
<!-- Installation files, folders, reg-keys, shortcuts, etc -->
<Directory Id="TARGETDIR" Name="SourceDir">
<!-- Program Files Folder -->
<Directory Id="ProgramFilesFolder">
<!-- Company Application Folder -->
<Directory Id="INSTALLDIR" Name="$(var.NameCompany)">
<!-- Main Application Files -->
<Component Id="CmpAppMain" Guid="$(var.GUID_CmpAppMain)">
<File Id="FileAppMainEXE" Source="$(var.PathExe)" Vital="yes" />
<RegistryKey Root="HKLM"
Key="SOFTWARE\$(var.NameCompany)\$(var.NameApp)">
<RegistryValue Name="installed"
Type="integer"
Value="1"
KeyPath="yes" />
</RegistryKey>
</Component>
<!-- Common DLLs for multiple apps -->
<Component Id="CmpAppLibs" Guid="$(var.GUID_CmpAppLibs)">
<File Id="FileDeviceDLL" Source="$(var.PathLibDevice)" Vital="yes" />
<File Id="FileUtilDLL" Source="$(var.PathLibUtil)" Vital="yes"/>
<RemoveFile Id="FileClrDevice" Directory="INSTALLDIR" Name="Comms.log" On="uninstall"/>
<RegistryKey Root="HKLM"
Key="SOFTWARE\$(var.NameCompany)">
<RegistryValue Name="Lib Path"
Type="string"
Value="[INSTALLDIR]" />
<RegistryValue Name="Lib Ver"
Type="string"
Value="1.0.0"
KeyPath="yes" />
</RegistryKey>
</Component>
<!-- Common Resource Files -->
<Directory Id="FolderResource" Name="rsc">
<Component Id="CmpAppRsc" Guid="$(var.GUID_CmpAppRscs)">
<File Id="RscOilDb" Source="$(var.PathRscOil)" Vital="no" KeyPath="yes"/>
</Component>
</Directory>
</Directory>
<!-- END - Company Application Folder -->
</Directory>
<!-- END - Program Files Folder -->
<!-- Start Menu Folder -->
<Directory Id="ProgramMenuFolder">
<!-- Start Menu Company Folder -->
<Directory Id="ProgramMenuCompany" Name="$(var.NameCompany)">
<Component Id="CmpLnks" Guid="$(var.GUID_CmpLnks)">
<Shortcut Id="LnkStartMenu"
Name="$(var.NameApp)"
Description="$(var.NameApp)"
Target="[INSTALLDIR]$(var.NameExe)"
WorkingDirectory="INSTALLDIR">
<Icon Id="IconApp" SourceFile="$(var.PathRscIco)" />
</Shortcut>
<RegistryKey Root="HKCU"
Key="SOFTWARE\$(var.NameCompany)">
<RegistryValue Name="Lnk"
Type="integer"
Value="1"
KeyPath="yes" />
</RegistryKey>
<RemoveFolder Id="RemoveStartLnk" Directory="ProgramMenuCompany" On="uninstall" />
</Component>
</Directory>
<!-- END - Start Menu Company Folder -->
</Directory>
<!-- END - Start Menu Programs Folder -->
</Directory>
<!-- END - TARTGETDIR -->
<Feature Id="FeatCore" Title="Core Application Files" Level="1">
<ComponentRef Id="CmpAppMain" />
<ComponentRef Id="CmpAppLibs" />
<ComponentRef Id="CmpAppRsc" />
</Feature>
<Feature Id="FeatLnks" Title="Start Menu Shortcut" Level="1">
<ComponentRef Id="CmpLnks" />
</Feature>
</Product>
</Wix>
Does the app have a manifest at all? I'm wondering if it's got a
highestavailable or asInvoker setting that means that it might
sometimes elevate, and I'm assuming from what you said that it doesn't
have a requiresAdministrator setting there.
A manifest is nearly always embedded in the exe itself, that's what
needs verifying. I'm guessing that the exe is being built with an
embedded manifest. No need to include it in the install.
Thanks for the info. The issue was with the manifest which wasn't being generated in the first place due to ClickOnce settings, and then once I've generated or made my own manifest for the project it's also not being embedded into the executable.
ClickOnce publishing places it within the installation directory with the installer it generates for you. Because I didn't want to use click once (and I assumed after reading about the manifest it would be embedded in exe) my app didn't have a manifest...
The only thing I'm curious about now is why the default behavior is to ask for admin rights (I thought that would be the worst thing to do by default).
Anyway... Thanks for the help

Wix - The directory is in the user profile but is not listed in the RemoveFile table

I want to create a subfolder in start menu, in my case PMFCompanyFolder already exists and contains links and I don't want remove when uninstalling my product.
So I don't push the RemoveFolder Property and I get :
ICE64: The directory PMFCompanyFolder is in the user profile but is not listed in the RemoveFile table.
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramMenuFolder">
<Directory Id="PMFCompanyFolder" Name="MyCompany">
<Directory Id="PMFProductFolder" Name="MyProduct"/>
</Directory>
</Directory>
</Directory>
<DirectoryRef Id="PMFProductFolder">
<Component Id="PMFProductFolderComponent" Guid="A13A7784-4C82-4CBE-9018-CEC6F266743B">
<RemoveFolder Id="null" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\Microsoft\MyCompany\MyProduct" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
</Component>
</DirectoryRef>
<Feature Id="ProductFeature" Title="" Level="1">
<ComponentRef Id="PMFProductFolderComponent"/>
</Feature>
You should pass the folder id:
<RemoveFolder Id="PMFCompanyFolder" On="uninstall"/>
It is safe to remove it on uninstall - pre-existing items will not be removed.

Resources