140

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?

Ron Harlev
  • 15,010
  • 24
  • 83
  • 128
  • 7
    "Robust" is a weasel word (https://stackoverflow.fogbugz.com/default.asp?W13086) - what specific traits to you require? – Aidan Ryan Oct 21 '08 at 00:40

17 Answers17

197

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.

developer82
  • 11,911
  • 18
  • 76
  • 134
TheSoftwareJedi
  • 32,535
  • 19
  • 103
  • 147
  • Simple solutions are the best solutions. Just did this to replace the existing "2nd process to periodically restart the first" setup. – geofftnz Mar 20 '10 at 02:22
  • 7
    Fyi the location is in the services panel, right click the service in question and select properties, then choose recovery tab. – James Michael Hare Jul 07 '11 at 14:35
  • 20
    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? – mgttlinger May 29 '13 at 13:47
  • 4
    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? – Dean Kuga Aug 12 '13 at 21:36
  • 3
    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. – John Leidegren Oct 28 '13 at 20:40
  • 5
    @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). – Luciano Mar 28 '14 at 19:58
  • 7
    @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. – Chris Rice Aug 02 '16 at 21:00
  • 2
    Should you maybe use the exit code 1641 "ERROR_SUCCESS_REBOOT_INITIATED"? – Espen Mar 29 '17 at 11:28
  • 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. – Ronnie W Jan 08 '20 at 16:04
22
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()
i3arnon
  • 101,022
  • 27
  • 291
  • 322
Khalid Rahaman
  • 2,134
  • 3
  • 26
  • 38
  • 3
    Remember to add " " arround you service name if it contains spaces. – apc Mar 05 '15 at 12:33
  • 4
    This is only for services running under privileged account, which is bad idea anyway. – Dmitry Gusarov Nov 09 '16 at 11:35
  • 1
    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. – Joppe Aug 02 '20 at 18:14
17

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

Greg Hewgill
  • 828,234
  • 170
  • 1,097
  • 1,237
  • 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. – n0p Nov 10 '15 at 09:20
  • Most Windows Services run as System, so it shouldn't be a problem. – Switch Sep 28 '18 at 11:32
  • @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. – AgentFire Jan 24 '19 at 17:35
  • @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. – Switch Jan 29 '19 at 15:14
  • @Switch but using the most available privileges is violating the [PoL-Principle](https://www.google.com/search?q=least+principle+programming&oq=least+principle+programming&aqs=chrome..69i57.9280j1j7&sourceid=chrome&ie=UTF-8). – AgentFire Jan 30 '19 at 09:16
12
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.

Filip
  • 2,108
  • 1
  • 16
  • 24
  • 9
    learned something new about & vs && : [command1] & [command2] will always execute both commands sequentially, while [command1] && [command2] runs command2 only if command1 runs successfully (http://www.autoitscript.com/forum/topic/54864-run-multiple-dos-commands-in-one-line/?p=415739) – Jeffrey Knight Jun 13 '13 at 19:29
12

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();
Simon Kissane
  • 3,383
  • 3
  • 30
  • 46
  • 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. – Clay Mar 26 '19 at 19:06
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?

Brody
  • 2,076
  • 1
  • 18
  • 23
  • 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. – David Kassa Apr 11 '13 at 16:38
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.

technophile
  • 3,456
  • 1
  • 18
  • 23
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) { }
}
buzzard51
  • 1,190
  • 18
  • 33
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.

tangentMan
  • 21
  • 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. – Espen Mar 29 '17 at 11:16
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.

dviljoen
  • 1,606
  • 1
  • 14
  • 28
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.

T.E.D.
  • 41,324
  • 8
  • 64
  • 131
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

Erik Martino
  • 3,561
  • 1
  • 15
  • 11
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.

CreativeManix
  • 2,048
  • 1
  • 15
  • 27
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

Kristian
  • 17
  • 1
  • 3
    All of these throw exception, exit(1) solutions all avoid proper cleanup of an application. Un-gracefully exiting is a poor solution – devshorts Aug 08 '12 at 19:16
-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.

plamb
  • 7
  • 2
-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

  • It's not working because the batch is stopped between both commands, because it is a child process of the service itself. – Orabîg Jan 17 '13 at 12:09
-3
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();
        }
    }
Lee Smith
  • 5,556
  • 4
  • 24
  • 33