To give you a background - I have a 4 MSI's which comes from our vendor and this has to go to our company servers (we are looking at around 3500 servers). As of now, my counterparts are managing this using vbs, ps1 scripts. But the problem with the script is that everytime an update comes we have to worry about uninstalling the existing package before running the new one and a ton of hardcoding.
I want to automate the whole process (with very less hardcoding) by setting up a WIX script to package all the 4 MSI's together. I read about the WIX bundle and used that to create a single MSI. But now there are lot of a variables to be passed to the 4 MSI's, so I thought of using custom actions to set these variables based on the environment/machine where the MSI is running. But I cant make custom action to work? Am i missing something?
A little bit of googling and I saw something like there are no CustomActions in Bundle? can someone confirm?
Also if there are no CA's what are my options? how can I manipulate the variables to be passed on to the 4 MSI's? Most of them needs to be set based on the machine its being run (like install path, user id's, app pool id's etc).
There is a fourth option, a useful lightweight hack, identified by Vijay Kotecha (see http://vijayskotecha.blogspot.com/2013/07/wix-bootstrapper-custom-action.html),...
Essentially, build an <ExePackage> around a pass-through .bat or .cmd batch file. The batch/command file contains the single line '%*' which re-executes all of the command line arguments as a first class command.
Thus:
<ExePackage ... SourceFile="SourcePath\WixCustomAction.cmd"
InstallCommand="my_custom_action.exe my_custom_parameters" />
<ExePackage ... SourceFile="SourcePath\WixCustomAction.cmd"
InstallCommand="my_next_action.exe my_next_parameters" />
Where WixCustomAction.cmd is a file containing only '%*'.
These <ExePackages> can be placed into the <Bundle><Chain> successively as needed using different InstallCommands as needed.
As I see it, you have three options:
Depending on what information you need, you can use the WixUtilExtension to perform simple tasks such as reading registry keys and performing file searches, which you can then pass the results to your installation packages as properties.
Implement custom actions in the individual installation packages themselves (not in the bundle).
Write your own custom bootstrapper application to determine all the properties you need to set, and then pass them to your installation packages. This is more complex than the #1 and #2, but if your interested the following links should get you started:
introducing managed bootstrapper applications and
write a wpf wix installer
Related
We have an WPF based external UI application i.e setup.exe which wraps the msi's developed in WiX. We have a requirement that some components/msi's requires multiple instance support and some don't require multiple instance support but all components are part of single package.
We pre-defined InstanceID's, ProductCode and UpdgradeCode for each instances during build time. Because creating instance ID on the fly create complexity in the patch/upgrade scenarios.
We want Setup.exe to do 2 things,
1. It should lookup the target computer and detect if any component is installed already. To do that Product code required.
2. It should automatically detect the msi's copied into the path and lookup the InstanceTransforms exist or not in the msi. If InstanceTransforms found then it should fetch the Instance ID's defined. Because we don't want to keep any business logic in Setup.exe. Reason is to avoid recompile Setup.exe every time we change the msi.
Now Setup.exe will know whether the msi requires instance ID or not based on that it will invoke the msi and pass commandline parameters. It's kind of plugin mechanism to avoid regression.
Issue:
We could not retrieve the InstanceTransforms from MSI maybe because it's not a property. We have checked the _Storages table and property table but we could not figure it out.
How can we retrieve the InstanceTransforms element and it's Instance definition (InstanceID and ProductCode) in the msi?
Please advise.
#Christopher Painter
I don't follow your question exactly. In general you can use the WiX DTF Microsoft.Deployment.WindowsInstaller library to do queries against the MSI database and MSI API. You can query the Property table to get the ProductCode then query installed products to see if it's installed. To see if the MSI has embedded transforms available you can query the _Storages stream, apply the transform and then read the properties for those also.
I'm adept at WPF/MVVM development if you want to hit me up for more detailed consulting.
I have created a Wix Installer which has following structure
It has Bootstrapper
It has MSI1
It has MSI2
The task of Bootstrapper is to provide options to user as which
application he wants to install. Based on his selection MSI1 or MSI2
will be launched.
Now both MSIs need to collect information of Database.
My requirement is that if I collect DB information from MSI1, I want to pass that information to MSI2 from Bootstrapper(as after MSI1 user will choose MSI2 from Bootstrapper)
We don't want the end user to provide this database information again and again.
Please note that we cannot collect the database information from Bootstrapper itself, as the user may go to installer source and execute MSI1 or MSI2 directly.
Any one has idea how we can store values of variables across Bootstrapper and MSIs getting called from that Bootstrapper?
Write that information to a well-known registry location during the installation.
Here's a resource to the "remember me pattern" blog post that Rob has written. I know it's bad practice to just link a blog post that may not exist in the future but I also don't want to just copy most of it.
The basic idea is to save properties that may be defined by the user at run-time into the registry. When you run the installer again, you can try to read that registry location and load up the properties from a previous run of the installer so you already know what the user is going to write.
In this case, your 2nd installer will know about the well know registry location that the first installer will write the db information to. Now the 2nd installer can read this information when it is run afterwards and use it during hte installation which means the customer doesn't have to re-enter the same information for both installs.
You can combine this with custom actions to encode sensitive information and decode it at run time. This is also a very commonly used technique for remembering the install directory of a product as this is something that is commonly changed by the user at run-time.
I reread the question and realized that getting the information from the bootstrapper is not something you can do but I'll leave this part of my answer here anyways. [I think you could gather the information in the bootstrapper and pass it to the MSIs. Just write that information during the install to a registry location and you can read it if it exists when installing if the user runs just from the MSI]
Alternatively you can update your bootstrapper's UI to gather this information and pass it in to both installers. This requires a bit of research into how the bootstrapper application works and how it generates its UI and stores properties. And again, you should use the remember me technique to read the already entered properties from the registry if they exist so you can pre-populate fields with the previous values on subsequent runs of your installation.
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.
i have multiple environments, build, test, prod etc.
i'd like to create a single installer in wix, which can hold multiple machine.config files. then based on the feature value passed in through the command line only one file will be installed.
conceptualy it sounds easy but i receive an ICE30 error stating that
"ICE30: The target file 'btfrgsa_|[FILENAME]' is installed in '[TARGETDIR]\NETFRAMEWORK40FULLINSTALLROOTDIR64\CONFIG\' by two different components on an LFN system: 'MachineConfigs.WS' and 'MachineConfigs.APP'. This breaks component reference counting.".
what would be the best way to do this?
thanks
Semaj
A couple of approaches:
Create several features and components with files of different names ( 1.config, 2.config ) and then use the CopyFile element to cause x.config to be copied to the real file name. ) This will result in x.config and real.config being deployed but it's harmless and the uninstall will work cleanly.
Identify the differences between the x.configs and use XML changes to apply them at install time using xpath statements.
The installer I'm writing using WiX 3.0 uses a RegistryValue element to modify an existing registry value (originally written by our main product). I'm trying to figure out a way to restore the registry value when the user uninstalls my utility. I'd like to avoid using a custom action, but that might be the only recourse? TIA.
I did this. The registry value in question was the application associated to a file extension, but it could be any registry value.
My first idea was to use a "custom action" for install and uninstall to preserve
and restore, respectively, the associated regy state. This
seemed simple enough.
I created a setup project in VS2008 and built the CA's as javascript files. The "on install" script grabbed the existing regy value and stashed it into a well-known place. The "on uninstall" script would look in the well-known place, and then put the value found there, back in the original location.
Easy, right?
There were two problems:
the script that ran during install, to preserve the pre-existing registry value,
runs AFTER the registry has already been updated
with the values for the newly installed thing. So it preserved the new setting instead of the setting that was there before the MSI ran. Not useful.
The script that runs during uninstall, runs AFTER the registry values, and in fact the entire directory subtree,
have been deleted. Including the stashed value. So it had lost its state.
To solve that I wrote another script that
re-orders the custom actions so they run at the proper times.
There's actually one more twist. Obviously, the "Restore" script (on
uninstall) won't work if it is run after the registry entries for the app have been deleted. I can't remember now, why... but I also determined that this script could not run before that. Somehow that wasn't working either.
So, I modified the MSI to run the restore script
twice. In phase 1, it transfers the stashed value to a "parking lot" in the registry.
Then the application's Keys and Values in the registry get deleted, but the parking lot remains. In
phase 2, outside the transactional protection, the restore script retrieves the state from the parking lot, restores
the file association, and then deletes the parking lot.
I can't remember exactly why I needed to do this in 2 steps, but I remember fighting with it for a while before coming up with that solution.
The way it works in development:
set the on install and on uninstall CA's in the VS project
build the VS Setup project
run the post-processing script that modifies the MSI.
When using the MSI, it's a little more complicated than I originally thought but it works.
If you are using WiX, you may have more control over the time and ordering of the steps, so may not need that post-processing step.
Finally, you said you wanted to avoid a CA. To me, CA's are avoided because they are painful to produce in C++, and producing them in .NET is often inappropriate. But, it's pretty simple to use Javascript for CA's. Some people think script is the wrong tool for the CA job. I think that's wrongheaded. I think script is a very good tool for this purpose. And once you can accept script as a good tool, then you don't need to hold your nose about creating a custom CA.
The registry table is incapable of writing a registry value during an uninstall so it is correct that this must be done via custom action. I am of the opinion that using script is bad. Whether you choose to listen is up to you. I can tell you that just the other day I was called into to trouble shoot a situation where some wrote a vbscript CA that was failing because the file system object had been unregistered as part of a security lockdown process.
I suggest C++ or C#/DTF depending on your needs.
Faced with similar issue, need to update the registry value on install and restore to previous value on uninstall.
Is the only solution possible is creating a custom action for this purpose?
I found a extension for wix that has fucntions for this purpose
link to wix extensions