代码之家  ›  专栏  ›  技术社区  ›  Kostas Konstantinidis

如何在C中以编程方式安装Windows服务?

  •  73
  • Kostas Konstantinidis  · 技术社区  · 16 年前

    我的vs解决方案中有3个项目。其中一个是Web应用,第二个是Windows服务,最后一个是我的Web应用的安装项目。

    我想要的是在安装项目中的Web应用安装结束后,在我的自定义操作中尝试安装我的Windows服务,因为那时我已经有了程序集的位置。

    8 回复  |  直到 6 年前
        1
  •  73
  •   Lars A. Brekken    15 年前

    我在您重用的代码中发现了几个错误,并修复了这些错误,还对其进行了一些清理。同样,原始代码取自 here .

    public static class ServiceInstaller
    {
        private const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
        private const int SERVICE_WIN32_OWN_PROCESS = 0x00000010;
    
        [StructLayout(LayoutKind.Sequential)]
        private class SERVICE_STATUS
        {
            public int dwServiceType = 0;
            public ServiceState dwCurrentState = 0;
            public int dwControlsAccepted = 0;
            public int dwWin32ExitCode = 0;
            public int dwServiceSpecificExitCode = 0;
            public int dwCheckPoint = 0;
            public int dwWaitHint = 0;
        }
    
        #region OpenSCManager
        [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
        static extern IntPtr OpenSCManager(string machineName, string databaseName, ScmAccessRights dwDesiredAccess);
        #endregion
    
        #region OpenService
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, ServiceAccessRights dwDesiredAccess);
        #endregion
    
        #region CreateService
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern IntPtr CreateService(IntPtr hSCManager, string lpServiceName, string lpDisplayName, ServiceAccessRights dwDesiredAccess, int dwServiceType, ServiceBootFlag dwStartType, ServiceError dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string lpDependencies, string lp, string lpPassword);
        #endregion
    
        #region CloseServiceHandle
        [DllImport("advapi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool CloseServiceHandle(IntPtr hSCObject);
        #endregion
    
        #region QueryServiceStatus
        [DllImport("advapi32.dll")]
        private static extern int QueryServiceStatus(IntPtr hService, SERVICE_STATUS lpServiceStatus);
        #endregion
    
        #region DeleteService
        [DllImport("advapi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool DeleteService(IntPtr hService);
        #endregion
    
        #region ControlService
        [DllImport("advapi32.dll")]
        private static extern int ControlService(IntPtr hService, ServiceControl dwControl, SERVICE_STATUS lpServiceStatus);
        #endregion
    
        #region StartService
        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern int StartService(IntPtr hService, int dwNumServiceArgs, int lpServiceArgVectors);
        #endregion
    
        public static void Uninstall(string serviceName)
        {
            IntPtr scm = OpenSCManager(ScmAccessRights.AllAccess);
    
            try
            {
                IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.AllAccess);
                if (service == IntPtr.Zero)
                    throw new ApplicationException("Service not installed.");
    
                try
                {
                    StopService(service);
                    if (!DeleteService(service))
                        throw new ApplicationException("Could not delete service " + Marshal.GetLastWin32Error());
                }
                finally
                {
                    CloseServiceHandle(service);
                }
            }
            finally
            {
                CloseServiceHandle(scm);
            }
        }
    
        public static bool ServiceIsInstalled(string serviceName)
        {
            IntPtr scm = OpenSCManager(ScmAccessRights.Connect);
    
            try
            {
                IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus);
    
                if (service == IntPtr.Zero)
                    return false;
    
                CloseServiceHandle(service);
                return true;
            }
            finally
            {
                CloseServiceHandle(scm);
            }
        }
    
        public static void InstallAndStart(string serviceName, string displayName, string fileName)
        {
            IntPtr scm = OpenSCManager(ScmAccessRights.AllAccess);
    
            try
            {
                IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.AllAccess);
    
                if (service == IntPtr.Zero)
                    service = CreateService(scm, serviceName, displayName, ServiceAccessRights.AllAccess, SERVICE_WIN32_OWN_PROCESS, ServiceBootFlag.AutoStart, ServiceError.Normal, fileName, null, IntPtr.Zero, null, null, null);
    
                if (service == IntPtr.Zero)
                    throw new ApplicationException("Failed to install service.");
    
                try
                {
                    StartService(service);
                }
                finally
                {
                    CloseServiceHandle(service);
                }
            }
            finally
            {
                CloseServiceHandle(scm);
            }
        }
    
        public static void StartService(string serviceName)
        {
            IntPtr scm = OpenSCManager(ScmAccessRights.Connect);
    
            try
            {
                IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus | ServiceAccessRights.Start);
                if (service == IntPtr.Zero)
                    throw new ApplicationException("Could not open service.");
    
                try
                {
                    StartService(service);
                }
                finally
                {
                    CloseServiceHandle(service);
                }
            }
            finally
            {
                CloseServiceHandle(scm);
            }
        }
    
        public static void StopService(string serviceName)
        {
            IntPtr scm = OpenSCManager(ScmAccessRights.Connect);
    
            try
            {
                IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus | ServiceAccessRights.Stop);
                if (service == IntPtr.Zero)
                    throw new ApplicationException("Could not open service.");
    
                try
                {
                    StopService(service);
                }
                finally
                {
                    CloseServiceHandle(service);
                }
            }
            finally
            {
                CloseServiceHandle(scm);
            }
        }
    
        private static void StartService(IntPtr service)
        {
            SERVICE_STATUS status = new SERVICE_STATUS();
            StartService(service, 0, 0);
            var changedStatus = WaitForServiceStatus(service, ServiceState.StartPending, ServiceState.Running);
            if (!changedStatus)
                throw new ApplicationException("Unable to start service");
        }
    
        private static void StopService(IntPtr service)
        {
            SERVICE_STATUS status = new SERVICE_STATUS();
            ControlService(service, ServiceControl.Stop, status);
            var changedStatus = WaitForServiceStatus(service, ServiceState.StopPending, ServiceState.Stopped);
            if (!changedStatus)
                throw new ApplicationException("Unable to stop service");
        }
    
        public static ServiceState GetServiceStatus(string serviceName)
        {
            IntPtr scm = OpenSCManager(ScmAccessRights.Connect);
    
            try
            {
                IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus);
                if (service == IntPtr.Zero)
                    return ServiceState.NotFound;
    
                try
                {
                    return GetServiceStatus(service);
                }
                finally
                {
                    CloseServiceHandle(service);
                }
            }
            finally
            {
                CloseServiceHandle(scm);
            }
        }
    
        private static ServiceState GetServiceStatus(IntPtr service)
        {
            SERVICE_STATUS status = new SERVICE_STATUS();
    
            if (QueryServiceStatus(service, status) == 0)
                throw new ApplicationException("Failed to query service status.");
    
            return status.dwCurrentState;
        }
    
        private static bool WaitForServiceStatus(IntPtr service, ServiceState waitStatus, ServiceState desiredStatus)
        {
            SERVICE_STATUS status = new SERVICE_STATUS();
    
            QueryServiceStatus(service, status);
            if (status.dwCurrentState == desiredStatus) return true;
    
            int dwStartTickCount = Environment.TickCount;
            int dwOldCheckPoint = status.dwCheckPoint;
    
            while (status.dwCurrentState == waitStatus)
            {
                // Do not wait longer than the wait hint. A good interval is
                // one tenth the wait hint, but no less than 1 second and no
                // more than 10 seconds.
    
                int dwWaitTime = status.dwWaitHint / 10;
    
                if (dwWaitTime < 1000) dwWaitTime = 1000;
                else if (dwWaitTime > 10000) dwWaitTime = 10000;
    
                Thread.Sleep(dwWaitTime);
    
                // Check the status again.
    
                if (QueryServiceStatus(service, status) == 0) break;
    
                if (status.dwCheckPoint > dwOldCheckPoint)
                {
                    // The service is making progress.
                    dwStartTickCount = Environment.TickCount;
                    dwOldCheckPoint = status.dwCheckPoint;
                }
                else
                {
                    if (Environment.TickCount - dwStartTickCount > status.dwWaitHint)
                    {
                        // No progress made within the wait hint
                        break;
                    }
                }
            }
            return (status.dwCurrentState == desiredStatus);
        }
    
        private static IntPtr OpenSCManager(ScmAccessRights rights)
        {
            IntPtr scm = OpenSCManager(null, null, rights);
            if (scm == IntPtr.Zero)
                throw new ApplicationException("Could not connect to service control manager.");
    
            return scm;
        }
    }
    
    
    public enum ServiceState
    {
        Unknown = -1, // The state cannot be (has not been) retrieved.
        NotFound = 0, // The service is not known on the host server.
        Stopped = 1,
        StartPending = 2,
        StopPending = 3,
        Running = 4,
        ContinuePending = 5,
        PausePending = 6,
        Paused = 7
    }
    
    [Flags]
    public enum ScmAccessRights
    {
        Connect = 0x0001,
        CreateService = 0x0002,
        EnumerateService = 0x0004,
        Lock = 0x0008,
        QueryLockStatus = 0x0010,
        ModifyBootConfig = 0x0020,
        StandardRightsRequired = 0xF0000,
        AllAccess = (StandardRightsRequired | Connect | CreateService |
                     EnumerateService | Lock | QueryLockStatus | ModifyBootConfig)
    }
    
    [Flags]
    public enum ServiceAccessRights
    {
        QueryConfig = 0x1,
        ChangeConfig = 0x2,
        QueryStatus = 0x4,
        EnumerateDependants = 0x8,
        Start = 0x10,
        Stop = 0x20,
        PauseContinue = 0x40,
        Interrogate = 0x80,
        UserDefinedControl = 0x100,
        Delete = 0x00010000,
        StandardRightsRequired = 0xF0000,
        AllAccess = (StandardRightsRequired | QueryConfig | ChangeConfig |
                     QueryStatus | EnumerateDependants | Start | Stop | PauseContinue |
                     Interrogate | UserDefinedControl)
    }
    
    public enum ServiceBootFlag
    {
        Start = 0x00000000,
        SystemStart = 0x00000001,
        AutoStart = 0x00000002,
        DemandStart = 0x00000003,
        Disabled = 0x00000004
    }
    
    public enum ServiceControl
    {
        Stop = 0x00000001,
        Pause = 0x00000002,
        Continue = 0x00000003,
        Interrogate = 0x00000004,
        Shutdown = 0x00000005,
        ParamChange = 0x00000006,
        NetBindAdd = 0x00000007,
        NetBindRemove = 0x00000008,
        NetBindEnable = 0x00000009,
        NetBindDisable = 0x0000000A
    }
    
    public enum ServiceError
    {
        Ignore = 0x00000000,
        Normal = 0x00000001,
        Severe = 0x00000002,
        Critical = 0x00000003
    }
    

    如果有人发现此代码有任何问题,请通知我!

        2
  •  72
  •   Luis Perez    6 年前

    好的,这是真正适合我的,它已经在多台不同操作系统(Vista、XP、Win2K、Win2003 Server)的计算机上进行了测试。

    代码取自 here 所以,所有的功劳都要归功于编写这段代码的人。

    一旦将dll或源文件添加到项目中,请确保添加了servicetools命名空间,然后您就可以访问一些非常方便的功能,如…

    //Installs and starts the service
    ServiceInstaller.InstallAndStart("MyServiceName", "MyServiceDisplayName", "C:\\PathToServiceFile.exe");
    
    //Removes the service
    ServiceInstaller.Uninstall("MyServiceName");
    
    //Checks the status of the service
    ServiceInstaller.GetServiceStatus("MyServiceName");
    
    //Starts the service
    ServiceInstaller.StartService("MyServiceName");
    
    //Stops the service
    ServiceInstaller.StopService("MyServiceName");
    
    //Check if service is installed
    ServiceInstaller.ServiceIsInstalled("MyServiceName");
    

    我希望这有帮助。

        3
  •  31
  •   Zanon    10 年前

    请看一下 this article .


    有时您可能希望以编程方式安装Windows服务,但目标计算机没有installUtil.exe。

    添加对的引用 System.Configuration.Install

    使用下面的代码。

    请注意 exeFileName 是InstallerClass.exe,而不是ServiceClass.exe。

    public static void InstallService(string exeFilename)
    {
        string[] commandLineOptions = new string[1] { "/LogFile=install.log" };
    
        System.Configuration.Install.AssemblyInstaller installer = new System.Configuration.Install.AssemblyInstaller(exeFilename, commandLineOptions);
    
        installer.UseNewContext = true;    
        installer.Install(null);    
        installer.Commit(null);
    
    }
    

    卸载:

    public static void UninstallService(string exeFilename)
    {
        string[] commandLineOptions = new string[1] { "/LogFile=uninstall.log" };
    
        System.Configuration.Install.AssemblyInstaller installer = new System.Configuration.Install.AssemblyInstaller(exeFilename, commandLineOptions);
    
        installer.UseNewContext = true;    
        installer.Uninstall(null);
    
    }
    
        4
  •  11
  •   Andrew Savinykh    11 年前

    在为我的服务创建了安装程序类的实例(非常基本)之后,我所要做的就是调用:

    ManagedInstallerClass.InstallHelper(new string[] { 
        Assembly.GetExecutingAssembly().Location });
    

    安装它和

    ManagedInstallerClass.InstallHelper(new string[] { "/u", 
        Assembly.GetExecutingAssembly().Location });
    

    卸载服务。这里的调用代码与服务可执行文件位于同一程序集中。

    要通过命令行安装服务,我所要做的就是通过命令行参数将其连接到可执行文件,并测试 System.Environment.UserInteractive 要知道它是正在执行的服务还是有人试图安装,请卸载它,然后看……没有时髦的互操作工具…没有指针泄漏…

    总共有大约20行代码分布在两个类中。

    要替换installUtil,请看一下 ManagedInstallerClass.InstallHelper

        5
  •  4
  •   Brian Low    14 年前

    通过使用 Topshelf 可通过调用可执行文件安装的项目:

    MyService.exe install
    

    Topshelf还负责其他Windows服务管道。

        6
  •  3
  •   davmos    9 年前

    因为我面临着以编程方式安装服务的挑战,这些服务在特定的用户下运行。我延长了 InstallAndStart 利用的方法 lp lpPassword

    不多,但可能有帮助。

    public static void InstallAndStart(
        string serviceName, 
        string displayName, 
        string fileName, 
        string username, 
        string password)
    {
        IntPtr scm = OpenSCManager(ScmAccessRights.AllAccess);
    
        try
        {
            IntPtr service = OpenService(
                scm, 
                serviceName, 
                ServiceAccessRights.AllAccess);
    
            if (service == IntPtr.Zero)
                service = CreateService(
                    scm, 
                    serviceName, 
                    displayName, 
                    ServiceAccessRights.AllAccess, 
                    SERVICE_WIN32_OWN_PROCESS, 
                    ServiceBootFlag.AutoStart, 
                    ServiceError.Normal, 
                    fileName, 
                    null, 
                    IntPtr.Zero, 
                    null, 
                    username, 
                    password);
    
            if (service == IntPtr.Zero)
                throw new ApplicationException("Failed to install service.");
    
            try
            {
                StartService(service);
            }
            finally
            {
                CloseServiceHandle(service);
            }
        }
        finally
        {
            CloseServiceHandle(scm);
        }
    }
    
        7
  •  1
  •   Tayfur Yılmaz    11 年前

    在这篇文章中,我阅读了所有的文章和评论,但是我仍然不知道在添加Windows服务时如何设置帐户类型和StartType方法。这个代码示例工作得很好,只是添加了一个服务自己的本地系统),但是我准备了安装程序 启动模式 用户帐户类型 方法,因为客户系统。
    每一个外表 Service Boo旗 枚举提供StartType,但 账户类型 还是个问题。

        [DllImport("advapi32.dll", EntryPoint = "CreateServiceA")]
    
        private static extern IntPtr CreateService(IntPtr hSCManager, string
    
        lpServiceName, string lpDisplayName, ServiceRights dwDesiredAccess, int
    
        dwServiceType, ServiceBootFlag dwStartType, ServiceError dwErrorControl,
    
        string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string
    
        lpDependencies, string lp, string lpPassword); 
    
        8
  •  0
  •   Mahsh Nikam    8 年前

    使用以下代码使用C_安装Windows服务:

    public void InstallWinService(string winServicePath)
    {
            try
            {
                ManagedInstallerClass.InstallHelper(new string[] { winServicePath});
            }
            catch (Exception)
            {
    
                throw;
            }
    }