服务管理实用工具开发详解
在服务管理的开发中,我们常常需要实现对服务状态的查询、启动、停止和恢复等操作。下面将详细介绍如何开发一个实用工具来完成这些任务。
1. 关键方法与结构
1.1 OpenService 方法
该方法用于打开一个服务并返回服务对象。代码如下:
' Open a service and return the service object
Public Function OpenService(Svc As ServiceStatus, rights As _
ServiceAccessRights) As ServiceObject
Dim hnd&
Dim newservice As New ServiceObject
' Service manager must be open to perform this operation
If schandle = 0 Then Exit Function
hnd = intOpenService(schandle, Svc.Name, rights Or SERVICE_QUERY_STATUS)
If hnd <> 0 Then
' Initialize the service via a Friend function
Call newservice.Initialize(hnd, Svc.Name, Svc.DisplayName)
Set OpenService = newservice
End If
End Function
1.2 EnumServicesStatus 函数
此函数用于检索包含
ServiceStatus
对象的集合,是较为复杂的一个函数。它涉及到
ENUM_SERVICE_STATUS
和
SERVICE_STATUS
两个结构。
C 语言中的结构声明 :
typedef struct _ENUM_SERVICE_STATUS { // ess
LPTSTR lpServiceName;
LPTSTR lpDisplayName;
SERVICE_STATUS ServiceStatus;
} ENUM_SERVICE_STATUS, *LPENUM_SERVICE_STATUS;
typedef struct _SERVICE_STATUS { // ss
DWORD dwServiceType;
DWORD dwCurrentState;
DWORD dwControlsAccepted;
DWORD dwWin32ExitCode;
DWORD dwServiceSpecificExitCode;
DWORD dwCheckPoint;
DWORD dwWaitHint;
} SERVICE_STATUS, *LPSERVICE_STATUS;
Visual Basic 中的结构声明 :
Public Type ENUM_SERVICE_STATUS
lpServiceName As Long
lpDisplayName As Long
Status As SERVICE_STATUS
End Type
' Service structures
Public Type SERVICE_STATUS
dwServiceType As Long
dwCurrentState As Long
dwControlsAccepted As Long
dwWin32ExitCode As Long
dwServiceSpecificExitCode As Long
dwCheckPoint As Long
dwWaitHint As Long
End Type
EnumServicesStatus
函数的代码如下:
' Retrieve a collection containing ServiceStatus objects
Public Function EnumServicesStatus() As Collection
Dim res&
Dim lpServices As Long
Dim BytesNeeded As Long
Dim ServicesReturned As Long
Dim ResumeHandle As Long
Dim Buffer() As Byte
ReDim Buffer(512)
Dim x&
Dim CurrentOffset&
Dim c As New Collection
Dim s As ServiceStatus
Dim continue As Boolean
Do
' Load the buffer with as many services as possible
' We exclude driver services for now
res = intEnumServicesStatus(schandle, &H30, 3, VarPtr(Buffer(0)), _
UBound(Buffer()), BytesNeeded, ServicesReturned, ResumeHandle)
If res = 0 And Err.LastDllError = ERROR_MORE DATA Then
' Buffer couldn't hold all the services
continue = True
Else
continue = False
End If
For x = 1 To ServicesReturned
Set s = New ServiceStatus
' The ServiceStatus buffer does the actual extraction
Call s.ParseServiceInfo(Buffer(), CurrentOffset)
c.Add s
' 36 is the size of the ENUM_SERVICE_STATUS structure
CurrentOffset = CurrentOffset + 36
Next x
If continue Then
' Prepare for the next call
CurrentOffset = 0
ReDim Buffer(BytesNeeded)
End If
Loop While continue
Set EnumServicesStatus = c
End Function
2. 服务状态类
ServiceStatus
类用于存储服务的最新状态信息。它有两种初始化方式:一种是解析
EnumServicesStatus
函数创建的缓冲区;另一种是使用
InternalInitialize
函数直接设置私有信息。
' Holds the latest value of each
Private Status As SERVICE_STATUS
Private SvcName As String
Private intDisplayName As String
' Used by the internal objects to initialize a new ServiceStatus object
' that is not created via an enumeration.
Friend Sub Internallnitialize(pName As String, pDisplay As String, _
stat As SERVICE_STATUS)
intDisplayName = pDisplay
SvcName = pName
LSet Status = stat
End Sub
' Extracts the private information from the buffer. Data is assumed
' to start at the specified offset
' Always returns 0
Friend Function ParseServiceInfo(Buffer() As Byte, ByVal Offset As Long) _
As Long
Dim svcstat As ENUM_SERVICE_STATUS
' Copy the service structure
Call RtlMoveMemory(VarPtr(svcstat), VarPtr(Buffer(Offset)), Len(svcstat))
' Extract the names
SvcName = GetVBStringFromAddress(svcstat.lpServiceName)
intDisplayName = GetVBStringFromAddress(svcstat.lpDisplayName)
' Store the SERVICE_STATUS portion only
LSet Status = svcstat.Status
End Function
' Get a VB string from an address
Public Function GetVBStringFromAddress(ByVal addr&) As String
Dim uselen&
Dim res$
If addr = 0 Then Exit Function
uselen = lstrlen(addr)
res$ = String$(uselen + 1, 0)
Call lstrcpy(res, addr)
GetVBStringFromAddress = Left$(res$, uselen)
End Function
3. 服务对象类
ServiceObject
类相对简单,它内部存储服务句柄、服务名称和显示名称,并提供了停止、启动和查询服务状态的方法。
' Service Object Class
' Copyright © 1998 by Desaware Inc. All Rights Reserved
' This object represents an open service
Option Explicit
Private intServiceHandle As Long
Private intServiceName As String
Private intDisplayName As String
' For convenience, this object is initialized by the ServiceManager
' with both the name and display name
Friend Sub Initialize(ByVal hnd As Long, Name As String, _
DisplayName As String)
intServiceHandle = hnd
intServiceName = Name
intDisplayName = DisplayName
End Sub
' Get the short name
Property Get ServiceName() As String
ServiceName = intServiceName
End Property
' Get the current status
Property Get QueryServiceStatus() As ServiceStatus
Dim stat As SERVICE_STATUS
Dim res&
Dim sres As New ServiceStatus
res = intQueryServiceStatus(intServiceHandle, stat)
If res <> 0 Then
' Another way to get a ServiceStatus object
Call sres.InternalInitialize(intServiceName, intDisplayName, stat)
Set QueryServiceStatus = sres
End If
End Property
' Perform Stop, Pause, Continue and Interrogate
Public Function ServiceControl(Operation As ServiceControlConstants) As Long
Dim stat As SERVICE_STATUS
Dim res&
res = intControlService(intServiceHandle, Operation, stat)
ServiceControl = res
End Function
' Perform start
Public Function StartService()
Dim res&
res = intStartService(intServiceHandle, 0, 0)
StartService = res
End Function
' Close the handle on termination
Private Sub Class_Terminate()
If intServiceHandle <> 0 Then
Call CloseServiceHandle(intServiceHandle)
End If
End Sub
4. 实用工具的开发
4.1 工具功能概述
该实用工具的目标是创建一个可以轻松停止并重新启动 Internet Information Services 的工具,以便替换 Active Server Page 调用的 DLL。它具有以下功能:
- 一个列表框,用于显示每个服务的名称和显示名称,支持多选。
- 启动和停止一个或多个服务的按钮。
- 一个按钮,用于将所有服务恢复到程序启动时的原始状态。
- 一种跟踪更改的方式,以便在程序关闭时提示是否将服务恢复到原始状态。
- 一个按钮,用于选择 IIS 服务。
4.2 变量定义
Dim sc As New ServiceManager
Dim ServiceCollection As Collection ' Hold current states
Dim OriginalStates As Collection ' Record original states
' Collection of ServiceObject objects that are currently
' being changed
Dim ChangesInProgress As Collection
Dim Changed As Boolean ' Mark true if any state change has been caused
Dim unloadpending As Boolean ' Mark true if final restore operation
' 0 to restore
Dim TargetState As ServiceStateConstants
4.3 表单加载事件
Private Sub Form_Load()
Dim res&
' Open the ServiceManager
res = sc.OpenSCManager("", "", SC_MANAGER_ENUMERATE_SERVICE _
Or SC_MANAGER_CONNECT)
' Update the ServiceCollection and display the services
LoadList True
Set OriginalStates = ServiceCollection
' We don't want to hold two references to the collection
Set ServiceCollection = Nothing
End Sub
4.4 加载服务列表函数
' Loads the ServiceCollection collection with a list of all services
' Optionally displays them
Private Sub LoadList(bDisplay As Boolean)
Dim res&
Dim svcstat As ServiceStatus
lstServices.Refresh
Set ServiceCollection = sc.EnumServicesStatus()
If bDisplay Then
For Each svcstat In ServiceCollection
lstServices.AddItem svcstat.Name & ": " & svcstat.DisplayName
Next
End If
End Sub
4.5 启用或禁用用户界面函数
' Enables or disables the user interface of the application.
' Also controls the mouse pointer and timer.
' The UI must be disabled while service start or stop is in progress
Private Function EnablePanel(ByVal bEnabled As Boolean)
cmdStop.Enabled = bEnabled
cmdStart.Enabled = bEnabled
cmdRestore.Enabled = bEnabled
lstServices.Enabled = bEnabled
If Not bEnabled Then
Screen.MousePointer = vbHourglass
Timeri.Enabled = True
Else
Screen.MousePointer = vbNormal
Timer1.Enabled = False
End If
End Function
4.6 打开选定服务函数
' Loads the ChangesInProgress collection with the
' ServiceObject objects for the selected services
Private Function OpenSelectedServices()
Dim index&
Dim currententry$
Dim svcstat As ServiceStatus
Call LoadList(False)
Set ChangesInProgress = New Collection
For index = 0 To lstServices.ListCount - 1
If lstServices.Selected(index) Then
currententry = lstServices.List(index)
For Each svcstat In ServiceCollection
' Match based on the service name
If InStr(currententry, svcstat.Name & ":") = 1 Then
ChangesInProgress.Add sc.OpenService(svcstat, _
SERVICE_START Or SERVICE_STOP), svcstat.Name
Exit For
End If
Next
End If
Next index
End Function
5. 服务操作代码
5.1 停止选定服务
' Stops the selected services
Private Sub cmdStop_Click()
Dim so As ServiceObject
Dim stat As ServiceStatus
Dim starttimer As Boolean
Dim RemoveThis As Boolean
Dim res&
Call OpenSelectedServices
For Each so In ChangesInProgress
Set stat = so.QueryServiceStatus
If stat.CurrentState <> SERVICE_STOPPED Then
res = so.ServiceControl(SERVICE_CONTROL_STOP)
If res <> 0 Then
starttimer = True
Else
RemoveThis = True
End If
Else
RemoveThis = True
End If
If RemoveThis Then ChangesInProgress.Remove stat.Name
RemoveThis = False
Next so
If starttimer Then
EnablePanel False
TargetState = SERVICE_STOPPED
Changed = True
Else
Set ChangesInProgress = Nothing
End If
End Sub
5.2 启动选定服务
' Starts the selected services
Private Sub cmdStart_Click()
Dim so As ServiceObject
Dim stat As ServiceStatus
Dim starttimer As Boolean
Dim RemoveThis As Boolean
Dim res&
Call OpenSelectedServices
For Each so In ChangesInProgress
Set stat = so.QueryServiceStatus
If stat.CurrentState <> SERVICE_RUNNING Then
res = so.StartService
If res <> Then
starttimer = True
Else
RemoveThis = True
End If
Else
RemoveThis = True
End If
If RemoveThis Then ChangesInProgress.Remove stat.Name
RemoveThis = False
Next so
If starttimer Then
EnablePanel False
TargetState = SERVICE_RUNNING
Changed = True
Else
Set ChangesInProgress = Nothing
End If
End Sub
5.3 恢复服务到原始状态
' Restores services to their original state
Private Sub cmdRestore_Click()
Dim so As ServiceObject
Dim stat As ServiceStatus
Dim starttimer As Boolean
Dim orig As ServiceStateConstants
Dim res&
Call LoadList(False)
Set ChangesInProgress = New Collection
For Each stat In ServiceCollection
If stat.CurrentState <> OriginalState(stat.Name) Then
ChangesInProgress.Add sc.OpenService(stat, SERVICE_START _
Or SERVICE_STOP), stat.Name
End If
Next
For Each so In ChangesInProgress
orig = OriginalState(so.ServiceName)
If orig = SERVICE_RUNNING Then
res = so.StartService
Else
res = so.ServiceControl(SERVICE_CONTROL_STOP)
End If
If res <> 0 Then
starttimer = True
Else
ChangesInProgress.Remove so.ServiceName
End If
Next so
If starttimer Then
EnablePanel False
TargetState = 0
Changed = True
Else
Set ChangesInProgress = Nothing
End If
End Sub
6. 程序关闭与定时器处理
6.1 表单卸载事件
Private Sub Form_Unload(Cancel As Integer)
Dim res&
If Changed Then
' If any change has been made, give the user
' the option to restore
res = MsgBox("Changes have been made - Restore service states?", _
vbYesNo, "Exit confirmation")
If res = vbYes Then
unloadpending = True
Me.Hide ' Hide the form - no reason to clutter the screen
cmdRestore_Click
Cancel = -1
Changed = False
Exit Sub
End If
End If
Set ServiceCollection = Nothing
Set OriginalStates = Nothing
Set ChangesInProgress = Nothing
Set sc = Nothing
End Sub
6.2 定时器事件
' Poll for the completion of the operation in progress
Private Sub Timer1_Timer()
Dim so As ServiceObject
Dim stat As ServiceStatus
Dim target As Long
For Each so In ChangesInProgress
Set stat = so.QueryServiceStatus
' Zero is the restore condition
If TargetState = 0 Then
target = OriginalState(stat.Name)
Else
target = TargetState
End If
If stat.CurrentState = target Then
' 服务进入目标状态,移除对象
End If
Next
' 集合为空时,重新启用用户界面
End Sub
总结
通过以上步骤,我们完成了一个服务管理实用工具的开发。该工具可以方便地对服务进行启动、停止和恢复操作,并且能够跟踪服务状态的变化。在开发过程中,我们使用了多个类和函数,涉及到服务状态的查询、解析和控制等操作。通过合理的设计和代码实现,我们可以有效地管理系统中的服务。
下面是一个简单的流程图,展示了服务操作的基本流程:
graph TD;
A[开始] --> B[打开服务管理器];
B --> C[加载服务列表];
C --> D{选择操作};
D -- 停止服务 --> E[打开选定服务];
D -- 启动服务 --> E;
D -- 恢复服务 --> F[对比当前状态与原始状态];
F --> E;
E --> G[执行操作];
G --> H{操作完成?};
H -- 是 --> I[移除已完成服务];
I --> J{所有服务完成?};
J -- 是 --> K[重新启用界面];
J -- 否 --> G;
H -- 否 --> G;
K --> L[结束];
同时,我们还可以用表格总结一下关键类和方法的功能:
| 类/方法 | 功能 |
| ---- | ---- |
| OpenService | 打开一个服务并返回服务对象 |
| EnumServicesStatus | 检索包含 ServiceStatus 对象的集合 |
| ServiceStatus | 存储服务的最新状态信息 |
| ServiceObject | 提供停止、启动和查询服务状态的方法 |
| LoadList | 加载服务列表 |
| OpenSelectedServices | 打开选定的服务 |
| cmdStop_Click | 停止选定的服务 |
| cmdStart_Click | 启动选定的服务 |
| cmdRestore_Click | 恢复服务到原始状态 |
服务管理实用工具开发详解(续)
7. 操作步骤详解
为了更好地理解如何使用这个服务管理实用工具,下面详细介绍各个操作的具体步骤。
7.1 启动服务操作步骤
- 选择服务 :在列表框中勾选要启动的服务。
-
点击启动按钮
:触发
cmdStart_Click事件,系统会执行以下操作:-
调用
OpenSelectedServices函数,该函数会先调用LoadList函数加载当前服务列表到ServiceCollection集合中,然后遍历列表框,将选中的服务对应的ServiceObject对象添加到ChangesInProgress集合。 -
遍历
ChangesInProgress集合,对于每个ServiceObject对象,调用QueryServiceStatus方法获取当前状态。 -
如果服务当前状态不是
SERVICE_RUNNING,则调用StartService方法启动服务。 -
若启动成功,设置
starttimer为True,调用EnablePanel函数禁用用户界面,设置TargetState为SERVICE_RUNNING,并标记Changed为True;若启动失败,从ChangesInProgress集合中移除该服务对象。
-
调用
以下是启动服务的代码示例:
' Starts the selected services
Private Sub cmdStart_Click()
Dim so As ServiceObject
Dim stat As ServiceStatus
Dim starttimer As Boolean
Dim RemoveThis As Boolean
Dim res&
Call OpenSelectedServices
For Each so In ChangesInProgress
Set stat = so.QueryServiceStatus
If stat.CurrentState <> SERVICE_RUNNING Then
res = so.StartService
If res <> 0 Then
starttimer = True
Else
RemoveThis = True
End If
Else
RemoveThis = True
End If
If RemoveThis Then ChangesInProgress.Remove stat.Name
RemoveThis = False
Next so
If starttimer Then
EnablePanel False
TargetState = SERVICE_RUNNING
Changed = True
Else
Set ChangesInProgress = Nothing
End If
End Sub
7.2 停止服务操作步骤
- 选择服务 :在列表框中勾选要停止的服务。
-
点击停止按钮
:触发
cmdStop_Click事件,系统会执行以下操作:-
调用
OpenSelectedServices函数,将选中的服务对应的ServiceObject对象添加到ChangesInProgress集合。 -
遍历
ChangesInProgress集合,对于每个ServiceObject对象,调用QueryServiceStatus方法获取当前状态。 -
如果服务当前状态不是
SERVICE_STOPPED,则调用ServiceControl方法停止服务。 -
若停止成功,设置
starttimer为True,调用EnablePanel函数禁用用户界面,设置TargetState为SERVICE_STOPPED,并标记Changed为True;若停止失败,从ChangesInProgress集合中移除该服务对象。
-
调用
以下是停止服务的代码示例:
' Stops the selected services
Private Sub cmdStop_Click()
Dim so As ServiceObject
Dim stat As ServiceStatus
Dim starttimer As Boolean
Dim RemoveThis As Boolean
Dim res&
Call OpenSelectedServices
For Each so In ChangesInProgress
Set stat = so.QueryServiceStatus
If stat.CurrentState <> SERVICE_STOPPED Then
res = so.ServiceControl(SERVICE_CONTROL_STOP)
If res <> 0 Then
starttimer = True
Else
RemoveThis = True
End If
Else
RemoveThis = True
End If
If RemoveThis Then ChangesInProgress.Remove stat.Name
RemoveThis = False
Next so
If starttimer Then
EnablePanel False
TargetState = SERVICE_STOPPED
Changed = True
Else
Set ChangesInProgress = Nothing
End If
End Sub
7.3 恢复服务到原始状态操作步骤
-
点击恢复按钮
:触发
cmdRestore_Click事件,系统会执行以下操作:-
调用
LoadList函数加载当前服务列表到ServiceCollection集合。 -
遍历
ServiceCollection集合,将当前状态与原始状态不同的服务对应的ServiceObject对象添加到ChangesInProgress集合。 -
遍历
ChangesInProgress集合,根据原始状态调用StartService或ServiceControl方法启动或停止服务。 -
若操作成功,设置
starttimer为True,调用EnablePanel函数禁用用户界面,设置TargetState为0,并标记Changed为True;若操作失败,从ChangesInProgress集合中移除该服务对象。
-
调用
以下是恢复服务到原始状态的代码示例:
' Restores services to their original state
Private Sub cmdRestore_Click()
Dim so As ServiceObject
Dim stat As ServiceStatus
Dim starttimer As Boolean
Dim orig As ServiceStateConstants
Dim res&
Call LoadList(False)
Set ChangesInProgress = New Collection
For Each stat In ServiceCollection
If stat.CurrentState <> OriginalState(stat.Name) Then
ChangesInProgress.Add sc.OpenService(stat, SERVICE_START _
Or SERVICE_STOP), stat.Name
End If
Next
For Each so In ChangesInProgress
orig = OriginalState(so.ServiceName)
If orig = SERVICE_RUNNING Then
res = so.StartService
Else
res = so.ServiceControl(SERVICE_CONTROL_STOP)
End If
If res <> 0 Then
starttimer = True
Else
ChangesInProgress.Remove so.ServiceName
End If
Next so
If starttimer Then
EnablePanel False
TargetState = 0
Changed = True
Else
Set ChangesInProgress = Nothing
End If
End Sub
8. 定时器的作用
定时器
Timer1
在服务状态改变操作中起着重要作用。当服务状态改变操作开始后,用户界面会被禁用,定时器开始运行,它会定期检查
ChangesInProgress
集合中每个服务的状态。
以下是定时器事件的代码示例:
' Poll for the completion of the operation in progress
Private Sub Timer1_Timer()
Dim so As ServiceObject
Dim stat As ServiceStatus
Dim target As Long
For Each so In ChangesInProgress
Set stat = so.QueryServiceStatus
' Zero is the restore condition
If TargetState = 0 Then
target = OriginalState(stat.Name)
Else
target = TargetState
End If
If stat.CurrentState = target Then
' 服务进入目标状态,移除对象
ChangesInProgress.Remove so.ServiceName
End If
Next
' 集合为空时,重新启用用户界面
If ChangesInProgress.Count = 0 Then
EnablePanel True
End If
End Sub
定时器的操作步骤如下:
1.
遍历集合
:遍历
ChangesInProgress
集合中的每个
ServiceObject
对象。
2.
获取状态
:调用
QueryServiceStatus
方法获取当前服务状态。
3.
判断目标状态
:根据
TargetState
的值确定目标状态。
4.
对比状态
:如果当前状态等于目标状态,从
ChangesInProgress
集合中移除该服务对象。
5.
检查集合是否为空
:如果
ChangesInProgress
集合为空,调用
EnablePanel
函数重新启用用户界面。
9. 程序关闭处理
当程序关闭时,会触发
Form_Unload
事件。如果在程序运行过程中服务状态发生了改变,会弹出提示框询问用户是否将服务恢复到原始状态。
以下是表单卸载事件的代码示例:
Private Sub Form_Unload(Cancel As Integer)
Dim res&
If Changed Then
' If any change has been made, give the user
' the option to restore
res = MsgBox("Changes have been made - Restore service states?", _
vbYesNo, "Exit confirmation")
If res = vbYes Then
unloadpending = True
Me.Hide ' Hide the form - no reason to clutter the screen
cmdRestore_Click
Cancel = -1
Changed = False
Exit Sub
End If
End If
Set ServiceCollection = Nothing
Set OriginalStates = Nothing
Set ChangesInProgress = Nothing
Set sc = Nothing
End Sub
程序关闭处理的操作步骤如下:
1.
检查状态改变
:检查
Changed
变量是否为
True
,如果是,则表示服务状态发生了改变。
2.
弹出提示框
:使用
MsgBox
函数弹出提示框,询问用户是否恢复服务状态。
3.
处理用户选择
:
- 如果用户选择
Yes
,设置
unloadpending
为
True
,隐藏表单,调用
cmdRestore_Click
函数恢复服务状态,取消关闭操作,将
Changed
标记为
False
。
- 如果用户选择
No
,继续执行关闭操作,释放相关集合和对象。
10. 注意事项
在使用这个服务管理实用工具时,需要注意以下几点:
-
服务权限
:确保程序具有足够的权限来执行服务的启动、停止和恢复操作。
-
状态更新
:由于服务状态的改变可能需要一些时间,因此在操作过程中要耐心等待,避免频繁操作。
-
资源释放
:在程序关闭时,要确保释放所有占用的资源,避免内存泄漏。
总结与展望
通过以上详细的开发过程和操作步骤,我们成功开发了一个功能完善的服务管理实用工具。该工具可以方便地对系统中的服务进行启动、停止和恢复操作,并且能够跟踪服务状态的变化。
在未来的开发中,我们可以进一步优化这个工具,例如添加更多的服务操作功能,如暂停、继续服务等;或者改进用户界面,提高用户体验。同时,我们还可以考虑将该工具集成到其他系统中,实现更复杂的服务管理需求。
下面是一个流程图,展示了程序关闭时的处理流程:
graph TD;
A[程序关闭] --> B{服务状态改变?};
B -- 是 --> C[弹出提示框];
C -- 恢复 --> D[恢复服务状态];
C -- 不恢复 --> E[释放资源];
B -- 否 --> E;
D --> E;
E --> F[结束];
另外,我们再用表格总结一下服务操作的关键步骤:
| 操作 | 关键步骤 |
| ---- | ---- |
| 启动服务 | 选择服务 -> 点击启动按钮 -> 打开选定服务 -> 执行启动操作 -> 定时器检查状态 -> 完成后重新启用界面 |
| 停止服务 | 选择服务 -> 点击停止按钮 -> 打开选定服务 -> 执行停止操作 -> 定时器检查状态 -> 完成后重新启用界面 |
| 恢复服务 | 点击恢复按钮 -> 对比状态 -> 打开选定服务 -> 执行操作 -> 定时器检查状态 -> 完成后重新启用界面 |
| 程序关闭 | 检查状态改变 -> 弹出提示框 -> 处理用户选择 -> 释放资源 |
超级会员免费看
2万+

被折叠的 条评论
为什么被折叠?



