I want to call a command line program of OpenOffice in WiX. To do so, I created a custom action, as seen below:
<CustomAction Id="ca_RunOpenOfficeProgram" Return="check" Directory="TARGETDIR" ExeCommand="cmd.exe /K "C:\OpenOffice.org3\program\unopgk.com list --shared"" />
The Custom Action is being run in an Install Execute Sequence:
<InstallExecuteSequence>
<Custom Action="ca_RunOpenOfficeProgram" Before="InstallFinalize" />
</InstallExecuteSequence>
When running the resulting MSI-File, I receive the following error message in a command line:
Invalid command 'C:\OpenOffice.org3\program\unopkg.com' could not be found.
Well, of course, the command is available and I may run it from command line. But it just doesnt work if the command line is being called by WiX.
It`s also notable that the part 'list --shared' is completely ignored.
Does anyone know what`s going on here?
I would recommend using the ShellExecute custom action from the WiX toolset.
Here is the sample code:
<Property Id="WixShellExecTarget" Value="[#myapplication.exe]" />
<CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
Change the Value of property WixShellExecTarget to cmd.exe /K "C:\OpenOffice.org3\program\unopgk.com list --shared" and it should work.
Are you sure that cmd.exe /K "C:\OpenOffice.org3\program\unopgk.com list --shared" works? It looks like you have the quotes in the wrong place.
And, do you really want the console window kept open (/k)? Does the user have to enter more commands before the installation continues? You might want /c instead. See the help with cmd /?.
But, if there is only one command needed, why not just run the program directly?
ExeCommand=""C:\OpenOffice.org3\program\unopgk.com" list --shared"
Finally, if the above is the only command needed and assuming C:\OpenOffice.org3\program\unopgk.com is a console application, a useless console window will be opened. This can be avoided with WiX's QtExecCmdLine custom action.
If you are running the program to gather information, and it is a console application, you could do:
cmd /c "C:\OpenOffice.org3\program\unopgk.com" list --shared >path\out.txt
and use another custom action to read the file and make decisions on it or show it to the user in a Windows Installer dialog. This would be a better experience than leaving the user with a console window with a blinking prompt that they have to exit out of.
Found the solution for my problem:
1) As written in my answer to Toms post, I had in typo in the command line ... stupid.
2) The quotes regarding the command line call had been misplaced (Toms answer)
3) I found out that running 'unopkg.com' with the 'shared' parameter is only executed when the command line is being run with administration rights. I thought that the attribute 'impersonated="yes"' in my CustomAction would be enough, but it didn`t help.
Guess I have to dig deeper into documentation of WiX regarding UAC.
Also thanks to Ralf. I didnt try his solutions, but you might give it a shot.
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.
I have a WiX installer all rigged up to run SqlPackage.exe to deploy some installed .dacpac-packaged SQL applications to a database. Actually deploying the database files as follows will succeed:
<Property Id="CONNSTRING" Value="Data Source=localhost;Integrated Security=True;Initial Catalog=MPQS-DACPAC" />
<Property Id="SQLPACKAGEPATH" Value="C:\Program Files (x86)\Microsoft SQL Server\110\DAC\bin\SqlPackage.exe" />
<CustomAction Id="DeployDatabase" Property="SQLPACKAGEPATH"
ExeCommand='/Action:Publish /tcs:"[CONNSTRING]" /sf:"[#My.Database.dacpac]" /p:BackupDatabaseBeforeChanges=True /p:RegisterDataTierApplication=True'
Return="check" Execute="deferred" Impersonate="yes" />
<InstallExecuteSequence>
<Custom Action="DeployDatabase" After="DuplicateFiles">NOT REMOVE</Custom>
</InstallExecuteSequence>
...and I can watch the output in the presented console window, mid-install.
However, it doesn't always succeed; for example, CONNSTRING can be specified in a dialog and may be incorrect. If there are errors, they appear for an instant, then the console closes and I get a 1722 error in the logs.
To capture the console output, I have tried:
<CustomAction Id="DeployDatabase" Property="SQLPACKAGEPATH"
ExeCommand='/Action:Publish /tcs:"[CONNSTRING]" /sf:"[#My.Database.dacpac]" /p:BackupDatabaseBeforeChanges=True /p:RegisterDataTierApplication=True > "[DBLogs]test.log"'
Return="check" Execute="deferred" Impersonate="yes" />
The > "[DBLogs]test.log" on the end should (theoretically) redirect output to a file at that location, but instead, the installer fails out at the moment the console window is shown. It appears that no text is displayed in the console in the instant it is presented.
The kicker is: I can copy the command that is logged with the error (with the > properly resolved to >), paste it into my own cmd window, and it will execute and log.
What am I doing wrong?
And more importantly: what can I do to execute this command and save stdout+stderr to a logfile?
Note: I have also tried this with the type 34 syntax (this way resolves to a type 50). Both exhibit the same behavior.
EXE custom actions have a number of concerns. Read:
Integration Hurdles for EXE Custom Actions
To address several of these issues, including stderr/stdout, Wix includes the Quiet Execution Custom Action.
I have had the same problem where I wanted to log the output of SqlPackage.exe during a WIX MSI Install, so I created a WIX binary extension that handles getting the standard output/error and logging it to a file for sqlpackage.exe.
Check it out at https://wixdacpacinstaller.codeplex.com/.
I made it free and open source.
Quick Snippet from the docs to show how to use it:
<!-- first, add the binary extension. Be sure to specify the source file as WixDacPacExtension.CA.dll. -->
<Binary
Id="WixDacPacExtensionBinary"
SourceFile="<Path to your file>\WixDacPacExtension.CA.dll"/>
<!-- Create a custom action to run first and set up all the parameters that are -->
<!-- passed to the Wix DacPac Extension. The property name MUST MATCH -->
<!-- the name of the custom action that executes the binary defined above. -->
<!-- The parameters in the Value property are semi-colon delimited. -->
<CustomAction
Id="SetupDacPacWIXDacPacInstallerExampleCustomAction"
Property="DacPacWIXDacPacInstallerExampleCustomAction"
Value="ShowUI=True;SqlPackagePath=c:\Program Files (x86)\Microsoft SQL Server\120\DAC\bin\SqlPackage.exe;DacPacPath=[INSTALLFOLDER]WIXDacPacInstallerExample.dacpac;LogFilePath=[TempFolder]\WIXDacPacInstallerExample.dacpac.log;TargetServerName=[DATABASESERVER];TargetDatabaseName=WIXDacPacInstallerExample;OtherParameters=/p:RegisterDataTierApplication=True /p:BlockWhenDriftDetected=False /p:BlockOnPossibleDataLoss=False"
/>
<!--
This custom action will execute the extension with the parameters from Step #1.
NOTE: the Id of this custom action matches the Property of the custom action
from Step #1.
-->
<CustomAction
Id="DacPacWIXDacPacInstallerExampleCustomAction"
BinaryKey="WixDacPacExtensionBinary"
DllEntry="Execute"
Execute="deferred"
Return="check"
/>
I believe there is something about SQLPackage.exe which routes the output (both standard and error) in a non-standard way. I encountered difficulties when running SQLPackage.exe from PowerShell and also had difficulties. No matter what I did, I could not get PowerShell to capture the output from SQLPackage.exe. Ultimately I was able to resolve the problem by using Start-Process cmdlet instead of Invoke-Expression to run SQLPackage.exe, and pass in -RedirectStandardOutput $out and -RedirectStandardError $errorLog. In this way I was at least able to capture the output, but I did notice that even when an error occurred, it wasn't redirected along with the Error redirect, but rather it was redirected to the standard output stream. I don't know exactly why this happens, but it seems like this would be relevant to the results you had in WiX.
I would love to see more about how you were able to incorporate SQLPackage into a WiX installation. Do you have any further information you can share, or resources on how you approached this?
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 am using wix and want to call command line after installation.
How can I do this?
My command line is here "bcdedit.exe /set {current} nx AlwaysOff" // this makes dep off
Yes, I've read about custom actions, but I didn't see any example with command line.
P.S. bcdedit is usual exe in Win 7 and higher.
P.S. currently I have next script and it does not work:
Directory ="INSTALLLOCATION"
ExeCommand ='echo hello> echo_test.txt'
Execute ="immediate"
Return ="asyncNoWait"
/>
echo is not an executable, it is the command of the command processor cmd.exe. Change your ExeCommand value to cmd.exe /c "echo hello >echo_test.txt".
Your echo_test.txt would be in an arbitrary directory, you have to use absolute paths to get predictable results.
Ok, this example works...
<CustomAction Id ="echo_test"
Directory ="INSTALLLOCATION"
ExeCommand ='NOTEPAD.EXE echo_test.txt'
Execute ="immediate"
Return ="asyncNoWait"
/>
My test example with echo didn't worked for some reason.
And bcdedit does not exist on WinXP, where I am testing now...
Hi there are lots of example available on net...
try these links
http://wix.sourceforge.net/manual-wix2/qtexec.htm
Execute Command Line In WiX Script?
WiX - CustomAction ExeCommand - Hide Console
Or try this example:
<CustomAction Id="SetQtExecCmd" Property="SetQtExec"
Value=""[PutPathOfThisFileHere]bcdedit.exe" /set {current} nx AlwaysOff" />
<CustomAction Id="SetQtExec" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="immediate" Return="check" />
I want to run a program when my software is being un-installed, it is a simple form that should gather some feedback on why people are un-installing my software.
I found some WiX examples that works to some degree.
It works pretty fine with a standard windows program (notepad), but when I try to run my own program, it does not work. I think the problems is that the program is removed, before it has been run.
I have tried to print the logs, but they did not give me any clues of what to do.
My code so far:
<CustomAction Id="LaunchFeedBackForm"
ExeCommand="notepad.exe" Directory="INSTALLDIR"
Return="asyncWait" >REMOVE="ALL"</CustomAction>
<InstallExecuteSequence>
<Custom Action="LaunchFeedBackForm" After="InstallValidate"/>
</InstallExecuteSequence>
So I need in some way the un-install process to halt or what ever, until the user has closed the feedback form. After the form has been closed, it should continue and remove all software including the feedback form program.
The way you'd like to get user's feedback seems not that natural to me. If I understand you correctly, you'd like to show this feedback form and wait while a user fills it in, and later on continue with uninstallation, right?
To my own experience, when a user decides to uninstall software, he/she would like it to get uninstalled as quickly and clear as possible. Bringing a "must fill" form in front of them would only negatively affect the user experience. Moreover, as you can see, it is more difficult from the technical point of view. I suppose you've also thought about passing this feedback on to your side, right? Is it emailing the info entered by user? How do you ensure the email gets sent?
Alternatively, you can have this form online on a certain web page of your site and start it when the uninstall is done (NOT in progress). In this way, you don't annoy the user blocking the uninstall process.
So, I would do the following:
have a custom action that starts a browser with a URL you need
the installation program SHOULD NOT WAIT for this to complete
BTW, do not expect lots of feedback - people rarely bother spending some time to give feedback :)
If your EXE is in the MSI, try using the FileKey attribute: http://wix.sourceforge.net/manual-wix2/wix_xsd_customaction.htm
For example, if your EXE is defined like this:
<File Id="FeedbackExe" Name="FeedbackExe.exe"/>
you can use:
<CustomAction Id="LaunchFeedBackForm"
FileKey="FeedbackExe" Execute="deferred"
Return="asyncWait">REMOVE="ALL"</CustomAction>
You can also try creating an uninstall log to see what happens with the custom action:
msiexec.exe /x <ProductCode> /L*V "C:\uninstall.log"
where you use your actual ProductCode.
So I need in some way the un-install process to halt or what ever,
until the user has closed the feedback form. After the form has been
closed, it should continue and remove all software including the
feedback form program.
To do this one would set After="InstallValidate"
<InstallExecuteSequence>
<Custom Action="InstallCustomLogic" After="InstallFinalize" />
<Custom Action="UninstallCustomLogic"
After="InstallValidate" >
NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
</Custom>
</InstallExecuteSequence>
Set Return attribute to check so that the installer will halt until the specified executable returns.
<CustomAction Id="UninstallCustomLogic"
Directory="INSTALLFOLDER"
ExeCommand="[INSTALLFOLDER]\RetailConnectCustomLogic.exe uninstall"
Return="check"
/>
This is useful in the event where you need to run something located in the program directory before uninstalling the program. One could also set the attribute to ignore if it doesn't return 0