Wix: Add a sequential registry key - wix

I am writing an installer using Wix 3 which installs a plugin to a 3rd party application. The application uses a group of registry keys to detect installed plugins. Each plugin is required to add a new subkey with a numerical name from 0-9 to the HKEY_LOCAL_MACHINE\SOFTWARE\Vendor\App\Plugins. The key contains a single string value which is the ProgId of a COM component which the application will instantiate. So for example I would need to add the following key to install my plugin:
HKEY_LOCAL_MACHINE\SOFTWARE\Vendor\App\Plugins\0
REG_SZ ProgId "MyCompany.MyPlugin"
However, if the 0 key already exits, my installer needs to be smart enough to create key 1 instead of 0 and on down through key 9. Is there a way to achieve this using the Wix syntax? I know I can write a custom action to do this, but if at all possible I would like to avoid it.

You could use RegistrySearch to find the first empty value. But it would be easier to use the approach I describe at http://www.joyofsetup.com/2007/07/01/semi-custom-actions/.

Related

Wix Bundle Upgrade Without Resupplying Password

I have authored an MSI that requires an account and password to be supplied to install and start a Windows Service, so I've added a couple of properties to my 'Product' element for that. I have a requirement that these properties should not need to be resupplied to perform an upgrade and since one of those properties is a password I don't want to persist it's value to the registry (or anywhere). I have achieved this with
<MajorUpgrade ... Schedule="afterInstallExecute" />
Now I'm authoring an exe bootstrapper to bundle this MSI with it's prerequisite, similarly the exe will need to receive values for the properties and pass them to the MSI, so I've added some 'Variable' elements to my bundle and passed them to my 'MsiPackage' element with child 'MsiProperty' elements. And this works great during first install when the values are supplied, but now when I want to upgrade the bundle without supplying values for the properties the bootstrapper passes empty values to the MSI. Something equivalent to...
msiexec /i MyMsi.msi ACCOUNT= PASSWORD=
Which breaks the upgrade. The new version of the Windows Service is attempting to start with an empty value for account and password.
Is there a way to conditionally pass variable values to MSI's as property values?
What happens when both the 'Variable' element attributes 'Hidden' and 'Persisted' are set? Will the password really be hidden?
Is there another pattern I don't know about / haven't thought of?
Something like this doesn't feel like it should require a custom action.
On upgrades you can disable the <InstallServices> standard action.
In one of the products I work with I have the following:
<!-- http://stackoverflow.com/questions/15965539/how-to-only-stop-and-not-uninstall-windows-services-when-major-upgrade-in-wix don't change service config on upgrade -->
<DeleteServices>NOT UPGRADINGPRODUCTCODE</DeleteServices>
<InstallServices>NOT WIX_UPGRADE_DETECTED OR V6INSTALLED</InstallServices>
Because I didn't want to reset the start types of the services is the user has decided to start them manually instead of automatically (it's an option in our product to set this).
By doing this, it should leave the service already installed as is when upgrading instead of trying to re-add it with empty parmeters for the login user/pass
An alternative is to make a salted hash of the password and store the user and salted&hashed password into the registry. On upgrades you can read these values decode the password and use those values.

How can I determine the ProductCode for the included products in my WiX ManagedBootstrapperApplication at runtime?

I have a managed bootstrapper application with a bundle that includes four products. Each of the products are defined with "*" for the Id and have a unique UpgradeCode.
In my managed bootstrapper, I need to get the ProductCode (GUID) for the products that are part of my bundle. The detect event handlers' event args (such as DetectPackageCompleteEventArgs) have a PackageId value which is the name of the msi (ex: the "Common.msi" package has a PackageId of "Common.msi").
I also checked the BootstrapperApplicationData.xml file, which has the ProductCode and UpgradeCode of the bundle itself, but not in the WixPackageProperties nodes for the individual products.
In the case of a related package being detected (upgrade), the ProductCode is included in the DetectRelatedMsiPackageEventArgs, but that event doesn't fire when the running the installer for the currently-installed version (ie: clicking "Modify" on Add/Remove Programs)
How can I determine at runtime in my MBA what the ProductCode is for the included products?
Additional info:
Ultimately, I'm using the product code to get the ARPINSTALLLOCATION value using MsiGetProductInfo(productCode, "InstallLocation", strbuffer, len); This is specifically for the case where the user is "modifying" an existing installation (ie: running the same installer version as an already installed bundle -- not an upgrade) and I need to determine the folder to which they installed originally.
Short answer: The product code isn't provided to the BA. But an explicit registry entry (for example, following the "remember property pattern" that Rob discusses) is going to be more reliable and easier to implement than ARPINSTALLLOCATION/MsiGetProductInfo.
Longer answer: Burn runs detection the same way in all "modes" but explicitly doesn't send package-level detection messages when the version of the package in the packages is the same that's installed on the machine.
The place for this kind of static data is in the BA manifest but in v3.8 it doesn't include the product code. Please file a feature request.

AllowAllTrustedApps registry key not available in Windows RT

I have tried to find the registry key
HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\Appx\AllowAllTrustedApps
in Windows RT and it appears to be missing. I got as far as the Windows folder, but the Appx folder isn't there. Am I missing something? Does the machine need to be connected to a domain to see this part of the registry? Or can I simply add the subfolder and key and expect it to work?
It turns out that adding the key and value works.
1) Make sure the key HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\Appx exists. If it doesn't, add it.
2) Make sure the 32bit DWORD HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\Appx\AllowAllTrustedApps exists. If it doesn't, add it.
3) Set the value of that DWORD to 1.
4) Voila! You can now side load your Windows Store App and/or inject a custom data file without a license xml file.
http://technet.microsoft.com/en-us/library/hh824882.aspx

How to execute custom action present in MSI without invoking installation?

Given:
Wix 3.0 is used to create MSI.
The product consists of multiple feature.
Each feature has few sub features. It’s a standard MSI feature tree.
Each feature or sub feature depend on multiple external components.
E.g. .NET 4, ASP.NET etc
Custom action written in C# using Wix 3.0 SDK processes these
dependency and evaluates if components are present or not for a
given set of features.
At time of install if dependent component is missing for given
selection of features, installation fails.
To achieve:
Ability to execute prerequisite check, which is already done in MSI as custom action during installation, without installing MSI on a given machine.
Failed Attempts:
1)
Custom action have function signature like this
[CustomAction]
public static ActionResult ProcessFeaturePrerequisite(Session session);
In order to get session object I used following API present in Wix 3.0 SDK
Session session = Installer.OpenPackage("Pathto\\Product.msi", true); // true doesn’t install it. Also tried with false, but didn’t work.
When I invoke the above method with above session following things fail.
session.Features["SomeFeature"].CurrentState;
This throws exception.
System.ArgumentException was unhandled by user code
Message=Feature ID not registered. SomeFeature
Source=Microsoft.Deployment.WindowsInstaller
StackTrace:
at Microsoft.Deployment.WindowsInstaller.FeatureInfo.get_CurrentState()
Also below critical API which determines prerequisite status always returns false.
session.EvaluateCondition(prereq);
2)
I know a command line way to specify features to the above MSI and install it. It goes like this
msiexec /i "Product.msi" ADDLOCAL=ALL REMOVE="Foo,Bar "
I couldn’t find any API in SDK which allows me to pass additional params which returns session object without starting installation. My guess is passing such param will make session.Features more valid.
Questions:
So how do I achieve above goal?
Is there
any API in Wix SDK which allows me to call custom action without
invoking installation?
any way to invoke custom action from command line for a given MSI
without installing?
any way to make Wix to change MSI into accepting a command string
containing custom action name which only evaluates the action?
any better way to do the same?
I suppose you're trying to solve the problem with the wrong tool. As far as I understand, you would like to check the installation prerequisites from inside a certain tool, but not from the installation. As long as the functionality is implemented as a custom action in the MSI package, you'd like to utilize that functionality in order not to duplicate the code.
I would choose a different way in your situation:
Extract the functionality which actually checks for prerequisites into a separate assembly, e.g. checkprereq.dll
Refactor your custom action to reference checkprereq.dll. Note that you'll have to add checkprereq.dll to your Binary table as well as the customaction.dll. You should divide the responsibility here: the custom action part works with MSI stuff - in your case, it's defining which prerequisites to check based on the combination of features a user selected - and the functional part - the actual prerequisites verification, which is done by checkprereq.dll
Use checkprereq.dll separately when you need to check prerequisites not triggering the installation process
The attempts you've outlined here demonstrate an important false assumption: the session object at install time is the same as the installation object you get by just opening the MSI database for read only purpose. IT'S NOT TRUE! Actually, I doubt it makes any sense to reference the session object outside the installation transaction. As its name states, it is an installation session, that is, available in process - not a static thing.
The MSI package should be treated just as a database when it is just a file and not a running installation. Hence, only static information living in MSI package can be queried and used when you just open it for reading and not installing. I mean you can query the Feature table, for instance, but don't expect it to contain information which makes sense in installation time only, like whether a user chose a feature for installation or not.
Hope this makes sense and shows you the right direction.

SetARPINSTALLLOCATION wrong value

<Custom Action="SetARPINSTALLLOCATION" After="InstallValidate">NOT (REMOVE="ALL" or REMOVE="ProgramFiles")</Custom>
I have this custom action called in InstallSequence, action that must populate in upgrade INSTALLDIR from registry, and this custom action is called in fresh install also.
In majority of cases this action, for fresh install, return one location from local machine, and the setup works without issues, but on a customer machine the value returned is \SomeDir\ and the setup faills.
How could I use this custom action to work correctly?
After some analysis I found that the functions works correctly, but the system contained an invalid registry entry, and for that value the result of SetARPINSTALLLOCATION was a wrong value.
The wrong value in registry was generated by the existence of record InstallLocation directly on uninstall registry key, without any guid like parent.
Thanks.