#NoTrayIcon #RequireAdmin ;~ ---------------------------------------------------------------------------- ;~ ;~ Script name: ServiceMenu.exe ;~ ;~ AutoIt Version: 3.3.0.0 ;~ ;~ Author: Dave Snyder 12/2009 ;~ ;~ ;~ Script Function: This script is an example of how to run an AutoIT script ;~ as a service natively, with the ability to add, delete, ;~ start, stop and manipulate the created service using ;~ services.au3 by Arcker. Script MUST be compiled to work. ;~ ;~ Service Menu v.3 - Run AutoIT compiled script as service, with tray menu. ;~ ;~ ---------------------------------------------------------------------------- ;~ Globals,options, and includes Opt('TrayMenuMode', 1) ; Default tray menu items (Script Paused/Exit) will not be shown. Opt('TrayAutoPause', 0) ; Script will not be paused when clicking the tray icon. Opt('TrayOnEventMode', 1) ; Enables event driven scripting ;~ Opt('MustDeclareVars', 1) ;~ #include ;~ #include #include ;~ #include #include ;~ #include ;~ #include ;~ #include ;~ #include #include ;~ #include ;~ #include ;~ #include #include ;~ #include #include 'Services.au3' ;~ The test variables! Global $n_Evidence Global $n_Monitor ;~ App Globals Global $n_Counter Global $s_LocalComputer Global $s_ScriptObject Global $s_ServiceDesc Global $s_InstallFolder Global $s_ScriptName Global $s_AppPath Global $s_AgentPath Global $s_CmdParam Global $s_StrictAppPath Global $s_LogfilePath Global $pid_AgentTray Global $h_Daemon ;~ Menu vars Global $TestMenuItem1 Global $ShowItem Global $ClearItem Global $ServiceSettingsMenu Global $AddServiceItem Global $RemoveServiceItem Global $ViewServiceItem Global $AboutItem Global $ExitItem Global $Separator MainInit() ;~ Service installed? If yes then run service code If Not @Compiled Then MsgBox(0x41040, $s_ScriptObject, 'Error. Service Daemon not compiled!', 5) WinMain() Else Select Case $s_CmdParam = '-agent' ;if $pid_AgentTray exists, don't run another one If $pid_AgentTray Then ExitScript() ;ConsoleWrite('**agent mode, user: ' & UserQuery() & @CRLF) _FileWriteLog($s_LogfilePath, '**agent mode start, user: ' & UserQuery()) FileClose($s_LogfilePath) WinMain() Case $s_CmdParam = '' Select Case _Service_Exists($s_ScriptObject) And ServiceCtrlStatus() = $SERVICE_START_PENDING ;Run only one service daemon If _Singleton($s_ScriptObject, 1) = 0 Then ExitScript() EndIf ;ConsoleWrite('**service mode start' & @CRLF) _FileWriteLog($s_LogfilePath, '**service mode start') FileClose($s_LogfilePath) _Service_init($s_ScriptObject) Case Else ;Run only one app instance If _Singleton($s_ScriptObject, 1) = 0 Then ExitScript() EndIf ;ConsoleWrite('**app mode start' & @CRLF) _FileWriteLog($s_LogfilePath, '**app mode start') FileClose($s_LogfilePath) WinMain() EndSelect Case Else ;ConsoleWrite('wtf?' & @CRLF) _FileWriteLog($s_LogfilePath, '**Load error') FileClose($s_LogfilePath) EndSelect EndIf ExitScript() ;~ set up app vars Func MainInit() If $CmdLine[0] > 0 Then $s_CmdParam = $CmdLine[1] $n_Evidence = '' $n_Monitor = '' $n_Counter = 0 $s_LocalComputer = '.' $s_ScriptObject = StringTrimRight(@ScriptName, 4) $s_ServiceDesc = $s_ScriptObject & ' Service Daemon' $s_InstallFolder = @ScriptDir & '\' $s_ScriptName = $s_ScriptObject & '.exe' $s_LogfilePath = $s_InstallFolder & $s_ScriptObject & '.log' $s_AppPath = $s_InstallFolder & $s_ScriptName $s_StrictAppPath = '"' & $s_AppPath & '"' $s_AgentPath = $s_StrictAppPath & ' -agent' $h_Daemon = TrayItemGetHandle(0) $pid_AgentTray = '' EndFunc ;==>MainInit ;~ Service mode Main routine. Func Main() While ServiceCtrlStatus() = $SERVICE_RUNNING;~ check to see if we run differently ServiceUI() WorkFunc() _Service_ReportStatus($SERVICE_RUNNING, $NO_ERROR, 5000) WEnd ; stoping program correctly. $n_Counter = 20000 Local $t1 = _Timer_SetTimer($h_Daemon, 500, 'ServiceCtrlTimer') ; sends _Service_ReportStatus($SERVICE_STOP_PENDING every timeout/10 ms like MSDN said. ExitCleanup() _Timer_KillTimer($h_Daemon, $t1) ; no more stop pending _Service_ReportStatus($SERVICE_STOPPED, $NO_ERROR, 0) ; That all! Our AutoIt Service stops just there! 0 timeout meens 'Now' GUIDelete($h_Daemon) _FileWriteLog(@ScriptDir & '\' & $s_ScriptObject & '.log', 'Exiting') FileClose(@ScriptDir & '\' & $s_ScriptObject & '.log') Exit EndFunc ;==>Main ;~ Application mode Main routine. Func WinMain() TrayInit() While True If $s_CmdParam = '-agent' Then ;agent mode only monitors the service process data and manages the app mode settings ;only the service can do the work if in service mode, if the service isnt running then the agent shouldn't be either If _Service_Exists($s_ScriptObject) And ServiceCtrlStatus() <> $SERVICE_RUNNING Then ExitScript() $n_Monitor = FileReadLine($s_LogfilePath, -1) ;ConsoleWrite($n_Monitor & @CRLF) Sleep(1000) Else WorkFunc() EndIf WEnd ExitScript() EndFunc ;==>WinMain Func ServiceUI() Local $user = UserQuery() Select Case $user If Not ProcessExists($pid_AgentTray) Then $pid_AgentTray = Run($s_AgentPath, $s_InstallFolder) EndIf Case Not $user If Not ProcessWaitClose($pid_AgentTray, 5) Then ProcessClose($pid_AgentTray) $pid_AgentTray = '' EndSelect EndFunc ;==>ServiceUI ;~ The thing this app actully does Func WorkFunc() ;~ Main App code Goes here; THIS code tests the service functionality Local $i For $i = 3600 To 1 Step -1 Sleep(1000) $n_Evidence = 'Count down: ' & $i & ' ' & UserQuery() & @CRLF ;ConsoleWrite($n_Evidence) _FileWriteLog($s_LogfilePath, $n_Evidence) If _Service_Exists($s_ScriptObject) And ServiceCtrlStatus() = $SERVICE_STOP_PENDING Then ExitLoop Next FileClose($s_LogfilePath) EndFunc ;==>WorkFunc ;~ Set up the Tray Func TrayInit() $TestMenuItem1 = TrayCreateItem('Show Counter') $ShowItem = TrayCreateItem('View Log File') $ClearItem = TrayCreateItem('Clear Log File') $ServiceSettingsMenu = TrayCreateMenu('Service Control') $AddServiceItem = TrayCreateItem('Add Service', $ServiceSettingsMenu) $RemoveServiceItem = TrayCreateItem('Remove Service', $ServiceSettingsMenu) $ViewServiceItem = TrayCreateItem('View Services...', $ServiceSettingsMenu) $Separator = TrayCreateItem('') $AboutItem = TrayCreateItem('About...') TrayItemSetState($TestMenuItem1, $TRAY_DEFAULT) TraySetIcon('Shell32.dll', 18) TraySetClick(16) TraySetState(1) TrayItemSetOnEvent($TestMenuItem1, 'TestFunction1') TrayItemSetOnEvent($ShowItem, 'ShowLog') TrayItemSetOnEvent($ClearItem, 'ClearLog') TrayItemSetOnEvent($AddServiceItem, 'InstallService') TrayItemSetOnEvent($RemoveServiceItem, 'RemoveService') TrayItemSetOnEvent($ViewServiceItem, 'ViewServiceItem') TrayItemSetOnEvent($AboutItem, 'ShowInfo') If $s_CmdParam <> '-agent' Then $Separator = TrayCreateItem('') $ExitItem = TrayCreateItem('Exit') TrayItemSetOnEvent($ExitItem, 'ExitScript') EndIf EndFunc ;==>TrayInit ;~ Service Functions ;~ Install the script as a service in 'Services.msc' Func InstallService() If Not @Compiled Then MsgBox(0x41040, $s_ScriptObject, 'Error. Service Daemon not compiled!', 5) Else ;ConsoleWrite('Installing service, please wait' & @CRLF) _Service_Create($s_ScriptObject, $s_ServiceDesc, BitOR($SERVICE_INTERACTIVE_PROCESS, $SERVICE_WIN32_OWN_PROCESS), $SERVICE_AUTO_START, $SERVICE_ERROR_NORMAL, $s_StrictAppPath) If @error Then ;ConsoleWrite('Problem installing service, Error number is ' & @error & @CRLF & ' message : ' & _WinAPI_GetLastErrorMessage()) _FileWriteLog($s_LogfilePath, 'Problem installing service, Error number is ' & @error & @CRLF & ' message : ' & _WinAPI_GetLastErrorMessage()) FileClose($s_LogfilePath) MsgBox(0x41040, $s_ScriptObject, 'Problem installing service, Error number is ' & @error & @CRLF & ' message : ' & _WinAPI_GetLastErrorMessage()) Else ;ConsoleWrite('Service installed.') _FileWriteLog($s_LogfilePath, 'Service installed.') FileClose($s_LogfilePath) MsgBox(0x41040, $s_ScriptObject, $s_ScriptObject & ' service installation complete. Reboot the PC for changes to take effect!') ExitScript() EndIf EndIf EndFunc ;==>InstallService ;~ Remove the script from 'Services.msc' Func RemoveService() If Not @Compiled Then MsgBox(0x41040, $s_ScriptObject, 'Error. Service Daemon not compiled!', 5) EndIf _Service_Stop($s_ScriptObject) _Service_Delete($s_ScriptObject) If @error Then ;ConsoleWrite('Error removing service, Error number is ' & @error & @CRLF & ' message : ' & _WinAPI_GetLastErrorMessage() & @CRLF) _FileWriteLog($s_LogfilePath, 'Error removing service, Error number is ' & @error & @CRLF & ' message : ' & _WinAPI_GetLastErrorMessage()) FileClose($s_LogfilePath) MsgBox(0x41040, $s_ScriptObject, $s_ScriptObject & 'Error removing service, Error number is ' & @error & @CRLF & ' message : ' & _WinAPI_GetLastErrorMessage()) Else ;ConsoleWrite('Service Removed.' & @CRLF) _FileWriteLog($s_LogfilePath, 'Service Removed.') FileClose($s_LogfilePath) MsgBox(0x41040, $s_ScriptObject, $s_ScriptObject & 'Service Removed.') ExitScript() EndIf EndFunc ;==>RemoveService ;~ Application functions Func TestFunction1() ;~ Show the counter progress If $s_CmdParam = '-agent' Then MsgBox(0x41040, $s_ScriptObject, $s_ScriptObject & ' log: ' & $n_Monitor, 5) If $s_CmdParam <> '-agent' Then MsgBox(0x41040, $s_ScriptObject, $s_ScriptObject & ' log: ' & $n_Evidence, 5) EndFunc ;==>TestFunction1 Func ViewServiceItem() ;~ call up 'services.msc' ShellExecute('services.msc') EndFunc ;==>ViewServiceItem ;~ About me! Func ShowInfo() MsgBox(0x41040, $s_ScriptObject, $s_ScriptObject & ' 0.3 for Windows 2000, XP or 2003. ©2009 Dave Snyder') EndFunc ;==>ShowInfo ;~ Show the logfile Func ShowLog() ShellExecute($s_LogfilePath, '', @ScriptDir & '\', 'open') EndFunc ;==>ShowLog ;~ Erase the log file Func ClearLog() Local $choice = MsgBox(0x41021, $s_ScriptObject, 'Are you sure you want to clear the log file?') If $choice = 1 Then _FileCreate($s_LogfilePath) _FileWriteLog($s_LogfilePath, 'Log file erased.') If @error > 0 Then MsgBox(0x41010, $s_ScriptObject, 'Error creating logfile!') Return Else MsgBox(0x41040, $s_ScriptObject, 'Log file has been erased.') EndIf FileClose($s_LogfilePath) EndIf EndFunc ;==>ClearLog ;- Check to see if service is running Func ServiceCtrlStatus() Local $exit = _Service_QueryStatus($s_ScriptObject) Return $exit[1] EndFunc ;==>ServiceCtrlStatus Func UserQuery() ; Generated by AutoIt Scriptomatic Local $wbemFlagReturnImmediately = 0x10 Local $wbemFlagForwardOnly = 0x20 Local $colItems = '' Local $objItem = '' Local $Output = '' Local $objWMIService = ObjGet('winmgmts:\\' & $s_LocalComputer & '\root\CIMV2') $colItems = $objWMIService.ExecQuery('SELECT * FROM Win32_ComputerSystem', 'WQL', _ $wbemFlagReturnImmediately + $wbemFlagForwardOnly) If IsObj($colItems) Then For $objItem In $colItems $Output = $objItem.UserName Next Else $Output = -1 EndIf Return $Output EndFunc ;==>UserQuery ; stop timer function. its said SCM that service is in the process of $SERVICE_STOP_PENDING Func ServiceCtrlTimer($hWnd, $msg, $iIDTimer, $dwTime) _Service_ReportStatus($SERVICE_STOP_PENDING, $NO_ERROR, $n_Counter) $n_Counter += -100 EndFunc ;==>ServiceCtrlTimer ;~ Close gracefully Func ExitCleanup() _FileWriteLog($s_LogfilePath, 'Cleaning up...') FileClose($s_LogfilePath) TraySetState(2) If Not ProcessWaitClose($pid_AgentTray, 5) Then ProcessClose($pid_AgentTray) ;If Not ProcessWaitClose($s_ScriptName, 5) Then ProcessClose($s_ScriptName) EndFunc ;==>ExitCleanup ;~ Bye now Func ExitScript() ExitCleanup() _FileWriteLog($s_LogfilePath, 'Exiting.') FileClose($s_LogfilePath) Exit EndFunc ;==>ExitScript