I have a MSI created using Wix
It has a property defined as below in product.wxs file
<Property Id="MY_FLAG">false</Property>
I will be calling my MSI from command line in the following ways
msiexec /i xyz.msi MY_FLAG=true
msiexec /i xyz.msi
I want my MSI to fail in case of invalid inputs such as
msiexec /i xyz.msi MY_FLAG=sDGsgfdsf
I want my installation to proceed only if someone passes MY_FLAG as true/false or not use MY_FLAG at all.
I want my MSI to fail if any other value apart from true/false is passed to MY_FLAG. The MSI should fail and not only abort. It should fail.
Thanks in advance for any help.
Check out the LaunchConditions action. It is checked early on in the install in the LaunchCondition action. This is the best practices way to do what you're attempting in an MSI.
Launch conditions must be true or the setup is aborted. Here is a snippet you can try.
Launch a cmd.exe and pass in 1 or 2 or some other value to msiexec.exe and then in rapid sequence: inspect, observe & observations.
Sample command line: msiexec /i WiXLaunchConditionTest.msi MYFLAG=0
<!-- LAUNCH CONDITION -->
<Property Id="MYFLAG" Hidden="yes" Secure="yes">Wrong Value</Property>
<Condition Message="Value for MYFLAG must be 1 (true) or 0 (false)">
<![CDATA[MYFLAG=0 OR MYFLAG=1]]>
</Condition>
Here is a whole sample for the above on github.com:
https://github.com/glytzhkof/WiXLaunchConditionTest
Related
Our problem is that custom actions don't run when using msiexec /i Setup.msi /qn (quiet mode installation without UI). They only run with normal installation with UI.
In our Product.wxs for example, we have defined the following:
<Binary Id="SetupCustomAction" SourceFile="$(var.SetupCustomActions.TargetDir)$(var.SetupCustomActions.TargetName).CA.dll" />
<CustomAction Id="UPDATE_CONFIG" BinaryKey="SetupCustomAction" DllEntry="UpdateConfiguration" Execute="commit" Return="check" Impersonate="no" />
<InstallExecuteSequence>
...
<Custom Action="UPDATE_CONFIG" After="InstallFiles"><![CDATA[NOT Installed AND USEIMPERSONATE="0"]]></Custom>
...
</InstallExecuteSequence>
Do we have to use "Quiet Execution Custom Action", trying this out didn't help though!
Please help!
The obvious explanation is that USEIMPERSONATE has the value 1 so the custom action will not run, but I assume perhaps you are setting it to 0 on the command line.
Apart from that it would be useful to know if the install actually succeeds, because if it normally requires elevation with a UAC prompt then this UAC dialog will not be shown, so the custom action will not run elevated and it will fail. The install might succeed because Commit custom actions run after the install, so "check" is not relevant because the install cannot roll back. If you configure that CA as an install custom action it might fail and roll back the install. So after InstallFiles is also not relevant because it's a Commit CA.
The log should show something.
Okay, I have found the cause of the error and a fix for it: The ALLUSERS OR PREVIOUSINSTALLSCOPE (read from the registry) Properties must be set to "1". That way, the DISABLE_IMPERSONATE Custom Action gets run and sets the USEIMPERSONATE Property to "0". Then UPDATE_CONFIG and other Custom Actions get run.
To sum up, the solution is:
Change DISABLE_IMPERSONATE Property to this: <Custom Action="DISABLE_IMPERSONATE" After="AppSearch"><![CDATA[ALLUSERS=1 OR PREVIOUSINSTALLSCOPE="1"]]></Custom>
Call msiexec like this: msiexec /i Bechtle.A365.Office.Client.msi /qn ALLUSERS=1
Thanks to #Ritmo2k, #Brian Sutherland and #PhilDW for pointing me to the right direction.
When I'm building my MSI file, and I use a basic condition, the expected happens. For example, let's say that I have this in Setup File:
<Property Id="myProperty" Value="0"/>
<Condition Message="Value of myProperty is [myProperty], and it should be 1.">
<![CDATA[Installed OR myProperty = "1"]]>
</Condition>
If I build this and run the MSI file, it works -- that is, it displays the error message and quits.
Working condition when running MSI
However, if I put the MSI into a Bundle, it doesn't work. That is, when I put just this into my Bootstrapper ("Properties" below is the name of my Setup project -- bad name, I realize):
<Chain>
<MsiPackage SourceFile="$(var.Properties.TargetPath)"/>
</Chain>
And then I run the setup file, I get an error. When the installation starts, it checks the condition, gives me the expected message box (same as above), and then I get this error message:
Setup Failed
Looking at the Log, I get three error messages:
Error 0x80070643: Failed to install MSI package.
Error 0x80070643: Failed to execute MSI package.
Error 0x80070643: Failed to configure per-machine MSI package.
With the exit code:
Exit code: 0x643, restarting: No
I'm such a noob at WiX that I'm not even sure how to go about researching what the problem is -- I can't even ask an intelligent question. Hence, I'm reaching out to you kind folks!
(I'm using WiX 3.10 and Visual Studio 2015)
EDIT:
Thanks for getting back to me! I tried your suggestions:
In the installer file, I made the property public and I made it secure. I left the condition the same, and, since I don't think that I should get the value here as opposed to in the bootstrapper, I left the value of the property out. Here is the code that I made for the property/condition:
<Property Id="MY_PROPERTY" Secure="yes"/>
<Condition Message="MY_PROPERTY is [MY_PROPERTY]. Should be 1">
<![CDATA[Installed OR MY_PROPERTY = "1"]]>
</Condition>
Then, in the boostrapper file, I added a child element of and gave it a value:
<MsiPackage SourceFile="$(var.LCondErrorInstaller.TargetPath)">
<MsiProperty Name="MY_PROPERTY" Value="0"/>
</MsiPackage>
When I ran it, I got almost the same behaviour as I did before, except for one difference -- when I get the error message. This time, I get the pop-up screen with the Message condition and the same error message as I did before (see "Setup Failed" above), except this time I get it happens little later in the installation, making me think that the condition is, in fact, getting triggered in the bootstrapper.
As far as the log files, they look the same (I'm not sure how to get log files of the MSI when running the Burn file, what I do now is just run the Burn file with the flag "/l", like so: > file.exe /l logFile.log).
For clarity, here are the parts of the log file that appear to be important:
Error 0x80070643: Failed to install MSI package.
Error 0x80070643: Failed to execute MSI package.
Error 0x80070643: Failed to configure per-machine MSI package.
...
Exit code: 0x643, restarting: No
I should have been more specific when I initially asked the question about what kind of behaviour I'm looking for...
I will have more than just that one MSI file in the Burn file. What I want to do is this: when the Burn file installs, if there is a condition in one of the MSI files that is not met, I want that MSI file to simply not be installed, and the rest of the MSIs to be installed. I don't care about the UI.
If there's another way to do this, I'm all ears.
If you have launch conditions in the MSI you can replicate or move those launch conditions into the bootstrapper bundle itself to stop this type of behaviour.
Launch condition failure returns Fatal Error 1603 (0x643 in hex) which is what I would expect to see when the MSI launched by the bootstrapper fails due to launch condition not met.
You should see something like this in the msi's logs
Action ended 17:33:38: LaunchConditions. Return value 3.
MSI (c) (08:4C) [17:33:38:610]: Doing action: FatalError
Action 17:33:38: FatalError.
Action start 17:33:38: FatalError.
...
MSI (c) (08:4C) [17:33:41:188]: MainEngineThread is returning 1603
To elaborate, you would have to change your msi package definition to the following to get it to run properly through the bootstrapper
<Chain>
<MsiPackage SourceFile="$(var.Properties.TargetPath)">
<MsiProperty Name="MYPROPERTY" Value="1"/>
</MsiPackage>
</Chain>
Additionally if you want to pass in a property from your bootstrapper to your MSI the property must be a public property which is a property whose name is ALL CAPS.
If you want to use this property somewhere in the Install phase of your msi you must also mark this property as secure.
I've tried tons of times to try to use a custom action just to simply copy a file to another place. I do think this should be easily worked, but... I was frustrated that it is always failed!
I post my code and the error log, please any one kindly enough to point me the way out... Thank you in advance!!
<CustomAction Id="QtExecCopyPropertyFileCmd"
Property="QtExec64CmdLine"
Value=""[SystemFolder]cmd.exe" /c copy "C:\Program Files\AptWare\AptWare View\Server\broker\webapps\portal\WEB-INF\classes\portal-links.properties" "C:\ProgramData\AptWare\VDM""/>
<CustomAction Id="QtExecCopyPropertyFile"
BinaryKey="WixCA"
DllEntry="CAQuietExec64"
Execute="immidiate"
Return="check"/>
And here is my action sequence:
<InstallExecuteSequence>
<Custom Action='SetOldPortalLinkFile' After='InstallInitialize'>NOT (Installed OR PORTALLINKFILEEXISTS) AND OLDPORTALLINKFILEEXISTS</Custom>
<Custom Action='SetPortalLinkFileDestFolder' After='SetOldPortalLinkFile'>NOT (Installed OR PORTALLINKFILEEXISTS) AND OLDPORTALLINKFILEEXISTS</Custom>
<Custom Action="QtExecCopyPropertyFileCmd" After="SetPortalLinkFileDestFolder">NOT (Installed OR PORTALLINKFILEEXISTS) AND OLDPORTALLINKFILEEXISTS</Custom>
<Custom Action="QtExecCopyPropertyFile" After="QtExecCopyPropertyFileCmd">NOT (Installed OR PORTALLINKFILEEXISTS) AND OLDPORTALLINKFILEEXISTS</Custom>
And some approach I've tried:
I do not think this is due to quto, or file/dir existence, from the log I copied the generated cmd running in a cmd shell it works
It is not related with 32bit or 64bit CA, I tried both 32 and 64 bit. All same failure.
I am not sure if this relate with privilege, but if I try deferred CA, still I got error... And in my scenario I need a immediate CA because the copied file will be removed during uninstall previous version. So I need it run before InstallFinalized
The last, error log:
操作 6:22:34: QtExecCopyPropertyFileCmd。
操作开始 6:22:34: QtExecCopyPropertyFileCmd。
MSI (s) (90:88) [06:22:34:743]: Transforming table CustomAction.
MSI (s) (90:88) [06:22:34:743]: PROPERTY CHANGE: Adding QtExec64CmdLine property. Its value is '"C:\Windows\SysWOW64\cmd.exe" /c copy "C:\Program Files\AptWare\AptWare View\Server\broker\webapps\portal\WEB-INF\classes\portal-links.properties" "C:\ProgramData\AptWare\VDM"'.
操作结束 6:22:34: QtExecCopyPropertyFileCmd。返回值 1。
MSI (s) (90:88) [06:22:34:743]: Doing action: QtExecCopyPropertyFile
操作 6:22:34: QtExecCopyPropertyFile。
操作开始 6:22:34: QtExecCopyPropertyFile。
MSI (s) (90:88) [06:22:34:746]: Transforming table CustomAction.
MSI (s) (90:98) [06:22:34:748]: Invoking remote custom action. DLL: C:\Windows\Installer\MSIB138.tmp, Entrypoint: CAQuietExec64
MSI (s) (90:2C) [06:22:34:762]: PROPERTY CHANGE: Deleting QtExec64CmdLine property. Its current value is '"C:\Windows\SysWOW64\cmd.exe" /c copy "C:\Program Files\AptWare\AptWare View\Server\broker\webapps\portal\WEB-INF\classes\portal-links.properties" "C:\ProgramData\AptWare\VDM"'.
CAQuietExec64: Error 0x80070001: Command line returned an error.
CAQuietExec64: Error 0x80070001: CAQuietExec64 Failed
CustomAction QtExecCopyPropertyFile returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
I go the answer now.
http://sharp-gamedev.blogspot.com/2009/07/wix-again.html
In above link, clearly, CAQuietExec must have some bugs to support build in dos command such as copy, ren, del etc. However, use xcopy.exe instead of copy it work, I tested, it really work. I think for ren or del can find other substitutions as well.
What a big trap for me!!
Thanks all the kindly replied!
As per my understand you need to copy file from your installation location to another location before uninstall the previous version in upgrade. Since the upgrade will remove all files are already installed. In that case try this code. If you schedule the Custom action before uninstall previous version it will work. I tried this with test project and its work for me.
<RemoveExistingProducts Before="InstallInitialize" />
<Custom Action="QtExecCopyPropertyFileCmd" After="AppSearch"> (NOT Installed)</Custom>
<Custom Action="QtExecCopyPropertyFile" After="QtExecCopyPropertyFileCmd"> (NOT Installed)</Custom>
The thing I see "wrong" is that you wrote a custom action at all. The CopyFile element supports the use of the MoveFile table to teach MSI that this file needs to be copied. This will then fully support rollback, upgrade and uninstall stories. You lose all of that when you shell out of process to a dos command.
Seems like you found your solution already.
But I am using copy almost exactly as you did, I think it might be worth sharing my solution for anyone do want use copy instead of xcopy. I did tried xcopy solution from your link, but for me, xcopy is more suitable for batch copy, on the other hand, I am copying single file and I also want define my own destinate file name but it is tricker to do using xcopy with CA (if possible).
For my project, I am using deferred Execute instead of immediate, immediate is possible as well, but the syntax will be different:
<!--Syntex for deferred-->
<!--<Property Id='QtExecCA' Value='"cmd.exe" /c copy C:\temp\test.txt C:\temp\test2.txt' />-->
<!--Syntex for immediate-->
<Property Id='QtExecCmdLine' Value='"cmd.exe" /c copy C:\temp\test.txt C:\temp\test2.txt' /><CustomAction Id='QtExecTest' BinaryKey='WixCA' DllEntry='CAQuietExec'
Execute='immediate' Return='check'/>
.
.
.
<InstallExecuteSequence>
<Custom Action='QtExecCA' After='InstallInitialize'/>
</InstallExecuteSequence>
That is in essential my code for copy.
It took my a few tries to get QtExec syntax right, and I think that might be where you have problem.
Reference for QtExec
I have the following command running at the end of my package install for an application.
<Property Id="WixShellExecTarget" Value="[INSTALLDIR]RCR.VDS.exe" />
<CustomAction Id="LaunchApplication" BinaryKey="WixCA"
DllEntry="WixShellExec" Impersonate="no" />
I can't use [#myApplication] because I run heat on my output folder on my build server so I don't know the auto generated id of my application. Any ideas on how to silently run my application after the install?
The log file shows this for the command line section
******* CommandLine: **********
MSI (c) (30:74) [09:47:14:156]: Note: 1: 2203 2: VDSInstall.msi 3: -2147287038
MSI (c) (30:74) [09:47:14:156]: MainEngineThread is returning 2
Please see: How To: Run the Installed Application After Setup
If you want the custom action called during a silent install add:
<InstallExecuteSequence>
<Custom Action="LaunchApplication" After="InstallFinalize">SOMECONDITION</Custom>
</InstallExecuteSequence>
Note SOMECONDITION should be an expression that checks the EXE is installed and the user wants the program to be launched.
The are a copy things you can consider:
The identifier from heat.exe will be stable. So you can use the ugly identifer in your [#UglyFileId1234abcef45612345asdf] custom action.
a. You could also apply a XSLT to transform the heat output for the executable file's Id to something nicer than the ugly identifier. Depends how readable you want the launch custom action to be.
If you want the executable launched silently then you'll probably want the Quite Execution custom action instead of the Shell execute custom action that "LaunchApplication" uses.
Someone told me there was a way for the CustomAction in WIX to display the output in the console log. I'm including an .exe called XmlPreprocess.exe to manipulate my web.config, based on parms in a file called SettingsFileGenerator.xml,
I'm running like this:
msiexec /i bin\Debug\TFBIC.RCT.WCFWebServicesWIXSetup.msi /L*V "C:\logs\WixInstall01.log"
This is my WIX build file:
<CustomAction Id="**SAMPLE_CONFIG**" BinaryKey="XMLPREPROCESS" ExeCommand="/i:"[INSTALLLOCATION]web.config" /x:"[INSTALLLOCATION]SettingsFileGenerator.xml" /e:QA /d:ServiceLocation=[SERVICELOCATION]" Execute="deferred" />
<Binary Id="XMLPREPROCESS" SourceFile="../TFBIC.RCT.WCFWebServices/RequiredBins/XMLPreprocess.exe" />
<InstallExecuteSequence>
<Custom Action="SAMPLE_CONFIG" After="StartServices"><![CDATA[NOT Installed]]></Custom>
</InstallExecuteSequence>
Install log shows this:
Action 15:22:27: StartServices. Starting services
Action start 15:22:27: StartServices.
MSI (s) (58:CC) [15:22:27:898]: Note: 1: 2205 2: 3: ServiceControl
MSI (s) (58:CC) [15:22:27:898]: Note: 1: 2228 2: 3: ServiceControl 4: SELECT `Name`,`Wait`,`Arguments`,`Event`, `Action` FROM `ServiceControl`, `Component` WHERE `Component_` = `Component` AND (`Action` = 0 OR `Action` = 1 OR `Action` = 2)
Action ended 15:22:27: StartServices. Return value 1.
MSI (s) (58:CC) [15:22:27:899]: Doing action: SAMPLE_CONFIG
Action 15:22:27: SAMPLE_CONFIG.
Action start 15:22:27: **SAMPLE_CONFIG**.
SAMPLE_CONFIG:
Action ended 15:22:27: **SAMPLE_CONFIG**. Return value 1.
This is my very first attempt to do WIX, so please bear with my ignorance.
Thanks
UPDATE:
This is a quote from another forum - but he doesn't specify how it works and he doesn't seem to check back often.
WiX has a custom action that captures
the console output and sticks it
directly into the verbose MSI log, so
that's what I use.
reference: http://xmlpreprocess.codeplex.com/Thread/View.aspx?ThreadId=79454
Would this be the tool he is talking about?
http://wix.sourceforge.net/manual-wix2/qtexec.htm
I get this error when trying it:
error LGHT0103: The system cannot find the file 'wixca.dll'.
I have searched entire disk for this .dll and could not find it.
To enable all possible logging while installing an msi, use the /lvx* logfile.txt option. However, even this will not log the STDOUT and STDERR output of command line applications invoked as a custom action.
If you have written the custom action yourself, you can add such logging to it. For example, the DTF libraries that come with wix have a handy Session.Log method that you can call. See c:\program files\windows installer xml v3\doc\dtf.chm, topic "Writing Managed Custom Actions" for more information.
If you have not written the application, you could write a custom action to wrap it. Such a wrapper could use the .NET Process class to invoke an executable, read the StandardError and StandardOutput streams, and log everything with the Session.Log method mentioned above.
edit: I don't know of any standard custom action in wix that sends console output to the log. Try the wix-users mailing list.
In Wix 3.10 you can use the Quiet Execution custom action to run an executable (silently, e.g. without a command window popup), and the console output will end up in the msi log.
As mentioned, you need the WixUtilExtension reference to get access to this feature.