Wix Burn: Unsetting variable upon failed registry search - wix

In my Bundle project, I'm searching registry for a version and setting a variable:
<Variable Name="Installed_Ver" bal:Overridable="yes" Type="version" Value="0.0.0.0" Persisted="yes" />
<util:RegistrySearch
Id="Self_Ver"
Root="HKLM"
Key="Software\MyCompany\MyProductName"
Value="Version"
Variable="Installed_Ver"
Format="raw"
Win64="yes"
/>
And using this in Condition as:
<util:RegistrySearchRef Id="Self_Ver" />
<bal:Condition Message="A recent or same version of this product is already installed on this machine. Please contact product support for more information." >
<![CDATA[WixBundleInstalled OR (WixBundleFileVersion > Installed_Ver)]]>
</bal:Condition>
This works fine if HKLM\Software\MyCompany\MyProductName\Version exists. But if this registry doesn't exist then registry search would fail and it is Unsetting the variable 'Installed_Ver'. This causes the condition wrongly evaluating to false.
By defining , I tried to set a default value i.e. "0.0.0.0" so it should have some value and condition would be evaluated properly i.e. to True in this case.
Here is the log, which shows that it is Unsetting the variable 'Installed_Ver'.
[5898:2AC4][2018-08-30T13:15:08]i000: Setting string variable 'WixBundleName' to value 'RegVersionCheck'
[5898:2AC4][2018-08-30T13:15:08]i000: Setting string variable 'WixBundleManufacturer' to value 'Testing'
[5898:3354][2018-08-30T13:15:08]i000: Setting numeric variable 'WixStdBALanguageId' to value 1033
[5898:3354][2018-08-30T13:15:08]i000: Setting version variable 'WixBundleFileVersion' to value '7.1.2.3'
[5898:2AC4][2018-08-30T13:15:08]i100: Detect begin, 1 packages
[5898:2AC4][2018-08-30T13:15:08]i000: Registry key not found. Key = 'Software\MyCompany\MyProductName'
[5898:2AC4][2018-08-30T13:15:08]i000: Unsetting variable 'Installed_Ver'
[5898:2AC4][2018-08-30T13:15:08]i101: Detected package: MainProduct, state: Absent, cached: None
[5898:2AC4][2018-08-30T13:15:08]i104: Detected package: MainProduct, feature: CalculatorFeature, state: Absent
[5898:2AC4][2018-08-30T13:15:08]i052: Condition 'WixBundleInstalled OR (WixBundleFileVersion > Installed_Ver)' evaluates to false.
[5898:2AC4][2018-08-30T13:15:08]e000: A recent or same version of this product is already installed on this machine. Please contact product support for more information.
[5898:2AC4][2018-08-30T13:15:08]e000: Error 0x81f40001: Bundle condition evaluated to false: WixBundleInstalled OR (WixBundleFileVersion > Installed_Ver)
[5898:2AC4][2018-08-30T13:15:08]i199: Detect complete, result: 0x0
I tried by hard-coding "0.0.0.0" in the condition in place of Installed_Ver and it works fine.
How should I get a default value for Installed_Ver if registry search fails?
Thanks

This is an open bug. You should be able to work around this by updating your condition to handle when the Variable is not defined.
WixBundleInstalled OR ((WixBundleFileVersion > Installed_Ver) AND Installed_Ver)

After some more searching, I've found a little workaround so that I can use the default value (0.0.0.0) being setup in the Variable definition. I defined another search for same registry key and getting the Boolean (exist) variable and depending upon this variable deciding to trigger the original search and get the version value. Here is my solution:
<Fragment Id="Self_Install_Check">
<?define ProdRegKey=Software\MyCompany\MyProductName?>
<Variable Name="Installed_Ver" bal:Overridable="yes" Type="version" Value="0.0.0.0" Persisted="yes" />
<util:RegistrySearch
Id="Self_Ver"
After="ProdRegExist"
Condition="ProdRegKeyExist"
Root="HKLM"
Key="$(var.ProdRegKey)"
Value="Version"
Variable="Installed_Ver"
Format="raw"
Win64="yes"
/>
<util:RegistrySearch
Id="ProdRegExist"
Root="HKLM"
Key="$(var.ProdRegKey)"
Value="Version"
Variable="ProdRegKeyExist"
Result="exists"
Format="raw"
Win64="yes"
/>
</Fragment>
So now the registry search for reading version would only be triggered if ProdRegKeyExist becomes true and Installed_Ver won't be Unset while retaining value=0.0.0.0 or else would get the Version value from registry...

Related

Wix Toolset: Successively check conditions

I am trying to check two conditions during installation, but need to check it in series: if condition A is false - show error A, do not check condition B.
In details: I have to conditions checks
My app is installed
Version of database schema
If my app is not installed - I need to show an error message and do not check database schema, which leads to unknown error during install.
<Property Id="MYAPPINSTALLED">
<RegistrySearch Id="MyAppInstalledSearch"
Name="MyAppInstalled"
Root="HKLM"
Key="Software\MyApp\Installed"
Type="raw"
Win64="no" />
</Property>
<Condition Message="!(loc.RequireMyApp)">
<![CDATA[(MYAPPINSTALLED="1")]]>
</Condition>
<PropertyRef Id="GETSCHEMAVERSION"/>
GETSCHEMAVERSION is a Custom Action used in other components, which tries to connect to the Database and fails if MyApp is not present on the machine.
How can I check GETSCHEMAVERSION property only in case MYAPPINSTALLED condition pass?
Updated:
GETSCHEMAVERSION Custom Action is used in some other helper applications.
The second custom action, which checks for DB schema, should be conditioned to run only if the app is installed:
<Custom Action="CheckDbSchema">MYAPPINSTALLED="1"</Custom>
That custom action should set another property, as far as I understand, e.g. DBSCHEMAISOK to 1.
Then the components that depends on the schema to be available can be conditioned with MYAPPINSTALLED="1" And DBSCHEMAISOK="1".
I might be missing the syntax details, but you should get the idea.

Burn: Pass RegistrySearch results value to Custom Bootstrapper

In my Bundle code, I'm trying to use the result of a registry search to set Variable to be used in my Custom Boostrapper:
<util:RegistrySearch
Id="ThirdPartyInstallDirSearch"
Variable="THIRDPARTY_INSTALL_DIR"
Root="HKLM"
Key="SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
Value="OceanSoftDir"
Result="exists"
/>
<Variable Name="THIRDPARTY_INSTALL_DIR" Type="string" Value="$(var.THIRDPARTY_INSTALL_DIR)"/>
But this would give an error:
Undefined preprocessor variable '$(var.THIRDPARTY_INSTALL_DIR)'
Basically, I want to pass the result of registry search to my custom bootstrapper application.
Thanks
As the error says, $(var.<NAME>) is a preprocessor define from either a wxi file or from the project file <DefineConstants>name=value</DefineConstants> or from a <?define?>. You can read more about the preprocessor here.
For your issue, the registry search itself should be defining the variable. I do something similar to what you want in a bootstrapper.
<Fragment>
<util:RegistrySearch
Id="ClientInstalledCheck"
Root="HKLM"
Key="SOFTWARE\Client"
Value="ClientPath"
Result="value"
Variable="ClientInstalled"/>
<util:DirectorySearch
Path='[ClientInstalled]'
Variable='InstallFolder'
After='ClientInstalledCheck'
Condition='ClientInstalled' />
</Fragment>
And then in the products installed by the bootstrapper I will pass in the "InstallFolder" value to these installs.
<MsiProperty Name="INSTALLDIR" Value="[InstallFolder]"/>
This way if the user has installed in a non-default install location, we pick up the custom location they chose and use that instead. If the registry key did not exist, we use the default location.
I also have the InstallFolder variable defined with a default location (since my use case is slightly different than yours) as
<Variable Name="InstallFolder" Type="string" Value="[ProgramFilesFolder]$(var.CompanyInstallDir)\" bal:Overridable="yes" Persisted="yes"/>
Where CompanyInstallDir is defined as a preprocessor variable through <DefineConstants> which is originally defined somewhere in an MSBuild properties file.
So to explain your issue, you are mixing preprocessor directives and Variables. In your registry search, you're using Result="exists" which will set the variable THIRDPARTY_INSTALL_DIR to '0' or '1'. You want to use Result="value". This will put the registry location's value in the variable you define in the Variable="" attribute.
If you do the registry search + directory search with condition, you can properly set a variable if and only if the registry exists AND the directory is actually present on the machine and properly handle cases where it isn't but the registry still exists.
Some things you may do differently since the use case is slightly different but hopefully this sets you on the right path for doing what you need to do.

Wix - How to distinguish registry key with no default value from no registry key

Given the following...
<Property Id="TESTSEARCH">
<RegistrySearch Id="LookingForKeyExists"
Root="HKLM"
Key="Software\Classes\.ext"
Type="raw" />
</Property>
... I can get one of three conditions.
The key is not present
The key is present but not set
The key is present and has a value
I would like to be able to tell the difference between the following conditions.
<Condition Message="The extension .ext is missing">
???
</Condition>
<Condition Message="The extension .ext has no default value">
???
</Condition>
But all I've been able to find/figure out is the OR of the two.
<Condition Message="The extension .ext is either missing or does not have a default value">
TESTSEARCH
</Condition>
Can the two separate conditions be distinguished without writing an extension? If so, how?
I don't think it is possible and it looks like Windows Installer limitation, rather than WiX toolset. This original article on MSDN states that explicitly:
Note that it is not possible to use the RegLocator table to check only
for the presence of the key. However, you can search for the default
value of a key and retrieve its value if it is not empty.

Custom action to set a property after a failed RegistrySearch is not running

The aim is that if the value isn't found in the registry then I want to assign a default value and then have that value display as the default value in a field in the installer UI. The default value I actually want to use is [ComputerName] but obviously I can't use [ComputerName] directly in the property value attribute because it will give me errors on compiling, specifically:
warning CNDL1077: The 'MYPROPERTY' Property contains '[ComputerName]'
in its value which is an illegal reference to another property. If
this value is a string literal, not a property reference, please
ignore this warning. To set a property with the value of another
property, use a CustomAction with Property and Value attributes.
I want to get it working with plain text before I even try [ComputerName] but so far I can't even get that working.
In my project I have a Product.wxs file which contains the Product element, lots of custom actions (most of which are running fine but they're all running significantly later) and the following elements which are not cooperating and which are all siblings under the Product element.
Property definition and registry search:
<Property Id="MYPROPERTY" Value="ADefaultValue">
<RegistrySearch Id="MyProperty" Type="raw" Root="HKLM" Win64="$(var.Win64)"
Key="Software\MyCompany\MyApplication" Name="MyProperty" />
</Property>
Custom action definition:
<CustomAction Id="SetMyPropertyDefault" Property="MYPROPERTY" Value="MyCustomValue" Execute="immediate"/>
Custom action execution:
<InstallExecuteSequence>
<Custom Action="SetMyPropertyDefault" After="AppSearch"><![CDATA[MYPROPERTY="ADefaultValue"]]></Custom>
</InstallExecuteSequence>
It just will not work for me at all.
For the custom element content I have tried:
<Custom Action="SetMyPropertyDefault" After="AppSearch"><![CDATA[MYPROPERTY="ADefaultValue"]]></Custom>
<Custom Action="SetMyPropertyDefault" After="AppSearch">1</Custom> // I thought this would always run the custom action.
<Custom Action="SetMyPropertyDefault" After="AppSearch">NOT MYPROPERTY</Custom> // Back when I wasn't using the default value on the property at all.
The result is always the same, I'm still getting "ADefaultValue" showing up in the UI, never the alternate "MyCustomValue".
According to every blog and SO post I've seen I'm doing exactly what I should be doing except clearly I'm missing something.
Any ideas?
UPDATE/Answer:
The piece of information that I was missing which was provided by #sutarmin-anton was that InstallUISequence runs before InstallExecuteSequence (seems counter-intuitive to me but there you go).
But as it happened I didn't need to explicitly duplicate the custom action call in each of the install sequence elements, instead I used the SetProperty element.
So now I've got the following in my Product.wxs as children of the Product element:
<Property Id="MYPROPERTY">
<RegistrySearch Id="MyProperty" Type="raw" Root="HKLM" Win64="$(var.Win64)" Key="Software\MyCompany\MyApplication" Name="MyProperty" />
</Property>
<SetProperty Id="MYPROPERTY" After="AppSearch" Value="[ComputerName]">NOT MYPROPERTY</SetProperty>
It now runs the SetProperty after AppSearch in both InstallUISequence and InstallExecuteSequence, but the second time it runs the NOT MYPROPERTY will come out false so it doesn't get reset, and of course if it's run in quiet mode it'll still work correctly.
When you going through installer UI, installation is in InstallUISequence. InstallExecuteSequence runs after all UI events. This is cause of you have not seen "MyCustomValue". To change your property before UI sequence you should place your custom action in "InstallUISequence".
By the way, why don't you set default value of your property to "MyCustomValue"? Then, if AppSearch wont find value in regisrty, it just leave default value that you are trying to set manually.
You may be overcomplicating things. The MYPROPERTY value will not be set at all if you don't set a default. So then you call your CA to set it if 'NOT MYPROPERTY'
I see that you've tried this, and I'd say it's the correct approach that I'd try to diagnose rather than try something else. A verbose log would be invaluable. Do a:
msiexec /i [path to msi] /l*vx [path to a text log file]
and see what CA is called, what AppSearch does, property values etc.
Your original comment of "I can't use [ComputerName] - if that's the problem why not tell us what happened and maybe there is a solution that doesn't require all this. What's the compile error, for example?

RegistrySearch in WiX is not returning true for a value that is in the registry

I have the following commands in my WiX Bundle:
<Variable Name="InstanceName" Value= "SQLExpress" />
<util:RegistrySearch Id="SqlInstanceKeyFound" Root="HKLM"
Key="SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL"
Value ="[InstanceName]" Result="exists" Variable="SqlInstanceKeyFound" />
I can see the SQL Server Express instance in Regedit on the machine, but my log file is reporting this back to me:
Registry key not found. Key = 'SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL'
Setting numeric variable 'SqlInstanceKeyFound' to value 0
Condition 'SqlInstanceKeyFound' evaluates to false.
Why is this not returning true when I can see the value in Regedit? To add a little more information, this is supposed to be a check for an SQL Express 2014 instance.
<util:RegistrySearch> defaults to searching the 32-bit registry. To search the 64-bit registry, set Win64="yes".
I'm irritated because of your WiX syntax. Why are you prefixing RegistrySearch with a util namespace?
Also, I'm not able to find s.th. in the lines of Result and Variable in the WiX documentation? Shouldn't the registry search be a child of a Property element?
Cf. the answer in Property value set in WiX based on registry key value for an example.