【问题标题】:How can I programmatically stop/start a windows service on a remote box?如何以编程方式在远程机器上停止/启动 Windows 服务?
【发布时间】:2010-10-02 19:33:00
【问题描述】:

我想编写一个控制台或 Click Once WinForms 应用程序,它将以编程方式停止和/或启动远程框上的 Windows 服务。

两个机器都运行 .NET 3.5 - 有哪些 .NET API 可用于完成此操作?

【问题讨论】:

    标签: windows-services


    【解决方案1】:

    我做了如下操作:

    注意:

    1. 如果您在尝试停止服务时未启动服务,则会引发异常。
    2. 如果你在 web.config 中配置这些东西,配置相关的异常就不会出现。无需在 IIS 中执行任何操作。

    Web.Config<configuration>

      <appSettings>
        <add key="ServiceName" value="YourServiceName" />
        <add key="MachineName" value="YourMachineName" />
      </appSettings>
      <system.web>
        <authentication mode="Windows"/>
        <identity impersonate="true" userName="YourUserName" password="YourPassword"/>
      </system.web>
    

    在我的服务类中:

    private void RestartService()
    {
        string serviceName = System.Configuration.ConfigurationSettings.AppSettings["ServiceName"];
        string machineName = System.Configuration.ConfigurationSettings.AppSettings["MachineName"];
    
        try
        {
            var service = new ServiceController(serviceName, machineName);
            if (service.Status != ServiceControllerStatus.Stopped)
            {
                service.Stop();
                service.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Stopped);
            }
    
            service.Start();
            service.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Running);
    
        }
        catch (Exception)
        {
        }
    }
    

    希望对您有所帮助。

    【讨论】:

      【解决方案2】:

      这是一个可以在远程电脑上启动和停止服务的 ServiceExtension。

      并且它可以设置服务的启动类型,甚至可以设置为“自动(延迟)”

      Answer 修改的版本可以在远程机器上工作。

      using System;
      using System.ComponentModel;
      using System.Runtime.InteropServices;
      using System.ServiceProcess;
      
      namespace Helpers
      {
          public enum ServiceStartModeEx
          {
              Automatic = 2,
              Manual = 3,
              Disabled = 4,
              DelayedAutomatic = 99
          }
          /// <summary>
          /// Extensions to the ServiceController class.
          /// </summary>
          public static class ServiceControlerExtensions
          {
              /// <summary>
              /// Set the start mode for the service.
              /// </summary>
              /// <param name="serviceController">The service controller.</param>
              /// <param name="mode">The desired start mode.</param>
              public static void SetStartMode(this ServiceController serviceController, ServiceStartModeEx mode)
              {
                  IntPtr serviceManagerHandle = OpenServiceManagerHandle(serviceController);
                  IntPtr serviceHandle = OpenServiceHandle(serviceController, serviceManagerHandle);
      
                  try
                  {
                      if (mode == ServiceStartModeEx.DelayedAutomatic)
                      {
                          ChangeServiceStartType(serviceHandle, ServiceStartModeEx.Automatic);
                          ChangeDelayedAutoStart(serviceHandle, true);
                      }
                      else
                      {
                          // Delayed auto-start overrides other settings, so it must be set first.
                          ChangeDelayedAutoStart(serviceHandle, false);
                          ChangeServiceStartType(serviceHandle, mode);
                      }
                  }
                  finally
                  {
                      if (serviceHandle != IntPtr.Zero)
                      {
                          CloseServiceHandle(serviceHandle);
                      }
                      if (serviceManagerHandle != IntPtr.Zero)
                      {
                          CloseServiceHandle(serviceManagerHandle);
                      }
                  }
              }
      
              private static IntPtr OpenServiceHandle(ServiceController serviceController, IntPtr serviceManagerHandle)
              {
                  var serviceHandle = OpenService(
                                                  serviceManagerHandle,
                                                  serviceController.ServiceName,
                                                  SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
      
                  if (serviceHandle == IntPtr.Zero)
                  {
                      throw new ExternalException("Open Service Error");
                  }
                  return serviceHandle;
              }
      
              private static IntPtr OpenServiceManagerHandle(ServiceController serviceController)
              {
                  var machineName = string.IsNullOrWhiteSpace(serviceController.MachineName)
                      ? null
                      : serviceController.MachineName;
                  IntPtr serviceManagerHandle = OpenSCManager(machineName, null, SC_MANAGER_ALL_ACCESS);
                  if (serviceManagerHandle == IntPtr.Zero)
                  {
                      throw new ExternalException("Open Service Manager Error");
                  }
                  return serviceManagerHandle;
              }
      
              private static void ChangeServiceStartType(IntPtr serviceHandle, ServiceStartModeEx mode)
              {
                  bool result = ChangeServiceConfig(
                                                   serviceHandle,
                                                   SERVICE_NO_CHANGE,
                                                   (uint)mode,
                                                   SERVICE_NO_CHANGE,
                                                   null,
                                                   null,
                                                   IntPtr.Zero,
                                                   null,
                                                   null,
                                                   null,
                                                   null);
      
                  if (result == false)
                  {
                      ThrowLastWin32Error("Could not change service start type");
                  }
              }
      
              private static void ChangeDelayedAutoStart(IntPtr hService, bool delayed)
              {
                  // Create structure that contains DelayedAutoStart property.
                  SERVICE_DELAYED_AUTO_START_INFO info = new SERVICE_DELAYED_AUTO_START_INFO();
      
                  // Set the DelayedAutostart property in that structure.
                  info.fDelayedAutostart = delayed;
      
                  // Allocate necessary memory.
                  IntPtr hInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SERVICE_DELAYED_AUTO_START_INFO)));
      
                  // Convert structure to pointer.
                  Marshal.StructureToPtr(info, hInfo, true);
      
                  // Change the configuration.
                  bool result = ChangeServiceConfig2(hService, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, hInfo);
      
                  // Release memory.
                  Marshal.FreeHGlobal(hInfo);
      
                  if (result == false)
                  {
                      ThrowLastWin32Error("Could not set service to delayed automatic");
                  }
              }
      
              private static void ThrowLastWin32Error(string messagePrefix)
              {
                  int nError = Marshal.GetLastWin32Error();
                  var win32Exception = new Win32Exception(nError);
                  string message = string.Format("{0}: {1}", messagePrefix, win32Exception.Message);
                  throw new ExternalException(message);
              }
      
              [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
              private static extern IntPtr OpenService(
                  IntPtr hSCManager,
                  string lpServiceName,
                  uint dwDesiredAccess);
      
              [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode,
                  SetLastError = true)]
              private static extern IntPtr OpenSCManager(
                  string machineName,
                  string databaseName,
                  uint dwAccess);
      
              [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
              private static extern Boolean ChangeServiceConfig(
                  IntPtr hService,
                  UInt32 nServiceType,
                  UInt32 nStartType,
                  UInt32 nErrorControl,
                  String lpBinaryPathName,
                  String lpLoadOrderGroup,
                  IntPtr lpdwTagId,
                  [In] char[] lpDependencies,
                  String lpServiceStartName,
                  String lpPassword,
                  String lpDisplayName);
      
              [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
              [return: MarshalAs(UnmanagedType.Bool)]
              private static extern bool ChangeServiceConfig2(
                  IntPtr hService,
                  int dwInfoLevel,
                  IntPtr lpInfo);
      
              [DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
              private static extern int CloseServiceHandle(IntPtr hSCObject);
      
              private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;
              private const uint SERVICE_QUERY_CONFIG = 0x00000001;
              private const uint SERVICE_CHANGE_CONFIG = 0x00000002;
              private const uint SC_MANAGER_ALL_ACCESS = 0x000F003F;
      
              private const int SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3;
      
              [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
              private struct SERVICE_DELAYED_AUTO_START_INFO
              {
                  public bool fDelayedAutostart;
              }
          }
      }
      

      你可以像这样启动一个服务

       using System.ServiceProcess;
      

      serviceName = "服务的名称"

      machineName = "远程/本地主机的名称"

       var service = new ServiceController(serviceName, machineName);
       try
       {
           service.SetStartMode(ServiceStartModeEx.DelayedAutomatic);
           service.Start();
       }
      
       finally
       {
           service.Close();
       }
      

      你可以停止这样的服务

      var service = new ServiceController(serviceName, machineName);
      try
      {
          if (service.CanStop)
          {
              service.SetStartMode(ServiceStartModeEx.Disabled);
              service.Stop();
      
          }
      }
      
      finally
      {
          service.Close();
      }
      

      要授予用户在远程 PC 上启动和停止服务的权限,您必须设置一些服务权限,您可以 google 什么是 subinacl.exe 以及在哪里下载它。

      C:\Program Files (x86)\Windows Resource Kits\Tools>subinacl.exe /service SERVICENAME /grant=MACHINENAME\USERNAME=F
      

      【讨论】:

        【解决方案3】:

        上面的 galets 代码 sn-p 是一个很好的开始。但是,请记住,它假定服务已经启动,或者更重要的是,

        sc.Status == System.ServiceProcess.ServiceControllerStatus.Running
        

        此外,在代码执行期间的某个时间点调用

        sc.Refresh();
        

        因为属性值(例如 ServiceControllerStatus)可能无法反映服务的实际属性。 例如,您可以调用

        sc.Start();
        

        并在此命令执行时无限期等待

        sc.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Running)
        

        这是我在编写时考虑到这些因素的代码版本。

                    //Restart Content Service on DEV. 
                String svcName = "TheServiceName";
                String machineName = "TheMachineName";
                var sc = new System.ServiceProcess.ServiceController(svcName, machineName);
                Console.WriteLine("Stopping Service '{0}' on machine '{1}", svcName, machineName);
                sc.Stop();
                sc.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Stopped);          
        
                //sc.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Running);
                do
                {
                    try
                    {
                        sc.Refresh();
                        if (sc.Status == System.ServiceProcess.ServiceControllerStatus.Running)
                        {
                            Console.WriteLine("Code has detected that servive start is pending, waiting 5 seconds to see if status changes..");
                            System.Threading.Thread.Sleep(5000);
                        }
                        else
                        {
                            Console.WriteLine("waiting 5 seconds and retrying start..");
                            System.Threading.Thread.Sleep(5000);
                            Console.WriteLine("Attempt Starting Service '{0}' on machine '{1}", svcName, machineName);
                            sc.Start();
                        }
                    }
        
                    catch(Exception ex)
                    {
                        //If it is already running, then abort do while
                        if (ex.InnerException.Message == "An instance of the service is already running")
                        {
                            Console.WriteLine(ex.InnerException.Message);
                            continue;
                        }
                        Console.WriteLine(ex.InnerException.ToString());
                    }
                } while (sc.Status != System.ServiceProcess.ServiceControllerStatus.Running);
        

        【讨论】:

          【解决方案4】:

          ServiceController.

          您需要有权限管理远程盒子上的服务。

          正如 Mehrdad 所说,您也可以使用 WMI。这两种方法都适用于启动和停止,但 WMI 需要更多编码,并且可以让您更多地访问其他资源

          【讨论】:

            【解决方案5】:

            如果您需要获取服务的名称:

            从命令行运行:

            sc查询

            例如,您将看到 SQL Server 的服务名称是“MSSQL$SQLEXPRESS”。

            所以要在 C# 中停止 SQL Server 服务:

                    ServiceController controller = new ServiceController();
                    controller.MachineName = "Machine1";
                    controller.ServiceName = "MSSQL$SQLEXPRESS";
            
                    if(controller.Status == ServiceControllerStatus.Running)
                        controller.Stop();
            
                    controller.WaitForStatus(ServiceControllerStatus.Stopped);
            

            【讨论】:

              【解决方案6】:

              在 C# 中:

              var sc = new System.ServiceProcess.ServiceController("MyService", "MyRemoteMachine");
              sc.Start();
              sc.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Running);
              sc.Stop();
              sc.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Stopped);
              

              【讨论】:

              • 我在我的项目中对此进行了编码,但我一直在调试模式下运行,逐步查看它为什么不再工作。我得到“..无法在计算机上启动服务”。 InnerException,“服务没有及时响应启动或控制请求”但它确实在 5 秒内从管理控制台启动,并使用完全相同的二进制文件!
              • 为我工作。如果您尝试对远程计算机执行此操作并且 rpc 调用失败或无法连接到远程计算机上的服务控制管理器,请确保在调用 WaitForStatus 时您将合理的时间跨度作为第二个参数(重载)传递。我认为 SC 在抛出异常之前最多会等待 2.5 分钟以启动或停止服务,所以我一直使用 2 分钟。
              • 我收到如下异常An unhandled exception of type 'System.InvalidOperationException' occurred in System.ServiceProcess.dll Additional information: Cannot open Service Control Manager on computer &lt;IP address&gt;. This operation might require other privileges.
              • 程序集引用显然是“System.ServiceProcess”
              【解决方案7】:

              您也可以使用sc 命令从命令控制台执行此操作:

               sc <server> start [service name]
               sc <server> stop [service name]
              

              使用

              sc <server> query | find "SERVICE_NAME"
              

              获取服务名称列表。

              选项&lt;server&gt; 的形式为\\ServerName

              示例

              sc \\MyServer stop schedule 将停止调度程序服务。

              【讨论】:

                【解决方案8】:

                如果您不想自己编写代码,Microsoft/Sysinternals 的 PsService 是一个命令行工具,可以满足您的需求。

                【讨论】:

                  【解决方案9】:

                  您可以使用System.Management API (WMI) 远程控制服务。 WMI 是执行管理任务的通用 API。

                  但是,对于这个问题,我建议你使用更容易使用的System.ServiceProcess.ServiceController 类。

                  【讨论】:

                    猜你喜欢
                    • 2017-10-22
                    • 1970-01-01
                    • 2010-09-22
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-01-11
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多