Conditions for SetDirectory in Wix - wix

I've been trying to figure out why my condition isn't working for my SetDirectory. I haven't been able to find any examples of anyone actually doing this, only that it is possible to do so, according to the WiX documentation.
I've tried the following:
<SetDirectory Id="INSTALLLOCATION" Value="TEST">ComputerName=LJPRESCOTT1</SetDirectory>
This compiles, but gets ignored at runtime. The files end up getting installed straight on the C:\ Drive, and not in C:\TEST like I'm aiming for.
and
<SetDirectory Id="INSTALLLOCATION" Value="TEST">[ComputerName]=LJPRESCOTT1</SetDirectory>
This doesn't compile and throws a "Bad conditional string" error.
If I do this, it creates a directory named LJPRESCOTT1 as expected:
<SetDirectory Id="INSTALLLOCATION" Value="[ComputerName]" />
So I know the Comp Name is correct.
Am I doing something wrong here, or is this not possible?
Thanks!

You use the syntax [ComputerName] only when you are doing formatting, or using values that are of type Formatted, hence the syntax error in the second example.
But in the first example what you are doing is comparing the value of the ComputerName property with the value of the LJPRESCOTT1 (public) property, which I imagine always evaluates to false. If you want to compare ComputerName to a hard string value you need some quotes:
ComputerName="LJPRESCOTT1"

Related

NLog Conditional Variable Value

In NLog, is there a way to have a variable with a conditional value? I've seen this: https://github.com/NLog/NLog/wiki/When-Layout-Renderer and tried the following:
<variable name="EnvironmentString" value="${when:when='${IsProd}' == 'true':Prod:else:Stage}"/>
but the value is just returned as a literal; the logic is not being processed.
Thanks,
1) Change the syntax to this:
${when:when='${var:IsProd}'=='true':inner=Prod:else=Stage}
Note:
:else= rather than :else:
:inner=
var:IsProd (assuming IsProd is another variable)
2) Move the whole conditional to the final Layout
<target ... layout="other stuff|${when:when='${var:IsProd}'=='true':inner=Prod:else=Stage}|other stuff" ... />
I've not managed to get conditionals working in variables. Perhaps someone else could say why.
This should work.
But it depends how the variable is used.
If you use:
${EnvironmentString}
Then it's evaluated when loading the configuration, and so you could use it for all parameters.
To evaluate it dynamically, use
${var:EnvironmentString}
But please note that ${var} only works if the parameter of the Target/Layout is of type Layout

How to pass string with symbol ";" from CustomActionData to a CustomAction using WiX?

I have deferred CustomAction in C# and another one to pass some properties to it.
<CustomAction Id="CustomAction1"
Property="CustomAction2"
Value="EncryptedString=[ENCRYPTEDSTRING]"
/>
However, if the property contain symbol ";" then
string encString=session.CustomActionData["EncryptedString"];
outputs only part before ";", because this symbol is considered as a delimiter between properties.
Is there any workaround to pass strings containing ";" ?
for example
ENCRYPTEDSTRING="12;3474dsfgee"
and output
encString="12"
You can't use DTF's CustomActionData; it assumes the custom action items are delimited by semicolons. Instead, grab CustomActionData directly and don't bother with the EncryptedString= prefix.
I will just add as an answer to get proper links. These may be helpful for implementing what Bob Arnson suggests:
Recommended: How to Access Windows Installer Property in Deferred Execution
Basic Differences in "Execute Immediate" and "Execute Deferred" CA
Everything About Properties [Wise Package Studio]

How can I check the existence of an environment variable?

The documentation for if/ifdef is slightly confusing. For <?if [expression] ?>, it states:
Variables can be used to check for existence
...
If the variable doesn't exist, evaluation will fail and an error will be raised.
It turns out if you just go: <?if $(env.MY_VAR) ?> and MY_VAR is not defined, compilation will fail. How do I check for existence?
Ordinarily, this is where one would use an ifdef, but these work strangely in Wix too. Instead of using $(var.Variable) syntax, they use <?ifdef Variable?>, meaning environment variables can't be checked this way.
What do I need to do to get the equivalent of normal c pre-processor:
#ifdef MY_ENVIRONMENT_VARIABLE
in Wix?
The correct way to reference environment variables in ifdef sections is:
<?ifdef env.MY_VAR?>
...
<?endif?>
This works as expected.
<Condition Message="Missing Environment Variable Message Goes Here"><![CDATA[%envvargoeshere]]></Condition>
Put the above element in the Package element of the wxs file.
The installation will fail at runtime (install time) with a nice message if the environment variable does not exist.

Using Item functions on metadata values

Background: I manage a fairly large solution. Every so often, people add a DLL reference to a project in the solution where they should've added a project reference. I want to issue a warning in such case. I want to do it by finding all reference with 'bin\debug' in their HintPath*. I know that references are Items in ItemGroup, with metadata "HintPath".
I expected something like this to work:
<Warning Text="Reference %(Reference.Identity) should be a project reference. HintPath: %(Reference.HintPath)"
Condition="%(Reference.HintPath).IndexOf('bin\debug') != -1"/>
However, Seems like I can't use string function IndexOf like that. I tried many permutations of the above, without success.
Edit: I know this check is not full-proof, but I just want to reduce honest mistakes.
Using MSBuild 4.0 Property Functions it is possible to do string comparisons:
<Target Name="AfterBuild">
<Message Text="Checking reference... '%(Reference.HintPath)'" Importance="high" />
<Warning Text="Reference %(Reference.Identity) should be a project reference. HintPath: %(Reference.HintPath)"
Condition="$([System.String]::new('%(Reference.HintPath)').Contains('\bin\$(Configuration)'))" />
</Target>
First not that your syntax is not correct for invoking functions, it would need to be:
%(Reference.HintPath.IndexOf(...)) # Note: not supported by MSBuild
However, property functions in MSBuild are not allowed on item metadata, so that is not going to help you either.
What you could work around this, by invoking a separate target which is basically called for every item.
<Target Name="CheckProjectReferences">
<MSBuild
Projects="$(MSBuildProjectFullPath)"
Properties="Identity=%(Reference.Identity);HintPath=%(Reference.HintPath)"
Targets="_Warn"/>
</Target>
<Target Name="_Warn">
<Warning Text="Reference $(Identity) should be a project reference. HintPath: $(HintPath)"
Condition="$(HintPath.IndexOf('bin\debug')) != -1"/>
</Target>
Frankly, I'm not sure if that is enough to catch all "violations". For example, the above will only work for bin\debug, but not for bin\Debug or other mixed-cased variations, which are functionally equivalent. To look for them as well, you'd need to call the IndexOf(string, StringComparison) overload, however just doing:
$(HintPath.IndexOf('bin\debug', System.StringComparison.OrdinalIgnoreCase))
Will not work, because the MSBuild overload resolution will pick IndexOf(char, Int32) and give you this error:
MSB4184: The expression ""bin\debug".IndexOf(bin\debug, System.StringComparison.OrdinalIgnoreCase)" cannot be evaluated. String must be exactly one character long.
So, you'll need to convince it by using the IndexOf(String, Int32, Int32, StringComparison) overload directly:
$(HintPath.IndexOf('bin\debug', 0, 9, System.StringComparison.OrdinalIgnoreCase))
You may need to also check for bin\Release or other variations. I'm not sure if that is the best way to figure out a reference should be a project reference, but if you know (and to a certain extend control) your environment it might be feasible.
#Christian.K is right in his analysis. Another solution would be to force the overload of type string using " for the quotes:
<Warning
Text="..."
Condition="$(HintPath.IndexOf("bin\debug", System.StringComparison.OrdinalIgnoreCase)) != -1" />

Specifying multiple MergeSection values when using LINK task on MSBuild

Is there anyway to specify more than one MergeSection value for the MSBuild LINK task? (The MergeSection param is the same as the /merge param for link.exe)
http://msdn.microsoft.com/en-us/library/ee862471.aspx
When calling link.exe you can specify more than one /merge value, but that doesn't seem possible with the MergeSection parameter.
So far the only way I can see to make this work is by using the AddtionalOptions param, but I'm hoping there's a better way to implement this parameter.
Thanks
I think you may have to use AdditionalOptions.
In the Link task the MergeSections property is a string value, not an array, so you can only set one string. Link.exe does not seem to allow you to pass multiple pairs in one command line parameter, you must specify a separate MERGE command line parameter for each pair. The Visual Studio property page only allows a single string for the MergeSections property.