123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- using System;
- using System.ComponentModel;
- using System.Runtime.ConstrainedExecution;
- using System.Runtime.InteropServices;
- using System.Text;
- using System.Threading;
- using Microsoft.Win32;
- namespace SvdCli.ServiceUtils
- {
- /// <summary>
- /// Windows 服务辅助类
- /// </summary>
- public static class ServiceHelper
- {
- /// <summary> 安装服务 </summary>
- /// <param name="serviceName">服务名</param>
- /// <param name="displayName">友好名称</param>
- /// <param name="binaryFilePath">映像文件路径,可带参数</param>
- /// <param name="description">服务描述</param>
- /// <param name="startType">启动类型</param>
- /// <param name="account">启动账户</param>
- /// <param name="dependencies">依赖服务</param>
- public static void Install(string serviceName, string displayName, string binaryFilePath, string description, ServiceStartType startType, ServiceAccount account = ServiceAccount.LocalSystem, string[] dependencies = null)
- {
- IntPtr scm = OpenSCManager();
- IntPtr service = IntPtr.Zero;
- try
- {
- service = Win32Class.CreateService(scm, serviceName, displayName, Win32Class.SERVICE_ALL_ACCESS, Win32Class.SERVICE_WIN32_OWN_PROCESS, startType, Win32Class.SERVICE_ERROR_NORMAL, binaryFilePath, null, IntPtr.Zero, ProcessDependencies(dependencies), GetServiceAccountName(account), null);
- if (service == IntPtr.Zero)
- {
- if (Marshal.GetLastWin32Error() == 0x431)//ERROR_SERVICE_EXISTS
- { throw new ApplicationException("服务已存在!"); }
- throw new ApplicationException("服务安装失败!");
- }
- //设置服务描述
- Win32Class.SERVICE_DESCRIPTION sd = new Win32Class.SERVICE_DESCRIPTION();
- try
- {
- sd.description = Marshal.StringToHGlobalUni(description);
- Win32Class.ChangeServiceConfig2(service, 1, ref sd);
- }
- finally
- {
- Marshal.FreeHGlobal(sd.description); //释放
- }
- }
- finally
- {
- if (service != IntPtr.Zero)
- {
- Win32Class.CloseServiceHandle(service);
- }
- Win32Class.CloseServiceHandle(scm);
- }
- }
- public static string GetBinPath(string serviceName)
- {
- var key = Registry.LocalMachine.OpenSubKey($@"SYSTEM\CurrentControlSet\services\{serviceName}");
- return key?.GetValue("ImagePath") == null
- ? null
- : key.GetValue("ImagePath").ToString();
- }
- /// <summary> 卸载服务 </summary>
- /// <param name="serviceName">服务名</param>
- public static void Uninstall(string serviceName)
- {
- IntPtr scmHandle = IntPtr.Zero;
- IntPtr service = IntPtr.Zero;
- try
- {
- service = OpenService(serviceName, out scmHandle);
- StopService(service); //停止服务。里面会递归停止从属服务
- if (!Win32Class.DeleteService(service) && Marshal.GetLastWin32Error() != 0x430) //忽略已标记为删除的服务。ERROR_SERVICE_MARKED_FOR_DELETE
- {
- throw new ApplicationException("删除服务失败!");
- }
- }
- catch (ServiceNotExistException) { } //忽略服务不存在的情况
- finally
- {
- if (service != IntPtr.Zero)
- {
- Win32Class.CloseServiceHandle(service);
- Win32Class.CloseServiceHandle(scmHandle);//放if里面是因为如果服务打开失败,在OpenService里就已释放SCM
- }
- }
- }
- /// <summary> 转换帐户枚举为有效参数 </summary>
- private static string GetServiceAccountName(ServiceAccount account)
- {
- if (account == ServiceAccount.LocalService)
- {
- return @"NT AUTHORITY\LocalService";
- }
- if (account == ServiceAccount.NetworkService)
- {
- return @"NT AUTHORITY\NetworkService";
- }
- return null;
- }
- /// <summary> 处理依赖服务参数 </summary>
- private static string ProcessDependencies(string[] dependencies)
- {
- if (dependencies == null || dependencies.Length == 0)
- {
- return null;
- }
- StringBuilder sb = new StringBuilder();
- foreach (string s in dependencies)
- {
- sb.Append(s).Append('\0');
- }
- sb.Append('\0');
- return sb.ToString();
- }
- /// <summary> 打开服务管理器 </summary>
- private static IntPtr OpenSCManager()
- {
- IntPtr scm = Win32Class.OpenSCManager(null, null, Win32Class.SC_MANAGER_ALL_ACCESS);
- if (scm == IntPtr.Zero)
- {
- throw new ApplicationException("打开服务管理器失败!");
- }
- return scm;
- }
- /// <summary> 打开服务 </summary>
- /// <param name="serviceName">服务名称</param>
- /// <param name="scmHandle">服务管理器句柄。供调用者释放</param>
- private static IntPtr OpenService(string serviceName, out IntPtr scmHandle)
- {
- scmHandle = OpenSCManager();
- IntPtr service = Win32Class.OpenService(scmHandle, serviceName, Win32Class.SERVICE_ALL_ACCESS);
- if (service == IntPtr.Zero)
- {
- int errCode = Marshal.GetLastWin32Error();
- Win32Class.CloseServiceHandle(scmHandle); //关闭SCM
- if (errCode == 0x424) //ERROR_SERVICE_DOES_NOT_EXIST
- {
- throw new ServiceNotExistException();
- }
- throw new Win32Exception();
- }
- return service;
- }
- /// <summary> 停止服务 </summary>
- private static void StopService(IntPtr service)
- {
- ServiceState currState = GetServiceStatus(service);
- if (currState == ServiceState.Stopped)
- {
- return;
- }
- if (currState != ServiceState.StopPending)
- {
- //递归停止从属服务
- string[] childSvs = EnumDependentServices(service, EnumServiceState.Active);
- if (childSvs.Length != 0)
- {
- IntPtr scm = OpenSCManager();
- try
- {
- foreach (string childSv in childSvs)
- {
- StopService(Win32Class.OpenService(scm, childSv, Win32Class.SERVICE_STOP));
- }
- }
- finally
- {
- Win32Class.CloseServiceHandle(scm);
- }
- }
- Win32Class.SERVICE_STATUS status = new Win32Class.SERVICE_STATUS();
- Win32Class.ControlService(service, Win32Class.SERVICE_CONTROL_STOP, ref status); //发送停止指令
- }
- if (!WaitForStatus(service, ServiceState.Stopped, new TimeSpan(0, 0, 30)))
- {
- throw new ApplicationException("停止服务失败!");
- }
- }
- /// <summary> 遍历从属服务 </summary>
- /// <param name="serviceHandle"></param>
- /// <param name="state">选择性遍历(活动、非活动、全部)</param>
- private static string[] EnumDependentServices(IntPtr serviceHandle, EnumServiceState state)
- {
- int bytesNeeded = 0; //存放从属服务的空间大小,由API返回
- int numEnumerated = 0; //从属服务数,由API返回
- //先尝试以空结构获取,如获取成功说明从属服务为空,否则拿到上述俩值
- if (Win32Class.EnumDependentServices(serviceHandle, state, IntPtr.Zero, 0, ref bytesNeeded, ref numEnumerated))
- {
- return new string[0];
- }
- if (Marshal.GetLastWin32Error() != 0xEA) //仅当错误值不是大小不够(ERROR_MORE_DATA)时才抛异常
- {
- throw new Win32Exception();
- }
- //在非托管区域创建指针
- IntPtr structsStart = Marshal.AllocHGlobal(new IntPtr(bytesNeeded));
- try
- {
- //往上述指针处塞存放从属服务的结构组,每个从属服务是一个结构
- if (!Win32Class.EnumDependentServices(serviceHandle, state, structsStart, bytesNeeded, ref bytesNeeded, ref numEnumerated))
- {
- throw new Win32Exception();
- }
- string[] dependentServices = new string[numEnumerated];
- int sizeOfStruct = Marshal.SizeOf(typeof(Win32Class.ENUM_SERVICE_STATUS)); //每个结构的大小
- long structsStartAsInt64 = structsStart.ToInt64();
- for (int i = 0; i < numEnumerated; i++)
- {
- Win32Class.ENUM_SERVICE_STATUS structure = new Win32Class.ENUM_SERVICE_STATUS();
- IntPtr ptr = new IntPtr(structsStartAsInt64 + i * sizeOfStruct); //根据起始指针、结构次序和结构大小推算各结构起始指针
- Marshal.PtrToStructure(ptr, structure); //根据指针拿到结构
- dependentServices[i] = structure.serviceName; //从结构中拿到服务名
- }
- return dependentServices;
- }
- finally
- {
- Marshal.FreeHGlobal(structsStart);
- }
- }
- /// <summary> 获取服务状态 </summary>
- private static ServiceState GetServiceStatus(IntPtr service)
- {
- Win32Class.SERVICE_STATUS status = new Win32Class.SERVICE_STATUS();
- if (!Win32Class.QueryServiceStatus(service, ref status))
- {
- throw new ApplicationException("获取服务状态出错!");
- }
- return status.currentState;
- }
- /// <summary> 等候服务至目标状态 </summary>
- private static bool WaitForStatus(IntPtr serviceHandle, ServiceState desiredStatus, TimeSpan timeout)
- {
- DateTime startTime = DateTime.Now;
- while (GetServiceStatus(serviceHandle) != desiredStatus)
- {
- if (DateTime.Now - startTime > timeout) { return false; }
- Thread.Sleep(200);
- }
- return true;
- }
- /// <summary> Win32 API相关 </summary>
- private static class Win32Class
- {
- #region 常量定义
- /// <summary>
- /// 打开服务管理器时请求的权限:全部
- /// </summary>
- public const int SC_MANAGER_ALL_ACCESS = 0xF003F;
- /// <summary>
- /// 服务类型:自有进程类服务
- /// </summary>
- public const int SERVICE_WIN32_OWN_PROCESS = 0x10;
- /// <summary>
- /// 打开服务时请求的权限:全部
- /// </summary>
- public const int SERVICE_ALL_ACCESS = 0xF01FF;
- /// <summary>
- /// 打开服务时请求的权限:停止
- /// </summary>
- public const int SERVICE_STOP = 0x20;
- /// <summary>
- /// 服务操作标记:停止
- /// </summary>
- public const int SERVICE_CONTROL_STOP = 0x1;
- /// <summary>
- /// 服务出错行为标记
- /// </summary>
- public const int SERVICE_ERROR_NORMAL = 0x1;
- #endregion 常量定义
- #region API所需类和结构定义
- /// <summary>
- /// 服务状态结构体
- /// </summary>
- [StructLayout(LayoutKind.Sequential)]
- public struct SERVICE_STATUS
- {
- public int serviceType;
- public ServiceState currentState;
- public int controlsAccepted;
- public int win32ExitCode;
- public int serviceSpecificExitCode;
- public int checkPoint;
- public int waitHint;
- }
- /// <summary>
- /// 服务描述结构体
- /// </summary>
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
- public struct SERVICE_DESCRIPTION
- {
- public IntPtr description;
- }
- /// <summary>
- /// 服务状态结构体。遍历API会用到
- /// </summary>
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
- public class ENUM_SERVICE_STATUS
- {
- public string serviceName;
- public string displayName;
- public int serviceType;
- public int currentState;
- public int controlsAccepted;
- public int win32ExitCode;
- public int serviceSpecificExitCode;
- public int checkPoint;
- public int waitHint;
- }
- #endregion API所需类和结构定义
- #region API定义
- [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
- public static extern bool ChangeServiceConfig2(IntPtr serviceHandle, uint infoLevel, ref SERVICE_DESCRIPTION serviceDesc);
- [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
- public static extern IntPtr OpenSCManager(string machineName, string databaseName, int dwDesiredAccess);
- [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
- public static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, int dwDesiredAccess);
- [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
- public static extern IntPtr CreateService(IntPtr hSCManager, string lpServiceName, string lpDisplayName, int dwDesiredAccess, int dwServiceType, ServiceStartType dwStartType, int dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string lpDependencies, string lpServiceStartName, string lpPassword);
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
- public static extern bool CloseServiceHandle(IntPtr handle);
- [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
- public static extern bool QueryServiceStatus(IntPtr hService, ref SERVICE_STATUS lpServiceStatus);
- [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
- public static extern bool DeleteService(IntPtr serviceHandle);
- [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
- public static extern bool ControlService(IntPtr hService, int dwControl, ref SERVICE_STATUS lpServiceStatus);
- [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
- public static extern bool EnumDependentServices(IntPtr serviceHandle, EnumServiceState serviceState, IntPtr bufferOfENUM_SERVICE_STATUS, int bufSize, ref int bytesNeeded, ref int numEnumerated);
- #endregion API定义
- }
- /// <summary> 服务状态枚举。用于遍历从属服务API </summary>
- private enum EnumServiceState
- {
- Active = 1,
- //InActive = 2,
- //All = 3
- }
- /// <summary> 服务状态 </summary>
- private enum ServiceState
- {
- Stopped = 1,
- //StartPending = 2,
- StopPending = 3,
- //Running = 4,
- //ContinuePending = 5,
- //PausePending = 6,
- //Paused = 7
- }
- }
- }
|