Imports System.ComponentModel
Imports System.ServiceProcess
Imports System.Configuration.Install
Imports System.Reflection

Public Class WinService
    ''' <summary>
    ''' 描述服务的状态
    ''' </summary>
    Enum ServiceStatusEnum
        <Description("未知")>
        Unknown
        <Description("不存在")>
        Nonentity
        <Description("已存在")>
        Exists
        <Description("停止中")>
        StopPending
        <Description("已停止")>
        Stopped
        <Description("启动中")>
        StartPending
        <Description("已启动")>
        Started
    End Enum


    ''' <summary>
    ''' 获取枚举描述特性
    ''' </summary>
    ''' <param name="enumValue">需要获取特性的枚举值</param>
    ''' <returns>枚举描述特性</returns>
    Public Shared Function GetEnumDescription(enumValue As [Enum]) As String
        Dim fi As FieldInfo = enumValue.GetType().GetField(enumValue.ToString())
        Dim attr() As DescriptionAttribute =
                DirectCast(fi.GetCustomAttributes(GetType(DescriptionAttribute),
                                                  False), DescriptionAttribute())

        If attr.Length > 0 Then
            Return attr(0).Description
        Else
            Return enumValue.ToString()
        End If
    End Function


    ''' <summary>
    ''' 检测服务是否存在
    ''' </summary>
    ''' <param name="serviceName">需要检测的服务名称</param>
    ''' <returns>服务是否存在</returns>
    Public Shared Function ServicesExists(serviceName As String) As Boolean
        Dim services() As ServiceController = ServiceController.GetServices
        For Each service As ServiceController In services
            If service.ServiceName = serviceName Then
                Return True
            End If
        Next
        Return False
    End Function


    ''' <summary>
    ''' 获取服务的状态
    ''' </summary>
    ''' <param name="serviceName">服务名称</param>
    ''' <returns></returns>
    Public Shared Function ServicesStatus(serviceName As String) As ServiceStatusEnum
        Dim status As ServiceStatusEnum
        Try
            If ServicesExists(serviceName) Then
                status = ServiceStatusEnum.Exists

                Try
                    Dim service As New ServiceController(serviceName)
                    If service.Status = ServiceControllerStatus.Running Then
                        status = ServiceStatusEnum.Started
                    ElseIf service.Status = ServiceControllerStatus.StartPending OrElse
                           service.Status = ServiceControllerStatus.ContinuePending Then
                        status = ServiceStatusEnum.StartPending
                    ElseIf service.Status = ServiceControllerStatus.StopPending OrElse
                           service.Status = ServiceControllerStatus.PausePending Then
                        status = ServiceStatusEnum.StopPending
                    Else
                        status = ServiceStatusEnum.Stopped
                    End If
                Catch ex As Exception
                    Console.WriteLine($"ServicesStatus Error:{ex.Message}")
                End Try
            Else
                status = ServiceStatusEnum.Nonentity
            End If
        Catch ex As Exception
            status = ServiceStatusEnum.Unknown
            Console.WriteLine($"Check ServicesStatus Error:{ex.Message}")
        End Try
        Return status
    End Function

    ''' <summary>
    ''' 安装服务
    ''' </summary>
    ''' <param name="filePath">服务程序所在路径</param>
    ''' <param name="savedState">服务状态，未知字段</param>
    Public Shared Function InstallService(filePath As String, savedState As IDictionary) As Boolean
        Try
            Dim app As AppDomain = AppDomain.CreateDomain("MyDomain")
            Dim assName As String = GetType(AssemblyInstaller).Assembly.FullName
            Dim fullName As String = GetType(AssemblyInstaller).FullName

            Using installer As AssemblyInstaller = CType(app.CreateInstanceAndUnwrap(assName, fullName), AssemblyInstaller)
                installer.UseNewContext = True
                installer.Path = filePath
                installer.Install(savedState)
                installer.Commit(savedState)
                installer.Dispose()
            End Using

            AppDomain.Unload(app)
            GC.Collect()
        Catch ex As Exception
            Console.WriteLine($"InstallService Error:{ex.Message}")
            Return False
        End Try
        Return True
    End Function


    ''' <summary>
    ''' 卸载服务
    ''' </summary>
    ''' <param name="filePath">服务程序所在路径</param>
    ''' <param name="savedState">服务状态，未知字段</param>
    Public Shared Function UnInstallService(filePath As String, savedState As IDictionary) As Boolean
        Try
            Dim app As AppDomain = AppDomain.CreateDomain("MyDomain")
            Dim assName As String = GetType(AssemblyInstaller).Assembly.FullName
            Dim fullName As String = GetType(AssemblyInstaller).FullName

            Using installer As AssemblyInstaller = CType(app.CreateInstanceAndUnwrap(assName, fullName), AssemblyInstaller)
                installer.UseNewContext = True
                installer.Path = filePath
                installer.Uninstall(savedState)
                installer.Dispose()
            End Using

            AppDomain.Unload(app)
            GC.Collect()
        Catch ex As Exception
            Console.WriteLine($"UnInstallService Error:{ex.Message}")
            Return False
        End Try
        Return True
    End Function

    '''' <summary>
    '''' 安装服务
    '''' </summary>
    '''' <param name="filePath">服务程序所在路径</param>
    '''' <param name="savedState">服务状态，未知字段</param>
    'Public Shared Sub InstallService(filePath As String, savedState As IDictionary)
    '    Using installer As New AssemblyInstaller
    '        installer.UseNewContext = True
    '        installer.Path = filePath
    '        installer.Install(savedState)
    '        installer.Commit(savedState)
    '        installer.Dispose()
    '    End Using
    'End Sub


    '''' <summary>
    '''' 卸载服务
    '''' </summary>
    '''' <param name="filePath">服务程序所在路径</param>
    '''' <param name="savedState">服务状态，未知字段</param>
    'Public Shared Function UnInstallService(filePath As String, savedState As IDictionary) As Boolean
    '    Try
    '        Using installer As New AssemblyInstaller
    '            installer.UseNewContext = True
    '            installer.Path = filePath
    '            installer.Uninstall(savedState)
    '        End Using
    '    Catch ex As Exception
    '        Console.WriteLine($"InstallService Error:{ex.Message}")
    '        Return False
    '    End Try
    '    Return True
    'End Function


    ''' <summary>
    ''' 开启服务
    ''' </summary>
    ''' <param name="serviceName">服务名称</param>
    Public Shared Sub StartService(serviceName As String)
        Using service As ServiceController = New ServiceController(serviceName)
            If service.Status <> ServiceControllerStatus.Running OrElse
               service.Status <> ServiceControllerStatus.StartPending OrElse
               service.Status <> ServiceControllerStatus.ContinuePending Then
                service.Start()
            End If
        End Using
    End Sub


    ''' <summary>
    ''' 关闭服务
    ''' </summary>
    ''' <param name="serviceName">服务名称</param>
    ''' <returns>关闭服务结果</returns>
    Public Shared Function StopService(serviceName As String) As Boolean
        Try
            Using service As ServiceController = New ServiceController(serviceName)
                If service.Status = ServiceControllerStatus.Running OrElse
                   service.Status = ServiceControllerStatus.StartPending OrElse
                   service.Status = ServiceControllerStatus.ContinuePending Then
                    service.Stop()
                End If
            End Using
        Catch ex As Exception
            Console.WriteLine($"StopService Error:{ex.Message}")
            Return False
        End Try
        Return True
    End Function

    ''' <summary>
    ''' 重启服务
    ''' </summary>
    ''' <param name="serviceName">服务名称</param>
    ''' <returns></returns>
    Public Shared Function RestartService(serviceName As String) As Boolean
        Try
            Using service As ServiceController = New ServiceController(serviceName)
                If service.Status = ServiceControllerStatus.Running OrElse
                   service.Status = ServiceControllerStatus.StartPending OrElse
                   service.Status = ServiceControllerStatus.ContinuePending Then
                    service.Stop()

                    While service.Status = ServiceControllerStatus.Running AndAlso
                          service.Status = ServiceControllerStatus.StopPending
                        service.Refresh()
                        Threading.Thread.Sleep(20)
                    End While
                End If
                Threading.Thread.Sleep(100)
                service.Start()
            End Using
        Catch ex As Exception
            Console.WriteLine($"RestartService:{ex.Message}")
            Return False
        End Try
        Return True
    End Function
End Class
