WIX: Restart service if Setup is cancelled? - c#

Is it possible to restart a service if setup MSI is cancelled by the user?
There is two scenarios where the MSI has to restart the service.
Stop and update the old service files and then start the service. If service fails to start, replace back the old files and restart the service. [This part is done with rollback]
Restart the service if MSI is cancelled intentionally during setup process.
I have a solution that I can call a CustomAction on cancel and used CMD.EXE to restart a service, but I don't like it. Please suggest any other solution like by using RestartResource or ResourceManager
Code:
<InstallExecuteSequence >
<RemoveExistingProducts
After="InstallInitialize"/>
<Custom Action="RenameFileOnCancel" OnExit="cancel">1</Custom>
</InstallExecuteSequence>
<CustomAction
Id='RestartService'
Directory='TARGETDIR'
ExeCommand='[SystemFolder]cmd.exe net stop AppServerSvc && net start AppServerSvc'
Return='asyncWait'
Execute='deferred'
/>

If you schedule the upgrade MSI during the transaction, for example use:
MajorUpgrade/#Schedule='afterInstallInitialize' or
MajorUpgrade/#Schedule='afterInstallExecute' or
MajorUpgrade/#Schedule='afterInstallExecuteAgain'
and use the ServiceControl element to start/stop/restart the service then the Windows Installer will do all the work for you.
This is by far the most recommended way to accomplish your goal.

Related

msiexec uninstall from c# fails to stop service

I have a service that runs and accepts remote commands. I am trying to send a remote command to uninstall. This works and executes the msiexec. The msiexec gets to the point where it tries to stop the service and then hangs and eventually fails saying no privileges to stop the service.
If I run the same command from a command window it works fine.
I have tried this running the service as local system (with admin privs) as well as administrator (I enabled the administrator account)
I just can't seem to figure out why I am getting permission errors stopping the service running the msiexec
added information
We have a single service - it does required work and sleeps periodically
the service typically runs as local system
we have the installer set using custom actions - and all cases work fine - installing and uninstalling from command line - but running msiexec uninstaller initiated within the C# code has this issue
When a remote request is made to uninstall the desire is to remove the service, registry entries, and files on disk
The request is handled by the running service - it goes to the registry, finds the product entry and retrieves the uninstall command
when the uninstall is performed I use C# process and include in the StartInfo parameters the program name, arguments, and verb (defined as run as) - among other parameters
the uninstaller starts properly, does some actions, and then gets to the point of stopping the service
stopping the service sits until it times out after which it writes in the uninstall log that there may not be sufficient privileges to stop the service
The note from Quercus triggered the solution to my problem. It turns out changing to not run the uninstall as a child of the service allowed the uninstaller to perform the msiexec without issue. To accomplish this I had to change UseShellExecute from false to true.

how to have a windows service self update?

All the solutions I can find on this topic are very old and none of them appear to answer my question...
I am trying to create a windows service that can self update (or auto update by some external trigger). In the past, I had created a windows service that was installed with InstallShield and we were able to update auto update the service in a hacky way by making the service write a batch script to the local machine and then run the batch script, which would stop the service, overwrite the service executable and other files with the new ones, and restart the service. This surprisingly worked.
However, I have updated the service to use InstallUtil.exe and this auto update script no longer works... I assume it's something to do with the way InstallShield handles the service install vs how InstallUtil does it... but I can only make guesses as I don't fully understand what each is doing to the registry.
Since I can't just overwrite the files and restart the service with the InstallUtil method, I thought I'd write a batch script that runs sc.exe to stop the service, uninstall it entirely, write the new files, install the new service files, and then start it... unfortunately, I can't seem to get sc.exe to run from a windows service automatically because it requires admin permissions... I tried to force it to self-elevate to admin using this snippet, but it doesn't appear to work as a service (it works fine if I run it from command line not as a service)
if not "%1"=="am_admin" (powershell start -verb runas '%0' am_admin & exit /b)
Does anyone know how I can cause a windows service to self update? I can look into updating to a .NET Core Worker service if there is some method of self update in .NET Core that I'm unaware of... Any ideas are much appreciated... it really shouldn't be this hard to accomplish...
For reference, here is the batch script I am currently using (ignore odd variables and such as I am dynamically replacing some of them, it works great when launched manually, just doesn't work when the service tries to run it):
#echo off
setlocal enableextensions enabledelayedexpansion
::make sure to run whole script as admin (this restarts scripts as admin if not already in admin mode)
if not "%1"=="am_admin" (powershell start -verb runas '%0' am_admin & exit /b)
pushd %networkDirectory%
::stop running service
for /F "tokens=3 delims=: " %%H in ('sc query %serviceName% ^| findstr " STATE"') do (
if /I "%%H" NEQ "STOPPED" (
net stop %serviceName%
if errorlevel 1 goto :stop
)
::delete existing service after stopping
sc delete %serviceName%
)
:: install updated service files
set "releaseDir=%networkDirectory%\Release"
set "programFilesCopyDir=%ProgramFiles%\{_companyDirectory}\%serviceName%\Release"
:: copy service Release dir to local system program files
xcopy "%releaseDir%" "%programFilesCopyDir%" /S /Y /Q
::execute the install
pushd "%programFilesCopyDir%"
CALL %serviceName%.exe --install
::start service
sc start %serviceName%
For anyone else trying to accomplish this that stumbles on this... I ended up finding a solution. I use the same script posted in my question above, but I wrote code to set up a scheduled task with Windows Task Scheduler. The scheduled task runs the above script as a one time scheduled task. This works like a charm.
I used this NuGet package to write the Task Scheduler code I needed:
https://www.nuget.org/packages/TaskScheduler/2.8.20?_src=template

Msi setup showing File in use message on uninstallation

I have developed an msi setup using WIX which consists of a desktop application as well as a windows service and both are running on C# .Net 3.5 framework. My windows service starts only when any user logs into the system which triggers the desktop application to start. The windows service is made to run as Local System. The msi setup is getting installed successfully at Win-8, Win-7 and Windows-XP but showing "File In Use" message while doing uninstallation even the service is not removed from the SCM. I have given the below codes at the OnStop() method of the service and inside the WIX page respectively.
onStop() method:
Process[] workers = Process.GetProcessesByName("filename");
workers[0].WaitForExit(1000);
workers[0].Kill();
workers[0].Dispose();
workers[0].Close();
Product.wxs inside WIX:
<ServiceInstall Id="ServiceInstaller" Name="Servicename"
DisplayName="service display name" Description="service description"
Start="auto" Account="LocalSystem" ErrorControl="normal"
Type="ownProcess"></ServiceInstall>
<ServiceControl Id="ServiceInstallerControl" Name="Servicename"
Start="install" Stop="both" Remove="uninstall" Wait="yes" />
The service is not removed from the SCM at all and the below popup message is displaying at the time of uninstallation process.
I have worked around with the WIX to solve the problem but unable to do so.
Any kind of help in this regard will be highly appreciated.
I'd recommend you to create custom action that handles the uninstall.You can create a batch file with the uninstall details , and then simply execute the batch file from the custom action( as a process).Its good practice to create installation and uninstall batch files with your service.

Elevated installer with elevated custom action does not elevate executable

I have an installer with InstallPrivileges="elevated". I include inside the file table an app.EXE
<Component Id="myapp" Guid="*">
<File Id="myapp" Source="myapp.exe"/>
</Component>
using these CA to run it in an elevcated state:
<CustomAction Id="SetProp" Property="Launch" Value =""[INSTALLDIR]myapp.exe""/>
<CustomAction Id="Launch" BinaryKey="WixCA" DllEntry="CAQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
scheduled like this:
<Custom Action="SetProp" Before="Launch">NOT Installed</Custom>
<Custom Action="Launch" Before="InstallFinalize">NOT Installed</Custom>
but according to Uachelper class (c#) and the missing result this process is not elevated BUT verbose log and myapp.exe log shows no error.
I also tried to use a type 2 custom action (binarykey etc.), a type 18 custom action (filekey etc.) and to include an app.manifest into myapp.exe with requiredadministrator.
nothing worked to elevate myapp.exe.. during installation.
Workaround I found is to start myapp.exe manually after installation completed (go to [INSTALLDIR] and doubleclick myapp.exe, will prompt uac etc.) but I want to avoid that.
I also thought about trying to use runas but am not sure if this would work or if this is feasible.
All of this is based on http://wixtoolset.org/documentation/manual/v3/customactions/qtexec.html , https://stackoverflow.com/a/10028939/4096653 and many more questions here on SO.
What am I missing or what else can I try?
By default a custom action will be elevated only if it is deferred, not impersonated, and sequenced between InstallInitialize and InstallFinalize, and if it's a per machine install, and then it will run elevated with the system account. Whether it will actually work or not depends on whether the code is prepared to deal with being aware that it's not running as the interactive user with access to HKCU, user profile items like PersonalFolder, and the code knows where its data is. When a custom action executable gets fired off it is via the CreateProcess() type of call (NOT a shell execute call) and so there is no UAC dialog/manifest checking stuff going on. So your WiX posted looks correct if you are deferred and before InstallFinalize. I have no idea what a UACHelper class thinks if are running as system and elevated. ProcessMonitor/Explorer might tell you what your rights/tokens are.
It should work if you create an impersonated launcher custom action that does an explicit Shell Execute of your exe, because that behaves like a run from Explorer by the interactive user. It will run the app which will then ask for elevation as long as the user doing the install is admin. In code that's something like ProcessStartInfo.UseShellExecute=true, ShellExecute() Win32.
If you need an impersonated CA to be elevated the MSI needs to be running as administrator or started from a program with an elevation manifest that then runs the MSI.
You'll see lots of advice (I hope) saying you should arrange to run this automatically the first time your app runs, just have it launched and remember whether that it has been run. Running as elevated user from an MSI is too complicated unless you have an elevated launching bootstrapper.
I assume you want this to run in Execute sequence and have scheduled your custom actions in InstallExecuteSequence
It might be so that your app is not able to run as Local system user so try to switch the Impersonate to yes Impersonate="Yes"

Run .NET Services from command line

How to run .NET services from command line?
net start "Sample Service" is not working.
Services are not supposed to be started from command line. First register it (installutil /i service_path) then start with net start or sc start.
Alternatively, create Main entry point in service project and start your logic, so you can test your service from command line. However this won't be real service environment.
Try using sc start
Make sure that you add an installer to your Windows service. You have to do this in order for the InstallUtil.exe utility to work. You can see how to that here.
After you add the installer, InstallUtil.exe can be used to install and uninstall your service.
Install: InstallUtil.exe <YourServiceExecutable>
Uninstall: InstallUtil.exe /u <YourServiceExecutable>
To start and stop your service, use Reed's solution.
If you want to get fancy, you can add some command-line logic to your service that will allow you to do all of this (install-and-start/stop-and-uninstall) directly from your service, i.e., you won't have to use InstallUtil.exe anymore. That solution is here.
You can start or stop a service by calling:
net start "Service Name"
net stop "Service Name"
For details, see this technet article.
This should work, provided the following are true:
The command prompt/user has the appropriate permissions to start the service. This will be required for ANY solution.
The service is installed and registered correctly with the system. My suspicion is that this may be the culprit. Is the service listed under services?

Categories

Resources