How to write setup build date to a property - wix

Update 2: The feature request has been declined, simply because WiX cannot support these "fake" variables cmd.exe offers. So the accepted answer below remains the best solution.
Update: I have issued a feature request for adding preprocessor dynamic environment variable support. As soon as the devs respond, I'll update this thread.
Original Question:
I want to write the date when the setup has been built to a property.
<Property Name="BuildDate" Value="$(env.Date)" />
or
<Property Name="BuildDate" Value="$(sys.Date)" />
throws CNDL0150: "Undefined preprocessor variable" while
<Property Name="BuildDate" Value="[Date]" />
will of course write the string "[Date]" to the MSI property.
How can I achieve this?
Edit: I've tried "Date" in all cases, same result. Also, adding a new environment variable called "BuildDate" and setting it to "%DATE%" removes the compilation error, but the value written to the MSI doesn't resolve to the actual date, instead it yields the string "%DATE%".

You can add a property in you wix project (.wixproj) in your first property group, for example (you can format the date and/or time to your liking):
<Date>$([System.DateTime]::Now.ToString("yyyyMMddhhmm"))</Date>
Then in your configurations property groups add this property in <DefineConstants>, if you have more constants then separate them with semicolon ;:
<DefineConstants>Date=$(Date)</DefineConstants>
Lastly add the MSI Property:
<Property Id="BuildDate" Value="$(var.Date)" />

Try to use all uppercase DATE, like this:
<Property Name="BuildDate" Value="$(env.DATE)" />
UPDATE: My initial guess was not correct - environment variables are case-insensitive when referenced this way.
It seems like it depends on the type of the environment variable. There are standard environment variables, like %TEMP%, %windir%, etc. Those are "static", meaning the value is not calculated each time you reference it.
There are dynamic environment variables, which are calculated each time they are referenced. These include %DATE%, %TIME%, etc. It seems that WiX preprocessor can't work with dynamic variables. You can verify this: put $(env.windir) and it will work, put $(env.time) - and it won't. More info about environment variables can be found here.
I have not verified whether there's a wish in WiX bug database to support this. Feel free to do it yourself.
So, back to your question. You can work around this limitation in the following way:
Create preprocessor extension
Reference the value from that extension instead of addressing environment variable direcly
The sample how to create preprocessor extension can be found here.
Here's a sample of the code which does the job:
public class DateExtension : PreprocessorExtension
{
public override string[] Prefixes
{
get
{
return new[] { "date" };
}
}
public override string GetVariableValue(string prefix, string name)
{
string result = null;
switch (prefix)
{
case "date":
switch (name)
{
case "Now":
result = DateTime.Now.ToShortDateString();
break;
}
break;
}
return result;
}
}
And in your WiX code you can use it in the following way:
<Property Id="BuildDate" Value="$(date.Now)" />
Don't forget to:
pass preprocessor extension DLL to the WiX setup project (-ext path/to/PreprocessorExtension.dll)
add [assembly: AssemblyDefaultWixExtension(typeof(PreprocessorWixExtension))] to the preprocessor extension project
Here is the result I observe in the MSI package:

To set property value from another property use the following syntax:
<SetProperty Id="BuildDate" Value="[Date]" After="InstallInitialize" />

Related

Unable to add custom user property even if it does not exist

I have implemented VSTO plug-in for Outlook 2016 / 2019. I have added one custom property. On production, for one user (this user has tried on different machines as well), whenever we try to add our custom property we get exception.
I am using following code snippet :--
UserProperty securedFlag = mailItem.UserProperties.Find("Custom.Secured", true);
int currentValue = -1;
if (securedFlag == null)
{
try
{
securedFlag = mailItem.UserProperties.Add("Custom.Secured", OlUserPropertyType.olInteger, false, OlFormatInteger.olFormatIntegerPlain);
}
catch(System.Exception ex)
{
DarkAddInEventLog.WriteException(ex, "Secure");
}
}
When we execute above code, it is throwing following exception :--
"A custom field with this name but a different data type already exists. Enter a different name."
I have also tried to search same property in non-custom properties by passing false (in second argument of find API) which also throws exception that this property does not exist. Therefore it seems confirmed that this property does not exist before.
Now I have 2 doubts :--
1- If this property does not exist, then why outlook is throwing this error ?
2- Same plug-in is working with other users but only one user is facing this issue. Is it related to some mailbox configuration ?
The error means a property with the same name has already been used in the same mailbox, it might not even be your property, but since all custom properties use the same GUID (PS_PUBLIC_STRINGS), you can run into this problem with duplicate names.
Your only option is to either use a different name (and make sure it is not generic enough to conflict with other properties created by some other apps - e.g. you can prefix all your properties with a unique prefix, such as "MyCompanyProp.") or avoid using UserProperties collection completely and set your custom properties using PropertyAccessor specifying the full DASL name of the named properties with your own custom GUID.

WiX default REFERENCED value OR Registry Value

I need to set a default database file for my application. I only want it to be set on initial installation. If the registry value--a string of the path to the sdf file--changes, then future upgrades should not try to set the value back to the default.
Another caveat, that seems to be a problem, though, is that if they've never setup a database file, the user should be able to use the program with a default database without having to go through setup.
So I set the DATABASEFILE with value="[INSTALLFOLDER]dust.sdf". but candle was complaining that [INSTALLFOLDER]:
The 'DATABASEFILE' Property contains '[INSTALLFOLDER]' 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.
So, following the error's instructions, I added a custom action. Now that custom action ALWAYS overwrites the DATABASEFILE attribute. I want it to only override that value if the value doesn't exist in the registry.
Here's the code:
<CustomAction Id='SetINSTALLFOLDERREF' Property='DATABASEFILE' Value='[INSTALLFOLDER]dust.sdf' Execute='immediate' />
<Property Id='DATABASEFILE' >
<RegistrySearch Id='DatabaseFile' Type='raw' Root='HKCU' Key='Software\DBG\Dust\Database' Name='File'/>
</Property>
Have you set conditions on your custom action with Id=SetINSTALLFOLDERREF? If not, you might want to do it.
A condition of NOT DATABASEFILE on your custom action should probably suffice.
With the above condition,
-In the case of a fresh installation, the registry entry does not exist. Hence, DATABASEFILE is NULL, the custom action condition evaluates to true and the custom action SetINSTALLFOLDERREF executes.
-For any subsequent maintenance operations, the registry key is present, the property DATABASEFILE will always contain a value, the custom action condition evaluates to FALSE and the custom action will not be triggered.
Another thing you might want to do is to add the property DATABASEFILE to the list of SecureCustomProperties ie. secure the property DATABASEFILE.

WiX: How to create file name from a property value

I have a working WiX installer that correctly writes properties to certain INI files, which works fine, but I have a need to generate the name of an INI file on the fly, from the computer name, eg.
MACHINE(xxx).INI
where xxx is my computer name.
I have tried all sorts of combinations of properties and I just can't seem to get it working. Can anyone put me right ?
This is my latest attempt that doesn't work:
<Property Id="MACHINEINI" Value="MACHINE([%COMPUTERNAME]).ini" />
...
<IniFile Id="IniPermissions"
Directory="MYDIR"
Action="addLine"
Name="[MACHINEINI]"
Section="[ComputerName]"
Key="Permissions"
Value="TEST" />
I never see the value of MACHINEINI, as the filename that gets created is actually called
[MACHINEINI]
The value it writes in is correct, so I see the contents as follows:
[xxx] Permissions=TEST
(where xxx is my machine name)
I have tried using [ComputerName], [COMPUTERNAME], [%COMPUTERNAME]
When I build the installer, I get the following error:
C:\Source\blah\BLAH.wxs(50) : warning CNDL1077 : The 'MACHINEINI' Pro
perty contains '[COMPUTERNAME]' in its value which is an illegal
reference to an other property. If this value is a string literal,
not a property reference, pl ease ignore this warning. To set a
property with the value of another property, use a CustomAction with
Property and Value attributes.
The underlying Windows Installer table doesn't support this. Note that the FileName column is of type FileName. Only the Formatted type can take a [PROPERTY].
IniFile Table
You could need a custom action to write temporary records to the IniFile table to transform the file name. The advantage versus using a custom action to literally write the INI file is that rollback would be automatically handled for you.
It's not possible to tell you how to do this exactly since I don't know what language you'd want to use to write the custom action.
A simpler approach (from the installers perspective) would be to transform the [KEY] name inside a single INI instead of writing to different INI files.

Conditions within custom actions

I recently separated our company installers in to two features (one enabled and one absent), to allow the user to select both, I've used UI_Mondo GUI to allow selection.
I've managed to get our custom action to work if the feature is selected:
<Custom Action="RestartIISForASPNet4" After="AspnetRegIIS"><![CDATA[(NOT INSTALLED) AND (&WebServiceFeature=3) AND NOT (!WebServiceFeature=3)]]></Custom
I tried but for some reason it's coming up with false (IMO, it can't as in the log the WixUI_InstallMode is set to InstallComplete.
<Custom Action="RestartIISForASPNet4" After="AspnetRegIIS"><![CDATA[((NOT INSTALLED) AND (&WebServiceFeature=3) AND NOT (!WebServiceFeature=3)) OR WixUI_InstallMode = "InstallComplete"]]></Custom>
Anybody have any ideas what I'm missing, it's probably really obvious.
WixUI_InstallMode is a private property. This means it uses its default value during InstallExecuteSequence (when your custom action runs).
A solution is to use custom action to save its value in a public property. You can then use that public property in your condition.
Public properties don't have lowercase letters in their names.

Output tag - TaskParameter and PropertyName, ItemName - what do this two include?

The msbuild contains output tag. It has avialable attributes: TaskParameter and PropertyName, ItemName.
How they can be used? What are they containing?
Please, can you help me to understand and give an example? For example you can use xmlpeek task with output tag inside.
(I read documentation on msdn but I still don't get it. :( )
The question has been answered, but I will follow up with an example.
In the MSBuild community task Time, an output parameter Month can be set to a property called
CurrentMonth as follows:
<Time>
<Output TaskParameter="Month" PropertyName="CurrentMonth" />
</Time>
In the MSBuild Community task time source code the property Month inside the Time class looks like this:
[Output]
public string Month
{
get { return month; }
}
All properties mapped with an [Output] attribute can be set as a task parameter and
assigned a MSBuild property name as specified above.
To read more about the Time task, a CHM file is available in the MSI file available at the following URL: http://msbuildtasks.tigris.org/
These are a way of passing values back from the task to the MSBuild script. It is basically a way of mapping a property in the compiled task code that has been decorated with the [Output] attribute back to a property in your MSBuild file. This page gives you more details about it: MSDN: Output Element (MSBuild). This article also has a good example of it in action: How to auto-increment assembly version using a custom MSBuild task