Leveraging heat.exe and harvest already localized file names and including them to msi using wix - wix

I have a question whether what i am trying to do is doable, and if the answer is yes how to do it.
I am new to the wix and have been doing some reading on how dynamically to include a folder to an installer and eventually i were able to do a task in nant that uses heat.exe to generate wxs file and latter adding newly generated wxs file to light and candle tasks. This allowed me to add the content of a folder to the msi and subsequently have that folder and its content to be installed.
My problem starts at the point where the folder that i am adding to the msi contains files that has their names already localized (this is a requirement).
When i am adding a file to the directory structure that has its name in Russian for example which is not 1252 codepage i am getting the error:
[exec] ......Templates.wxs(65) : error LGHT0311 : A string was
provided with characters that are not available in the specified
database code page '1252'. Either change these characters to ones that
exist in the database's code page, or update the database's code page
by modifying one of the following attributes: Product/#Codepage,
Module/#Codepage, Patch/#Codepage, PatchCreation/#Codepage, or
WixLocalization/#Codepage.
I tried to set Product/#Codepage to 65001 (UTF-8) however that did not solve the problem.
Eventually what i want to do is to have an ability to add a folder and its content to installer and someone else latter add any number of files that has their names localized into that folder. This way whenever the build runs and subsequent creation of msi happens, msi would contain that folder and its content.
Thank you very much in advance.

This is what WiX.chm says about setting the code page of the MSI database:
You can set this to a valid Windows code page by integer like 1252, or
by web name like Windows-1252. UTF-7 and UTF-8 are not officially
supported because of user interface issues. Unicode is not supported.
As long as you are going to have files named in different languages, that is, File table won't fit into a single Windows code page, you have very little choice. UTF-8 is said to be not officially supported, and this leaves a place for a hope.
If you set the CodePage attribute of the Product element to UTF-8, it will build successfully. And you can install/uninstall the resulting MSI with no problem. I have played a bit with it, and didn't face with any "interface issues" mentioned in that warning above.
Furthermore, I've googled the topic a bit, and found out that InstallShield allows setting the MSI database code page to UTF-8, which is reflected in their docs (search for 'utf-8' on that page). They have more to say about the potential interface issues:
However, some scenarios result in user interface issues. For example,
if an end user specifies the /qb command-line option or uninstalls the
product from Add or Remove Programs, Windows Installer uses very small
fonts to display the user interface text in a UTF-8 database.
They also want to stay on the safe side, hence this setting is false by default (no UTF-8, just ASCII).
So, finally, what would I do in your situation?
if that's a strict requirement to the installation package, use UTF-8 as code page
test all possible scenarios (install / uninstall / repair / upgrade / etc.) on all possible combinations related to localization (English OS, non-English OS, various combinations of current culture and culture UI)
if you face with those ghost "interface issues", show those to your stakeholders, decide whether this is what you can live with and publish known issues if you do
otherwise, recycle this idea and just thank your life for an opportunity to level-up your skills in this area :)
Hope this helps.

Related

Install two mutually-exclusive files with the same name to the same directory

My program deploys with a configuration option that I've chosen to expose as a feature. This option can be one of two values.
Each configuration changes a set of settings files. They have different input file names (for the sake of example, let's call it option1-config20-lv80.xml), but should be installed to the configuration directory as config20-lv80.xml. Each option has a prefix that should be stripped like that, which also means only one of these options can be selected for install at a time. However, even with conditions preventing the install of one feature when the other is selected, my output is littered with:
LGHT0204: ICE30: The target file 'config20-lv80.xml' is installed in 'path' by to different components... This breaks component reference counting.
How can I give my users the option to choose between these configuration options and get around my ICE30 issues without any negative side effects?
I saw an similar question answered, but I'm not 100% sure how to implement it in wix#, or if there are other ways open to me to achieve my goal without disabling ICE30 validation or creating 2 installers.
A bit rushed, have a look...
Milk & Honey Winnie: In cases like this I prefer to install both files with different names using two different components and then switch between them with an option shown in the application itself. On launch or in the preferences. Makes deployment simpler, it is already complex (section "The Complexity of Deployment"). The linked answer you refer to can work technically, as can more hacky approaches.
Alternatives: I have a long answer here on different ways to install settings files: Create folder and file on Current user profile, from Admin Profile ranging from eliminating the whole file and using internal defaults, to downloading settings files from the network or just relying on clouded web-service settings retrieval from a database. Not 100% match, but maybe give it a skim?
A related issue is when you have a settings file that regular users can't write to. This is a list of approaches for eliminating that condition: System.UnauthorizedAccessException while running .exe under program files.

Is there a way to set a property value to the formatted install date/time

Is there a way to set a property value to the formatted install date/time?
I'm in the process of creating an MSI installer for an old VB6 application we still depend on (yes, I know, upgrade before it dies).
I'm trying to add a backup folder for the user data files in the install-folder (not my application design, nor my application). Unfortunately every user of this application has their own copy of the data file installed on their system (dedicated machines per user), and the installer has the default file. I would like to create a backup folder so that I can manually (if necessary) go back and retrieve previous versions of the file.
What I'm thinking is
c:\program files (x86)\app*.mdb => c:\program files (x86)\app\backups\201804091125
This will be rushed. Please tell me what is not clear.
Custom Action: In order to implement exactly what you describe, you generally need a custom action. This is always unfortunate since they are very error prone: Why is it a good idea to limit the use of custom actions in my WiX / MSI setups?
Alternative?: If you ask me I would install the database in a component of its own, make the file the key file and set the component to permanent and never overwrite if key path exists.
In the WiX source: for the WiX component element, set these attributes: Permanent="yes" NeverOverwrite="yes"). I am not 100% sure what will happen if you do something stupid such as setting REINSTALLMODE="amus" during installation (force overwrite all files regardless of version). It has been a while since I tested the NeverOverwrite flag. But for normal deployment done the regular way the database file should be left alone and not overwritten.
Custom Action Overview: There are properties called Time and Date that are automatically set in the installer, but the Date property will generally contain characters that are illegal in path names. It is possible to just get the properties and replace the illegal characters. However, the date separation characters are probably different based on regional settings and hence hard to predict. Your code could get messy quickly and testing would be challenging (potentially many locales to test depending on distribution scope - a truly globally capable package is challenging).
I would rather get the date and time some other way - via some programming API call where I can determine what format the data comes back in. You also need to run this custom action elevated in deferred mode to ensure it doesn't fall over with access denied (insufficient user rights for operation). This is always quite a bit of clunk to set up and get working. Maybe try the alternative approach first?
I have long considered adding a custom action to abort the install if REINSTALLMODE="amus" has been specified. I would prefer that and the alternative approach described with "never overwrite" to a custom action doing all this copying.

How can the contents of an installed file marked Permanent="No" be preserved during an upgrade?

Installers of previous versions of our software include a Component File that was NOT marked with Permanent="Yes". Now, we wish to read the pre-upgrade contents of this file during the upgrade process, which will overwrite the file with different contents. Is there a good way to do this?
It would help if you said exactly what you were doing that would cause the file to be overwritten. Some major upgrades (is that what you're doing?) will do a complete uninstall of the product first, followed by a complete install of the newer product. If that's the situation then use a custom action sequenced before RemoveExistingProducts to back up the file somewhere so that your application can retrieve the content, or get the content you need before it's ovewritten.
If you are doing a major upgrade sequenced later (such as afterInstallExecute) or you are doing a patch then it is by no means certain that the file will be overwritten because file overwrite rules will not replace a file that has been updated since it was installed. If the application altered the file then this type of upgrade will not overwrite it:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa370531(v=vs.85).aspx
Or in the case that the file is unchanged since it was installed, change the dates so it appears to be modified, as described here:
https://blogs.msdn.microsoft.com/astebner/2013/05/23/updating-the-last-modified-time-to-prevent-windows-installer-from-updating-an-unversioned-file/
It's also not clear that Permanent=yes is what you want anyway - that would glue the file to the system forever. You may be thinking of NeverOverwrite, but it's typically not required if the app changes the files, and easier to decide at upgrade time (by changing dates) instead of committing to NeverOverwrite when it's sometimes unclear what the product may need in the future.
A comment refers to retrieving the previous version of the product during the upgrade. There are a number of ways to do this:
If you know the ProductCode of the previous version, MsiGetProductInfo (and equivalents in script etc) will return product version values or strings:
https://msdn.microsoft.com/en-us/library/aa370130(v=vs.85).aspx
Or if you'd rather not hardcode the value, MsiEnumProducts passing the UpgradeCode will return a list of installed ProductCodes. This technique is most useful if you have your own bootstrapper or UI where you want to show the user the current installed version.
In a WiX major upgrade the associated property (WIX_UPGRADE_DETECTED) is a list of the ProductCodes detected (usually a list of one) so you can use that to get the version of the product being upgraded. In a small vbscript example, something like:
set installer = CreateObject("WindowsInstaller.Installer")
and:
prodversionstring = installer.productinfo(WIX_UPGRADE_DETECTED, "VersionString")
will get you close.
Assuming this file is a configuration file such as an XML file, I find this is just a tough area of Windows Installer. You ship file version 1, the end user modifies certain attributes and then you ship file version 2 to which you want to preserve those customizations.
The problem is this is a very complex merge. It works somewhat OK if you only care about 1-2 attributes but if the answer is I need to preserve all of it then you are stuck between losing all the customizations or not getting the changes from version 2 of the file.
You could write extensive custom actions to do all this during the installer but I propose there is a better way: Have 2 files.
1 file that is owned by the installer and can always be safely overwritten and 1 file that is owned by the application that overrides are stored in. Think of it like a transformation file. The installer doesn't know about this file so it never overwrites or deletes it. (The very definition of user data from an MSI perspective.)
For example the .NET framework Web.Config schema AppSettings element has a file attribute that was designed to support this nicely.
Specifies a relative path to an external file containing custom
application configuration settings. The specified file contains the
same kind of settings that are specified in the , , and
elements and uses the same key/value pair format as those
elements. The path specified is relative to the main configuration
file. For a Windows Forms application, this would be the binary folder
(such as /bin/debug), not the location of the application
configuration file. For Web Forms applications, the path is relative
to the application root, where the web.config file is located.
Note that the runtime ignores the attribute if the specified file can
not be found.

wix installer ice03 Invalid Language Id

I have a night build which runs on different machine then mine
on my machine i can compile the installer and use the msi without a problem
however on the night build machine i get
C:\Builds\73\Tools\AppInstaller\src\AppInstaller\APPExportReleaseDir.wxs (693): ICE03: Invalid Language Id; Table: File, Column: Language, Key(s): filAAED19CB2C0BBE304CDB8EB1AAF7473F
The file which is showing that is system.windows.interactivity.xml
Can you please explain what are ICE03 errors and how can I fix this ?
It says here to remove the Language or set it to "0"
http://softwarepackaging.co.uk/iceerrors but there is no explanation.
ICE rules are Internal Consistency Evaluators - essentially a series of self-diagnosis scripts checking database
referential integrity, database consistency, data type and schema
compliance as well as running several content validation tests or
logical tests on the whole MSI (which is really an MS-SQL database in disguise).
DB Integrity: In other words you check the content of the database against the database schema and a number of content validation rules and determine if the database is objectively speaking containing what it is supposed to, and that it is connected in correct ways.
Sanity-Checks: Some further checks move beyond simple schema and type checking and perform sanity-checks relating to how the application is actually being installed. Certain files should not go to certain locations, some registry keys should never be written to, and similar checks. Custom checks can be created and added as QA steps for corporate deployment.
Custom Actions: The checks are written as custom actions (essentially MSI's mechanism for running executable, custom installation logic during installation) and inspect and evaluate the database during a Validation run. The Windows SDK tool Orca is capable of running validation on any MSI file as illustrated in this picture:
ICE3: Your particular error message relates to ICE03 - it checks several different things, but this checkpoint is relevant here (please visit the link to see what else it checks):
Invalid Language Id - The database contains an invalid numeric Language Identifier (LANGID). See the Language data type. See Language Identifier Constants and Strings. For example, 1033 for the U.S. and 0 for language neutral.
In your case you need to remove the language specifier altogether if the file is language neutral, or fix your WiX source file to set an appropriate language code. Here is an example from the MSI file installing Orca (1033 is English, most files are unspecified, meaning they are language neutral):
Links:
Wix Custom Dialog Error : ICE03: Not a valid foreign key Visual Studio 2019
WIX Error - ICE03: Not a valid foreign key
ICE03: String overflow (greater than length permitted in column); Table: CustomAction
https://learn.microsoft.com/en-us/windows/win32/msi/ice03
WIX Toolset 3.11: Including es-419 language DLLs gives ICE03 error. (.NET 4.8)
Do you really even need to include that file in your MSI? Typically xml files with the name of the assembly are just useful to developers as SDK documentation and doesn't belong in a shipped product.
For that matter, did you even author the wxs or are you using some form of dynamic authoring at build time? If so, add this to my list of reasons of why this is an anti-pattern. (Don't worry, almost no one else wants to believe me either. )
For more information on ICE's ( basically unit tests ) see:
MSI Tip: Authoring an ICE using C# / DTF

Wix custom action is failed to load the dll file?

I am trying to do a custom action at the time of msi installation. But the dll required for my custom action is depends on other dlls. At the time of installtion it is giving the error like "a dll required for this install to complete could not run".How can I load the dependent dll files for this custom action to run properly.
The code that I am using is
<CustomAction Id='CheckingPID' BinaryKey='CheckPID' DllEntry='ValidateKey' />
<Binary Id ='CheckPID' SourceFile='$(sys.CURRENTDIR)\LicenseKeyClient_32d.dll'/>
<Binary Id ='CheckPID2' SourceFile='$(sys.CURRENTDIR)\curllib.dll'/>
<Binary Id ='CheckPID3' SourceFile='$(sys.CURRENTDIR)\libsasl.dll'/>
<Binary Id ='CheckPID4' SourceFile='$(sys.CURRENTDIR)\openldap.dll'/>
The files that you add in binary table usually get extracted with temporary names during the installation, so your DLL will not be able to locate the other DLLs you add next to it.
A workaround is to add those DLLs as normal files in the Temp system folder and delete them when the installation ends. This has the limitation that you need to set your custom action as deferred, so it executes after the files are installed, thus your DLLs get copied to Temp folder.
I don't know if wix has a support for temporary files, similar with the one present in Advanced Installer, but if not you could try to write a custom action to simulate it. Basically what Advanced Installer does is to extract those files in the Temp folder the moment the MSI is launched, and also deletes them when the installation is complete. This has the advantage that you can use the temporary files and in immediate custom actions, i.e. well before the files from your package are installed.
Despite Bogdan's excellent answer, allow me to add my 2 cents:
It looks like you are dealing with some form of license key validation? The best way is generally to deal with your license keys in the application itself, unless you want it written to HKLM instead of HKCU - in which case you might need the temporary admin rights generally acquired during installation.
You can also open a HKLM key for writing during setup, and write it from the application though this is generally frowned upon security-wise. This allows you to write a single license key for all users directly from the application.
The application features more flexibility and control of the process of registering your license key, and crucially an easy way to run the process again. From my perspective this is almost always needed for a serious application - often due to trial versions with the eventual need to register the license key at the end of the trial period from within the application itself - instead of uninstalling the application and reinstalling, or running the setup in repair / maintenance mode - which seems extremely clunky.
I have described this issue previously in some more detail: Reasons to deal with licensing in the application rather than the setup.
I'll also add that WiX DTF .NET custom actions really simplify this problem by allowing you to embed content into the self extracting custom action package and make them available in the current directory at runtime. Very easy.
But yes, Glytzhkof is correct. Any licensing / DRM done inside of an MSI is easily defeated. It's best to do this in the app or both. For example I've worked at companies where it's a share responsibility. You can enter one now or later. I've also worked at companies where the license key had bits embedded in it that drove feature selection. It gets complicated fast so try not to have to go down that road.