#include-once
#include "_Service_Constants.au3"
#AutoIt3Wrapper_Change2CUI=y
#include<WinApi.au3>

Func _CreateService($sComputerName, $sServiceName, $sDisplayName, $sBinaryPath, $sServiceUser = "LocalSystem", $sPassword = "", $nServiceType = 0x00000010, $nStartType = 0x00000002, $nErrorType = 0x00000001, $nDesiredAccess = 0x000f01ff, $sLoadOrderGroup = "")
	Local $hAdvapi32
	Local $hKernel32
	Local $arRet
	Local $hSC
	Local $lError = -1
	$hAdvapi32 = DllOpen("advapi32.dll")
	If $hAdvapi32 = -1 Then Return 0
	$hKernel32 = DllOpen("kernel32.dll")
	If $hKernel32 = -1 Then Return 0
	$arRet = DllCall($hAdvapi32, "long", "OpenSCManager", "str", $sComputerName, "str", "ServicesActive", "long", $SC_MANAGER_ALL_ACCESS)
	If $arRet[0] = 0 Then
		$arRet = DllCall($hKernel32, "long", "GetLastError")
		$lError = $arRet[0]
	Else
		$hSC = $arRet[0]
		$arRet = DllCall($hAdvapi32, "long", "OpenService", "long", $hSC, "str", $sServiceName, "long", $SERVICE_INTERROGATE)
		If $arRet[0] = 0 Then
			$arRet = DllCall($hAdvapi32, "long", "CreateService", "long", $hSC, "str", $sServiceName, "str", $sDisplayName, "long", $nDesiredAccess, "long", $nServiceType, "long", $nStartType, "long", $nErrorType, "str", $sBinaryPath, "str", $sLoadOrderGroup, "ptr", 0, "str", "", "str", $sServiceUser, "str", $sPassword)
			If $arRet[0] = 0 Then
				$arRet = DllCall($hKernel32, "long", "GetLastError")
				$lError = $arRet[0]
			Else
				DllCall($hAdvapi32, "int", "CloseServiceHandle", "long", $arRet[0])
			EndIf
		Else
			DllCall($hAdvapi32, "int", "CloseServiceHandle", "long", $arRet[0])
		EndIf
		DllCall($hAdvapi32, "int", "CloseServiceHandle", "long", $hSC)
	EndIf
	DllClose($hAdvapi32)
	DllClose($hKernel32)
	If $lError <> -1 Then
		SetError($lError)
		Return 0
	EndIf
	Return 1
EndFunc


Func _DeleteService($sComputerName, $sServiceName)
	Local $hAdvapi32
	Local $hKernel32
	Local $arRet
	Local $hSC
	Local $hService
	Local $lError = -1

	$hAdvapi32 = DllOpen("advapi32.dll")
	If $hAdvapi32 = -1 Then Return 0
	$hKernel32 = DllOpen("kernel32.dll")
	If $hKernel32 = -1 Then Return 0
	$arRet = DllCall($hAdvapi32, "long", "OpenSCManager", "str", $sComputerName, "str", "ServicesActive", "long", $SC_MANAGER_ALL_ACCESS)
	If $arRet[0] = 0 Then
		$arRet = DllCall($hKernel32, "long", "GetLastError")
		$lError = $arRet[0]
	Else
		$hSC = $arRet[0]
		$arRet = DllCall($hAdvapi32, "long", "OpenService", "long", $hSC, "str", $sServiceName, "long", $SERVICE_ALL_ACCESS)
		If $arRet[0] = 0 Then
			$arRet = DllCall($hKernel32, "long", "GetLastError")
			$lError = $arRet[0]
		Else
			$hService = $arRet[0]
			$arRet = DllCall($hAdvapi32, "int", "DeleteService", "long", $hService)
			If $arRet[0] = 0 Then
				$arRet = DllCall($hKernel32, "long", "GetLastError")
				$lError = $arRet[0]
			EndIf
			DllCall($hAdvapi32, "int", "CloseServiceHandle", "long", $hService)
		EndIf
		DllCall($hAdvapi32, "int", "CloseServiceHandle", "long", $hSC)
	EndIf
	DllClose($hAdvapi32)
	DllClose($hKernel32)
	If $lError <> -1 Then
		SetError($lError)
		Return 0
	EndIf
	Return 1
EndFunc

Func _StopService($sComputerName, $sServiceName)
	Local $hAdvapi32
	Local $hKernel32
	Local $arRet
	Local $hSC
	Local $hService
	Local $lError = -1

	$hAdvapi32 = DllOpen("advapi32.dll")
	If $hAdvapi32 = -1 Then Return 0
	$hKernel32 = DllOpen("kernel32.dll")
	If $hKernel32 = -1 Then Return 0
	$arRet = DllCall($hAdvapi32, "long", "OpenSCManager", "str", $sComputerName, "str", "ServicesActive", "long", $SC_MANAGER_CONNECT)
	If $arRet[0] = 0 Then
		$arRet = DllCall($hKernel32, "long", "GetLastError")
		$lError = $arRet[0]
	Else
		$hSC = $arRet[0]
		$arRet = DllCall($hAdvapi32, "long", "OpenService", "long", $hSC, "str", $sServiceName, "long", $SERVICE_STOP)
		If $arRet[0] = 0 Then
			$arRet = DllCall($hKernel32, "long", "GetLastError")
			$lError = $arRet[0]
		Else
			$hService = $arRet[0]
			$arRet = DllCall($hAdvapi32, "int", "ControlService", "long", $hService, "long", $SERVICE_CONTROL_STOP, "str", "")
			If $arRet[0] = 0 Then
				$arRet = DllCall($hKernel32, "long", "GetLastError")
				$lError = $arRet[0]
			EndIf
			DllCall($hAdvapi32, "int", "CloseServiceHandle", "long", $hService)
		EndIf
		DllCall($hAdvapi32, "int", "CloseServiceHandle", "long", $hSC)
	EndIf
	DllClose($hAdvapi32)
	DllClose($hKernel32)
	If $lError <> -1 Then
		SetError($lError)
		Return 0
	EndIf
	Return 1
EndFunc

Func _ServiceExists($sComputerName, $sServiceName)
	Local $hAdvapi32
	Local $hKernel32
	Local $arRet
	Local $hSC
	Local $hService
	Local $lError = -1

	$hAdvapi32 = DllOpen("advapi32.dll")
	If $hAdvapi32 = -1 Then Return 0
	$hKernel32 = DllOpen("kernel32.dll")
	If $hKernel32 = -1 Then Return 0
	$arRet = DllCall($hAdvapi32, "long", "OpenSCManager", "str", $sComputerName, "str", "ServicesActive", "long", $SC_MANAGER_CONNECT)
	If $arRet[0] = 0 Then
		$arRet = DllCall($hKernel32, "long", "GetLastError")
		$lError = $arRet[0]
	Else
		$hSC = $arRet[0]
		$arRet = DllCall($hAdvapi32, "long", "OpenService", "long", $hSC, "str", $sServiceName, "long", $SERVICE_STOP)
		If $arRet[0] = 0 Then
			$arRet = DllCall($hKernel32, "long", "GetLastError")
			$lError = $arRet[0]
		EndIf
		DllCall($hAdvapi32, "int", "CloseServiceHandle", "long", $hSC)
	EndIf
	DllClose($hAdvapi32)
	DllClose($hKernel32)
	If $lError <> -1 Then
		SetError($lError)
		Return 0
	EndIf
	Return 1
EndFunc

Func _Service_Cleanup()
	$service_error = _WinAPI_GetLastError()
	If ($tService_Status_handle) Then _Service_ReportStatus($SERVICE_STOPPED, $service_error, 0);
EndFunc

Func _Service_Ctrl($ctrlCode)
	Switch ($ctrlCode)
		Case $SERVICE_CONTROL_PAUSE
			DllStructSetData($tService_Status, "dwCurrentState", $SERVICE_PAUSED)
		Case $SERVICE_CONTROL_CONTINUE
			DllStructSetData($tService_Status, "dwCurrentState", $SERVICE_RUNNING)
		Case $SERVICE_CONTROL_STOP
			_Service_ReportStatus($SERVICE_STOP_PENDING, $NO_ERROR, 0);
			_Service_SetStopEvent();
			_Service_Cleanup()
			Exit
		Case $SERVICE_CONTROL_INTERROGATE
			;break;
			; invalid control code
			;
		Case Else
			;
	EndSwitch
	_Service_ReportStatus(DllStructGetData($tService_Status, "dwCurrentState"), $NO_ERROR, 0);
EndFunc

Func _Service_Halting()
	Return (_WinAPI_WaitForSingleObject($service_stop_event, $NTSL_LOOP_WAIT) == $WAIT_OBJECT_0);
EndFunc

Func _Service_Init($sServiceName)
	$tServiceCtrl = DllCallbackRegister("_Service_Ctrl", "int", "uint")
	$tServiceMain = DllCallbackRegister("_Service_ServiceMain", "int", "int;str")
	$tdispatchTable = DllStructCreate("ptr[2];ptr[2]")
	$tServiceName = DllStructCreate("char[128]")
	DllStructSetData($tServiceName, 1, $sServiceName)
	DllStructSetData($tdispatchTable, 1, DllStructGetPtr($tServiceName), 1)
	DllStructSetData($tdispatchTable, 1, DllCallbackGetPtr($tServiceMain), 2)
	DllStructSetData($tdispatchTable, 2, 0, 1)
	DllStructSetData($tdispatchTable, 2, 0, 2)
	$ret = DllCall("advapi32.dll", "int", "StartServiceCtrlDispatcher", "ptr", DllStructGetPtr($tdispatchTable))
	;If $ret[0] = 0 Then MsgBox(0, "", " Error " & _WinAPI_GetLastError() & @CRLF)
EndFunc

Func _Service_ReportStatus($currentState, $exitCode, $waitHint)
	Local $checkPoint = 1;
	Local $rc = True;
	If Not ($service_debug_mode) Then ;when debugging we don't report to the SCM
		If ($currentState == $SERVICE_START_PENDING) Then
			DllStructSetData($tService_Status, "dwControlsAccepted", 0);
		Else
			DllStructSetData($tService_Status, "dwControlsAccepted", $SERVICE_ACCEPT_STOP)
		EndIf

		DllStructSetData($tService_Status, "dwCurrentState", $currentState)
		DllStructSetData($tService_Status, "dwWin32ExitCode", $exitCode)
		DllStructSetData($tService_Status, "dwWaitHint", $waitHint)
		If $currentState == $SERVICE_RUNNING Or $currentState == $SERVICE_STOPPED Then
			DllStructSetData($tService_Status, "dwCheckPoint", 0)
		Else
			DllStructSetData($tService_Status, "dwCheckPoint", $checkPoint + 1);
		EndIf
		; report the status of the service to the service control manager.
		If Not ($rc = _Service_SetServiceStatus($tService_Status_handle, DllStructGetPtr($tService_Status))) Then ConsoleWrite("+ " & $NTSL_ERROR_SERVICE_STATUS & @TAB & _WinAPI_GetLastError() & @CRLF)
	EndIf
	Return ($rc);
EndFunc

Func _Service_ServiceMain($iArg, $sArgs)
	$ret = DllCall("advapi32.dll", "hwnd", "RegisterServiceCtrlHandler", "ptr", DllStructGetPtr($tServiceName), "ptr", DllCallbackGetPtr($tServiceCtrl));register service
	If $ret[0] = 0 Then
		MsgBox(0, "Error", _WinAPI_GetLastError())
		Exit
	EndIf
	$tService_Status_handle = $ret[0]
	If Not ($tService_Status_handle) Then
		_Service_Cleanup()
		Return
	EndIf

	DllStructSetData($tService_Status, "dwServiceType", $SERVICE_WIN32_OWN_PROCESS)
	DllStructSetData($tService_Status, "dwServiceSpecificExitCode", 0);
	; report the status to the service control manager.
	If Not (_Service_ReportStatus($SERVICE_START_PENDING, $NO_ERROR, 3000)) Then
		_Service_Cleanup()
		Return
	EndIf
	_Service_Start($iArg, $sArgs);
	_Main()
	Return;
EndFunc

Func _Service_SetServiceStatus($hServiceStatus, $lpServiceStatus)
	$ret = DllCall("advapi32.dll", "int", "SetServiceStatus", "hwnd", $hServiceStatus, "ptr", $lpServiceStatus)
	Return $ret[0]
EndFunc

Func _Service_SetStopEvent()
	If ($service_stop_event) Then _WinAPI_SetEvent($service_stop_event)
EndFunc

Func _Service_Start($argc, $argv)
	If Not (_Service_ReportStatus($SERVICE_START_PENDING, $NO_ERROR, 3000)) Then Return
	$service_stop_event = _WinAPI_CreateEvent(0, True, False, 0);
	If Not ($service_stop_event) Then Return;
	;report the status to the service control manager.
	If Not _Service_ReportStatus($SERVICE_START_PENDING, $NO_ERROR, 3000) Then Return;
	;report the status to the service control manager.
	If Not _Service_ReportStatus($SERVICE_RUNNING, $NO_ERROR, 0) Then Return;
EndFunc

#endregion