I need to write robust code in .NET to enable a windows service (server 2003) to restart itself. What is the best way to so this? Is there some .NET API to do it?
Set the service to restart after failure (double click the service in the control panel and have a look around on those tabs - I forget the name of it). Then, anytime you want the service to restart, just call Environment.Exit(1)
(or any non-zero return) and the OS will restart it for you.
Service.ExitCode = 1
and then execute Service.Stop()
along with enabling the checkbox 'Enable actions for stops with errors` on the service recovery setting screen. Doing it this way allows for a proper shutdown and still triggers the restart. –
Hardfavored Dim proc As New Process()
Dim psi As New ProcessStartInfo()
psi.CreateNoWindow = True
psi.FileName = "cmd.exe"
psi.Arguments = "/C net stop YOURSERVICENAMEHERE && net start YOURSERVICENAMEHERE"
psi.LoadUserProfile = False
psi.UseShellExecute = False
psi.WindowStyle = ProcessWindowStyle.Hidden
proc.StartInfo = psi
proc.Start()
You can't be sure that the user account that your service is running under even has permissions to stop and restart the service.
You can create a subprocess using Windows cmd.exe
that restarts yourself:
Process process = new Process();
process.StartInfo.FileName = "cmd";
process.StartInfo.Arguments = "/c net stop \"servicename\" & net start \"servicename\"";
process.Start();
const string strCmdText = "/C net stop \"SERVICENAME\"&net start \"SERVICENAME\"";
Process.Start("CMD.exe", strCmdText);
where SERVICENAME
is the name of your service (double quotes included to account for spaces in the service name, can be omitted otherwise).
Clean, no auto-restart configuration necessary.
It would depend on why you want it to restart itself.
If you are just looking for a way to have the service clean itself out periodically then you could have a timer running in the service that periodically causes a purge routine.
If you are looking for a way to restart on failure - the service host itself can provide that ability when it is setup.
So why do you need to restart the server? What are you trying to achieve?
I don't think you can in a self-contained service (when you call Restart, it will stop the service, which will interrupt the Restart command, and it won't ever get started again). If you can add a second .exe (a Console app that uses the ServiceManager class), then you can kick off the standalone .exe and have it restart the service and then exit.
On second thought, you could probably have the service register a Scheduled Task (using the command-line 'at' command, for example) to start the service and then have it stop itself; that would probably work.
The problem with shelling out to a batch file or EXE is that a service may or may not have the permissions required to run the external app.
The cleanest way to do this that I have found is to use the OnStop() method, which is the entry point for the Service Control Manager. Then all your cleanup code will run, and you won't have any hanging sockets or other processes, assuming your stop code is doing its job.
To do this you need to set a flag before you terminate that tells the OnStop method to exit with an error code; then the SCM knows that the service needs to be restarted. Without this flag you won't be able to stop the service manually from the SCM. This also assumes you have set up the service to restart on an error.
Here's my stop code:
...
bool ABORT;
protected override void OnStop()
{
Logger.log("Stopping service");
WorkThreadRun = false;
WorkThread.Join();
Logger.stop();
// if there was a problem, set an exit error code
// so the service manager will restart this
if(ABORT)Environment.Exit(1);
}
If the service runs into a problem and needs to restart, I launch a thread that stops the service from the SCM. This allows the service to clean up after itself:
...
if(NeedToRestart)
{
ABORT = true;
new Thread(RestartThread).Start();
}
void RestartThread()
{
ServiceController sc = new ServiceController(ServiceName);
try
{
sc.Stop();
}
catch (Exception) { }
}
I would use the Windows Scheduler to schedule a restart of your service. The problem is that you can't restart yourself, but you can stop yourself. (You've essentially sawed off the branch that you're sitting on... if you get my analogy) You need a separate process to do it for you. The Windows Scheduler is an appropriate one. Schedule a one-time task to restart your service (even from within the service itself) to execute immediately.
Otherwise, you'll have to create a "shepherding" process that does it for you.
The first response to the question is the simplest solution: "Environment.Exit(1)" I am using this on Windows Server 2008 R2 and it works perfectly. The service stops itself, the O/S waits 1 minute, then restarts it.
I don't think it can. When a service is "stopped", it gets totally unloaded.
Well, OK, there's always a way I suppose. For instance, you could create a detached process to stop the service, then restart it, then exit.
Just passing: and thought i would add some extra info...
you can also throw an exception, this will auto close the windows service, and the auto re-start options just kick in. the only issue with this is that if you have a dev enviroment on your pc then the JIT tries to kick in, and you will get a prompt saying debug Y/N. say no and then it will close, and then re-start properly. (on a PC with no JIT it just all works). the reason im trolling, is this JIT is new to Win 7 (it used to work fine with XP etc) and im trying to find a way of disabling the JIT.... i may try the Environment.Exit method mentioned here see how that works too.
Kristian : Bristol, UK
Create a restart.bat file like this
@echo on
set once="C:\Program Files\MyService\once.bat"
set taskname=Restart_MyService
set service=MyService
echo rem %time% >%once%
echo net stop %service% >>%once%
echo net start %service% >>%once%
echo del %once% >>%once%
schtasks /create /ru "System" /tn %taskname% /tr '%once%' /sc onstart /F /V1 /Z
schtasks /run /tn %taskname%
Then delete the task %taskname% when your %service% starts
Create a separate appdomain to host the application code. When requires restart, we could unload and reload the appdomain instead the process (windows service). This is how IIS app pool works, they dont run asp.net app directly, they use separate appdmain.
Using Process.Start
to run sc stop OwnServiceName
followed by sc start OwnServiceName
only works when the service-process has permission to control itself - which they invariably don't.
...and even if the service-process does have permission, simply running cmd.exe /c sc stop Self && sc start Self
isn't going to work because sc stop
returns immediately without waiting for the service to stop (while the older net stop
command does wait, it doesn't wait indefinitely). A better approach is to put this multi-step logic in a .cmd
batch file and run that with Process.Start( "cmd.exe", "/c RestartService.cmd" )
.
As for ensuring the Service has sufficient permission to restart itself:
If you used WIX MSI to install the Windows Service, you can use WIX's util:PermissionEx
element:
<Component Id="cmpYourServiceHostExe" Directory="INSTALLDIR" Guid="*">
<File Id="fleYourServiceHostExe" KeyPath="yes" Source="YourServiceHost.exe">
<netfx:NativeImage Id="Ngen_fleYourServiceHostExe" Platform="32bit" Priority="0" AppBaseDirectory="INSTALLDIR"/>
</File>
<ServiceInstall
Id="srvYourService"
Name="YourService"
Account= "NT AUTHORITY\LocalService"
Type= "ownProcess"
>
<ServiceConfig
DelayedAutoStart="yes"
FailureActionsWhen="failedToStopOrReturnedError"
OnInstall="yes"
OnUninstall="no"
OnReinstall="yes"
/>
<util:ServiceConfig
FirstFailureActionType="restart"
SecondFailureActionType="restart"
ThirdFailureActionType="restart"
ResetPeriodInDays="1"
/>
<util:PermissionEx
User = "NT AUTHORITY\LocalService"
ServiceEnumerateDependents = "yes"
ServiceInterrogate = "yes"
ServicePauseContinue = "yes"
ServiceQueryConfig = "yes"
ServiceQueryStatus = "yes"
ServiceStart = "yes"
ServiceStop = "yes"
ServiceUserDefinedControl = "yes"
/>
</ServiceInstall>
</Component>
If you're manually installing your service - or using some other technique, then you can set service permissions by running sc sdset
, which requires familiarizing yourself with Windows' SDDL syntax and how it relates to Windows Service security.
The default SDDL for a Windows Service seems to be this:
D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)
...using these definitions:
winsvc.h Service Access Right == Iads.h ADS_RIGHT name == Sddl.h == SDDL String
--------------------------------------------------------------------------------------------------------
SERVICE_QUERY_CONFIG 0x0001 == ADS_RIGHT_DS_CREATE_CHILD == SDDL_CREATE_CHILD == "CC"
SERVICE_CHANGE_CONFIG 0x0002 == ADS_RIGHT_DS_DELETE_CHILD == SDDL_DELETE_CHILD == "DC"
SERVICE_QUERY_STATUS 0x0004 == ADS_RIGHT_ACTRL_DS_LIST == SDDL_LIST_CHILDREN == "LC"
SERVICE_ENUMERATE_DEPENDENTS 0x0008 == ADS_RIGHT_DS_SELF == SDDL_SELF_WRITE == "SW"
SERVICE_START 0x0010 == ADS_RIGHT_DS_READ_PROP == SDDL_READ_PROPERTY == "RP"
SERVICE_STOP 0x0020 == ADS_RIGHT_DS_WRITE_PROP == SDDL_WRITE_PROPERTY == "WP"
SERVICE_PAUSE_CONTINUE 0x0040 == ADS_RIGHT_DS_DELETE_TREE == SDDL_DELETE_TREE == "DT"
SERVICE_INTERROGATE 0x0080 == ADS_RIGHT_DS_LIST_OBJECT == SDDL_LIST_OBJECT == "LO"
SERVICE_USER_DEFINED_CONTROL 0x0100 == ADS_RIGHT_DS_CONTROL_ACCESS == SDDL_CONTROL_ACCESS == "CR"
...then the default SSDL expands to this:
(A;;CC LC SW RP WP DT LO CR RC;;;SY)
Allow "LOCAL SYSTEM" (SY):
CC = SERVICE_QUERY_CONFIG
LC = SERVICE_QUERY_STATUS
SW = SERVICE_ENUMERATE_DEPENDENTS
RP = SERVICE_START
WP = SERVICE_STOP
DT = SERVICE_PAUSE_CONTINUE
LO = SERVICE_INTERROGATE
CR = SERVICE_USER_DEFINED_CONTROL
RC = SDDL_READ_CONTROL
(A;;CC DC LC SW RP WP DT LO CR SD RC WD WO;;;BA)
Allow "Built-in Administrators" (BA)
CC = SERVICE_QUERY_CONFIG
LC = SERVICE_QUERY_STATUS
SW = SERVICE_ENUMERATE_DEPENDENTS
RP = SERVICE_START
WP = SERVICE_STOP
DT = SERVICE_PAUSE_CONTINUE
LO = SERVICE_INTERROGATE
CR = SERVICE_USER_DEFINED_CONTROL
SD = SDDL_STANDARD_DELETE
RC = SDDL_READ_CONTROL
WD = SDDL_WRITE_DAC
WO = SDDL_WRITE_OWNER
(A;;CC LC SW LO CR RC;;;IU)
Allow "Interactive Users" (IU)
CC = SERVICE_QUERY_CONFIG
LC = SERVICE_QUERY_STATUS
SW = SERVICE_ENUMERATE_DEPENDENTS
LO = SERVICE_INTERROGATE
CR = SERVICE_USER_DEFINED_CONTROL
RC = SDDL_READ_CONTROL
(A;;CC LC SW LO CR RC;;;SU)
Allow a process with "Service" token (see "SECURITY_SERVICE_RID")
CC = SERVICE_QUERY_CONFIG
LC = SERVICE_QUERY_STATUS
SW = SERVICE_ENUMERATE_DEPENDENTS
LO = SERVICE_INTERROGATE
CR = SERVICE_USER_DEFINED_CONTROL
RC = SDDL_READ_CONTROL
We change the SU
rule to this:
(A;;CC LC SW RP WP DT LO CR RC;;;SU)
SECURITY_SERVICE_RID
CC = SERVICE_QUERY_CONFIG
LC = SERVICE_QUERY_STATUS
SW = SERVICE_ENUMERATE_DEPENDENTS
RP = SERVICE_START
WP = SERVICE_STOP
DT = SERVICE_PAUSE_CONTINUE
LO = SERVICE_INTERROGATE
CR = SERVICE_USER_DEFINED_CONTROL
RC = SDDL_READ_CONTROL
So you run this from an elevated process (e.g. your setup program or service-installer):
sc sdset YourServiceName "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)((A;;CCLCSWRPWPDTLOCRRC;;;SU)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)
...which then means your service can run Process.Start( "cmd.exe", "/c RestartService.cmd" )
.
This is what my RestartService.cmd
looks like:
(With acknowledgement to @js2010's answer posted here)
sc stop YourServiceName
:loop
sc query YourServiceName | find "STOPPED"
if errorlevel 1 (
timeout /t 2
goto loop
)
sc start YourServiceName
The better approach may be to utilize the NT Service as a wrapper for your application. When the NT Service is started, your application can start in an "idle" mode waiting for the command to start (or be configured to start automatically).
Think of a car, when it's started it begins in an idle state, waiting for your command to go forward or reverse. This also allows for other benefits, such as better remote administration as you can choose how to expose your application.
The easiest way is to have a batch file with:
net stop net start
and add the file to the scheduler with your desired time interval
private static void RestartService(string serviceName)
{
using (var controller = new ServiceController(serviceName))
{
controller.Stop();
int counter = 0;
while (controller.Status != ServiceControllerStatus.Stopped)
{
Thread.Sleep(100);
controller.Refresh();
counter++;
if (counter > 1000)
{
throw new System.TimeoutException(string.Format("Could not stop service: {0}", Constants.Series6Service.WindowsServiceName));
}
}
controller.Start();
}
}
© 2022 - 2024 — McMap. All rights reserved.