How can a windows service programmatically restart itself?
Asked Answered
J

18

151

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?

Johnajohnath answered 21/10, 2008 at 0:12 Comment(2)
"Robust" is a weasel word (stackoverflow.fogbugz.com/default.asp?W13086) - what specific traits to you require?Cap
@AidanRyan, I couldn't find "robust" in that article. "Robust" means reliable under a variety of circumstances. This being Windows, there are innumerable weird unknown unknowns, and it would be good to know solutions that are broadly compatible.Faintheart
J
210

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.

Jabez answered 21/10, 2008 at 0:58 Comment(11)
Simple solutions are the best solutions. Just did this to replace the existing "2nd process to periodically restart the first" setup.Baryram
Fyi the location is in the services panel, right click the service in question and select properties, then choose recovery tab.Baumbaugh
I see how this achieves the desired behaviour but a returncode of 1 is meant to tell the system that there was an error. Isn't this bad practice if there was in fact no error and you just wanted to restart your service?Hydrops
This can be done programmaticaly by the service installer in the after install event, no need to click around... What if your service is on a remote server and you need to install it several times a day, for example during testing?Addi
I like it, but as pointed out by @devshorts calling exit won't ensure that the process is shutdown or disposed of properly, that can have detrimental effect.Spirituous
@mgttlinger: I think that yes, is a bad practice when your service is healthy, so it'll never need to be restarted. But some services architecture are out of our reach and if they need to be restarted is a symptom that it isn't well, so no problem to close and free some minimum resorces (if possible) and 'cut their feet', since leaving the service running improperly can be worse (useless).Coralline
@JohnLeidegren if you want a proper shutdown you can set the 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
Should you maybe use the exit code 1641 "ERROR_SUCCESS_REBOOT_INITIATED"?Faithfaithful
Note: This wasn't working for me and I was banging my head figuring out why. Turns out I wasn't being patience enough. It takes 2 minutes for it to restart automatically.Meier
@Faithfaithful That code indicates a system restart. I think exit code 1467 would be more appropriate. That indicates an application restart with the message "ERROR_RESTART_APPLICATION".Bouley
This solution does also imply that the service restarts after EVERY error. This is a side effect which may not be very useful. An other error may occur and you might not want to restart the service after this error...Synclastic
A
25
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()
Anthemion answered 31/5, 2011 at 5:26 Comment(4)
Remember to add " " arround you service name if it contains spaces.Abshire
This is only for services running under privileged account, which is bad idea anyway.Decommission
Depending on your environment you might have to ensure that the cmd.exe process runs in a a separate process group. I tried to implement this, but the cmd.exe child process died whenever "net stop" was executed. To avoid this you need to call CreateProcess with CREATE_NEW_PROCESS_GROUP specified.Ricks
Too follow up on what @Ricks said, psi.UseShellExecute = true. This will allow the second "net start" part to run.Alveolus
E
18

You can't be sure that the user account that your service is running under even has permissions to stop and restart the service.

Expatriate answered 21/10, 2008 at 0:19 Comment(6)
Although +1-ed as I faced the same issue, when looking at sc.exe it turns out that it uses SERVICE_QUERY_CONFIG as dwDesiredAccess when calling OpenSCManager () and then calls OpenService() with SERVICE_START | SERVICE_STOP to open a particular service and it works OK (no problem with access). Not sure about how can this be done in .NET.Stanfield
Most Windows Services run as System, so it shouldn't be a problem.Inchworm
@Switch there is a huge circle of services which are run under NETWORK SERVICE, which by default doesn't even have rights to open its own executable.Partake
@AgentFire, correct, but default is Local System, not NETWORK SERVICE. Local System has inherently the highest level of privileges to the operating system - exceeding those assigned to members of local Administrators group.Inchworm
@Switch but using the most available privileges is violating the PoL-Principle.Partake
-1 Not a good solution. Not a solution at all, really. More of a comment. @Jabez has a good example of a solution.Faintheart
L
18

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();
Largescale answered 11/2, 2009 at 21:13 Comment(1)
This, without doing anything else, will inherit the security context of the calling thread...and as long as that's a context that has enough horsepower to restart, you're good.Hydranth
S
14
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.

Sigh answered 12/4, 2013 at 14:10 Comment(1)
learned something new about & vs && : [command1] & [command2] will always execute both commands sequentially, while [command1] && [command2] runs command2 only if command1 runs successfully (autoitscript.com/forum/topic/…)Endocarp
H
5

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?

Hyperostosis answered 21/10, 2008 at 1:2 Comment(1)
This wouldn't work if you're concerned with the Large Object Heap and memory fragmentation. Supposedly .NET 4/4.5 and 64-bit processes have helped a lot with this.Enrichetta
L
4

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.

Larner answered 21/10, 2008 at 0:18 Comment(0)
T
3

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) { }
}
Tammietammuz answered 27/4, 2016 at 15:23 Comment(0)
D
2

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.

Deadly answered 3/3, 2009 at 17:23 Comment(0)
S
2

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.

Successful answered 12/11, 2010 at 3:48 Comment(1)
How long it waits depends on how long you set it up to wait. Default is 1 minute though, but if someone doesn't want that your answer will give the wrong impression.Faithfaithful
I
1

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.

Imprudent answered 21/10, 2008 at 0:18 Comment(0)
S
0

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

Sherrillsherrington answered 9/8, 2011 at 9:30 Comment(1)
All of these throw exception, exit(1) solutions all avoid proper cleanup of an application. Un-gracefully exiting is a poor solutionTe
M
0

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

Mcglone answered 14/8, 2014 at 9:2 Comment(0)
I
0

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.

Ionia answered 13/7, 2015 at 9:54 Comment(0)
M
0

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
Monkshood answered 28/4 at 0:49 Comment(0)
U
-1

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.

Upbeat answered 21/10, 2008 at 0:40 Comment(0)
M
-2

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

Milore answered 11/6, 2009 at 4:52 Comment(1)
It's not working because the batch is stopped between both commands, because it is a child process of the service itself.Osswald
C
-4
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();
        }
    }
Cotta answered 12/10, 2010 at 15:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.