Stopping/Starting a remote Windows service and waiting for it to open/close

后端 未结 11 1505
广开言路
广开言路 2020-12-02 06:42

The top answer to this question tells me how to stop/start a remote service. Great. Now, all I need is to wait for the actual stop/start to complete. So, what I\'m looking f

相关标签:
11条回答
  • 2020-12-02 07:20

    I've never actually seen something that does this specifically but it would be quite easy to knock such a utility out in C\C#\VB or any other language that gives easy access to the Service API. Here's a sample of something in C#.

    using System;
    using System.ComponentModel;
    using System.ServiceProcess;
    
    namespace SCSync
    {
        class Program
        {
            private const int ERROR_SUCCESS = 0;
    
            private const int ERROR_INVALID_COMMAND_LINE = 1;
            private const int ERROR_NO_ACCESS = 2;
            private const int ERROR_COMMAND_TIMEOUT = 3;
            private const int ERROR_NO_SERVICE = 4;
            private const int ERROR_NO_SERVER = 5;
            private const int ERROR_INVALID_STATE = 6;
            private const int ERROR_UNSPECIFIED = 7;
    
            static int Main(string[] args)
            {
    
                if (args.Length < 2 || args.Length > 4)
                {
                    ShowUsage();
                    return ERROR_INVALID_COMMAND_LINE;
                }
    
                string serviceName = args[0];
                string command = args[1].ToUpper();
                string serverName = ".";
                string timeoutString = "30";
                int timeout;
    
                if (args.Length > 2)
                {
                    if (args[2].StartsWith(@"\\"))
                    {
                        serverName = args[2].Substring(2);
                        if (args.Length > 3)
                        {
                            timeoutString = args[3];
                        }
                    }
                    else
                    {
                        timeoutString = args[2];
                    }
                }
    
                if (!int.TryParse(timeoutString, out timeout))
                {
                    Console.WriteLine("Invalid timeout value.\n");
                    ShowUsage();
                    return ERROR_INVALID_COMMAND_LINE;
                }
    
                try
                {
                    ServiceController sc = new ServiceController(serviceName, serverName);
                    switch (command)
                    {
                        case "START":
                            sc.Start();
                            sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 0, timeout));
                            break;
                        case "STOP":
                            sc.Stop();
                            sc.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 0, timeout));
                            break;
                        case "PAUSE":
                            sc.Pause();
                            sc.WaitForStatus(ServiceControllerStatus.Paused, new TimeSpan(0, 0, 0, timeout));
                            break;
                        case "CONTINUE":
                            sc.Continue();
                            sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 0, timeout));
                            break;
                        default:
                            Console.WriteLine("Invalid command value.\n");
                            ShowUsage();
                            return ERROR_INVALID_COMMAND_LINE;
                    }
                }
                catch (System.ServiceProcess.TimeoutException)
                {
                    Console.WriteLine("Operation timed out.\n");
                    return ERROR_COMMAND_TIMEOUT;
                }
                catch (UnauthorizedAccessException)
                {
                    Console.WriteLine("You are not authorized to perform this action.\n");
                    return ERROR_NO_ACCESS;
                }
                catch (InvalidOperationException opEx)
                {
                    Win32Exception winEx = opEx.InnerException as Win32Exception;
                    if (winEx != null)
                    {
                        switch (winEx.NativeErrorCode)
                        {
                            case 5: //ERROR_ACCESS_DENIED
                                Console.WriteLine("You are not authorized to perform this action.\n");
                                return ERROR_NO_ACCESS;
                            case 1722: //RPC_S_SERVER_UNAVAILABLE
                                Console.WriteLine("The server is unavailable or does not exist.\n");
                                return ERROR_NO_SERVER;
                            case 1060: //ERROR_SERVICE_DOES_NOT_EXIST
                                Console.WriteLine("The service does not exist.\n");
                                return ERROR_NO_SERVICE;
                            case 1056: //ERROR_SERVICE_ALREADY_RUNNING
                                Console.WriteLine("The service is already running.\n");
                                return ERROR_INVALID_STATE;
                            case 1062: //ERROR_SERVICE_NOT_ACTIVE
                                Console.WriteLine("The service is not running.\n");
                                return ERROR_INVALID_STATE;
                            default:
                                break;
                        }
                    }
                    Console.WriteLine(opEx.ToString());
                    return ERROR_UNSPECIFIED;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                    return ERROR_UNSPECIFIED;
                }
    
                return ERROR_SUCCESS;
            }
    
            private static void ShowUsage()
            {
                Console.WriteLine("SCSync usage:\n");
                Console.WriteLine("SCSync.exe service command <server> <timeout>\n");
                Console.WriteLine("    service   The name of the service upon which the command will act. (Required)");
                Console.WriteLine("    command   The command to execute - one of: start|stop|pause|continue. (Required)");
                Console.WriteLine("    server    The name of the server on which the target service runs. This must start with \\. (Optional)");
                Console.WriteLine("    timeout   The timeout period in seconds in which the command should finish. The default is 30 seconds. (Optional)");
                Console.WriteLine("\n");
            }
        }
    }
    

    The WaitForStatus is just a polling loop and could be easily replaced in any other language. The rest is just OpenService and ControlService.

    0 讨论(0)
  • 2020-12-02 07:22

    I created a set of batch scripts that use sc.exe to do just this. They are attached below. To run these scripts, you should be a user with administration rights on the target machine and running this from a computer that is a member of the same domain. It's possible to set it up to be able to run from outside of the domain (like from a VPN) but there are a lot of layers of security to work through involving firewalls, DCOM and security credentials.

    One of these days, I'm going to figure out the PowerShell equivalent, which should be much easier.

    safeServiceStart.bat

    @echo off
    :: This script originally authored by Eric Falsken
    
    IF [%1]==[] GOTO usage
    IF [%2]==[] GOTO usage
    
    ping -n 1 %1 | FIND "TTL=" >NUL
    IF errorlevel 1 GOTO SystemOffline
    SC \\%1 query %2 | FIND "STATE" >NUL
    IF errorlevel 1 GOTO SystemOffline
    
    :ResolveInitialState
    SC \\%1 query %2 | FIND "STATE" | FIND "STOPPED" >NUL
    IF errorlevel 0 IF NOT errorlevel 1 GOTO StartService
    SC \\%1 query %2 | FIND "STATE" | FIND "RUNNING" >NUL
    IF errorlevel 0 IF NOT errorlevel 1 GOTO StartedService
    SC \\%1 query %2 | FIND "STATE" | FIND "PAUSED" >NUL
    IF errorlevel 0 IF NOT errorlevel 1 GOTO SystemOffline
    echo Service State is changing, waiting for service to resolve its state before making changes
    sc \\%1 query %2 | Find "STATE"
    timeout /t 2 /nobreak >NUL
    GOTO ResolveInitialState
    
    :StartService
    echo Starting %2 on \\%1
    sc \\%1 start %2 >NUL
    
    GOTO StartingService
    :StartingServiceDelay
    echo Waiting for %2 to start
    timeout /t 2 /nobreak >NUL
    :StartingService
    SC \\%1 query %2 | FIND "STATE" | FIND "RUNNING" >NUL
    IF errorlevel 1 GOTO StartingServiceDelay
    
    :StartedService
    echo %2 on \\%1 is started
    GOTO:eof
    
    :SystemOffline
    echo Server \\%1 is not accessible or is offline
    GOTO:eof
    
    :usage
    echo %0 [system name] [service name]
    echo Example: %0 server1 MyService
    echo.
    GOTO:eof
    

    safeServiceStop.bat

    @echo off
    :: This script originally authored by Eric Falsken
    
    IF [%1]==[] GOTO usage
    IF [%2]==[] GOTO usage
    
    ping -n 1 %1 | FIND "TTL=" >NUL
    IF errorlevel 1 GOTO SystemOffline
    SC \\%1 query %2 | FIND "STATE" >NUL
    IF errorlevel 1 GOTO SystemOffline
    
    :ResolveInitialState
    SC \\%1 query %2 | FIND "STATE" | FIND "RUNNING" >NUL
    IF errorlevel 0 IF NOT errorlevel 1 GOTO StopService
    SC \\%1 query %2 | FIND "STATE" | FIND "STOPPED" >NUL
    IF errorlevel 0 IF NOT errorlevel 1 GOTO StopedService
    SC \\%1 query %2 | FIND "STATE" | FIND "PAUSED" >NUL
    IF errorlevel 0 IF NOT errorlevel 1 GOTO SystemOffline
    echo Service State is changing, waiting for service to resolve its state before making changes
    sc \\%1 query %2 | Find "STATE"
    timeout /t 2 /nobreak >NUL
    GOTO ResolveInitialState
    
    :StopService
    echo Stopping %2 on \\%1
    sc \\%1 stop %2 %3 >NUL
    
    GOTO StopingService
    :StopingServiceDelay
    echo Waiting for %2 to stop
    timeout /t 2 /nobreak >NUL
    :StopingService
    SC \\%1 query %2 | FIND "STATE" | FIND "STOPPED" >NUL
    IF errorlevel 1 GOTO StopingServiceDelay
    
    :StopedService
    echo %2 on \\%1 is stopped
    GOTO:eof
    
    :SystemOffline
    echo Server \\%1 or service %2 is not accessible or is offline
    GOTO:eof
    
    :usage
    echo Will cause a remote service to STOP (if not already stopped).
    echo This script will waiting for the service to enter the stopped state if necessary.
    echo.
    echo %0 [system name] [service name] {reason}
    echo Example: %0 server1 MyService
    echo.
    echo For reason codes, run "sc stop"
    GOTO:eof
    

    safeServiceRestart.bat

    @echo off
    :: This script originally authored by Eric Falsken
    
    if [%1]==[] GOTO usage
    if [%2]==[] GOTO usage
    
    ping -n 1 %1 | FIND "TTL=" >NUL
    IF errorlevel 1 GOTO SystemOffline
    SC \\%1 query %2 | FIND "STATE" >NUL
    IF errorlevel 1 GOTO SystemOffline
    
    :ResolveInitialState
    SC \\%1 query %2 | FIND "STATE" | FIND "RUNNING" >NUL
    IF errorlevel 0 IF NOT errorlevel 1 GOTO StopService
    SC \\%1 query %2 | FIND "STATE" | FIND "STOPPED" >NUL
    IF errorlevel 0 IF NOT errorlevel 1 GOTO StartService
    SC \\%1 query %2 | FIND "STATE" | FIND "PAUSED" >NUL
    IF errorlevel 0 IF NOT errorlevel 1 GOTO SystemOffline
    echo Service State is changing, waiting for service to resolve its state before making changes
    sc \\%1 query %2 | Find "STATE"
    timeout /t 2 /nobreak >NUL
    GOTO ResolveInitialState
    
    :StopService
    echo Stopping %2 on \\%1
    sc \\%1 stop %2 %3 >NUL
    
    GOTO StopingService
    :StopingServiceDelay
    echo Waiting for %2 to stop
    timeout /t 2 /nobreak >NUL
    :StopingService
    SC \\%1 query %2 | FIND "STATE" | FIND "STOPPED" >NUL
    IF errorlevel 1 GOTO StopingServiceDelay
    
    :StopedService
    echo %2 on \\%1 is stopped
    GOTO StartService
    
    :StartService
    echo Starting %2 on \\%1
    sc \\%1 start %2 >NUL
    
    GOTO StartingService
    :StartingServiceDelay
    echo Waiting for %2 to start
    timeout /t 2 /nobreak >NUL
    :StartingService
    SC \\%1 query %2 | FIND "STATE" | FIND "RUNNING" >NUL
    IF errorlevel 1 GOTO StartingServiceDelay
    
    :StartedService
    echo %2 on \\%1 is started
    GOTO:eof
    
    :SystemOffline
    echo Server \\%1 or service %2 is not accessible or is offline
    GOTO:eof
    
    :usage
    echo Will restart a remote service, waiting for the service to stop/start (if necessary)
    echo.
    echo %0 [system name] [service name] {reason}
    echo Example: %0 server1 MyService
    echo.
    echo For reason codes, run "sc stop"
    GOTO:eof
    
    0 讨论(0)
  • 2020-12-02 07:23

    What about PowerShell and Restart-Service commandlet? :)

    Get-Service W3SVC -computer myserver | Restart-Service
    
    0 讨论(0)
  • 2020-12-02 07:23

    I've made a minor change to the script, so that it is running under Windows 10 or similar Versions. The command "sleep" was replaced with the command "timeout".

    @ECHO off
    :: This script originally authored by Eric Falsken http://stackoverflow.com/
    :: Revised by George Perkins 10/20/2011
    :: Revised by Armando Contestabile 02/23/2015
    :: Revised by Sascha Jelinek 11/13/2020
    IF "%1"=="" GOTO Usage
    IF "%2"=="" GOTO Usage
    
    SET ACTION=%1
    SET SERVICENAME=%2
    
    IF "%3"=="" (
        SET SYSTEMNAME=%COMPUTERNAME%
    ) ELSE (
        SET SYSTEMNAME=%3
    )
    
    IF "%ACTION%" == "stop" (
        SET ACTION=STOP
    ) ELSE IF "%ACTION%" == "STOP" (
        SET ACTION=STOP
    ) ELSE IF "%ACTION%" == "start" (
        SET ACTION=START
    ) ELSE IF "%ACTION%" == "START" (
        SET ACTION=START
    ) ELSE IF "%ACTION%" == "restart" (
        SET ACTION=RESTART
    ) ELSE IF "%ACTION%" == "RESTART" (
        SET ACTION=RESTART
    ) ELSE GOTO Usage
    
    SET STATE=
    SET CURRENT_STATUS=
    SET /A DEFAULT_DELAY=5
    SET /A SLEEP_COUNT=0
    SET /A RESTARTED=0
    SET /A MAX_WAIT_PERIODS=5
    
    ECHO.
    ECHO Attempting to %ACTION% service %SERVICENAME% on computer %SYSTEMNAME%.
    
    PING -n 1 %SYSTEMNAME% | FIND "Antwort von" >nul 2>&1
    IF ERRORLEVEL 1 IF NOT ERRORLEVEL 2 (
        ECHO Failure!! Server \\%SYSTEMNAME% or service %SERVICENAME% is not accessible or is offline!
        EXIT /B 1
    )
    SC \\%SYSTEMNAME% query %SERVICENAME% | FIND "FAILED 1060" >nul 2>&1
    IF ERRORLEVEL 0 IF NOT ERRORLEVEL 1 (
        ECHO Failure! Service %SERVICENAME% is not valid!
        EXIT /B 2
    )
    SC \\%SYSTEMNAME% query %SERVICENAME% | FIND "STATE" >nul 2>&1
    IF ERRORLEVEL 1 IF NOT ERRORLEVEL 2 (
        ECHO Failure! Server \\%SYSTEMNAME% or service %SERVICENAME% is not accessible or is offline!
        EXIT /B 3
    )
    
    :Dispatch
    FOR /f "tokens=*" %%i IN ('SC \\%SYSTEMNAME% query %SERVICENAME% ^| FIND "STATE"') DO SET STATE=%%i
    
    ECHO %STATE% | FINDSTR /C:"1" >nul
    IF %ERRORLEVEL%==0 SET CURRENT_STATUS=STOPPED
    ECHO %STATE% | FINDSTR /C:"2" >nul
    IF %ERRORLEVEL%==0 SET CURRENT_STATUS=START_PENDING
    ECHO %STATE% | FINDSTR /C:"3" >nul
    IF %ERRORLEVEL%==0 SET CURRENT_STATUS=STOP_PENDING
    ECHO %STATE% | FINDSTR /C:"4" >nul
    IF %ERRORLEVEL%==0 SET CURRENT_STATUS=RUNNING
    ECHO %STATE% | FINDSTR /C:"5" >nul
    IF %ERRORLEVEL%==0 SET CURRENT_STATUS=CONTINUE_PENDING
    ECHO %STATE% | FINDSTR /C:"6" >nul
    IF %ERRORLEVEL%==0 SET CURRENT_STATUS=PAUSE_PENDING
    ECHO %STATE% | FINDSTR /C:"7" >nul
    IF %ERRORLEVEL%==0 SET CURRENT_STATUS=PAUSED
    
    ECHO Current status of service is %CURRENT_STATUS%
    
    IF NOT "%CURRENT_STATUS%"=="RUNNING" IF NOT "%CURRENT_STATUS%"=="STOPPED" IF NOT "%CURRENT_STATUS%"=="PAUSED" (
        IF "%SLEEP_COUNT%"=="%MAX_WAIT_PERIODS%" (
            ECHO Service state won't change. Script exececution is canceled.
            EXIT /B 4
        )
        ECHO Service State is changing, waiting %DEFAULT_DELAY% seconds...
        TIMEOUT /t %DEFAULT_DELAY% /NOBREAK
        SET /A SLEEP_COUNT+=1
        GOTO Dispatch
    )
    
    IF "%ACTION%"=="START" (
        IF "%CURRENT_STATUS%"=="RUNNING" (
            ECHO Service %SERVICENAME% is running.
            GOTO EndExit
        ) ELSE (
            GOTO StartService
        )
    ) ELSE IF "%ACTION%"=="RESTART" (
        IF "%CURRENT_STATUS%"=="RUNNING" (
            IF %RESTARTED%==1 (
                ECHO Service %SERVICENAME% restarted.
                GOTO EndExit
            )
            SET /A SLEEP_COUNT=0
            GOTO StopService
        ) ELSE (
            SET /A RESTARTED=1
            GOTO StartService
        )
    ) ELSE IF "%ACTION%"=="STOP" (
        IF "%CURRENT_STATUS%"=="STOPPED"  (
            ECHO Service %SERVICENAME% is stopped.
            GOTO EndExit
        ) ELSE (
            GOTO StopService
        )
    )
    
    :StartService
    ECHO Starting %SERVICENAME% on \\%SYSTEMNAME%
    SC \\%SYSTEMNAME% start %SERVICENAME% >nul 2>&1
    SET SLEEP_COUNT=0
    GOTO Dispatch
    
    :StopService
    ECHO Stopping %SERVICENAME% on \\%SYSTEMNAME%
    SC \\%SYSTEMNAME% stop %SERVICENAME% >nul 2>&1
    SET SLEEP_COUNT=0
    GOTO Dispatch
    
    :Usage
    ECHO This script can start/stop/restart a local or remote service, waiting for the service to stop/start ^(if necessary^).
    ECHO.
    ECHO Usage:
    ECHO %0 ^<start^|stop^|restart^> ^<SERVICE^> [SYSTEM]
    ECHO.
    ECHO If no SYSTEM is provided, the script attempts to execute on the local system.
    EXIT /B 5
    
    :EndExit
    ECHO.
    EXIT /B 0
    
    0 讨论(0)
  • 2020-12-02 07:27

    Eric Falsken's scripts are fantastic for this purpose. But note that they use the timeout command which is only available in Vista/Server2003 and newer. For an XP machine you can use sleep.exe from the NT Resource Kit instead. (This should be a commment to Eric's answer but not enough rep to do that).

    0 讨论(0)
  • 2020-12-02 07:27

    I don't believe you can do this with a straight dos command. You might check Code Project or other similar sites to see if there's a custom solution for this already.

    If not, you could write a secondary Windows service that does this for you by exposing the start/stop functionality through a WCF endpoint. To access this secondary service remotely, you could write a simple console app that connects to this service to start/stop the Windows service in question. Since it's a console app, it would mimic the desired behavior of working from the command line and not returning until complete (or an error occurred). It's not the straightforward, simple solution you're looking for, but I'll throw it out there for consideration.

    0 讨论(0)
提交回复
热议问题