I'm using WiX 3.8 and trying to update my existing .config file on minor update but I'm having difficulties understanding how to achieve this.
I created a custom action to read values from the existing config file but can't figure out how to insert them into the new file?
I followed this article : Having WiX upgrade a config file with missing items (and subsequently this one : How can multiple elements be added to an XML config file with wix?) but
settings don't seem to be getting overwritten with previous values, can anyone give me a pointer please?
Basically I want to preserve the settings entered by the user during installation but overwrite the rest of the config if it has changed from version to version.
In a nutshell, this is a huge weakness of Windows Installer. Natively MSI has no support for XML operations. At it's core, Windows Installer treats files atomically where as an XML file is in a sense like an entire registry hive.
WiX adds in XML transforming capabilities to solve the first problem but the second problem is really impossible to solve. You'd have to have a copy of the original XML, the current XML and the proposed XML and then you'd have to have business rules to know what to merge and what not to merge.
In order to avoid this problem in the first place, what I personally suggest is to have 2 XML files. One installed and fully owned by the installer ( stock.xml) and one not touched by the installer and owned by the application (override.xml). Then in your XML reader have the contents of the override.xml take precedence over the contents of the stock.xml. In this way the installer can always do what it does best ) install files without having to do complicated data processing.
I currently do this with a combination of custom action and XmlConfig.
The custom action is run after CostFinalize and reads current values from the config file(s) and saves them in public properties.
string configFile = Path.Combine(session["INSTALLLOCATION"], "app.exe.config");
ExeConfigurationFileMap map = new ExeConfigurationFileMap();
map.ExeConfigFilename = configFile;
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
session["OLD_PRESERVEDVALUE"] = config.AppSettings.Settings["PreservedValue"].Value;
then I have an XmlConfig entry like below which sets the preserved values from the public properties:
<Component Id="RestoreOldPreservedValue" Guid="<GUID>" >
<Condition>OLD_PRESERVEDVALUE</Condition>
<CreateFolder/>
<util:XmlConfig
Id='RestoreOldPreservedValue'
Action='create'
On='install'
Node='value'
ElementPath='/configuration/applicationSettings/app.Properties.Settings/setting[\[]#name="PreservedValue"[\]]/value'
File='[#app.exe.config]'
Value='[OLD_PRESERVEDVALUE]'>
</util:XmlConfig>
</Component>
My next iteration will be to have the custom action create entries in the XmlConfig table directly.
The ultimate solution would be a WiX extension that populates a custom table and schedules a custom action which saves the values to be preserved after CostFinalize, and then another custom action that restores the values after the new config file(s) have been copied by the installer.
Related
What is the standard way to handle web.config files during major upgrade.I'm aware how the unversioned files are handled during upgrade,the file will not be replaced if the file has been modified by the user.
Is there a way to deal with the scenario where in there are new entries added to config file bundled with the latest installer that needs to be installed,and also retain the existing entries modified by the user during major upgrade in Wix.
The simple solution that a lot of my customers have liked is to not put user data in the web.config. Instead we use the AppSettings#file and ConnectionStrings#ConfigSource elements to specify an override file and keep the user data there. What MSI doesn't know about it won't tamper with. Now you don't have to be an MSI component rules wizard.
https://msdn.microsoft.com/en-us/library/ms228154(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/system.configuration.sectioninformation.configsource(v=vs.100).aspx
I know the question is for Wix, but just wanted to point out how much time commercial tools can save in such scenarios.
For example, using Advanced Installer you can read and load into an MSI property any XML values and then use the XML Files updater to write dynamic content in the files, at install(upgrade) time. (check the videos at the end of each article for a quicker overview)
Disclaimer: I work on the team building Advanced Installer.
Set the component to always overwrite and write a custom action to add the needed information to the config file.
The only way that seems possible is a custom action to merge the entries in the new file into the existing file, because you want data from the existing a new files. You would also need the upgrade scheduled late (after InstallExecute) so that the upgrade isn't an uninstall of the old product followed by an install of the new product.
If you are doing an upgrade (the WIX_UPGRADE_DETECTED property will be set by a MajorUpgrade element), so update the existing file, otherwise install the new one.
There might be a way to express the updates as an Xml transform, so something in the WiX util:xml tools might help do an update.
I am working on an installer with WiX which takes a "name" input from the user using a textbox in a dialog. This name will be used to name the product that i am installing.
But, i am not able to set the product name dynamically during install. Even if i use a custom action, the registry entry is getting created with the static name that i have provided earlier. This is ultimately leading to inconsistency.
Can anyone please help me regarding this?
This cannot be done in a custom action. It's true that you can set the ProductName property in a custom action (such as a type 51) early in the install and that will indeed change the name in the UI, but it will NOT change the name of the installed product - it will remain the same as the original value. For example, enumerating installed products will return the original name.
So the only good way to do this is to modify the ProductName in the MSI file before you launch it. You would have a launching program that modifies the MSI file and then installs it. The MSI file is a database that can be modified in the Property table to change the value of ProductName. This example will give you the general idea:
How do I add/update a property inside an MSI from the command-line?
but basically you open the open the database (MsiOpenDatabase or equivalent) then MsiOpenView with a SQL such as:
"UPDATE Property SET Property.Value = 'Your variable' WHERE Property.Property = 'CurrentProductName' "
then MsiViewExecute and close handles etc. Details depend on your preferred coding language environment.
This is not an ideal solution because if your MSI file is digitally signed you have tampered with it and it is no longer correct.
Another way is to generate a transform file, based on altering a copy of the MSI file. If you make a copy of the MSI file, and then do the alteration of ProductName as above you can then do an MsiDatabaseGenerateTransform() call which will generate a transform file, a .mst file, the difference between the two MSI files. You then install the original unaltered MSI file with a command line that includes TRANSFORMS=[the .mst file] which will update the ProductName and start the install.
None of this is very easy because Windows Installer products are not designed to have dynamic product names. Maybe historically and before Windows Installer setups this was more practical, but not in MSI setups.
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.
We have a CustomTable that we add to our msi when it is created. This custom table contains some binary data (xml files) that our deployment software reads when the msi is being uploaded to our deployment server.
We now have the requirement to support deployment of msp files. This means that should the user upload an msp file to our deployment server, our software should still be able to read the binary data in our CustomTable. However I've been unable to find a way to ensure that the msp file we create contains the same CustomTable.
I know that the msp contains the updated xml because I've used ORCA to see the updated tables and files. Unfortunately I don't seem to be able to programatically read data from our CustomTable because it doesnt (apparently) exist within the patch. Does anyone know of a way to access a CustomTable from an msp file?
Thanks - If clarification is needed let me know!
The patch (msp) is supposed to contain the changes to the previous MSI package only. If you don't change your custom table in a newer version of your MSI package, then it won't be mentioned in the patch. And that's expected.
When the patch is applied, it changes the initial MSI package (actually, patches it) and runs the reinstall. This means if your custom action which reads the custom table is NOT explicitly scheduled not to run on reinstall, then it will run and it will find the custom table.
Ok, so finally have a solution to my problem.
Since we don't have the original msi, we can create a blank msi in a temp directory and then apply the msp on top of this using ApplyTransform. Providing we create the expected table, and fill it with dummy data (fortunately we know in advance what rows are expected within this table), the process of applying the patch on top of our dummy msi allows us to successfully query the _Storages table and access the updated stream containing the new xml. This can then be processed during our msp upload.
Not pretty but it works.
i am using wix 3.0.i have a folder name "images".so i want to copy all the files from images into msi package.when i copy all the file and install msi to another PC then it did not bind the images. And when i use simple File Element then the files bind to msi package. so what should i do,
Reply me soon
Thanking You
Samir
Apparently you want to create an application that can generate a screensaver installer that includes a number of image files selected by the user — a sort of specialized InstallShield application just for creating screensavers installers.
I would first create a template MSI file (with wix
or something else) that does most of
the work. You don't know which files the user is going to select later on, so add a placeholder component with the ID "UserSelectedFiles". Distribute this
template MSI file with your application.
Then, when the user wants to generate a
new installer, start by copying the
template. Edit the copied msi so that it also installs the user selected files. You can do this with the .NET classes
you can find in the "deployment
tools foundation" API. This is a set of assemblies that come with wix; take a look at
C:\program files\Windows Installer
XML v3\sdk.
I have only used the DTF API for some simple stuff, like this example which counts the File table entries in the MSI:
var database = new Database(#"\path\to\some\file.msi");
var list = database.ExecuteQuery("SELECT FileName FROM File");
Console.WriteLine(list.Count);
Your case will be a bit more complicated. You will probably have to
generate a CAB archive that contains
the image files with the classes you
find in the
Microsoft.Deployment.Compression.Cab
namespace.
merge the CAB archive into the msi
with Database.Merge
Add new entries to the File table
of the MSI
Change the product code to something unique for each generated MSI
etcetera
To get an idea of the content of an MSI file and how it can be edited, I recommend you first play around a bit with the orca and msidb tools.