WIX invoking Batch file in InstallExecution Sequence - batch-file

I am new to WIX and I am trying to invoke a batch file from my WIX installer.
MyBatch.bat contains a simple Copy of a Text file from c:\ to D:\
I am unable to invoke the batch file. Below is my code.
Please help me out.
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define ProductName='Test Product' ?>
<?define ProductVersion='1.0.0.0' ?>
<?define ProductCode='b7bc7c6f-9a4e-4973-be84-eca8e3427c97'?>
<?define UpgradeCode='06a81104-1e30-463d-87e1-e8a79b4c682a'?>
<?define Manufacturer='Respond'?>
<Product Id="$(var.ProductCode)" Language="1033" Version='$(var.ProductVersion)' Name='Respond'
Manufacturer='$(var.Manufacturer)' UpgradeCode='$(var.UpgradeCode)'>
<Package InstallerVersion='200' Compressed='yes' />
<Media Cabinet='media1.cab' EmbedCab='yes' Id ='1' />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id='ProgramFilesFolder'>
<Directory Id='INSTALLDIR' Name='$(var.ProductName)'>
<Component Id='ProductComponent' Guid='b11556a2-e066-4393-af5c-9c9210187eb2'>
<File Id='Calc' DiskId='1' Source='C:\WINDOWS\system32\calc.exe'/>
<File Id='my_batch_script' Name='MyBatch.bat' DiskId='1' Source='MyBatch.bat' />
</Component>
</Directory>
</Directory>
<Directory Id='ProgramMenuFolder'>
<Directory Id='ApplicationProgramsFolder' Name='$(var.ProductName)'>
<Component Id='MainExecutable' Guid='EDED00D8-2465-46CA-86D6-B20DE921EFA6'>
<Shortcut Id='ShortcutCalc' Description='$(var.ProductName)' Name ='Calculator of Windows'
Target='[INSTALLLOCATION]Calc.exe' WorkingDirectory='INSTALLLOCATION'/>
<RemoveFolder Id='ApplicationProgramsFolder' On='uninstall'/>
<RegistryValue Root='HKCU' Key='Software\$(var.Manufacturer)\$(var.ProductName)' Type ='multiString'
Name='installed' Value='1' KeyPath='yes'/>
</Component>
</Directory>
</Directory>
</Directory>
<Feature Title='SetupProject1' Level='1' Id ='AppFeature'>
<ComponentRef Id='ProductComponent' Primary='yes'/>
<ComponentRef Id='MainExecutable'/>
</Feature>
<Property Id='WIXUI_INSTALLDIR' Value='INSTALLDIR' ></Property>
<WixVariable Id='WixUILicenseRtf' Overridable='yes' Value='License.rtf'/>
<?include CustomActions.wxi?>
<UI>
<?include UISequence.wxi?>
<?include Dialogs.wxi?>
</UI>
<CustomAction Id="BatchCmd" Property="ThePath" Value="[INSTALLDIR]MyBatch.bat" />
<CustomAction Id="BatchRun" Property="ThePath" ExeCommand='"[INSTALLDIR]MyBatch.bat"' Return='asyncWait' Impersonate="yes"/>
<InstallExecuteSequence>
<Custom Action="BatchCmd" After="InstallFiles" >Not Installed </Custom>
<Custom Action="BatchRun" After="BatchCmd">Not Installed </Custom>
</InstallExecuteSequence>
</Product>
</Wix>

This worked for me.
<CustomAction Id="test"
ExeCommand="[INSTALLDIR]MyBatch.bat"
Directory="INSTALLDIR" Execute="deferred" Return="asyncWait"/>
<InstallExecuteSequence>
<Custom Action="test" After="InstallFiles">NOT Installed</Custom>
</InstallExecuteSequence>

It seems you're solving your problem with the wrong tool. If the file to copy is a part of your application, you should author a Component with this File inside. Otherwise, if you need just to copy some external file already present on a target system, you should author a CopyFile element, nesting it under Component element.

I agree with Yan, you solve your task the wrong way.
As for batch files, they are not executed on its own like an .exe, it is cmd.exe which reads the bat file and executes it line by line.
To run a batch file, you should run it this way:
cmd.exe /c batch.bat

Add your CustomAction defnition as given below. Make sure that Return="ignore" if you are not returning anything from bat file.
<CustomAction Id="RunBat" Directory="your_directory"
ExeCommand='"c:\test\test.BAT"'
Execute='deferred' Impersonate='no' Return='ignore'/>
Also in the installExecuteSequence sequence add the action before InstallFinalize
<InstallExecuteSequence>
<Custom Action="RunBat" Before="InstallFinalize">
NOT (REMOVE~="ALL")
</Custom>
</InstallExecuteSequence>

Related

Wix:CustomAction failure in action immediate or deferred either

Environment: Windows 7 x64, Wix Toolset 3.10
Hello
I cannot call cmd.exe by Custom Action through Execute attribute "immediate" or "deferred" in either way.
Runtime Error always happen.
I can call vbs command by a Custom Action, but cannot fire .bat file,
What's wrong? I suppose there might be inconsistency between the path of input and output, but I cannot find it.
If I can output the log files with only English, please teach me....
In deferred action, log InstallInitialize, InstallFinalize might suggest me to do something with Orca, but I am not sure what I should do.
Below is detail of my attempt:
Following a sample suggested in https://alexanderst.wordpress.com/2010/10/28/wix-how-to-run-application-with-parameters-from-custom-action/
My definition of a property for cmd.exe below:
<Property Id="CMD">
<DirectorySearch Id="CmdFolder" Path="[SystemFolder]" Depth="1">
<FileSearch Id="CmdExe" Name="cmd.exe" />
</DirectorySearch>
</Property>
My definition of a directory for a batch file below:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder" Name="PFiles">
<Directory Id="FugaFuga" Name="FugaFuga">
<Directory Id="INSTALLDIR" Name="Hoge 1.0">
<Component Id="FugaFugaLibrary" Guid="MY-UID">
<File Id="test.bat" Name="test.bat" DiskId="1" Source="test.bat" KeyPath="yes" />
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
My definition of the Publish Event DoAction is below:
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="Next(&N)">
<Publish Event="DoAction" Value="CallCmdTest">1</Publish>
<Publish Event="NewDialog" Value="NextDlg" />
</Control>
test.bat content is below (both of hoge.txt and temp\hoge folder exist)
copy /Y C:\temp\hoge\hoge.txt C:\temp\hoge\hoge%date:~-10,4%%date:~-5,2%%date:~-2,2%.txt
It simply copys the hoge.txt to a hoge{%date% without slash}.txt
I tried Execute="deferred" but Runtime Error happens when the next button is pushed.
<CustomAction Id="CallCmdTest" Property="CMD" Return="check" Execute="deferred" Impersonate="yes" ExeCommand="/c ""[#test.bat]""" />
Its log file (I'm sorry for the japanese characters...)(msiexec /i Main.msi /lv* Main-install.log result:) Error 2762 but return code is 3...
アクション開始 11:04:22: CallCmdTest。
MSI (c) (08:C0) [11:04:22:176]: Note: 1: 2762
DEBUG: Error 2762: Unable to schedule operation. The action must be scheduled between InstallInitialize and InstallFinalize.
このパッケージをインストールしているときに、インストーラーに予期しないエラーが発生しました。このパッケージに問題がある可能性があります。エラー コードは、2762 です。引数: 、、
MSI (c) (08:C0) [12:03:24:486]: 製品: HogeHoge 1.12.0 -- このパッケージをインストールしているときに、インストーラーに予期しないエラーが発生しました。このパッケージに問題がある可能性があります。エ ラー コードは、2762 です。引数: 、、
アクション終了 12:03:24: CallCmdTest。 戻り値 3。
I tried Execute="immediate" but a Runtime Error happens either when the next button is pushed:
<CustomAction Id="CallCmdTest" Property="CMD" Return="check" Execute="immediate" ExeCommand="/c ""[#test.bat]""" />
Its log file (I'm sorry for the japanese characters...)(msiexec /i Main.msi /lv* Main-install.log result:) Error 2762 but return code is 3..
アクション開始 17:13:23: CallCmdTest。
MSI (c) (B8:C4) [17:13:24:034]: Note: 1: 1722 2: CallCmdTest 3: C:\Windows\SysWOW64\cmd.exe 4: /c ""C:\Program Files (x86)\HogeHoge\Hoge 1.0\test.bat""
エラー 1722。この Windows インストーラー パッケージに問題があります。セットアップの一部として実行されるプログラムが正常に完了しませんでした。サポート担当者またはパッケージ開発元に問い合わせてください。アクション CallCmdTest、場所: C:\Windows\SysWOW64\cmd.exe、コマンド: /c ""C:\Program Files (x86)\HogeHoge\Hoge 1.0\test.bat""
MSI (c) (B8:C4) [17:13:25:694]: 製品: HogeHoge 1.12.0 -- エラー 1722。この Windows インストーラー パッケージに問題があります。セットアップの一部として実行されるプログラムが正常に完了しませんでした。サポート担当者またはパッケージ開発元に問い合わせてください。アクション CallCmdTest、場所: C:\Windows\SysWOW64\cmd.exe、コマンド: /c ""C:\Program Files (x86)\HogeHoge\Hoge 1.0\test.bat""
アクション終了 17:13:25: CallCmdTest。 戻り値 3。
DEBUG: Error 2896: Executing action CallCmdTest failed.
このパッケージをインストールしているときに、インストーラーに予期しないエラーが発生しました。このパッケージに問題がある可能性がありま す。エラー コードは、2896 です。引数: CallCmdTest、、
アクション終了 17:13:25: WelcomeDlg。 戻り値 3。
MSI (c) (B8:B8) [17:13:25:716]: Doing action: FatalError
アクション 17:13:25: FatalError。
All files (Main.wxs, Main.wixobj, Main.msi, test.bat) exist in one folder.
All commands of candle, light, msiexec are executed in the same directory.
cd /d %~dp0
candle.exe Main.wxs
light.exe -cultures:ja-jp -ext WixUIExtension -out Main.msi Main.wixobj
msiexec /i Main.msi /lv* Main-install.log
As Gerhard Barnard metioned, using a custom action is probably the right way to do this. It looks like you are trying to trigger your custom action from the UI which
Can't be deferred as it is happening from the UI
Can't have elevation unless the entire installer was launched elevated
Won't be able to access the bat file if you haven't actually installed the product yet.
I would instead rewrite your bat file as a c# or c++ custom action and include that in your installer following the instructions in the link in Gerhard's comment and other SO questions.
Additionally, Custom actions launched from a UI event can't write logs easily to the msi log (or at all?). If you schedule your custom action appropriately you will be able to do informative logging about what your custom action is doing. You can also debug your custom action if necessary. In the past I've shown a message box at the start of the custom action then attached a debugger for debugging.

How to access local env variables from ANT file

I have this ANT file that calls a batch file to set the environment variables, before these environment variables are referred to in the ANT script.
So my batch file (set_env.bat) contains:
REM ------ ECLIPSE ------
SET DIR_ECLIPSE=C:\Eclipse
SET DIR_ECLIPSE_MARS=%DIR_ECLIPSE%\eclipse-dsl-mars-2-win32
SET DIR_DEST=C:\mydsl\generated
Then my ANT script will call it like this:
<target name="setvars" >
<exec executable="cmd">
<arg value="/c"/>
<arg value="set_env.bat"/>
<arg value="-p"/>
</exec>
</target>
<property environment="env"/>
<property name="tool" value="${env.DIR_ECLIPSE_MARS}" />
<property name="destDir" value=${env.DIR_DEST}" />
<target name="create_run_jar">
<copy file="${env.DIR_ECLIPSE_MARS}/plugins/org.eclipse.xtext_2.9.1.v201512180746.jar"
todir="${destDir}/myDsl_lib" />
</target>
I keep on getting an error saying the file cannot be found and when I print the log via , I get "[echo] ${env.DIR_ECLIPSE_MARS}". So this means that the env vars are not set.
During copy, it displays this log:
D:\mydsl\antbuild.xml:129: Warning: Could not find file D:\mydsl\${env.DIR_ECLIPSE_4_5_2}\plugins\org.eclipse.xtext_2.9.1.v201512180746.jar to copy.
I execute the setvars target first followed by the create_run_jar.

List of files located in text file to be send as parameter to WinSCP in script mode

I've to create solution that is based on WinSCP and windows batch script. Below are tasks the script has to do:
Save list of files from remote directory, to file located on machine where batch script is run.
Run WinSCP in command mode (/command) using WinSCP script passed as parameter (/script=name_of_script_file.txt) and get files (get command) from previously generated list.
The most important is to get file list, save it and pass names of files located in created file to WinSCP to get them.
How to implement this?
There's no easy way to implement this using WinSCP scripting only. It's possible, see the very end of my answer, but it may not be an ideal solution.
Why do you do this in two steps? Why don't you directly download the directory?
winscp.com /command ^
"option batch abort" ^
"option confirm off" ^
"open scp://user:password#example.com/" ^
"get /path/*" ^
"exit"
If you really need the list (e.g. for some further processing), you can instead of getting a list of files in the directory, get a list of actually downloaded files.
Enable the XML logging, and get the list from the XML log.
winscp.com /xmllog=log.xml /command ^
....
You get a log like:
<?xml version="1.0" encoding="UTF-8"?>
<session xmlns="http://winscp.net/schema/session/1.0" name="user#host" start="2015-01-30T06:45:57.008Z">
<download>
<filename value="/path/file1.txt" />
<destination value="C:\path\file1.txt" />
<result success="true" />
</download>
<download>
<filename value="/path/file2.txt" />
<destination value="C:\path\file2.txt" />
<result success="true" />
</download>
</session>
If you need a plain-text list, you can use the XSLT to convert it (e.g. download.xslt):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:winscp="http://winscp.net/schema/session/1.0">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match='winscp:download[winscp:result[#success="true"]]/winscp:filename'>
<xsl:value-of select="#value"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
To process it, use any XSLT processor:
Microsoft msxsl.exe (deprecated, but available from Internet Archive)
msxsl.exe log.xml download.xslt
Libxml2 xsltproc.exe:
xsltproc.exe download.xslt log.xml
And you get:
/path/file1.txt
/path/file2.txt
See Transforming XML Log to Text Output Using XSLT Transformation.
To answer your actual question. If you really insist on getting a file list and downloading files according to it, I suggest you use the WinSCP .NET assembly from a PowerShell script.
Use the Session.ListDirectory to retrieve a contents of a remote directory.
See the Session.ListDirectory documentation and its example code.
For recursive listing, you can also use the Session.EnumerateRemoteFiles.
Iterate the results and call the Session.GetFiles for every file.
See the Session.GetFiles documentation and its example code.
If the simple scripting is your preference:
The only way to obtaining a plain text list of remote files reliably, is to use the XML logging to capture an output of the ls scripting command:
winscp.com /xmllog=log.xml /command ^
"option batch abort" ^
"option confirm off" ^
"open scp://user:password#example.com/" ^
"ls /path" ^
"exit"
You get a log like:
<?xml version="1.0" encoding="UTF-8"?>
<session xmlns="http://winscp.net/schema/session/1.0" name="user#host" start="2015-01-30T07:08:27.749Z">
<ls>
<destination value="/path" />
<files>
<file>
<filename value="." />
<type value="d" />
<modification value="2014-07-28T07:06:49.000Z" />
<permissions value="rwxr-sr-x" />
</file>
<file>
<filename value=".." />
<type value="d" />
<modification value="2015-01-23T12:22:44.000Z" />
<permissions value="rwxr-xr-x" />
</file>
<file>
<filename value="file1.txt" />
<type value="-" />
<size value="1306091" />
<modification value="2015-01-29T23:58:12.000Z" />
<permissions value="rw-rw-rw-" />
</file>
<file>
<filename value="file2.txt" />
<type value="-" />
<size value="88" />
<modification value="2007-11-17T22:40:43.000Z" />
<permissions value="rw-r--r--" />
</file>
</files>
<result success="true" />
</ls>
</session>
Again, use the XSLT to convert the XML log to a plain-text list of files:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:winscp="http://winscp.net/schema/session/1.0">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match='winscp:file[winscp:type/#value="-"]/winscp:filename'>
<xsl:value-of select="#value"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
You get:
file1.txt
file2.txt
To get WinSCP script to download files according to a plain-text list, see scripting example Uploading a list of files. It's for an upload, but just replace the put with the get and reverse an order of arguments to turn it to a download.
Note that the /command parameter does not enter a "command" (scripting?) mode. It's for passing scripting commands on a command line (as used in my answer). You are using a script file (/script). There's no point adding an empty /command parameter to it.
See the documentation of WinSCP scripting command-line parameters.

Wix: use computername in custom action directory

I want to use the name of the computer where the msi is running in a customaction to execute a batch file that is deployed with the msi file in a directory that is the same as the computername.
For example, after installation of the msi on a machine named: 'MyServer' the directory looks like: C:\program files\MyApp\Config\MyServer\Config.bat
I want to run the Config.bat file after installation, so i came up with custom action
<CustomAction
Id="CONFIGURE"
Directory="INSTALLFOLDER\Config\[COMPUTERNAME]"
ExeCommand="Configure.bat"
Execute="deferred"
Return="ignore" />
<InstallExecuteSequence>
<Custom Action="CONFIGURE"
After="InstallFiles" />
</InstallExecuteSequence>
Now i need the real computername instead of the [COMPUTERNAME].
Or something like this:
<CustomAction
Id="CONF"
Directory="INSTALLFOLDER"
ExeCommand="\Config\[COMPUTERNAME]\Configure.bat"
Execute="deferred"
Return="ignore"
HideTarget="no"
Impersonate="no" />
The above examples looks better, but at installation the log file tells me:
Action: CONF, location: c:\Program Files (x86)\MyApp\, command: \Config\\Configure.bat
So the computername is empty...
+++
Finally thanks to the one Dusan Plavak, the solution is:
<CustomAction
Id="CONF"
Directory="INSTALLFOLDER"
ExeCommand="\Config\[ComputerName]\Configure.bat"
Execute="deferred"
Return="ignore"
HideTarget="no"
Impersonate="no" />
<InstallExecuteSequence>
<Custom Action="CONF"
After="InstallFiles" />
</InstallExecuteSequence>
You should use ComputerName instead of COMPUTERNAME
so for example you can copy ComputerName to COMPUTERNAME name using custom action"
<CustomAction Id="SetMachineName" Property="COMPUTERNAME" Value="[ComputerName]" Execute="immediate"></CustomAction>

Running a Batch file with ant 'build.xml'

I am trying to run a batch file called 'sparql.bat' by using ant build.xml. The last line in the batch file is -
java %JVM_ARGS% -Dlog4j.configuration="%LOGGING%" -cp "%JENA_CP%" arq.sparql --data=\apache-jena-2.10.0\test.ttl --query=\apache-jena-2.10.0\testing.rq
Where %JVM_ARGS% is -Xmx1024M and the other things can also be replaced.
I am trying to run this using an ant build.xml, "--data=\apache-jena-2.10.0\test.ttl --query=\apache-jena-2.10.0\testing.rq" are the arguments.
I have prepared the build.xml like this-
<?xml version="1.0"?>
<project name="apache-jena-2.10.0" basedir="." default="notify">
<target name="notify">
<java JVM_ARGS="-Xmx1024M" Dlog4j.configuration="%LOGGING%" cp="C:\apache-jena-2.10.0\lib\*" arq.sparql="%*">
<arg data="C:\apache-jena-2.10.0\test.ttl"/>
<arg query="\apache-jena-2.10.0\ASKTest.rq"/>
<classpath>
<pathelement location="."/>
</classpath>
</java>
</project>
Can somebody please help me to write a build.xml file which can execute this?
Thanks
Somsuvra
Try something more along these lines:
<java classname="arq.sparql">
<arg value="--data=C:\apache-jena-2.10.0\test.ttl"/>
<arg value="--query=C:\apache-jena-2.10.0\testing.rq"/>
<jvmarg value="-Xmx1024M"/>
<jvmarg value="-Dlog4j.configuration=log4j.properties”/>
<classpath>
<pathelement location="C:\apache-jena-2.10.0\lib"/>
</classpath>
</java>
Final solutions is -
<?xml version="1.0"?>
<project name="apache-jena-2.10.0" basedir="." default="notify">
<target name="notify">
<java classname="arq.sparql" fork="true">
<arg value="--data=C:\apache-jena-2.10.0\test.ttl"/>
<arg value="--query=C:\apache-jena-2.10.0\ASKTest.rq"/>
<jvmarg value="-Xmx1024M"/>
<classpath>
<pathelement location="C:\apache-jena-2.10.0\lib\jena-core-2.10.0.jar"/>
<pathelement location="C:\apache-jena-2.10.0\lib\jena-arq-2.10.0.jar"/>
<pathelement location="C:\apache-jena-2.10.0\lib\log4j-1.2.16.jar"/>
</classpath>
</java>
</target>
</project>
Now my question is instead of writing all the .jar files seperately is there any way of specifying them in a single line.
Thanks and regards
Somsuvra
You could replace the pathelement elements with a fileset. Something like this:
<fileset dir="C:\apache-jena-2.10.0\lib">
<include name="**/*.jar"/>
</fileset>

Resources