Installing TopShelf with WiX fails due to Admin Rights - wix

So I am trying to install an application built using TopShelf, the application itself runs well and has no problems. The problem I run into is when I try to install the service. The TopShelf service is installed (from an administrator command line) using the myapp.exe install <options> instruction. I have wrapped the instruction in a custom action (see below). This runs, in that I can see a black box pop-up on install. The service fails to install, however. When I run the msi install from an administrator command line, the service installs correctly. I have included all Administrator related parameters in the WiX file (see below also). I am completely out of ideas and in need of help, can anyone see anything in the WiX files or does anyone have any idea what is preventing the service from installing?
What I have tried:
Topshelf - Custom Action in Wix Not Executing
Add Coffee and Shake Well - TopShelf
I have also tried wrapping the call to the topshelf app in a separate WiX custom Action project to execute and this also fails for the same reason.
<Product Id="*" Name="PackageName"
Language="1033"
Version="1.0.0.0"
Manufacturer="Manufacturer"
UpgradeCode="e7780903-3cf9-4ecc-b65a-45bc18b500df">
<Package InstallerVersion="200"
Compressed="yes"
InstallScope="perMachine"
InstallPrivileges="elevated"
Platform="x64" />
<Property Id="MSIUSEREALADMINDETECTION" Value="1" />
<MajorUpgrade AllowSameVersionUpgrades="yes"
DowngradeErrorMessage="A newer version of [ProductName] is already installed."
Schedule="afterInstallInitialize" />
<MediaTemplate EmbedCab="yes"/>
<Feature Id="ProductFeature" Title="FeatureName" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
<CustomAction Id="InstallService"
FileKey="MyApp.exe"
ExeCommand="install"
Impersonate="yes"
Execute="immediate" />
<CustomAction Id="StopService"
FileKey="MyApp.exe"
ExeCommand="stop"
Execute="immediate" />
<CustomAction Id="UninstallService"
FileKey="MyApp.exe"
ExeCommand="uninstall"
Execute="immediate" />
<InstallExecuteSequence>
<Custom Action="InstallService" After="InstallFinalize" >
NOT Installed AND NOT REMOVE
</Custom>
<Custom Action="StopService" After="InstallInitialize" >
(NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
</Custom>
<Custom Action="UninstallService" After="StopService">
(NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
</Custom>
</InstallExecuteSequence>
</Product>

There are a couple of problems with your custom actions. One is that the InstallService CA is immediate which means 1) It's before the files are installed and 2) it won't run with elevation. It needs to be deferred and before InstallFinalize.
If this is just an ordinary Windows service, then you should use a ServiceInstall node to install it (and uninstall it) as well as ServiceControl to stop, start, and delete it.

I solved this problem using the following code
<CustomAction Id="InstallService" FileKey="MyApp.exe" ExeCommand="install start" Impersonate="no" Execute="deferred" />
<CustomAction Id="UninstallService" FileKey="MyApp.exe" ExeCommand="stop uninstall" Impersonate="no" Execute="deferred" />
<InstallExecuteSequence>
<Custom Action="InstallService" Before="InstallFinalize">
NOT Installed AND NOT REMOVE
</Custom>
<Custom Action="UninstallService" Before="RemoveFiles">
(NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
</Custom>
</InstallExecuteSequence>

Related

Wix Run Custom Action When "Newer version is installed"

How can I setup custom action to run my app when installation finished with "newer version is already installed"?
What I want: If newer version is installed, just run it. Run application always except deleting.
My configuration:
<CustomAction Id="LaunchApplication" Directory='INSTALLFOLDER' ExeCommand="[INSTALLFOLDER]\MyApp.exe"
Return="asyncNoWait" />
<InstallExecuteSequence>
<Custom Action="LaunchApplication" After="InstallFinalize">NOT (REMOVE="ALL")</Custom>
</InstallExecuteSequence>
Thanks
Solved with logs: app.msi /l*v log.txt
Working configuration:
<CustomAction Id="LaunchApplication" Directory='INSTALLFOLDER' ExeCommand="[INSTALLFOLDER]\MyApp.exe"
Return="asyncNoWait" />
<CustomAction Id="SetLaunchApplicationPath" Property="LaunchApplicationPath" Value="[ProgramFilesFolder][Manufacturer]\[ProductName]\MyApp.exe">
</CustomAction>
<CustomAction Id="LaunchApplicationOnDowngrade" ExeCommand="[SetLaunchApplicationPath]" Property="LaunchApplicationPath"
Return="asyncNoWait" />
<InstallUISequence >
<Custom Action="SetLaunchApplicationPath" After="FindRelatedProducts">WIX_DOWNGRADE_DETECTED</Custom>
<Custom Action="LaunchApplicationOnDowngrade" After="SetLaunchApplicationPath">LaunchApplicationPath</Custom>
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="LaunchApplication" After="InstallFinalize" >NOT (REMOVE="ALL")</Custom>
</InstallExecuteSequence>
Action LaunchApplication executes when install/update finished
Action LaunchApplicationOnDowngrade executes when install failed when newer version of product is found by FindRelatedProducts Action
I use <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." /> to configure upgrade.

WIX Uninstall Custom Action Error Code 2753

I'm having problems with a WIX CustomAction that I'd like to run when a user uninstalls an application.
Here's my XML
http://schemas.microsoft.com/wix/2006/wi'>
<Package Description='pak' InstallerVersion='200' Compressed='yes' />
<Media Id='1' Cabinet='setup.cab' EmbedCab='yes' />
<Property Id='ARPSYSTEMCOMPONENT'>1</Property>
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id="TempFolder">
<Directory Id="INSTALLLOCATION" Name="~_tmpdir">
<Component Id='MyComponent' DiskId='1' Guid=''>
<File Id="File0" Name="Runtime.exe" Source="Runtime.exe" />
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id='InstallFeature' Title='Install Feature' Level='1'>
<ComponentRef Id='MyComponent' />
</Feature>
<CustomAction Id="RunInstall" Return="ignore" Execute="deferred" FileKey="File0" ExeCommand="Runtime.exe" HideTarget="no" Impersonate="no" />
<CustomAction Id="RunUninstall" Return="ignore" Execute="deferred" FileKey="File0" ExeCommand="Runtime.exe" HideTarget="no" Impersonate="no" />
<InstallExecuteSequence>
<Custom Action="RunInstall" Before="InstallFinalize">NOT REMOVE~="ALL"</Custom>
<Custom Action="RunUninstall" Before="InstallFinalize">REMOVE~="ALL"</Custom>
</InstallExecuteSequence>
The Runtime.exe launches as expected when installing the application but when I uninstall I get an error "The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is 2753".
Looking at the event viewer sheds a little more light on the problem, it contains the following "The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is 2753. The arguments are: File0, , ".
So, it seems like it can't find Runtime.exe but I'm not sure why. The file is bundled into the MSI and it runs on install but I can't work out why it doesn't run on uninstall.
Many thanks
You should sequence the uninstall custom action earlier. "Before InstallFinalize" is very late, and almost certainly results in attempting to run the program after RemoveFiles has deleted it, hence the error. Look at the InstallExecuteSequence in your MSI file and see where RemoveFiles is relative to your CA and InstallFinalize. You may need to be before StopServices and other actions that remove registry values, depending on how much of the installed product your code needs. Or run it from the Binary table (beware of dependencies) if it really needs to be literally just before the uninstall completes.

WiX Remove files on uninstall but not update

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?>

WIX: Non responsive app prevents install/update

We had deployed our Notification area (Tray) app with a fault. It had no top level window and therefore did not receive WM_CLOSE events. In short, if Windows Installer tries to close it with its built in functions during an upgrade or uninstall (it shows a dialog to shut down the app), it will fail closing the app. The tray process is still running in Taskmanager and the exe file is therefore locked.
I would like to have the following:
The setup should kill the running tray application up until a specific file/install version.
Or, if this is not possible prevent installation for old versions and tell the user to manually remove it.
I tried to start Notepad (as test) for old versions before the "please shutdown" dialog appears but did not succeeded.
I tried to start it with Before="ValidateInstall" but it did not run.
I tried to start it with After="ValidateInstall" then notepad opened but ran AFTER the setup detected that the file is running.
Perhaps not all properties are set Before="ValidateInstall"?
Perhaps my Custom Actions are somehow autmatically deferred?
Some code snippets:
<Product Id="*"
Codepage="65001"
Language="!(loc.LANGUAGE)"
Manufacturer="$(var.AppManufacturer)"
Name="$(var.AppCode), $(var.AppVersion)"
UpgradeCode="$(var.AppUpgradeCode)"
Version="$(var.AppVersion)">
<Package Comments="$(var.AppCode),
$(var.AppVersion)"
Compressed="yes"
InstallPrivileges="limited"
InstallScope="perUser"
InstallerVersion="301"
Languages="!(loc.LANGUAGE)"
Manufacturer="$(var.AppManufacturer)"
Platform="x86"
SummaryCodepage="1252"/>
<Property Id="PROP_APP_IGNORES_SHUTDOWN">
<DirectorySearch Id="DirSrch_PIAS_Version" Path="[DIR_ID_USERPROGRAMFOLDER]">
<FileSearch Name="$(var.MyExe.TargetFileName)"
MaxVersion="6.1.3432.99999"/>
</DirectorySearch>
</Property>
<Property Id="QtExecCmdLine"
Value='"$(var.SysSystem32)\taskkill.exe" /F /IM $(var.MyExe.TargetFileName)'/>
<CustomAction Id="CA_KillApp"
BinaryKey="WixCA"
DllEntry="CAQuietExec"
Execute="immediate"
Impersonate="yes"
Return="ignore" />
<Property Id='NOTEPAD'>$(var.SysWindir)\Notepad.exe</Property>
<CustomAction Id="CA_OpenNotepad"
Property="NOTEPAD"
ExeCommand=""
Return="asyncNoWait" />
<MajorUpgrade Schedule="afterInstallValidate"
DowngradeErrorMessage="[VSDVERSIONMSG]"
AllowDowngrades="no"
AllowSameVersionUpgrades="yes" />
<InstallExecuteSequence>
<Custom Action="CA_OpenNotepad" Before="CA_KillApp">PROP_APP_IGNORES_SHUTDOWN</Custom>
<Custom Action="CA_KillApp" Before="InstallValidate">PROP_APP_IGNORES_SHUTDOWN</Custom>
</InstallExecuteSequence>
</Product>

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>