WiX Remove files on uninstall but not update - wix

I have an app that can log when given the correct flags at install time (/logLevel=debug on install gets passed to the app when the service starts). Our update process is a automated uninstall then install with a new MSI package. I know there is built in patch functionality with WiX, but this is our process.
Similarly with the logLevel parameter, I'd like to pass something to the effect of UPDATE="true" on the command line during uninstall. When this parameter is passed to the uninstaller it should not delete the log files. Currently we delete the files every time, but would like to retain the log files during an update. This is what I am trying to extend as of right now:
<?if $(var.BUILD_CONFIG) = "Debug" ?>
<?else?>
<CustomAction Id="Cleanup_logfile" Directory="TempTest"
ExeCommand="cmd /C "del %systemroot%\temp\hexis_hawkeye_g.log.*""
Execute="deferred" Return="ignore" HideTarget="no" Impersonate="no" />
<InstallExecuteSequence>
<Custom Action="Cleanup_logfile" Before="RemoveFiles" >
REMOVE="ALL"
</Custom>
</InstallExecuteSequence>
<?endif?>
And I've been playing with code similar to something like the following but it doesn't seem to work:
<?if $(var.BUILD_CONFIG) = "Debug" ?>
<?else?>
<?if '[UPDATE]' = "true" ?>
<?else?>
<CustomAction Id="Cleanup_logfile" Directory="TempTest"
ExeCommand="cmd /C "del %systemroot%\temp\hexis_hawkeye_g.log.*""
Execute="deferred" Return="ignore" HideTarget="no" Impersonate="no" />
<InstallExecuteSequence>
<Custom Action="Cleanup_logfile" Before="RemoveFiles" >
REMOVE="ALL"
</Custom>
</InstallExecuteSequence>
<?endif?>
<?endif?>
I'm not sure if I'm not initializing the UPDATE variable correctly, or if this really is some pre-processing that cannot be implemented in this fashion. I would think it would not work because these constructs are described on the preprocessor doc page, however, the /logLevel and various other parameters seem to work fine at run-time installation. I'm totally WiX illiterate and have been trying to read their documentation to no avail, any helpful links appreciated.

The problem as I see it: during a major upgrade when the application is uninstalled (and later on installing the new version) REMOVE=ALL is also true during uninstalling the application, so the files will be deleted.
You need to additionally check if the UPGRADINGPRODUCTCODE is also set or not, which would only be true during an update.
Check this answer where the correct condition is given (and bookmark the question as I did, it is very very useful for all the possible states and conditions ;-)).
The correct condition should be the following in your case:
<InstallExecuteSequence>
<Custom Action="Cleanup_logfile" Before="RemoveFiles" >
(NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
</Custom>
</InstallExecuteSequence>

This is probably a bit hackish, but I was able to pass what I wanted by insinuating from the LOGLEVEL what action to take instead of passing an arbitrary variable:
msiexec.exe /x {blah-blah-guid-blah} INSTALLLEVEL=2
And for the configuration of my custom action:
<?if $(var.BUILD_CONFIG) = "Debug" ?>
<?else?>
<CustomAction Id="Cleanup_logfile" Directory="TempTest"
ExeCommand="cmd /C "if [INSTALLLEVEL] GEQ 2 del %systemroot%\temp\hexis_hawkeye_g.log.*""
Execute="deferred" Return="ignore" HideTarget="no" Impersonate="no" />
<InstallExecuteSequence>
<Custom Action="Cleanup_logfile" Before="RemoveFiles" >
REMOVE="ALL"
</Custom>
</InstallExecuteSequence>
<?endif?>

Related

Unable to Silent Execute the Command in WiX Script

Below is my code when i try to install my MSI it does everything but the lines below shows no sign of execution.
<Binary Id="unzipExeId" SourceFile="unzip.exe"/>
<Property Id="WixQuietExec64CmdLine" Value="[#$(var.InstallationLocation)\unzip.exe]"/>
<CustomAction Id="unzipAction" BinaryKey="unzipExeId" DllEntry="WixQuietExec64" Execute='deferred' Return ='asyncWait' Impersonate='no'/>
<InstallExecuteSequence>
<Custom Action='unzipAction' Before='InstallFinalize'/>
</InstallExecuteSequence>
Did I miss anything.
But When i Try this code
<Binary Id="unzipExeId" SourceFile="unzip.exe"/>
<CustomAction Id="unzipAction" BinaryKey="unzipExeId" ExeCommand="START /B unzip.exe" Execute='deferred' Return ='asyncWait' Impersonate='no'/>
<InstallExecuteSequence>
<Custom Action='unzipAction' Before='InstallFinalize'/>
</InstallExecuteSequence>
Everything Works Just Fine but the execution of the unzip.exe causes a pop up in the machine. The exe is not silently installed. But i need to silently execute the EXE.
Thanks in Advance
It could be related to DllEntry instead of using "WixQuietExec64" try to use "CAWixQuietExec64".
There is an open issue regarding this.
http://wixtoolset.org/issues/4802/
This is as a common use:
<CustomAction Id="CA_RunBatchScript"
BinaryKey="WixCA"
DllEntry="CAQuietExec"
Execute="deferred"
Return="check" />

Wix - How to kill any process forcefully through custom action?

I have a created msi and installed it but i am facing an issue during uninstall. I want to close the application forcefully before uninstall through custom action - wix
I've searched a lot but didn't find any solution.
I want a solution something like that.
<Property Id="QtExecCmdLine" Value='"[SystemFolder]taskkill.exe" /F /IM ConfigurationManager.exe /t'/>
<CustomAction Id="StopUserExe" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="immediate" Return="ignore"/>
<InstallExecuteSequence>
<Custom Action='StopUserExe' Before='RemoveFiles'></Custom>
</InstallExecuteSequence>
Have you tried Taskkill?
Example:
<CustomAction Id="TaskKill" Execute="deferred" ExeCommand="taskkill /f /im notepad.exe" />
In your custom action you can enumerate processes (EnumProcesses()) and call TerminateProcess() for the needed one.

Wix: how to forcefully kill a process/task?

I need to forcefully kill a process that is running in the background before attempting to delete any files, when running an Uninstall from an MSI created with Wix. The main application consist of a trayicon which reflects the status of the bg-process monitoring local windows services (made on C#, though this may not be so relevant going further).
I first tried the following:
<File Id='FooEXE' Name='Foo.exe' Source='..\Source\bin\Release\Foo.exe' Vital='yes' />
...
<InstallExecuteSequence>
<Custom Action="CloseTray" Before="InstallValidate" />
</InstallExecuteSequence>
...
<CustomAction Id="CloseTray" ExeCommand="-exit" FileKey="FooEXE" Execute="immediate" Return="asyncWait" />
The tray icon is immediately closed after confirming application-close dialog, but the Foo.Exe task still appears on the taskmgr after the uninstall completed. Also,the following error message was given:
Thats why, then I tried this:
<InstallExecuteSequence>
<Custom Action="Foo.TaskKill" Before="InstallValidate" />
</InstallExecuteSequence>
...
<CustomAction Id="Foo.TaskKill" Impersonate="yes" Return="asyncWait" Directory="WinDir" ExeCommand="\System32\taskkill.exe /F /IM Foo.exe /T" />
After obtaining the same result, tried:
<Property Id="QtExecCmdLine" Value='"[WinDir]\System32\taskkill.exe" /F /IM Foo.exe'/>
...
<InstallExecuteSequence>
<Custom Action="MyProcess.TaskKill" Before="InstallValidate" />
</InstallExecuteSequence>
...
<CustomAction Id="MyProcess.TaskKill" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="immediate" Return="ignore"/>
Sample which I took from here: How to kill a process from WiX
lately when all else failed, I also tried this without any success:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
...
<InstallExecuteSequence>
<Custom Action="WixCloseApplications" Before="InstallValidate" />
</InstallExecuteSequence>
...
<util:CloseApplication Id="CloseFoo" CloseMessage="yes" Description="Foo is still running!" ElevatedCloseMessage="yes" RebootPrompt="yes" Target="Foo.exe" />
This one gave me a different error:
I'm thinking on building a statue in honor to this process that just refuses to die!!! ... either that or think a problem on the application side exists, where I should add something like Application.Exit(); or Environment.Exit(0); at some line inside Program.cs.
Is there any other thing I could do at either Wix or my application to attempt closing it successfully at Uninstall?
Thanks!
Personally I think the best option for you to go with is the in-built CloseApplication method rather than your previous options.
The error you are getting for that (Error code 2762) is because you are trying to schedule the action in the immediate sequence but have the ElevatedCloseMessage="yes" set which triggers it as a deferred action. Either remove this attribute or schedule it in the deferred sequence.

WiX: make symbolic link with UAC enabled

I want to execute a custom action in a Windows Installer (with WiX script) that makes symbolic links at the end of installation. mklink requires administrator privilege, as the installer restricts. This is what I wrote:
<CustomAction Id="mklink_cmdline" Property="QtExecCmdLine" Value='"[SystemFolder]cmd.exe" /c mklink "[SystemFolder]my_app.dll" "[INSTALLDIR]my_app.dll"' />
<CustomAction Id="mklink_exec" BinaryKey="WixCA" DllEntry="CAQuietExec" Return="ignore" />
...
<InstallExecuteSequence>
<Custom Action="mklink_cmdline" Before="InstallFinalize">
...
</Custom>
<Custom Action="mklink_exec" After="mklink_cmdline">
...
</Custom>
...
</InstallExecuteSequence>
This works perfectly if UAC is completely disabled. However, when enabling UAC in any level, this custom action fails with
CAQuietExec: You do not have sufficient privilege to perform this operation.
even if I allowed in the consent window. I tried to change Execute to deferred, Impersonate to no, or change package's InstallPrivileges to elevated, none of them works.
Any suggestion I can bypass? Thank you!
Edit: revised code with deferred custom action
<CustomAction Id="mklink_cmdline" Property="mklink_exec" Value='"[SystemFolder]cmd.exe" /c mklink "[SystemFolder]my_app.dll" "[INSTALLDIR]my_app.dll"' />
<CustomAction Id="mklink_exec" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Impersonate="no" Return="ignore" />
...
<InstallExecuteSequence>
<Custom Action="mklink_exec" Before="InstallFinalize">
...
</Custom>
<Custom Action="mklink_cmdline" Before="mklink_exec">
...
</Custom>
...
</InstallExecuteSequence>
Does it work when ran from an administrator command prompt? I assume it does.
From what I found the msi cannot raise the UAC level which is what you need here. I had to create a setup.exe that wrapped the msi as an embedded resource and executed it. The setup.exe includes the app.manifest requesting administrator execution level which raises the UAC level appropriately:
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<assemblyIdentity version="1.0.0.0" name="Setup.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</asmv1:assembly>
I may just not understand WIX, custom actions and UAC enough, but this is what I ended up doing.
Are you scheduling it between InstallInitialize and InstallFinalize when you mark it for Deferred? Your Before and after looks a little wierd:
InstallFinalize
_cmdline before InstallFinalize
_mkline_exec after _cmdline
Sounds a little nondeterministic. You might find _cmdline occurring after InstallFinalize and deferred won't work there.
Try:
InstallFinalize
_exec before InstallFinalize
_cmldline before _exec
If it's actually mklink that is requiring elevation, you might try using SysInternals junction.exe instead.
I ended up bundling elevate.exe from wintellect, deploy it to some temp folder and supply it with a path to command-line script which created all symbolic links. Than it was invoked via the custom action.
Command line file in turn has some goodness inside to detect proper program files folder. or get it from the command line, if needed.
It appears that even though WiX correctly elevates the custom action, msi (or Windows Installer) itself doesn't grant it sufficient rights to properly run mklink command.
Also note that Impersonate="yes" in the CA. I believe that's what will let msi to show elevation dialog box when it executes the action.
command line file:
cd /D %~p0
IF EXIST "%PROGRAMFILES(x86)%" SET PROGFILES=%PROGRAMFILES(x86)%
IF "%PROGFILES%".=="". SET PROGFILES=%PROGRAMFILES%
SET INSTALLPATH=%PROGFILES%\MyGreatProduct
SET DATAPATH=%PROGRAMDATA%\MyGreatProduct
IF NOT "%~1."=="." SET INSTALLPATH=%~1
IF NOT "%~2."=="." SET DATAPATH=%~2
IF EXIST "%INSTALLPATH%" mklink "%INSTALLPATH%\veryimportant.ini" "%DATAPATH%\veryimportant.ini"
in the wxs file:
<Component Directory="TempFolder" Id='Comp_Temp_Makesymlinks' Guid='47a58219-1291-4321-4321-176987154921'>
<File Id='makesymlinks_cmd' Source='makesymlinks.cmd'>
<Permission User='Everyone' GenericAll='yes' />
</File>
<File Id='elevate_exe' Source='elevate.exe'>
<Permission User='Everyone' GenericAll='yes' />
</File>
</Component>
<SetProperty Id="CA_MakeSymLinksCmd" Before="CA_MakeSymLinksCmd" Sequence="execute"
Value=""[TempFolder]\elevate.exe" "[TempFolder]\makesymlinks.cmd"" />
<CustomAction Id="CA_MakeSymLinksCmd" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Return="ignore" Impersonate="yes" />
<InstallExecuteSequence>
<Custom Action="CA_MakeSymLinksCmd" Before="InstallFinalize"><![CDATA[NOT Installed AND VersionNT >= 600 ]]></Custom>
</InstallExecuteSequence>

Wix Open web page when uninstall completes

I'm using Wix3. I need to open a web page when the user uninstalls the product.
Any ideas how it can be done?
Thanks.
Here's a sample of the code we use, we don't actually set the URL at compile time, but update properties in the MSI post-build so this might seem a little "over engineered". We use the WiXShellExec CA and have an additional condition so that the webpage is only displayed during uninstall, and not during a major upgrade.
<Fragment>
<Property Id="MyURL"><![CDATA[http://www.blah.blah.blah/]]></Property>
<CustomAction Id="SetOpenURL" Property="WixShellExecTarget" Value="[MyURL]" />
<CustomAction Id="OpenURL" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" Return="ignore" />
<InstallExecuteSequence>
<!-- Launch webpage during full uninstall, but not upgrade -->
<Custom Action="SetOpenURL" After="InstallFinalize"><![CDATA[REMOVE ~= "ALL" AND NOT UPGRADINGPRODUCTCODE]]></Custom>
<Custom Action="OpenURL" After="SetOpenURL"><![CDATA[REMOVE ~= "ALL" AND NOT UPGRADINGPRODUCTCODE]]></Custom>
</InstallExecuteSequence>
</Fragment>
Add these XML elements somewhere under your <Product> element:
<CustomAction Id="LaunchBrowser"
ExeCommand="explorer.exe http://www.google.com"
Directory="INSTALLDIR"
Return="asyncNoWait" >
REMOVE="ALL"
</CustomAction>
<InstallExecuteSequence>
<Custom Action="LaunchBrowser" After="InstallValidate"/>
</InstallExecuteSequence>
The REMOVE="ALL" condition will make sure the custom action is executed only if the product is being completely removed.
The After="InstallValidate" makes sure that the custom action is executed right after the REMOVE property value becomes known.
The example provided by FireGiant Launch the Internet doesn't work for me but it inspire me to come out my own solution as below.
The condition NOT Installed mean new installation while Installed means it only trigger when uninstall.
<CustomAction Id="LaunchBrowser" Directory="INSTALLDIR" Return="asyncNoWait" ExeCommand="explorer.exe http://www.google.com/" />
<InstallExecuteSequence>
<Custom Action="LaunchBrowser" After="InstallFinalize">Installed</Custom>
</InstallExecuteSequence>
Here is what I did for both install and uninstall:
<Product>
...
<CustomAction Id="LaunchBrowserInstall" Directory="TARGETDIR" Execute="immediate" Impersonate="yes" Return="asyncNoWait" ExeCommand="explorer.exe https://www.example.com/post_install/" />
<CustomAction Id="LaunchBrowserUninstall" Directory="TARGETDIR" Execute="immediate" Impersonate="yes" Return="asyncNoWait" ExeCommand="explorer.exe https://www.example.com/post_uninstall/" />
<InstallExecuteSequence>
<Custom Action="LaunchBrowserInstall" After="InstallFinalize">NOT Installed AND NOT REMOVE</Custom>
<Custom Action="LaunchBrowserUninstall" After="InstallFinalize">REMOVE ~= "ALL"</Custom>
</InstallExecuteSequence>
...
</Product>