WiX: Conditional Inner Text Not Evaluating Properly - c#

WiX first timer here.
I am building an installer for my product using WiX, and I am attempting to validate that MSMQ is installed before continuing the installation, following this SO answer. I am using a Condition element, defined like this:
<Condition Message="MSMQ must be installed in order to proceed.">
<![CDATA[MSMQ_INSTALLED<>"false"]]>
</Condition>
My Property and RegistrySearch look like this:
<Property Id="MSMQ_INSTALLED" Value="false" Secure="yes">
<RegistrySearch Id="Msmq.RS"
Root="HKLM"
Key="SOFTWARE\Microsoft\MSMQ"
Name="Values"
Type="raw"/>
</Property>
But it never evaluates properly. The installation stops with the message, regardless that the Registry Key does exist. So my questions are:
Am I using the Condition element correctly?
What have I defined incorrectly in the evaluation?
On further testing, I have found that the MSMQ_INSTALLED property contains the value "1: 0 2:", regardless of the Registry Key that I search for, either existing or fake.
EDIT: The Condition element exists inside the Product element; that is an important distinction as the Condition element is overloaded.
EDIT: Modified Condition to use CDATA directive, and invert Inner Condition logic, to more accurately reflect issue.

Well, the answer was on SO the whole time. Apparently, searching for a Registry Key is not supported out of the box with WiX, so I created a Custom Actions project and used the Binary tag to import it into my MSI, then run the Custom Action at the appropriate spot during the install. In my case, it was before LaunchConditions.
For reference, the code is:
public class CustomActions
{
[CustomAction]
public static ActionResult CustomAction1(Session session)
{
session.Log("Begin CustomAction1");
var key = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\MSMQ");
session["MSMQ_INSTALLED"] = key == null ? "-1" : "1";
return ActionResult.Success;
}
}
(The only class in the Custom Actions project)
<Binary Id="WixCustomAction" SourceFile="C:\work\RelayMed\src\dev\WixCustomAction\bin\Debug\WixCustomAction.CA.dll"/>
<CustomAction Id="CheckMsmq" BinaryKey="WixCustomAction" DllEntry="CustomAction1" Execute="immediate" Return="check"/>
(The import of the Binary into WiX, under the Product node.)
<InstallUISequence>
<Custom Action="CheckMsmq"
Before="LaunchConditions"/>
</InstallUISequence>
(The running of the Custom Action before LaunchConditions)
The Condition and the Property remained the same from the original post. The RegistrySearch was removed completely.
EDIT: Noted removal of the RegistrySearch tag.

Your authoring says "if HKLM\SOFTWARE\Microsoft\MSMQ#Values has the literal value 'false', then the install can continue."
Just use "MSMQ_INSTALLED" to check for any string found in the registry value.

Related

Default install path to ProgramFilesFolder without specifying ProgramFilesFolder in wxs

I currently have a legacy Visual Studio Install Projects project that creates an MSI. With this I can specify "TARGETDIR="somepath"" on the command line and have it install to "somepath". Now with WIX, if I don't specify ProgramFilesFolder in my wxs, "TARGETDIR" still works, however in my UI of the installer the default path is "C:\Manufacturer\Product" whereas I still want it to default to ProgramFilesFolder. Having support of "TARGETDIR" is necessary to also support upgrading to the legacy MSI from within the app itself.
I have found some ways to change the UI default directory to ProgramFilesFolder, however TARGETDIR doesn't change to this directory (or a directory the user specifies) so it still installs to C:\Manufacturer\Product.
Does anyone have any ideas here? I'm guessing some kind of custom action will do it but I feel like I've tried most of the suggestions such as:
https://stackoverflow.com/a/13058798/9993088 but this would override any selected directory in the or at the command line
I've also tried creating a WiX property "TARGETDIR" to expose it on the command line but as this is already an internal one it didn't make a difference.
How to set default value of WiX Msi install path in the ui to Program Files? as mentioned this would then prevent me from using "TARGETDIR" on the command line
https://wixtoolset.org//documentation/manual/v3/wixui/dialog_reference/wixui_advanced.html and then using a custom action to set "TARGETDIR" to "APPLICATIONFOLDER". With this I can set the default location to ProgramFilesFolder but then I haven't found a way to set "TARGETDIR" at the right time to use the command line value or the user selected one. I almost need a way to do "if 'APPLICATIONFOLDER' is not default; use its value OR if 'TARGETDIR' is not default; use its value ELSE use the default value". This also seems to not allow variable expansion so I can't use "[ProgramFilesFolder]", I have to explicitly write out "C:\Program Files..." which isn't ideal.
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLDIR" Name="blah">
As mentioned, I must be able to use "TARGETDIR" and not "INSTALLDIR" (although that works).
If I really have to use "INSTALLDIR" then I can make it work but it makes maintaining the legacy MSI and the WiX one tricky due to the nature of how they're used.
Edit
Solution:
<Custom Action="SetINSTALLDIR" Before="AppSearch">Not Installed</Custom> in both InstallExecuteSequence and InstallUISequence.
This points to:
<CustomAction Id="SetINSTALLDIR" BinaryKey="CustomActionsBinary" DllEntry="SetInstallDir" />
SetInstallDir is the following:
[CustomAction]
public static ActionResult SetInstallDir(Session session)
{
TraceLogger.yRTraceInfo(nameof(SetInstallDir));
string installDir = session["APPLICATIONFOLDER"];
string targetDir = string.Empty;
try
{
targetDir = session["TARGETDIR"];
}
catch (Exception e)
{
Console.log(e.Message);
}
if (string.IsNullOrEmpty(installDir) && !string.IsNullOrEmpty(targetDir))
{
session["APPLICATIONFOLDER"] = targetDir;
console.log($"Setting APPLICATIONFOLDER to {targetDir}");
}
return ActionResult.Success;
}
I suppose you could have a SetProperty custom action to assign INSTALLDIR a value if it is empty and TARGETDIR has a value and Not Installed. Schedule it early on in both the Install UI and Install Execute sequences ahead of AppSearch.
FYI in WiX INSTALLLOCATION is more commonly used. INSTALLDIR is more of an InstallShield thing and TARGETDIR a Visual Studio thing.
<SetProperty Id="INSTALLDIR" Value="[TARGETDIR]" Before="AppSearch">Not INSTALLDIR and TARGETDIRDIR and Not Installed</SetProperty>

How can I use Wix Properties

I'm working with Wix v4 to create a msi package. I had the problem, that I must set a Property in a CustomAction (C#) at the beginning of the installation.
This works fine, but now I'm a little bit confused. The property can't be used in all my cases.
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
<Property Id="ANUMMER" Value="A2000-0000" />
<Binary Id='CustomActionReadConfig' SourceFile='...'/>
<InstallUISequence>
<!-- Set the property over session["ANUMMER"] = "..." -->
<Custom Action="CustomActionReadConfig" Before="AppSearch" />
</InstallUISequence>
The ini file hasn't a problem
<IniFile Id="Programm.ini" Action="createLine" Key="ANUMMER" Name="Programm.ini" Section="Programm" Value="[ANUMMER]" Directory="Dir" />
For the directory I found the follow workaround
<SetDirectory Action="SetApplicationFolder" Id="APPLICATIONFOLDER" Value="[ProgramFilesFolder]\[COMPANYNAME]\[MYPROGRAMM]\[ANUMMER]" Sequence="ui"/>
But the shortcuts can' use it and I didn't find a workaround
<Shortcut Id="DesktopShortcut" Directory="DesktopFolder" Name="Programm [ANUMMER]" WorkingDirectory="Dir" Advertise="yes" Icon="DesktopIcon.exe" IconIndex="0" />
<Shortcut Id="DesktopShortcut" Directory="DesktopFolder" Name="Programm" WorkingDirectory="Dir" Advertise="yes" Icon="StartMenuIcon.exe" IconIndex="0">
<ShortcutProperty Key="Name" Value="Programm [ANUMMER]"/>
</Shortcut>
Like this, I need this property in some further cases. Do I use it wrong or do I have to use an special escape combination? Can't I use properties in Name attributes? Is there an other way, to use the input as variable witch I can set in the CustomAction? Or what is the basic problem, that I can't use such a custom runtime property in sutch ways?
Thanks for help
After searching for further options I found the reason for the problem for this in an other question here: Dynamically assigning name to shortcut at run time in WIX
The property value can be used in Formatted type. I wanted to use it in LongFileNameType (Simple Type) or in strings.
If someone knows a way, to fill a variable at runtime to solve this problem, it would be nice to share it with us.
Info: The value could also be a localization variable with the format !(loc.VARIABLE).

WIX cannot write value to registry if key contains space

I need to enable IE feature for WebBrowser control. To emulate IE11, I need to write a value to registry key
HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION
This manipulation need to be done during installation.
Here is my code in WIX script:
<?define var.IEFeatureEmulationKey = "Software\Microsoft\Internet Explorer\FeatureControl\FEATURE_BROWSER_EMULATION" ?>
....
<Fragment>
<DirectoryRef Id="TARGETDIR">
<Component Id="registryValues" Guid="{some-guid}" >
<RegistryKey Root="HKCU" Key="$(var.IEFeatureEmulationKey)" Action="create">
<RegistryValue Name="MyApp.EXE" Value="11000" Type="integer" Action="write"/>
</RegistryKey>
</Component>
</Fragment>
This code work only if $(var.IEFeatureEmulationKey) contains no spaces. But I need to write a value to this specific key.
Please help, how tell WiX to write value to registry even in registry key contains spaces.
UPD:
Added appropriate issue in WiX repository
This Blogpost uses some special syntax I have not seen before either. It states that variable can be defined this way, even if it contains a white space character like so:
<!–?define var.IEFeatureEmulationKey = "Software\Microsoft\Internet Explorer\FeatureControl\FEATURE_BROWSER_EMULATION" ?>
The !- let aside, it looks quite similar to what you got. However, your value contains more than one whitespaces. I'm not sure if thats the value you truly want or if it was just for pointing the whitespace out clearly...
Still cannot overcome the issue, however I want to show a workaround that helped me.
I used custom action that allowed in WiX. First of all I added custom .NET assembly with following method within it
[CustomAction]
public static ActionResult SetRegistryItems(Session session)
{
session.Log("Begin SetRegistryItems");
try
{
// this private method actually does manipulation with registry
SetRegistry();
}
catch (Exception e)
{
session.Log(e.ToString());
}
return ActionResult.Success;
}
Then this method should be referenced in WiX config file (.wxs)
<Fragment>
...
<CustomAction Id='SetRegistryItems' BinaryKey='<NameOfTheAssemblyWithoutExtension>' DllEntry='SetRegistryItems' Execute='immediate'/>
...
<InstallExecuteSequence>
...
<Custom Action="SetRegistryItems" Before="LaunchConditions"/>
...
</InstallExecuteSequence>
...
</Fragment>

How to set a Directory path in WIX using CustomAction?

I have a directory structure like this in WIX
<Directory Id="TARGETDIR" Name="SourceDir" >
<Directory Id="XXXFOLDER" Name="XXX">
<Directory Id="YYYFOLDER" Name="YYY">
<Directory Id="MAINFOLDER" Name="MAIN">
Now this MAINFOLDER Directory Resolves to D:\XXX\YYY\MAIN\
I get a path for the MAINFOLDER from a service which resolves to E:\XXX\YYY\MAIN
I have also assigned a customAction in a cs file
Below is the code
[CustomAction]
public static ActionResult GetNewDataPath(Session session)
{
sNewDataDir = xxxservice.GetPath();
if (!String.IsNullOrEmpty(sNewDataDir.ToString()))
{
sNewDataDir+= "\\MAIN\\";
}
session["MAINFOLDER"] = sNewDataDir;
return ActionResult.Success;
}
My Custom Actions are as below :
<CustomAction Id="GETDATAPATH" BinaryKey="InstallerCA"
DllEntry="GetNewDataPath" Execute="immediate"/>
This is the Install Sequence:
<Custom Action="GETDATAPATH" Before="CostFinalize" />
The sNewDataDir contains this value = "E:\XXX\YYY\MAIN" and I assign to session["MAINFOLDER"]. It gets assigned. But It does not get reflected on WIX side because my files are still being copied to D:\XXX\YYY\Main inspite of assigning it to E:\XXX\YYY\Main . How do we change the direcory path of session["MAINFOLDER"] using CustomAction?
It's usually a matter of sequence. The values of the properties are assigned to the Directory paths during the CostFinalize action per MSDN. You're custom action above must be sequenced sometime before CostFinalize runs in the execute sequence.
It can also be a matter of privilege: MAINFOLDER may be a restricted public property and isn't making it to the execute sequence (doesn't apply if your custom action ran in the execute sequence). Read about Restricted Public Properties to see if that might be your issue.
And it can also be your computer's anti-virus or some other issue with the script engines.
To have a good idea (or at least find someone else who can figure out what the issue really is) you will need to generate a good log of your failed attempt. Most of the time voicewarmup (or /l*v) is the best value to use (tends to give you most but not all of what you want, along with way too much of what you don't) and is the value most installation development experts use when generating the logs they use and share. It does slow down your installs a fair bit, however.

Data Driven MSTest: DataRow is always null

I am having a problem using Visual Studio data driven testing. I have tried to deconstruct this to the simplest example.
I am using Visual Studio 2012. I create a new unit test project.
I am referencing system data.
My code looks like this:
namespace UnitTestProject1
{
[TestClass]
public class UnitTest1
{
[DeploymentItem(#"OrderService.csv")]
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "OrderService.csv", "OrderService#csv", DataAccessMethod.Sequential)]
[TestMethod]
public void TestMethod1()
{
try
{
Debug.WriteLine(TestContext.DataRow["ID"]);
}
catch (Exception ex)
{
Assert.Fail();
}
}
public TestContext TestContext { get; set; }
}
}
I have a very small csv file that I have set the Build Options to to 'Content' and 'Copy Always'. I have added a .testsettings file to the solution, and set enable deployment, and added the csv file.
I have tried this with and without |DataDirectory|, and with/without a full path specified (the same path that I get with Environment.CurrentDirectory). I've tried variations of "../" and "../../" just in case. Right now the csv is at the project root level, same as the .cs test code file.
I have tried variations with xml as well as csv.
TestContext is not null, but DataRow always is.
I have not gotten this to work despite a lot of fiddling with it. I'm not sure what I'm doing wrong.
Does mstest create a log anywhere that would tell me if it is failing to find the csv file, or what specific error might be causing DataRow to fail to populate?
I have tried the following csv files:
ID
1
2
3
4
and
ID, Whatever
1,0
2,1
3,2
4,3
So far, no dice.
I am using ReSharper, could it be interfering in some way?
Updated
I have it mostly working now! I am able to use XML, but when I use CSV my column, which is named ID comes back as ID
Not sure why. I've checked the actual file of course, and no weird characters are present.
For anyone having a similar problem, I turned off Just My Code and enabled Net Framework source stepping, etc. so that I could get more detailed debug information. This allowed me to determine that ReSharper was causing me problems. I disabled resharper and modified my attributes like this:
[DeploymentItem("UnitTestProject1\\OrderService.csv")]
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "|DataDirectory|\\bin\\Debug\\OrderService.csv", "OrderService#csv", DataAccessMethod.Sequential)]
And it worked (except as noted). I am still suspicious of the "bin\debug" in my path, but I'm just happy my DataRow is no longer null.
Thanks!
Any ideas?
I was struggling with a similar problem today when trying to make data-driven tests work with CSV input file. The name of the first column had some garbage at the beggining of it, i.e. ID instead of just ID.
It turned out it was an encoding issue. The CSV file was saved in UTF-8 which adds a byte order mark at the beginning, obviously confusing the parser. Once I saved the file in ANSI encoding, it worked as expected.
I know it's an old question, but this information might help someone else ending up on this page.
Have you tried adding it through the properties window?
Go to Test menu -> Windows -> Test View -> the tests will load up.
Click on the test to alter i.e. TestMethod1 and press F4 (properties).
Look for 'Data Source' and click the ellipses next to it
It will walk you through a wizard that sets up the attributes properly for the TestMethod
You have the deployment part set up properly, which is normally the big stumbling block.
You also don't have to set the build action to Copy Always as the deployment does this for you. This option is used if you include items like .xml files you use for configs, or icons/images as part of your project.
Update 1:
Also try this tutorial on MSDN.
Update 2:
Try this post, involving ProcMon
I see that you said you tried putting the CSV itself into the testsettings file, but have you tried just putting in the directory?
<Deployment>
<DeploymentItem filename="Test\Data\" />
</Deployment>
Then your DataSource line will look something like this:
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "|DataDirectory|\\YOURCSV.csv", "YOURCSV#csv", DataAccessMethod.Sequential)]
If you do it this way, you don't need to specify the DeploymentItem line.
Our folder structure looks like this: Trunk\Test\Test\Data
We include: Test\Data in the deployment
We then access Test\Data via the |DataDirectory|\
All CSVs live within the \Data folder

Categories