WiX Toolset - Hidden property no longer hidden when referenced elsewhere - wix

To hide a property in WiX I'm using
<Property Id="MY_PASSWORD" Hidden="yes" />
Or
<Control Id="Password" Password="yes" Type="Edit" X="30" Y="173" Width="220" Height="18" Property="MY_PASSWORD">
What I haven't found a reliable solution for is how to keep a previously hidden password hidden in a log file after it's been evaluated in:
Another property (e.g. SetProperty, type 51 CustomAction, publish property ina dialog)
A util:XmlFile entry
Is there a way to have a password evaluated without exposing the password once it's used in another WiX element?
In the instances of SetProperty or a type 51 CustomAction, is there a way to hide the original password without needed to hide the rest of the string.
For example set Property="SOME_STRING" Value="password=[MY_PASSWORD];other=[ANOTHER_PROPERTY]", have the property string show up as "password=********;other=the evaluated string"
In the instance of using util:XmlFile, is there any way to hide a password that's evaluated here from showing up in a log file?

This is the solution I came up with, I'm open to better solutions.
In the case of (e.g. SetProperty, type 51 CustomAction, or publish property in a dialog), what I did is create a reference to the property, which I mark as Hidden="yes".
<Property Id="Property_2" Hidden="yes" />
To handle the case: For example set Property="SOME_STRING" Value="password=[MY_PASSWORD];other=[ANOTHER_PROPERTY]", have the property string show up as "password=********;other=the evaluated string"
What I did was write a type 51 CustomAction to create that new property
<CustomAction Id="Set.NewProperty" Property="NewProperty" Value="password=[MY_PASSWORD];other=[ANOTHER_PROPERTY]" />
And add a separate reference to the new property to hide it.
<Property Id="NewProperty" Hidden="yes" />
This worked for me to obscure these details from util:XmlFile.
This solution, in my opinion is less than perfect. Using this method hides the entire string. I would prefer a solution that only hides the password details portion of the string.

Related

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?

WiX File Search Conditional

I have a WiX installer project that I'm creating where I'd like the installer to check to see if another application is already installed on the user's machine. If it is, then I'd like to set the install level of one of the features to "1", otherwise it should remain hidden (i.e. install level = 0). To find out where the application is installed, I first do a registry search:
<Property Id="MYAPPINSTALLFOLDER">
<RegistrySearch Id='InstallPathRegistry'
Type='raw'
Root='HKLM'
Key='SOFTWARE\SomeLongAppPath' Name='FileName'
Win64='yes'/>
</Property>
You'll notice that the registry value that I end up getting is actually the directory of the installed application including the actual program name with extension (let's say myapp.exe). So, once I get the full path of the installed application, I check to see if the file exists:
<Property Id="MYAPPINSTALLED">
<DirectorySearch Id="CheckFileDir" Path="[MYAPPINSTALLFOLDER]" AssignToProperty="yes">
<FileSearch Id="CheckFile" Name="myapp.exe" />
</DirectorySearch>
</Property>
Now, what I would expect to see is that if the file actually exists in that location, then the Property called "MYAPPINSTALLED" would be set to 1, otherwise it would be 0. Then, when I setup my features I use something like this:
<Feature Id="ThirdPartyPlugins" Title="Third Party Plugins" Level="0">
<Condition Level="1">MYAPPINSTALLED</Condition>
<ComponentGroupRef Id="MyAppPlugin" />
</Feature>
However, when I run my installer the third party plugin feature is always hidden. I've enabled msi datalogging by setting the property like this:
<Property Id="MsiLogging" Value="voicewarmupx"/>
And when I check the log file I can definitely see that the MYAPPINSTALLFOLDER property gets changed to the correct file path when it does the registry search. However, if I search the log for the property MYAPPINSTALLED, then I can see the following:
AppSearch: Property: MYAPPINSTALLED, Signature: CheckFileDir
Action ended 15:55:06: AppSearch. Return value 1.
So, it looks like it worked, however it doesn't seem to ever set the Property to equal the search value. Am I doing something wrong? Can someone explain why my feature install level never gets set to 1 even though the application file exists?
Edit
Ok, after more debugging... I think the issue is that the directory search is trying to use a path that includes the file name and extension (i.e. C:/Program Files/MyApp/myapp.exe") instead of just the directory where the file comes from. This is because the registry search has the full path including the file name stored (but not just the install directory). If I do a directory search just using the correct absolute directory (not using the registry search) then the process works. So, my follow up question is... my Property MYAPPINSTALLFOLDER contains the full path with file name and extension. Is there a way to strip the file name and extension from this property so that I just have the proper directory name to search for?
You're checking to see if another application is installed but that's rather a long way around. Also, the file search returns a path, not zero or 1, but either way a full verbose log should tell you if the properties are being set. It might help if you could post the entire log somewhere rather than the parts you think are the only relevant ones. e.g. There's probably an AppSearch in the execute sequence for silent installs.
It's requently easier to do a single search for other applications that were installed with MSI packages in these ways:
If you know the other product's UpgradeCode (and version ranges if applicable) then add Upgrade/UpgradeVersion elements with onlydetect set to yes, and that search will set a property if the product is detected.
If you know (or can find out) the Component id of any of the relevant components from that other product, then you can use them in a WiX ComponentSearch. If you get the target property set then that component is installed. This post contains a couple of ways to find out component guids:
How to find out which application requires a certain assembly from GAC?
It's also puzzling that the AppSearch log extract you posted only refers to one property. The Directory/FileSearch is also an AppSearch, so if the MSI actually contains two searches in AppSearch there should be references to all the properties being set. Again, that's a reason to post the entire log and look in the MSI file for those searches. The RegLocator search is documnented to occur before the DRLocator, so why is there no MYAPPINSTALLFOLDER property in the AppSearch log entry? You're not on a 32-bit system are you? (noticing the win64 search).
Per the WiX documentation:
Use the AssignToProperty attribute to search for a file but set the outer property to the directory containing the file. When this attribute is set to yes, you may only nest a FileSearch element with a unique Id or define no child element [of the DirectorySearch].
I added the text in the brackets to make it more clear.
So, after reading this sentence a few times and cross referencing your WiX XML, I think I see what the problem is with your current WiX XML. You perform a separate registry search from the directory search. Instead, you should nest these. There are two ways to perform the search, depending on what you want to do. One way is to simply retrieve the registry value from the registry, and if the value exists, then you make the assumption that the feature's required application is installed, at which point you appropriately set a property that would enable hiding/showing the feature within your installer's feature selection tree. The other way is to actually find the file you're interested in, using the results of the registry search as the basis for the file search.
Below is the XML for just a registry search, which doesn't check that the file actually exists on disk. You're making the assumption that if this registry value exists, the file is installed and available.
<Property Id="MYAPPINSTALLFOLDER">
<RegistrySearch Id='InstallPathRegistry'
Type='raw'
Root='HKLM'
Key='SOFTWARE\SomeLongAppPath' Name='FileName'
Win64='yes'/>
</Property>
<Property Id="SHOW_APP_FEATURE" Value="hidden" />
<SetProperty Id="SHOW_APP_FEATURE" Value="collapse" Sequence="both" After="CostFinalize">
<!-- If MYAPPINSTALLFOLDER is defined and contains any non-empty value, this
evaluates to TRUE; otherwise, it evaluates to FALSE.
-->
MYAPPINSTALLFOLDER
</SetProperty>
<!-- You could also be more explicit:
<SetProperty Id="SHOW_APP_FEATURE" Value="collapse" Sequence="both" After="CostFinalize">
<![CDATA[MYAPPINSTALLFOLDER <> ""]]>
</SetProperty>
-->
<Feature Id="MyAwesomeFeature" Title="My Awesome App Feature"
Display="[SHOW_APP_FEATURE]">
... <!-- Component/ComponentRefs go here -->
</Feature>
If you want to ensure that, even if the registry value exists in the registry, that the file it points to is 1) actually a file path; and 2) that the file actually exists on disk, then you need to perform a nested file search within a directory search, which itself is nested within a registry search. You would again need to use a SetProperty custom action to set a property that would enable the hiding/showing of the feature within your installer's feature selection tree. Here's the XML for this search:
<!-- Performing a FileSearch nested within a DirectorySearch,
which is itself nested within a RegistrySearch
This search twill ensure that the file exists on disk, and
if so, assign the full filename and path to the
MYAPPINSTALLFOLDER property.
-->
<Property Id="MYAPPINSTALLFOLDER">
<RegistrySearch Id='InstallPathRegistry'
Type='raw'
Root='HKLM'
Key='SOFTWARE\SomeLongAppPath' Name='FileName'
Win64='yes'>
<DirectorySearch Id='InstallPathDirectory' AssignToProperty='yes'>
<FileSearch Id='InstallPathFile' Name='myapp.exe' />
</DirectorySearch>
</RegistrySearch>
</Property>
<Property Id="SHOW_APP_FEATURE" Value="hidden" />
<SetProperty Id="SHOW_APP_FEATURE" Value="collapse" Sequence="both" After="CostFinalize">
<!-- If MYAPPINSTALLFOLDER is defined and contains any non-empty value, this
evaluates to TRUE; otherwise, it evaluates to FALSE.
-->
MYAPPINSTALLFOLDER
</SetProperty>
<Feature Id="MyAwesomeFeature" Title="My Awesome App Feature"
Display="[SHOW_APP_FEATURE]">
... <!-- Component/ComponentRefs go here -->
</Feature>
This should allow you to accomplish what you're trying to achieve.

How can I have a WiX Property default to null?

I am working on a set of WiX installers that are going to share a common form. Every application needs the values set, but they are going to be different for each application.
I am trying to allow for the properties (which are linked to controls) to have a default value (or not) and to allow for the property values to be set via command line.
in my "SharedDialog.wxs" I have:
<Fragment>
<PropertyRef Id="PROP1"/>
<PropertyRef Id="PROP2"/>
<UI>
<Dialog Id="SharedDialog" Width="370" Height="270" Title="[ProductName]">
<Control Type="Edit" Id="1" Property="PROP1" Wid... Indirect="no" />
<Control Type="CheckBox" Id="2" Property="PROP2" Wid...
CheckBoxValue="1" Indirect="no"/>
</Dialog>
</Fragment>
In a file for the application specific project:
<Fragment>
<Property Id="PROP1" Value="Test"/>
<Property Id="PROP2" Value="1"/>
</Fragment>
This all works for what I am trying to do, but the problem is that when I want to clear the values as so: (so they don't have a default)
<Fragment>
<Property Id="PROP1"/>
<Property Id="PROP2"/>
</Fragment>
I get this error:
Unresolved reference to symbol 'Property:PROP1' in section 'Fragment:'.
Unresolved reference to symbol 'Property:PROP2' in section 'Fragment:'.
WiX also will not let you set value to "". The problem is that as far I can tell the checkbox will always be checked if the property has a value. How can I set the property "PROP2" to "null"?
You can undefine an existing property by setting it equal to an empty string wrapped in braces like this: PROPERTY = {} in a set property custom action. This means the property doesn't exist, it is not an empty string. See the explanation here.
For properties to be available from the command line they must be PUBLIC - they are the properties that are all UPPERCASE.
Setting PUBLIC PROPERTIES secure means they can be passed from the client to the server installation process which is required for properties that are used in deferred mode custom actions. Technically this adds them to the list of SecureCustomProperties.
You can give PUBLIC PROPERTIES a default value in the Property table, and then set other values from the command line. The command line overrides the defaults:
msiexec.exe /I "C:\Test.msi" /QN /L*V "C:\log.log" TEST="MyValue" TEST2="MyValue"
See more info:
How to set a check box to "unchecked" from the msiexec command line?
Nevermind, I found the solution:
<Fragment>
<Property Id="PROP1" Secure="yes"/>
<Property Id="PROP2" Secure="yes"/>
</Fragment>

unmask custom properties in the log files

I have a wix setup project, I build it and install it using following command line:
msiexec /i setup.msi /l* log.txt
My project has properties which are used by textboxes.
<Property Id="DataSource" Value="."/>
<Control Id="DataSourceText" Type="Edit" Text="." Height="17" Width="150" X="200" Y="18" Property="DataSource"/>
Those properties don't display in the log file. Is there a way to make them displayed?
Use upper case for property names and they will.
I use /l*v option to get the verbose log from MSI. All events where a value is assigned to a property are logged, along with the new value. (This holds true both for upper-case public properties and mixed-case private properties.)
If a property holds a password or other sensitive information, it should be marked with attribute Hidden="Yes". The values of such properties are not logged.
For anyone visiting this topic: there's still a difference between /lv* and /lvoicewarmupx which uses all the log options.
With the second option you see the initial value of the properties as well.
zzz.msi /lvoicewarmupx debug.log

How to use a property that was set in a custom action?

I want to set a property in a custom action and use it in the standard custom action "util:User" afterwards. But no matter where I put the property in my wxs-file, I always get "error LGHT0094 : Unresolved reference to symbol"
Details:
In my setup I want to add a new user by using util:User. The user should be added to the group "Power Users" by using util:GroupRef. No Problem so far. Unfortunately the group names are language dependent. In german "Power Users" is "Hauptbenutzer". So I want to look up the well known SID S-1-5-32-547 in a custom action, set a property in this custom action by calling MsiSetProperty and then use the property for util:GroupRef.
As far as I understand, the property must be declared somewhere in the wxs-file.
In the examples I found, the property was never declared as follows (but I also tried that):
<Property Id="TextSID" Value="Power Users" />
In the examples there always was a custom action to set the property, like:
<CustomAction Id="SetTextSID"
Property="TextSID"
Value="Power Users"
Return="check" />
My problem is, that the creation of the user fails to "compile" because the property "TextSID" is not known:
<Component Id="CreateUser" Guid="Some GUID here in my original wxs file">
<util:User Id="UserUser"
Name="User" Password="Password"
CanNotChangePassword="yes" PasswordNeverExpires="yes">
<util:GroupRef Id="TextSID" />
</util:User>
</Component>
I have never done a custom action before and I'm a new to WiX and MSI, so any idea would be very welcome.
Regards
Ralf
Sorry for wasting your time.
I stared at my XML for hours before I posted this question, just to find the answer immediately after my post :-(
My only problem was, that it is not possible to reference to something that isn't there. In this case the "util:Group" was missing.