What's wrong with my custom Action? - wix

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

Related

WiX: Custom Actions don't run when using msiexec /i Setup.msi /qn (quiet mode installation without UI)

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.

Wix: Executing installed exe as a first action on Uninstall

In my msi, I have to call the installed exe file with some parameters, as a very first action of uninstall. Here is the code:
<CustomAction Id="UnRegisterOnUninstallApplication"
Execute="immediate"
FileKey="MyProgram.exe" ExeCommand="/unregister" Return="asyncNoWait" />
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize"/>
<Custom Action="UnRegisterOnUninstallApplication" Before="UnpublishComponents" >Installed AND NOT UPGRADINGPRODUCTCODE</Custom>
</InstallExecuteSequence>
Although, it is scheduled Before UnpublishComponents, but issue is, at some computers, custom action is called during or after dependency dlls are uninstalled and so MyProgram.exe crashes here.
This is something seems unpredictable...
Can anyone please guide, if I'm missing something or doing something wrong???
Thanks a bunch.
There are several things incorrect here:
The design issue is that you shouldn't be running code to register or unregister. The recommended way is to capture the registry entries and add them as registry entries in the same component as that binary. Then it all just works.
Your custom action is asyncNoWait, and that means the uninstall continues while the program runs. If it takes a long time for some reason, or simply doesn't get enough of the processor, then yes, files may have been removed by the time it runs.
It's an immediate custom action, so it can run and start unregistering as the uninstall proceeds. However if the uninstall fails and rolls back the deleted registration will not be restored, so you'll end up with a broken product still installed, those registration entries will stay removed. It should be a deferred custom action and return = ignore or check, depending on whether you care if the program fails.
I might schedule the action before 'RemoveFiles' so that no dependent dlls have been removed before your custom action gets to run.

Kill my application during uninstall

I have run into challenging problem trying to accomplish the following:
my application installs a service (watchdog.exe) and an exe file (app.exe).
After the installation is done the service starts and creates process "app.exe".
during uninstall I want to kill the process "app.exe" (which is running under local system account, so I must be running as admin).
problem 1:
The installs says that it requires a reboot since it sees that file "app.exe" is being held (running) during the CostFinalize phase (please correct me if I'm wrong about the phase that checks if a reboot will be required). It would be much better to kill the process when the uninstallation begins. I have verified that if the process is not running during the uninstall then the install does not complain about a reboot required.
problem 2:
using a custom action to kill the process is problematic. the action must run elevated, but on the other hand it must run before the costFinalize (otherwise - it's back to problem 1).
I would appreciate any suggestion. Also, any alternative solutions (is there another way to close the process maybe during install that will not require a reboot?)
The custom action code I have now (not good since it both unnecessarily asks for a reboot and fails to kill the process due to lack of permissions):
<InstallExecuteSequence>
<!--<ScheduleReboot After="InstallFinalize" />-->
<Custom Action="MyProcess.TaskKill" Before="InstallValidate"></Custom>
</InstallExecuteSequence>
<!--<Property Id="Net">Net.exe</Property>-->
<Property Id="QtExecCmdLine" Value='"[%SYSTEMROOT]\System32\taskkill.exe" /F /IM App.exe' />
<CustomAction Id="MyProcess.TaskKill"
BinaryKey="WixCA"
DllEntry="CAQuietExec"
Execute="immediate"
Return="ignore" />
Here is the log for the failure:
CAQuietExec: Error 0x80070001: Command line returned an error.
CAQuietExec: Error 0x80070001: CAQuietExec Failed
CustomAction MyProcess.TaskKill returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
Action ended 18:15:54: MyProcess.TaskKill. Return value 1603.
There are few ideas that I have, namely:
Use EventWaitHandles, which allow processes to communicate between each other, and delegate your wish to app.exe. Your app.exe can then terminate, as needed. This is clean solution and should be prefered.
If for whatever reason you decide to kill the application like you don't care about anything at all in the world, then you can:
I saw today a project that can run MSI completely elevated, in all stages. It was Visual Stdio template, however I can't find it right now, but know that it exists there.
You can also use this, maybe it works: How do I get WiX installer to request administrative privileges?
Basically there are so many hackery tricks you can do, to kill the application. Such as using WiX Burn and requiring administration rights, then doing your thing. I would go with solution#1(create your own mechanisms)
By the way, if you use ServiceControl element in WiX, it will STOP the service before REINSTALLING/UNISTALLING. You can hook to OnStop() method in Service and kill your App.exe there. If you have set Service as App.exe parent, then there should be flag that any child processes die with parent.

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.

Why doesn't this silent launch work in WIX?

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.