How to execute .bat from .msi without including .bat in .msi itself? - wix

My goal is to create a simple msi package that is supposed to do nothing but running a .bat script located in the same folder with the .msi. I don't need to copy any files on target machine or create folders, etc. I tried to use Wix 3.5 with vb-script which will run .bat i need. The vb-code itself works perfectly, but inside .msi it acts in a strange way - i can see a message box with the 'path', i got no errors, but script doesn't execute .bat.
<Property Id="Launch">
<![CDATA[
Function Main()
Set shell = CreateObject("WSCript.shell")
path = Session.Property("SourceDir")
MsgBox path
shell.Run path & "sample.bat", 0, False
Set shell = Nothing
Main = 1
End Function
]]>
</Property>
<CustomAction Id="Die"
VBScriptCall="Main"
Property="Launch"
Return="check"
Impersonate="yes"/>
<InstallExecuteSequence>
<Custom Action='Die' Before='RegisterProduct'> NOT Installed </Custom>
</InstallExecuteSequence>
I also tried another way:
<Property Id='CMD'>cmd.exe</Property>
<CustomAction Id='LaunchFile' Property='CMD' ExeCommand='[SourceDir]sample.bat' Return='check' Impersonate='yes'/>
But if I put 'notepad.exe' in property - everythings works great, when I use 'cmd.exe' console opens and closes without executing my sample.bat. In case of 'notepad.exe', it shows the content of 'sample.bat'. Could you guys help me out with this?

Try adding /C to ExeCommand
<CustomAction Id='LaunchFile' Property='CMD' ExeCommand='/C [SourceDir]sample.bat' Return='check' Impersonate='yes'/>

Well, that was easy... Actually, everything was ok, but .bat itself was calculating relative paths from %WinDir%\System32\ I just put CD %~dp0 as the first line of my .bat and it started to work properly.

Related

Wix Toolset - CustomAction - ExeCommand - 1 fails 1 succeeds. Can you help understand why?

I need to run a cmd command during install, so I created a CustomAction which is set to run after InstallFiles.
The command is: Update.exe --someargs
The installer keeps on failing with:
"CustomAction RUN_UPDATE returned actual error code -1" which means nothing to me nor google.
For the sake of experiment, I created another CustomAction which just runs a batch file, and it works.
Here are the 2 CustomActions:
<CustomAction Id="RUN_UPDATE" Directory="APPLICATIONFOLDER" Execute="deferred" Impersonate="yes" ExeCommand="cmd.exe /c "Update.exe"" Return="check" />
<CustomAction Id="RUN_BAT" Directory="APPLICATIONFOLDER" Execute="deferred" Impersonate="yes" ExeCommand="cmd.exe /c "runme.bat"" Return="check" />
I really don't understand why the first one fails while the second one succeeds.
Both Update.exe and runme.bat exist in the APPLICATIONFOLDER
I use Impersonate = yes cause my app is installed in LocalAppDir and doesn't need elevated permissions.
Thanks!
The "Update.exe" application you execute returns -1 on exit. This is considered as error reported by your action, therefore the installer fails the installation. You told the installer to do so by setting Return="check". If the exe returned 0, that would be considered success (your batch file does that). You can ignore the application exit code, using Return="ignore". Or make Update.exe return 0, not -1.

How to call a command line program in WiX

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.

WiX/MSI: Redirect stdout to file, type 50 custom action

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?

What's wrong with my custom Action?

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

Call command line after installation in Wix

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" />