找回密码
 加入
搜索
查看: 3353|回复: 7

[系统综合] 使用P版的服务程序,运行一段时间后,手动停止服务后进程无法立即停止

  [复制链接]
发表于 2017-2-11 19:14:17 | 显示全部楼层 |阅读模式
本帖最后由 imutraveler 于 2017-2-13 00:36 编辑

最近在写自己使用的服务端和客户端程序

服务端采用  “服务” 的 方式,读取ACCESS,给客户端发送数据

使用的是P版的服务程序,运行过程中没问题!

只是,当手动停止该服务时,
会出现:服务显示已停止,但是进程依然在,需要等待  30秒,服务超时,进程才会退出。

为什么会出现这种问题呢?

经排查应该是循环的问题,不过在while 1 中添加了判断,当服务状态被设置为stopped时候,循环退出。循环是退出了,可进程还是在~

才疏学浅,请指点下!

服务端代码,编译之后,手动运行即可安装服务



#NoTrayIcon
#Region ;**** 由 AccAu3Wrapper_GUI 创建指令 ****
#AccAu3Wrapper_OutFile=Server.exe
#AccAu3Wrapper_UseX64=n
#AccAu3Wrapper_Res_Comment=Traveler.
#AccAu3Wrapper_Res_Description=服务程序
#AccAu3Wrapper_Res_Fileversion=1.0
#AccAu3Wrapper_Res_ProductVersion=1.0
#AccAu3Wrapper_Res_LegalCopyright=Traveler.
#AccAu3Wrapper_Res_Language=2052
#AccAu3Wrapper_Res_requestedExecutionLevel=None
#EndRegion ;**** 由 AccAu3Wrapper_GUI 创建指令 ****
;~ #AccAu3Wrapper_Res_Field=ProductName|TestName
;~ #AccAu3Wrapper_Res_Field=CompanyName|TestCompany

#include <File.au3>
#include <LocalSecurityAuthority.au3>

Const $SERVICE_WIN32_OWN_PROCESS = 0x00000010
Const $SERVICE_WIN32_SHARE_PROCESS = 0x00000020
Const $SERVICE_INTERACTIVE_PROCESS = 0x00000100

Const $SERVICE_STOPPED = 0x00000001
Const $SERVICE_RUNNING = 0x00000004
Const $SERVICE_CONTINUE_PENDING = 0x00000005
Const $SERVICE_PAUSED = 0x00000007

Const $SERVICE_ACCEPT_STOP = 0x00000001
Const $SERVICE_ACCEPT_PAUSE_CONTINUE = 0x00000002

Const $SERVICE_CONTROL_STOP = 0x00000001
Const $SERVICE_CONTROL_PAUSE = 0x00000002
Const $SERVICE_CONTROL_CONTINUE = 0x00000003
Const $SERVICE_CONTROL_INTERROGATE = 0x00000004

Const $tagSERVICE_STATUS = "dword ServiceType;dword CurrentState;dword ControlsAccepted;dword Win32ExitCode;dword ServiceSpecificExitCode;dword CheckPoint;dword WaitHint"

; 定义主服务全局变量
Global $sIPAddress = @IPAddress1
Global $sPort = 33891
Global $DbPath, $DbPass, $TableGroup, $TableServer, $TableUsers, $RS, $Conn
Global $MainSocket, $ConnectedSocket, $szIP_Accepted, $Recv, $sRecv, $Msg
Global $OnlineList[0][2] ;在线列表
Global $LogDir = @ScriptDir & "\Log"
Global $LogFile = $LogDir & "\" & @YEAR & "-" & @MON & "-" & @MDAY & ".log"

; SCM - 服务控制器 (Service Control Manager)。

; 此服务程序的名称。
Global $sServiceName = "066scTest"

; 判断此服务程序是否由SCM启动。
Local $tProcessBasic = DllStructCreate("dword;ptr;ulong_ptr;ulong[3]")
_NtQueryInformationProcess(-1, 0, DllStructGetPtr($tProcessBasic), 24)

If DllStructGetData($tProcessBasic, 4, 3) <> ProcessExists("Services.exe") Then
        $hService = _LsaOpenService($sServiceName, 0xF01FF)
        If ($hService = 0) Then $hService = _CreateService($sServiceName, $sServiceName, _
                        bitOr($SERVICE_WIN32_OWN_PROCESS, $SERVICE_INTERACTIVE_PROCESS), _
                                2, 0, @ScriptFullPath, "", 0)
        _StartService($hService)
        _LsaCloseServiceHandle($hService)
        exit
EndIf

; 定义几个全局变量,以便在多个函数中共享它们的值。
Global $hServiceMain, $hHandlerEx, $tServiceTable
Global $tServiceStatus, $pServiceStatus, $hServiceStatus

; 注册服务入口点函数,用于在SCM启动服务时,向SCM报告自己的状态。
$hServiceMain = DllCallbackRegister("_ServiceMain", "none", "dword;ptr")
; 子控制请求函数,用于接收SCM发来的请求,并报告自己的状态。
$hHandlerEx = DllCallbackRegister("_HandlerEx", "dword", "dword;dword;ptr;ptr")

$tServiceTable = DllStructCreate("ptr;ptr;ptr;ptr;char[256]")
DllStructSetData($tServiceTable, 1, DllStructGetPtr($tServiceTable, 5)) ; 服务名称。
DllStructSetData($tServiceTable, 2, DllCallbackGetPtr($hServiceMain)) ; 入口点函数地址。
DllStructSetData($tServiceTable, 5, $sServiceName)

$tServiceStatus = DllStructCreate($tagSERVICE_STATUS)
$pServiceStatus = DllStructGetPtr($tServiceStatus)
DllStructSetData($tServiceStatus, "ServiceType", _ ; 服务类型
                        bitOr($SERVICE_WIN32_OWN_PROCESS, _ ; 独享进程。
                        $SERVICE_INTERACTIVE_PROCESS)) ; 允许与桌面交互。

DllStructSetData($tServiceStatus, "ControlsAccepted", _ ; 指定允许接收的后续控制请求。
                        $SERVICE_ACCEPT_STOP) ; 服务可以停止。
;~                         bitOr($SERVICE_ACCEPT_STOP, _ ; 服务可以停止。
;~                         $SERVICE_ACCEPT_PAUSE_CONTINUE)) ; 服务可以暂停和继续。

; 启动服务的控制调度分派线程。
; 当SCM启动某个服务时,服务进程的主线程必须在30秒内调用此函数。
; SCM将分派表传递给StartServiceCtrlDispatcher,这将把调用进程的主线程转换为控制分派器,
; 该分派器启动一个新线程,该线程运行分派表中每个服务的 ServiceMain 入口点函数。
; 如果 StartServiceCtrlDispatcher 函数30秒没有被调用, 便会出现1053的错误(服务没有及时响应控制请求)。
_StartServiceCtrlDispatcher(DllStructGetPtr($tServiceTable))

Func _ServiceMain($iNumberofArgs, $pArguments)
        Local $aProcess, $hProcess, $hToken, $aPriv[1][2] = [[$SE_DEBUG_NAME, 2]]

        ; 注册服务的“控制处理器”,以用于接收停止、暂停等请求操作,应在ServiceMain函数中尽早调用。
        $hServiceStatus = _RegisterServiceCtrlHandlerEx($sServiceName, DllCallbackGetPtr($hHandlerEx))

        ; 向SCM报告自己的状态。
        DllStructSetData($tServiceStatus, "CurrentState", $SERVICE_RUNNING)
        _SetServiceStatus($hServiceStatus, $pServiceStatus)

        ; 自定义功能
        ; 服务主函数
        Call("MainSvr")
EndFunc        ;==>_ServiceMain

; 服务的“控制处理器”,用于处理SCM发出的各种请求。
Func _HandlerEx($iRequest, $iEventType, $pEventData, $pContext)
        Switch $iRequest
                Case $SERVICE_CONTROL_STOP ; 服务需停止。
                        ; 如果30秒内没有向SCM报告自己的状态,将会出现1053的错误。
                        DllStructSetData($tServiceStatus, "CurrentState", $SERVICE_STOPPED)
                        _SetServiceStatus($hServiceStatus, $pServiceStatus)
                        Call("SvrExit")
                        Return 0
                Case $SERVICE_CONTROL_PAUSE ; 服务需暂停。
                        ; 同上,必须在30秒调用,否则出错。
                        DllStructSetData($tServiceStatus, "CurrentState", $SERVICE_PAUSED)
                        _SetServiceStatus($hServiceStatus, $pServiceStatus)
                        Return 0
                Case $SERVICE_CONTROL_CONTINUE ; 服务需要继续。
                        ; 同上。
                        DllStructSetData($tServiceStatus, "CurrentState", $SERVICE_RUNNING)
                        _SetServiceStatus($hServiceStatus, $pServiceStatus)
                        Return 0
                Case $SERVICE_CONTROL_INTERROGATE ; 服务需要向SCM报告自己的状态。
                        _SetServiceStatus($hServiceStatus, $pServiceStatus)
                        Return 0
        EndSwitch
EndFunc        ;==>_HandlerEx

Func _SetServiceStatus($hServiceStatus, $pServiceStatus)
        Local $iResult
        $iResult = DllCall("AdvApi32.dll", "int", "SetServiceStatus", _
                                        "hWnd", $hServiceStatus, "ptr", $pServiceStatus)
        Return SetError(_GetLastError(), 0, $iResult[0])
EndFunc        ;==>_SetServiceStatus
Func _StartService($hService, $iNumberofArgs = 0, $pArguments = 0)
        Local $iResult
        $iResult = DllCall("AdvApi32.dll", "int", "StartService", "hWnd", $hService, _
                        "dword", $iNumberofArgs, "ptr", $pArguments)
        Return SetError(_GetLastError(), 0, $iResult[0])
EndFunc        ;==>_StartService

Func _RegisterServiceCtrlHandlerEx($sServiceName, $pHandlerEx, $pContext = 0)
        Local $iResult
        $iResult = DllCall("AdvApi32.dll", "hWnd", "RegisterServiceCtrlHandlerEx", _
                        "str", $sServiceName, "ptr", $pHandlerEx, "ptr", $pContext)
        Return SetError(_GetLastError(), 0, $iResult[0])
EndFunc        ;==>_RegisterServiceCtrlHandlerEx

Func _StartServiceCtrlDispatcher($pServiceTable)
        Local $iResult
        $iResult = DllCall("AdvApi32.dll", "int", "StartServiceCtrlDispatcher", _
                        "ptr", $pServiceTable)
        Return SetError(_GetLastError(), 0, $iResult[0])
EndFunc        ;==>_StartServiceCtrlDispatcher

Func _CreateService($sServiceName, $sDisplayName, $iServiceType, $iStartType, _
                $iErrorControl, $sBinaryPath, $sLoadOrderGroup, $aDependencies, _
                $sStartName = "", $sPassword = "", $iDesiredAccess = 0xF01FF)

        Local $iResult, $tServiceName, $tDisplayName, $tBinaryPath, $hSC
        Local $tLoadOrder, $tDependencies, $tStartName, $tPassword, $sBuffer

        $tServiceName = DllStructCreate("wchar ServiceName[" & StringLen($sServiceName) + 1 & "]")
        DllStructSetData($tServiceName, "ServiceName", $sServiceName)
        If $sDisplayName <> "" Then
                $tDisplayName = DllStructCreate("wchar DisplayName[" & StringLen($sDisplayName) + 1 & "]")
                DllStructSetData($tDisplayName, "DisplayName", $sDisplayName)
        EndIf
        If $sBinaryPath <> "" Then
                $tBinaryPath = DllStructCreate("wchar BinaryPath[" & StringLen($sBinaryPath) + 1 & "]")
                DllStructSetData($tBinaryPath, "BinaryPath", $sBinaryPath)
        EndIf
        If $sLoadOrderGroup <> "" Then
                $tLoadOrder = DllStructCreate("wchar LoadOrder[" & StringLen($sLoadOrderGroup) + 1 & "]")
                DllStructSetData($tLoadOrder, "LoadOrder", $sLoadOrderGroup)
        EndIf
        If $sStartName <> "" Then
                $tStartName = DllStructCreate("wchar StartName[" & StringLen($sStartName) + 1 & "]")
                DllStructSetData($tStartName, "StartName", $sStartName)
        EndIf
        If $sPassword <> "" Then
                $tPassword = DllStructCreate("wchar Password[" & StringLen($sPassword) + 1 & "]")
                DllStructSetData($tPassword, "Password", $sPassword)
        EndIf
        If IsArray($aDependencies) And UBound($aDependencies, 0) = 1 Then
                For $i = 0 To UBound($aDependencies) - 1
                        $sBuffer &= "wchar[" & StringLen($aDependencies[$i]) + 1 & "];"
                Next
                $tDependencies = DllStructCreate($sBuffer & ";wchar[1]")
                For $i =  0 To UBound($aDependencies) - 1
                        DllStructSetData($tDependencies, ($i + 1), $aDependencies[$i])
                Next
        EndIf
        $hSC = _LsaOpenSCManager("", 2)
        $iResult = DllCall("AdvApi32.dll", "hWnd", "CreateServiceW", _
                        "hWnd", $hSC, _
                        "ptr", DllStructGetPtr($tServiceName), _
                        "ptr", DllStructGetPtr($tDisplayName), _
                        "dword", $iDesiredAccess, _
                        "dword", $iServiceType, _
                        "dword", $iStartType, _
                        "dword", $iErrorControl, _
                        "ptr", DllStructGetPtr($tBinaryPath), _
                        "ptr", DllStructGetPtr($tLoadOrder), _
                        "ptr", 0, _
                        "ptr", DllStructGetPtr($tDependencies), _
                        "ptr", DllStructGetPtr($tStartName), _
                        "ptr", DllStructGetPtr($tPassword))
        Return SetError(_GetLastError(), _LsaCloseServiceHandle($hSC), $iResult[0])
EndFunc        ;==>_CreateService

Func _NtQueryInformationProcess($hProcess, $iClass, $pBuffer, $iSizeofBuffer)
        Local $iResult
        $iResult = DllCall("Ntdll.dll", "dword", "NtQueryInformationProcess", "hWnd", $hProcess, _
                                        "int", $iClass, "ptr", $pBuffer, "ulong", $iSizeofBuffer, "ulong*", 0)
        Return SetError($iResult[0], $iResult[5], $iResult[0] = 0)
EndFunc        ;==>_NtQueryInformationProcess

Func MainSvr()
        If Not FileExists($LogDir) Then DirCreate($LogDir)
;~         AdlibRegister("ReduceMemory", 1000 * 10) ;10秒整理一次内存
        Call("ConnectDB")
        TCPStartup()
        $ConnectedSocket = -1
        
        ;创建监听
        $MainSocket = TCPListen($sIPAddress, $sPort)
        If $MainSocket = -1 Then
                _FileWriteLog($LogFile, "创建监听失败,请重新设置端口" )
                Exit
        Else
                _FileWriteLog($LogFile, "创建监听成功,当前服务端口: " & $sPort )
        EndIf
        
        While 1
                _Accept()
                _Delete()
                _RecvMsg()
                Sleep(50)
        WEnd
        
EndFunc

Func _Accept() ;接受新客户端连接,并维护socket表
        $ConnectedSocket = TCPAccept($MainSocket)
        If $ConnectedSocket <> -1 Then ;<>-1表示有新客户端连接
                $szIP_Accepted = SocketToIP($ConnectedSocket) ;socket转成IP
                $all = UBound($OnlineList) + 1
                ReDim $OnlineList[$all][2] ;在线列表数组增加
                $OnlineList[$all - 1][0] = $ConnectedSocket ;socket
                $OnlineList[$all - 1][1] = $szIP_Accepted ;ip
                _FileWriteLog($LogFile, $szIP_Accepted & " 上线 - " & @HOUR & ":" & @MIN & ":" & @SEC)
        EndIf
EndFunc   ;==>_Accept

Func _Delete() ;删除关闭的socket列表
        For $i = 0 To UBound($OnlineList) - 1
                $recv = TCPsend($OnlineList[$i][0], "")
                If @error Then
                        ;显示离线
                        _FileWriteLog($LogFile, $OnlineList[$i][1] & " 离线 - " & @HOUR & ":" & @MIN & ":" & @SEC)
                        ;删除数组
                        _ArrayDelete($OnlineList, $i)
                        ExitLoop ;这个退出用来防止删除过多的socket,如果没有这个可能导致 $recv = TCPRecv($OnlineList[$i][0], "") 这一句数组错误。
                EndIf
        Next
EndFunc   ;==>_Delete

Func _RecvMsg() ;接收客户机发来数据
        For $i = 0 To UBound($OnlineList) - 1
                $sRecv = TCPRecv($OnlineList[$i][0], 1024 * 100, 1)
                If $sRecv <> "" Then
                        _FileWriteLog($LogFile, "收到来自 " & $OnlineList[$i][1] & "(" & $OnlineList[$i][0] & ") 的消息: " & BinaryToString($sRecv, 4))
                        Switch $sRecv
                                Case "GetTvList"
                                        Local $gList
                                        $RS = ObjCreate("ADODB.Recordset")
                                        $RS.ActiveConnection = $Conn
                                        ;~ $RS.Open("Select * From " & $TableGroup & " Order By GroupName")
                                        $RS.Open("Select * From " & $TableGroup)

                                        While Not $RS.eof And Not $RS.bof
                                                If @error = 1 Then ExitLoop
                                                $gList &= "|" & $RS.Fields(1).value
                                                $RS.movenext
                                        WEnd
                                        _SendText($OnlineList[$i][0], $gList)
                                Case Else
                                        _SendText($OnlineList[$i][0], "已接收到消息: " & BinaryToString($sRecv, 4))
                        EndSwitch
                EndIf
        Next
EndFunc   ;==>_Delete

Func _SendText($cSocket, $Text);将数据转换成二进制再发送
        If $Text <> "" Then
                TCPSend($cSocket, StringToBinary($Text, 4))
        EndIf

EndFunc   ;==>_SendText

Func SocketToIP($SHOCKET)
        Local $sockaddr, $aRet
        $sockaddr = DllStructCreate("short;ushort;uint;char[8]")
        $aRet = DllCall("Ws2_32.dll", "int", "getpeername", "int", $SHOCKET, _
                        "ptr", DllStructGetPtr($sockaddr), "int*", DllStructGetSize($sockaddr))
        If Not @error And $aRet[0] = 0 Then
                $aRet = DllCall("Ws2_32.dll", "str", "inet_ntoa", "int", DllStructGetData($sockaddr, 3))
                If Not @error Then $aRet = $aRet[0]
        Else
                $aRet = 0
        EndIf
        $sockaddr = 0
        Return $aRet
EndFunc   ;==>SocketToIP

Func ConnectDB()   ;连接数据库
        $DbPath = @ScriptDir & "\" & "Data.mdb"
        $DbPass = "123"
        $TableGroup = "TB_Group"
        $TableServer = "TB_Server"
        $TableUsers = "TB_Users"
        $Conn = ObjCreate("ADODB.Connection")
        $Conn.Open("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & $DbPath & "; Jet OLEDB:Database Password=" & $DbPass)
EndFunc   ;==>ConnectDB

Func ReduceMemory()
        $i_PID = @AutoItPID
        Local $ai_Handle = DllCall("kernel32.dll", 'int', 'OpenProcess', 'int', 0x1f0fff, 'int', False, 'int', $i_PID)
        Local $ai_Return = DllCall("psapi.dll", 'int', 'EmptyWorkingSet', 'long', $ai_Handle[0])
        DllCall('kernel32.dll', 'int', 'CloseHandle', 'int', $ai_Handle[0])
        Return $ai_Return[0]
EndFunc   ;==>_ReduceMemory

Func SvrExit()
        TCPShutdown()
        $RS.Close
        $Conn.Close
        _FileWriteLog($LogFile, "服务停止")
EndFunc


测试使用的客户端程序,,目前TCP通讯没发现什么错误!

#Region ;**** 由 AccAu3Wrapper_GUI 创建指令 ****
#AccAu3Wrapper_OutFile_x64=客户机_x64.exe
#AccAu3Wrapper_Res_Language=2052
#AccAu3Wrapper_Res_requestedExecutionLevel=None
#EndRegion ;**** 由 AccAu3Wrapper_GUI 创建指令 ****

#include <ButtonConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <StaticConstants.au3>
#include <EditConstants.au3>

Opt("GUIOnEventMode", 1) ;GUIi事件模式

; 设置一些常用信息
;--------------------------
Local $ConnectedSocket, $szData
Local $szIPADDRESS = "192.168.1.232"
Local $nPORT = 33891


$Form1 = GUICreate("客户端", 300, 200)
GUISetOnEvent($Form1, "_gui")
GUISetOnEvent($GUI_EVENT_CLOSE, "_gui")
$Label1 = GUICtrlCreateLabel("", 8, 8, 280, 20)
GUICtrlSetBkColor(-1, 0xcccccc)
$Label2 = GUICtrlCreateLabel("", 8, 32, 280, 20)
GUICtrlSetBkColor(-1, 0xcccccc)
$Input1 = GUICtrlCreateInput("", 8, 55, 220, 20)
$Button1 = GUICtrlCreateButton("发送", 232, 54, 56, 22)
GUICtrlSetOnEvent(-1, "_Gui")
$Edit1 = GUICtrlCreateEdit("", 8, 78, 280, 80, BitOR($ES_WANTRETURN, $WS_VSCROLL))
GUISetState(@SW_SHOW)

; 开始 TCP 服务
TCPStartup()

; 初始化
$ConnectedSocket = -1

Func _Gui()
        Switch @GUI_CtrlId
                Case $GUI_EVENT_CLOSE
                        Exit
                Case $Button1
                         SendDataToServer(GUICtrlRead($Input1))
        EndSwitch
EndFunc   ;==>_Gui

While 1
        _Recv()
        Sleep(50)
WEnd

Func _Recv()
        $recv = TCPRecv($ConnectedSocket, 1024 * 50, 1)
        If @error Then
                _Conn()
                Return 0
        Else
                GUICtrlSetData($Label1, "Socket : " & $ConnectedSocket)
                GUICtrlSetData($Label2, "服务器 : 在线")
                GUICtrlSetColor($Label2, 0x00ff00)
                If $recv <> "" Then;如果接收不为空
                        GUICtrlSetData($Edit1, BinaryToString($recv, 4) & @CRLF , "|")
                EndIf
        EndIf

EndFunc   ;==>_Recv

Func _Conn()
        GUICtrlSetData($Label1, "Socket : " & $ConnectedSocket)
        GUICtrlSetData($Label2, "服务器 : 离线")
        GUICtrlSetColor($Label2, 0xff0000)
        $ConnectedSocket = TCPConnect($szIPADDRESS, $nPORT)
EndFunc   ;==>_Conn

Func SendDataToServer($sData)
        If $sData <> "" Then
                $sMsg = StringToBinary($sData, 4)
                TCPSend($ConnectedSocket, $sMsg)
        EndIf
EndFunc   ;==>SendDataToServer

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?加入

×
 楼主| 发表于 2017-2-12 09:35:26 | 显示全部楼层
人都去哪里了???没人能帮个忙吗?
发表于 2017-2-28 17:51:19 | 显示全部楼层
你这个服务程序,能够在WIN10运行吗。
发表于 2017-2-28 17:51:19 | 显示全部楼层
你这个服务程序,能够在WIN10运行吗。
发表于 2017-2-28 21:23:46 | 显示全部楼层
win10里各种测试没通过
 楼主| 发表于 2017-3-1 08:53:43 | 显示全部楼层
回复 5# lin6051


    我自己就是 win10 系统, 服务运行 没问题,,只是进程不退出,要等 30秒。
    后来没办法,我在服务退出的 最后一步  执行了 processclose
 楼主| 发表于 2017-3-1 08:53:54 | 显示全部楼层
回复 4# 862228699


        我自己就是 win10 系统, 服务运行 没问题,,只是进程不退出,要等 30秒。
    后来没办法,我在服务退出的 最后一步  执行了 processclose
发表于 2017-3-7 00:38:49 | 显示全部楼层
回复 7# imutraveler
求源码文件  862228699@qq.com   谢谢
您需要登录后才可以回帖 登录 | 加入

本版积分规则

QQ|手机版|小黑屋|AUTOIT CN ( 鲁ICP备19019924号-1 )谷歌 百度

GMT+8, 2024-4-20 14:47 , Processed in 0.088460 second(s), 24 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表