Dynamically Set WixUIBannerBmp Location Based on Property - user-interface

I have the following:
<WixVariable Id="WixUIBannerBmp" Value="FirstBanner.jpg" />
I want to be able to set the value of this WixVariable using a property that gets passed in when the installer is kicked off:
msiexec.exe /i MyInstaller.msi /l* install.log MYPROPERTY=SomeValue
So, based on the the value of MYPROPERTY, I set the value of the WixVariable to one of two (or more) values.
My initial stab at this proved unsuccessful:
<Property Id="BANNERLOCATION" Value="FirstBanner.jpg" />
<WixVariable Id="WixUIBannerBmp" Value="[BANNERLOCATION]" />
I get the following build error:
error LGHT0103: The system cannot find the file '[BANNERLOCATION]'.
Is what I am trying to achieve possible? This is simply branding the install based on a passed-in property, after all ...

No, what you are trying to accomplish is not supported by the Windows Installer. Sorry.

Related

How to properly structure UWP app icons in AppxManifest.xml file for a Win32 app converted using Desktop Bridge (Project Centennial)

I'm trying to convert my Win32 app into a UWP app using Project Centennial converter (i.e. Desktop bridge.)
After the app is converted, I need to adjust AppxManifest.xml file to ensure the following:
That all icon formats are configured properly.
That I properly specified app resources for: English (US), German, French and Russian languages.
So I followed this Windows 10 icon guide and created the following .png images that were all placed into the Assets folder. I got these:
And my AppxManifest.xml was structured as such:
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10">
<Identity Name="MyApp.Name" ProcessorArchitecture="x86" Publisher="CN=My Name, O=My Name, STREET="Street Address", L=City, S=State, PostalCode=12345, C=US" Version="1.2.3.4" />
<Properties>
<DisplayName>MyApp.Name</DisplayName>
<PublisherDisplayName>PublisherName</PublisherDisplayName>
<Logo>Assets\StoreLogo-50x50.png</Logo>
</Properties>
<Resources>
<Resource Language="en-us" />
<Resource Language="de-DE" />
<Resource Language="fr-FR" />
<Resource Language="ru-RU" />
</Resources>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14342.0" MaxVersionTested="10.0.14342.0" />
</Dependencies>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
<Applications>
<Application Id="MyApp.Name" Executable="VFS\Users\ContainerAdministrator\AppData\Local\PublisherName\App Name\RunFile.exe" EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements DisplayName="My App Name" Description="My app does this ... and this ..."
BackgroundColor="#aabbcc"
Square71x71Logo="Assets\AppNameSmallTile.png"
Square150x150Logo="Assets\AppNameMedTile.png"
Square310x150Logo="Assets\AppNameWideTile.png"
Square310x310Logo="Assets\AppNameLargeTile.png"
Square44x44Logo="Assets\AppNameAppList.png"
>
<uap:DefaultTile>
<uap:ShowNameOnTiles>
<uap:ShowOn Tile="square150x150Logo" />
<uap:ShowOn Tile="Square310x150Logo" />
<uap:ShowOn Tile="Square310x310Logo" />
</uap:ShowNameOnTiles>
</uap:DefaultTile>
</uap:VisualElements>
<Extensions />
</Application>
</Applications>
</Package>
So can someone answer these questions:
I'm somewhat confused about specifying icons for correct sizes and scaling. Did I do it correctly?
Do I need to specify scaling for Assets\StoreLogo-50x50.png icon as well?
And lastly, did I specify resources for 4 different languages correctly?
EDIT: While waiting, I tried compiling my AppxManifest.xml above with the assets I showed. But makeappx.exe tool gives me the following error:
MakeAppx : error: Error info: error C00CE015: App manifest validation
error: The app manifest must be valid as per schema: Line 22, Column
27, Reason: The attribute 'Square71x71Logo' on the element
'{http://schemas.microsoft.com/appx/manifest/uap/windows10}VisualElements'
is not defined in the DTD/Schema.
It evidently doesn't like the following icon sizes:
Square71x71Logo
Square310x150Logo
Square310x310Logo
But then even if I remove the icon sizes above from AppxManifest.xml, when I try to pack it with the following command:
"C:\Program Files (x86)\Windows Kits\10\bin\x64\makeappx.exe" pack /d "path-to-folder-to-pack" /p "path-to\MyAppxPackage.appx"
I get the following errors about naming assets for scaling:
MakeAppx : error: Manifest validation error: Line 22, Column 27,
Reason: The file name "Assets\AppNameMedTile.png" declared for element
"[local-name()='Applications']/[local-name()='Application']/[local-name()='VisualElements']"
doesn't exist in the package. If this file has multiple variations or
resource versions for language, scale, contrast, etc., use the /l
command line option to disable this validation. MakeAppx : error:
Manifest validation error: Line 23, Column 27, Reason: The file name
"Assets\AppNameAppList.png" declared for element
"[local-name()='Applications']/[local-name()='Application']/[local-name()='VisualElements']"
doesn't exist in the package. If this file has multiple variations or
resource versions for language, scale, contrast, etc., use the /l
command line option to disable this validation. MakeAppx : error:
Package creation failed. MakeAppx : error: 0x80080204 - The specified
package format is not valid: The package manifest is not valid.
Using suggested /l command as such:
"C:\Program Files (x86)\Windows Kits\10\bin\x64\makeappx.exe" pack /l /d "path-to-folder-to-pack" /p "path-to\MyAppxPackage.appx"
builds the package with a lot of warnings. But when I install it later, all icons in it seem to be blank.
There must be something that I'm missing here?
I see two main problems: you're defining tile images in the wrong place, and you don't talk at all about a resources.pri file.
The UAP schema is fairly involved, but also reasonably well documented, so make sure you respect its requirements. In particular, the uap:VisualElements element only has the image attributes Square150x150Logo and Square44x44Logo. If you want to define other sizes, they go in attributes on a child uap:DefaultTile element. Note that your uap:ShowOn elements' attributes include an incorrect Square310x150Logo that should instead be wide310x150Logo.
Once you sort that out, makeappx should succeed with your original command line, i.e., without the additional /l parameter. However this is not enough to make your alternate DPI images show up. For that you will need to create a resources.pri file. You can do this manually by using makepri.exe to create a template priconfig.xml, update it, then invoke makepri again to build the resources.pri from it.
Alternately, use a tool that will create an app package with a resources.pri for you (such as Visual Studio), and extract the resources.pri and add it to your package. Just make sure the locations you use in your package match the ones in the package created by the tool, as relative paths are stored in the resources.pri file. (As a bonus, you can look at the AppxManifest.xml that this creates to verify that your structure from the first part is correct. Just don't forget that apps using the desktop bridge will have additional namespaces and capabilities that Visual Studio won't offer.)

Wix FileSearch is not returning true for an existing file

I'm using the following FileSearch in my Bootstrapper (Wix 3.9 version), and it always returns false, even when the file exists:
<Wix xmlns="htttp://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"
...
<Fragment>
<util:FileSearch Path="%systemdrive%\SomeFile.txt" Varialbe="FileExists" Result="exists"/>
...
The log file has the following line:
File search: *******(some random guid), did not find path: %systemdrive%\SomeFile.txt
Setting numeric variable 'FileExists' to value 0
Am I doing something wrong?
Unfortunately, it's not quite so easy to use a runtime environment variable in that way. You first need to set a WiX property with the environment variable and then use that WiX property inside your Path attribute. So something like this (note the single % prefix):
<SetProperty Id="PROP_SYSTEM_DRIVE" Before="InstallInitialize" Sequence="execute" Value="[%SYSTEMDRIVE]" />
Then you should be able to use it in your FileSearch:
<util:FileSearch Path="[PROP_SYSTEM_DRIVE]\SomeFile.txt" Variable="FileExists" Result="exists"/>

Wix : Declare or set property after ResolveSource

I try to search a file in my project. The problem is I use the var "SourceDir" and it's work fine in install with UI but no with silent install.
I found it's because SourceDir is not setted before I try to used it in silent mode.
That's why I want to set my property after the "ResolveSource" action
<Property Id='CUSTOMCONFIGFILEEXISTS'>
<DirectorySearch Id='DirSearch' Path='[SOURCEDIR]' Depth='0'>
<FileSearch Id='FileSearch' Name='EasyFolderApplicationDesktopToolbar.exe.config'/>
</DirectorySearch>
</Property>
...
<Component Id="CustomMainExecutableConfig" Guid="A952C40B-0274-4EA8-8A48-0216395455CF" Directory="INSTALLDIR" NeverOverwrite="yes">
<Condition>CUSTOMCONFIGFILEEXISTS</Condition>
<CopyFile Id="CustomEasyFolderApplicationDesktopToolbarCONFIG" SourceProperty="CUSTOMCONFIGFILEEXISTS" DestinationProperty="INSTALLDIR" />
<!--<CopyFile Id="CustomEasyFolderApplicationDesktopToolbarCONFIG" SourceProperty="CONFIGFILEEXISTS" DestinationProperty="INSTALLDIR" />-->
</Component>
I already try this, with no result :
<Property Id='CUSTOMCONFIGFILEEXISTS'>
<DirectorySearch Id='DirSearch' Path='[Temp]' Depth='0'>
<FileSearch Id='FileSearch' Name='EasyFolderApplicationDesktopToolbar.exe.config'/>
</DirectorySearch>
</Property>
<CustomAction Id='SET_CUSTOMCONFIGFILEEXISTS'
Property='Temp'
Value='[SourceDir]'/>
...
<InstallExecuteSequence>
<ResolveSource After="CostInitialize" ></ResolveSource>
<Custom Action='SET_CUSTOMCONFIGFILEEXISTS' After='ResolveSource'></Custom>
<Custom Action="AlreadyUpdated" After="FindRelatedProducts">SELFFOUND</Custom>
<Custom Action="NoDowngrade" After="FindRelatedProducts">NEWERFOUND</Custom>
<RemoveExistingProducts After="InstallExecute" />
</InstallExecuteSequence>
I already see this How do I use the SourceDir MSI property in WiX?
But I don't know how to do...
Can you help me ?
That search is ultimately an AppSearch, and in a WiX-built MSI that's the first thing that runs, so your 'CUSTOMCONFIGFILEEXISTS' property has already been processed by the search, so it's false by the time that component results in the component not being installed and the copyfile not being done. So the reason for the failure is not ResolveSource - it's because 'CUSTOMCONFIGFILEEXISTS' is being set false and you are conditioning the component and the copyfile on that false condition.
IMO you are over-thinking this. Don't bother with a search. Just pick a component relevant to the file you want to copy (don't invent a transitional component for it with a condition). Add the CopyFile to that component with [SourceDir] as the source. If the file is there it will be copied, if not then it won't be.
Don't add an unconditional ResolveSource action because it will happen every time the an installer action takes place (repair, removing features, patches, uninstall) and that is generally unnecessary. In any case I am certain you do not need a ResolveSource. There is an implicit ResolveSource at first install or it wouldn't even know where the MSI was! Just do the copyfile as I suggested.

WIX public properties displayed on the UI

We have an installer created using WIX. As part of this install we would like to show the currently selected installation path. I thought this would be much easier than it is, apparently. I have tried using the a public property "INSTALLDIR" (I know we're not using Installshield, this value is a directory ID.)
<Directory Id="INSTALLDIR" Name="AcmeInc">
I can also see where INSTALLDIR gets set when running the install
MSI(EC:6C) Dir (target): Key: INSTALLDIR , Object: C:\Program Files\AcmeInc\
but when I try to show this on the UI using a Text attribute I get "...\." which doesn't even look to be a relative path.
I know there has got to be something simple I'm missing here.
Assuming you're using WiX 3.5 and the MajorUpgrade element - the following should work (I usually use APPLICATIONFOLDER instead of INSTALLDIR - but they should be interchangeable).
First, let's set ARPINSTALLOCATION as described on http://robmensching.com/blog/posts/2011/1/14/ARPINSTALLLOCATION-and-how-to-set-it-with-the-WiX-toolset
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CostFinalize" />
Now lets set the selected installation folder to the previous installation folder, if one previously existed that is.
<Property Id="INSTALLDIR" Secure="yes">
<RegistrySearch Id="FindInstallLocation"
Root="HKLM"
Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[WIX_UPGRADE_DETECTED]"
Name="InstallLocation"
Type="raw"
Win64="yes" />
</Property>
And during the UI sequence, we want this value to be set 'early'
<InstallUISequence>
<AppSearch After="FindRelatedProducts"/>
</InstallUISequence>

Detecting the presence of a directory at install time

In WiX DirectorySearch can be used to determine if a specific directory exists on the target computer. But I don't understand if there's a consistent way to determine that a directory does not exist.
For example:
<Property Id="INSTALLDIR" Secure="yes">
<RegistrySearch Id='InstallDir' Type='directory'
Root='HKLM' Key='Software\Company\Product\Install' Name='InstallPath'/>
</Property>
<Property Id='IS_INSTALLED' Secure='yes'>
<DirectorySearch Id='IsInstalled' Path='[INSTALLDIR]' />
</Property>
When both the registry key and the directory exist, the IS_INSTALLED property is set to the path returned by DirectorySearch.
When the directory does not exist, IS_INSTALLED appears to be set to "C:\".
Is a condition like:
<Condition>NOT (IS_INSTALLED = "C:\")</Condition>
a reliable way to detect that the directory was found? Is there a better way?
Answer: Here is WiX code based on mrnxs answer that I accepted
<Property Id="PRODUCT_IS_INSTALLED" Secure="yes">
<RegistrySearch Id='RegistrySearch1' Type='directory'
Root='HKLM' Key='Software\Company\Product\Version\Install' Name='Path'>
<DirectorySearch Id='DirectorySearch1' Path='[PRODUCT_IS_INSTALLED]'/>
</RegistrySearch>
</Property>
<CustomAction Id='SET_INSTALLDIR'
Property='INSTALLDIR'
Value='[PRODUCT_IS_INSTALLED]'/>
<InstallExecuteSequence>
<Custom Action='SET_INSTALLDIR' After='AppSearch'></Custom>
</InstallExecuteSequence>
Usually this happens when the property is used as a property-based folder. In this case the CostFinalize action automatically sets the property to a valid path (for example "C:\") so the folder can be used by Windows Installer.
Since this path is generated automatically, you cannot be sure that it will be "C:\" on all your client machines, so you shouldn't use this value in your condition. Instead, you can try this:
use a custom property for your folder
use a type 51 custom action (property set with formatted text) to set this property to a valid default path (for example "[ProgramFilesFolder]MyCompany\MyProduct")
use another property for the search
use another type 51 custom action to set the folder property to your search property
For example, if your search is IS_INSTALLED your folder can use IS_INSTALLED_PATH. IS_INSTALLED_PATH can be set to a default path and after AppSearch action you can set it to IS_INSTALLED if the search found something.
This way you can use for conditioning:
IS_INSTALLED
or
NOT IS_INSTALLED
Understaing AppSearch's RegLocator and DrLocator patterns can be a little tricky. I reccomend ignoring the condition for a moment and logging the install to verify that AppSearch is properly setting the properties you want. Fix the problems you find on that end first. When it works the property will be set to the value of the registry or the path to the directory.
Then you should be able to use:
<Condition>IS_INSTALLED/> <!-- it's not important what the value is, just that it exists -->
<Condition>Not IS_INSTALLED/>
Btw, I would avoid using the property INSTALLDIR. In my installers ( InstallShield ) that has special meaning as the main focal point of the installation.
Another approach could be this, in this you can continue the Install Sequence if you want to set the InstallDir to anywhere else, if the SystemDir and RegisteryDir is not same
<Property Id="RegisteryDir" Secure="yes">
<RegistrySearch Id='InstallDir' Type='directory'
Root='HKLM' Key='Software\Company\Product\Install' Name='InstallPath'/>
</Property>
<Property Id='SystemDir' Secure='yes'>
<DirectorySearch Id='IsInstalled' Path='[RegisteryDir]' />
</Property>
<CustomAction Id="SET_INSTALL_DIR" Property="INSTALLDIR" Value="[SystemDir] />
<InstallExecuteSequence>
<Custom Action='SET_INSTALLDIR' After='AppSearch'>
SystemDir AND SystemDir=RegisteryDir
</Custom>
</InstallExecuteSequence>

Resources