JHa
Joined: 11 Nov 2009 Posts: 1
|
Posted: Wed Nov 11, 2009 12:22 am Post subject: Windows Service in AHK |
|
|
Hi,
I try to create Windows service program in pure AHK. I follow an example at http://msdn.microsoft.com/en-us/library/bb540474%28VS.85%29.aspx and WinAPI functions related to it. Basically it works somehow, but…
1. recommended function "WaitForSingleObject" which should be at the end of main() – auto-execute section:
the service can be started and stopped from Service manager or command line but UserProgram() is “dead”.
2. AHK [Sleep, DelayInMiliseconds] does not work anywhere in the program (causing the program hang) but is the only way I have found so far (by coincidence) to end the auto-execute section of the program. In this case the UserProgram() works and also service control works.
| Code: |
;http://msdn.microsoft.com/en-us/library/bb540474%28VS.85%29.aspx
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
#SingleInstance Ignore
#NoTrayIcon
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
SetBatchLines -1
/*
typedef struct _SERVICE_STATUS {
DWORD dwServiceType;
DWORD dwCurrentState;
DWORD dwControlsAccepted;
DWORD dwWin32ExitCode;
DWORD dwServiceSpecificExitCode;
DWORD dwCheckPoint;
DWORD dwWaitHint;
}SERVICE_STATUS, *LPSERVICE_STATUS;
*/
SERVICE_AUTO_START = 0x00000002
SERVICE_BOOT_START = 0x00000000
SERVICE_DEMAND_START = 0x00000003
SERVICE_DISABLED = 0x00000004
SERVICE_SYSTEM_START = 0x00000001
SERVICE_ERROR_CRITICAL = 0x00000003
SERVICE_ERROR_IGNORE = 0x00000000
SERVICE_ERROR_NORMAL = 0x00000001
SERVICE_ERROR_SEVERE = 0x00000002
SERVICE_CONTROL_CONTINUE = 0x00000003
SERVICE_CONTROL_INTERROGATE = 0x00000004
SERVICE_CONTROL_NETBINDADD = 0x00000007
SERVICE_CONTROL_NETBINDDISABLE = 0x0000000A
SERVICE_CONTROL_NETBINDENABLE = 0x00000009
SERVICE_CONTROL_NETBINDREMOVE = 0x00000008
SERVICE_CONTROL_PARAMCHANGE = 0x00000006
SERVICE_CONTROL_PAUSE = 0x00000002
SERVICE_CONTROL_STOP = 0x00000001
SERVICE_STOPPED = 0x00000001
SERVICE_START_PENDING = 0x00000002
SERVICE_STOP_PENDING = 0x00000003
SERVICE_RUNNING = 0x00000004
SERVICE_CONTINUE_PENDING = 0x00000005
SERVICE_PAUSE_PENDING = 0x00000006
SERVICE_PAUSED = 0x00000007
SERVICE_ACTIVE = 0x00000001
SERVICE_INACTIVE = 0x00000002
SERVICE_STATE_ALL = 0x00000003
SERVICE_ACCEPT_STOP = 0x00000001
SERVICE_ACCEPT_PAUSE_CONTINUE = 0x00000002
SERVICE_ACCEPT_SHUTDOWN = 0x00000004
SERVICE_ACCEPT_PARAMCHANGE = 0x00000008
SERVICE_ACCEPT_NETBINDCHANGE = 0x00000010
SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 0x00000020
SERVICE_ACCEPT_POWEREVENT = 0x00000040
SERVICE_ACCEPT_SESSIONCHANGE = 0x00000080
SERVICE_ACCEPT_PRESHUTDOWN = 0x00000100
SERVICE_ADAPTER = 0x00000004
SERVICE_FILE_SYSTEM_DRIVER = 0x00000002
SERVICE_KERNEL_DRIVER = 0x00000001
SERVICE_RECOGNIZER_DRIVER = 0x00000008
SERVICE_WIN32_OWN_PROCESS = 0x00000010
SERVICE_WIN32_SHARE_PROCESS = 0x00000020
SERVICE_ALL_ACCESS = 0xF01FF
SERVICE_INTERROGATE = 0x0080
SERVICE_PAUSE_CONTINUE = 0x0040
SERVICE_QUERY_CONFIG = 0x0001
SERVICE_QUERY_STATUS = 0x0004
SERVICE_START = 0x0010
SERVICE_STOP = 0x0020
SERVICE_CONFIG_DESCRIPTION = 1
NO_ERROR = 0x0
VarSetCapacity(SvcStatus, 28, 0)
DesiredAccess = 0xF003F
ServiceType := SERVICE_WIN32_OWN_PROCESS
StartType := SERVICE_DEMAND_START
ErrorControl := SERVICE_ERROR_NORMAL
LoadOrderGroup =
TagId =
Dependencies =
ServiceStartName = 0
Password =
Description = AHK service test`r`nbeeps every 5 seconds
m_Name =
db_Name =
DesAccess = 0x2
Display_Name =
SSP =
Timer_1 = Timer1
s_NameValue = AHK1Service
StringTrimRight, d_NameValue, A_ScriptName, 4
StatusLogfile = %A_ScriptDir%\%d_NameValue%.log
p_NameValue = %A_ScriptDir%\%d_NameValue%.exe
If A_IsCompiled <> 1
{
msgbox, Program not compiled!
ExitApp
}
WinGetClass, class, A
If class = CabinetWClass ;ConsoleWindowClass
{
msgbox, Please run the program from command line!
Run, cmd.exe
Sleep, 1000
SendInput, "%p_NameValue%"
ExitApp
}
NumPar = %0%
LinePar = %1%
DllCall("AttachConsole"
, "int", -1)
FileAppend, `r `n, CONOUT$
If NumPar <> 0
{
If LinePar not in -i,-r,-s,-d,-m
{
FileAppend, `rDESCRIPTION:`n`n, CONOUT$
FileAppend, `r service beeps every 5 seconds`n`n, CONOUT$
FileAppend, `rUSAGE:`n`n, CONOUT$
FileAppend, `r %d_NameValue% -i install service`n, CONOUT$
FileAppend, `r %d_NameValue% -r start service`n, CONOUT$
FileAppend, `r %d_NameValue% -s stop service`n, CONOUT$
FileAppend, `r %d_NameValue% -d delete service`n, CONOUT$
FileAppend, `r %d_NameValue% -m launch Service Manager`n`n, CONOUT$
FileAppend, `r%A_ScriptDir%>, CONOUT$
ExitApp
}
}
If (NumPar <> 0 And LinePar = "-m")
{
Run, mmc services.msc
ExitApp
}
If (NumPar <> 0 And LinePar = "-i")
{
Open_SCManager(m_Name, db_Name, DesAccess)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- Open_SCManager -- HNDL: %sc_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, CONOUT$
FileAppend, %TimeString% -- Open_SCManager -- HNDL: %sc_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
If Open_Service(sc_Handle, s_NameValue, SERVICE_INTERROGATE)
{
FileAppend, `r PCLog service already installed!`n`n, CONOUT$
FileAppend, `r%A_ScriptDir%>, CONOUT$
ExitApp
}
Else
{
Create_Service(sc_Handle, s_NameValue, d_NameValue, DesiredAccess, ServiceType, StartType, ErrorControl, p_NameValue, LoadOrderGroup, TagId, Dependencies, ServiceStartName, Password)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- Create_Service -- HNDL: %s_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, CONOUT$
FileAppend, %TimeString% -- Create_Service -- HNDL: %s_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %StatusLogfile%
Description_Service(s_Handle, Description)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- Description_Service -- HNDL: %cResult% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, CONOUT$
FileAppend, %TimeString% -- Description_Service -- HNDL: %cResult% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %StatusLogfile%
Close_Service(sc_Handle, s_Handle)
FileAppend, ******************************`r`n, CONOUT$
FileAppend, ******************************`r`n, %StatusLogfile%
FileAppend, `r%A_ScriptDir%>, CONOUT$
ExitApp
}
}
If (NumPar <> 0 And LinePar = "-r")
{
Open_SCManager(m_Name, db_Name, DesAccess)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- Open_SCManager -- HNDL: %sc_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, CONOUT$
FileAppend, %TimeString% -- Open_SCManager -- HNDL: %sc_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
Open_Service(sc_Handle, s_NameValue, SERVICE_START)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- Open_Service -- HNDL: %s_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, CONOUT$
FileAppend, %TimeString% -- Open_Service -- HNDL: %s_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
Start_Service(s_Handle)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- Start_Service -- HNDL: %cResult% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, CONOUT$
FileAppend, %TimeString% -- Start_Service -- HNDL: %cResult% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
FileAppend, ******************************`r`n, CONOUT$
FileAppend, ******************************`r`n, %StatusLogfile%
FileAppend, `r%A_ScriptDir%>, CONOUT$
ExitApp
}
If (NumPar <> 0 And LinePar = "-s")
{
Open_SCManager(m_Name, db_Name, DesAccess)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- Open_SCManager -- HNDL: %sc_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, CONOUT$
FileAppend, %TimeString% -- Open_SCManager -- HNDL: %sc_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
Open_Service(sc_Handle, s_NameValue, SERVICE_STOP)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- Open_Service -- HNDL: %s_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, CONOUT$
FileAppend, %TimeString% -- Open_Service -- HNDL: %s_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
Stop_Service(s_Handle)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- Stop_Service -- HNDL: %cResult% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, CONOUT$
FileAppend, %TimeString% -- Stop_Service -- HNDL: %cResult% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
FileAppend, ******************************`r`n, CONOUT$
FileAppend, ******************************`r`n, %StatusLogfile%
FileAppend, `r%A_ScriptDir%>, CONOUT$
ExitApp
}
If (NumPar <> 0 And LinePar = "-d")
{
Open_SCManager(m_Name, db_Name, DesAccess)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- Open_SCManager -- HNDL: %sc_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, CONOUT$
FileAppend, %TimeString% -- Open_SCManager -- HNDL: %sc_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
Open_Service(sc_Handle, s_NameValue, SERVICE_ALL_ACCESS)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- Open_Service -- HNDL: %s_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, CONOUT$
FileAppend, %TimeString% -- Open_Service -- HNDL: %s_Handle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
Delete_Service(s_Handle)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- Delete_Service -- HNDL: %cResult% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, CONOUT$
FileAppend, %TimeString% -- Delete_Service -- HNDL: %cResult% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
FileAppend, ******************************`r`n, CONOUT$
FileAppend, ******************************`r`n, %StatusLogfile%
FileAppend, `r%A_ScriptDir%>, CONOUT$
ExitApp
}
If (NumPar = 0 And class = "ConsoleWindowClass")
{
FileAppend, `rDESCRIPTION:`n`n, CONOUT$
FileAppend, `r service beeps every 5 seconds`n`n, CONOUT$
FileAppend, `rUSAGE:`n`n, CONOUT$
FileAppend, `r %d_NameValue% -i install service`n, CONOUT$
FileAppend, `r %d_NameValue% -r start service`n, CONOUT$
FileAppend, `r %d_NameValue% -s stop service`n, CONOUT$
FileAppend, `r %d_NameValue% -d delete service`n, CONOUT$
FileAppend, `r %d_NameValue% -m launch Service Manager`n`n, CONOUT$
FileAppend, `r%A_ScriptDir%>, CONOUT$
ExitApp
}
VarSetCapacity(DispatchTable, 16, 0)
SvcMainAddress := RegisterCallback("SvcMain")
NumPut(&s_NameValue, DispatchTable, 0)
NumPut(SvcMainAddress, DispatchTable, 4)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- RegisterCallback -- HNDL: %SvcMainAddress% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
cResult := DllCall("Advapi32\StartServiceCtrlDispatcherA"
, "UInt", &DispatchTable)
Exit
;}
SvcMain(dwArgc = 0, lpszArgv = 0)
{
Global
Critical
SvcCtrlHandlerAddress := RegisterCallback("SvcCtrlHandler")
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- Callback: CtrlHandlerAddr -- HNDL: %SvcCtrlHandlerAddress% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
SvcStatusHandle := DllCall("Advapi32\RegisterServiceCtrlHandlerA"
, "Str", s_NameValue
, "UInt", SvcCtrlHandlerAddress)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- RegisterServiceCtrlHandler -- HNDL: %SvcStatusHandle% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
NumPut(SERVICE_WIN32_OWN_PROCESS, SvcStatus, 0)
NumPut(0, SvcStatus, 16)
ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000)
hSvcStopEvent := DllCall("CreateEventA"
, "UInt", 0
, "UInt", TRUE
, "UInt", FALSE
, "UInt", 0)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- CreateEvent -- HNDL: %hSvcStopEvent% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
If hSvcStopEvent = 0
{
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0)
Return
}
ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0)
;Auto-execute Section
ProgAddr := RegisterCallback("UserProg")
DllCall("User32.dll\SetTimer"
, "UInt", 0
, "UInt", Timer_1
, "UInt", 5000
, "UInt", ProgAddr)
SoundBeep, 523, 50
SoundBeep, 30000, 50
SoundBeep, 587, 50
SoundBeep, 30000, 50
SoundBeep, 698, 100
;cResult := DllCall("WaitForSingleObject"
; , "UInt", hSvcStopEvent
; , "UInt", -1)
;ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0)
Sleep, 10000 ;
;Exit
;Return
}
UserProg()
{
Global
Critical
SoundBeep, 1000, 50 ;Does not work if not Critical
;Sleep, 500 ;Does not work
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- UserProg -- HNDL: %cResult% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
Return
}
ReportSvcStatus(CurrentState, Win32ExitCode, WaitHint)
{
Global
Static CheckPoint = 1
; SERVICE_STATUS structure.
NumPut(CurrentState, SvcStatus, 4)
NumPut(Win32ExitCode, SvcStatus, 12)
NumPut(WaitHint, SvcStatus, 24)
If (CurrentState = SERVICE_START_PENDING)
NumPut(0, SvcStatus, 8)
Else
NumPut(SERVICE_ACCEPT_STOP, SvcStatus, 8)
If (CurrentState = SERVICE_RUNNING Or CurrentState = SERVICE_STOPPED)
NumPut(0, SvcStatus, 20)
Else
NumPut(NumGet(SvcStatus, 20)+1, SvcStatus, 20)
cResult := DllCall("Advapi32\SetServiceStatus"
, "UInt", SvcStatusHandle
, "UInt", &SvcStatus)
CurState := NumGet(SvcStatus, 4)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- SetServiceStatus -- HNDL: %cResult% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
Return
}
SvcCtrlHandler(dwCtrl)
{
Global
Critical
If (dwCtrl = SERVICE_CONTROL_STOP)
{
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0)
CurrentState := NumGet(SvcStatus, 4)
FormatTime, TimeString, , HH:mm:ss
FileAppend, %TimeString% -- SvcCtrlHandler -- CTRL: %dwCtrl% LErr: %A_LastError% ELev: %ErrorLevel%`r`n, %Statuslogfile%
SoundBeep, 698, 50
SoundBeep, 30000, 50
SoundBeep, 587, 50
SoundBeep, 30000, 50
SoundBeep, 523, 100
cResult := DllCall("SetEvent"
, "UInt", hSvcStopEvent)
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0)
Return
}
Return
}
SvcReportEvent(ByRef szFunction)
{
Global
hEventSource = DllCall("Advapi32/RegisterEventSourceA"
, "UInt", 0
, "Str", s_Name)
If hEventSource <> 0
{
DllCall("Advapi32/ReportEvent"
, "UInt", hEventSource
, "UInt", EVENTLOG_ERROR_TYPE
, "UInt", 0
, "UInt", SVC_ERROR
, "UInt", NULL
, "UInt", 2
, "UInt", 0
, "UInt", lpszStrings
, "UInt", NULL)
DllCall("Advapi32/DeregisterEventSource"
, "UInt", hEventSource)
}
Return
}
Open_SCManager(m_Name, db_Name, DesAccess)
{
Global sc_Handle
sc_Handle := DllCall("Advapi32\OpenSCManagerA"
,"Str", m_Name
,"UInt", db_Name
,"Uint", DesAccess)
Return sc_Handle
}
Create_Service(sc_Handle, s_Name, d_Name, DesiredAccess, ServiceType, StartType, ErrorControl, p_Name, LoadOrderGroup = 0, TagId = 0, Dependencies = "", ServiceStartName = 0, Password = "")
{
Global s_Handle
s_Handle := DllCall("Advapi32\CreateServiceA"
,"UInt", sc_Handle
,"Str", s_Name
,"Str", d_Name
,"UInt", DesiredAccess
,"Uint", ServiceType
,"UInt", StartType
,"UInt", ErrorControl
,"Str", p_Name
,"UInt", LoadOrderGroup
,"UInt", TagId
,"Str", Dependencies
,"UInt", ServiceStartName
,"Str", Password)
Return s_Handle
}
Start_Service(s_Handle)
{
Global
cResult := DllCall("Advapi32\StartServiceA"
, "Uint", s_Handle
, "Uint", 0
, "Str", "")
Return cResult
}
Stop_Service(s_Handle)
{
Global
VarSetCapacity(@SSP, 36)
cResult := DllCall("Advapi32\ControlService"
, "Uint", s_Handle
, "Uint", 0x1
, "Uint", &@SSP)
Return cResult
}
Delete_Service(s_Handle)
{
Global
cResult := DllCall("Advapi32\DeleteService"
, "Uint", s_Handle)
Return cResult
}
Description_Service(s_Handle, Description)
{
Global
cResult := DllCall("Advapi32\ChangeServiceConfig2A"
, "UInt", s_Handle
, "UInt", 1
, "Str*", Description)
Return cResult
}
Open_Service(sc_Handle, s_NameValue, DesAccess)
{
Global
s_Handle := DllCall("Advapi32\OpenServiceA"
, "UInt", sc_Handle
, "Str", s_NameValue
, "UInt", DesAccess )
Return s_Handle
}
Close_Service(sc_Handle, s_Handle)
{
DllCall("Advapi32\CloseServiceHandle"
, "Uint", s_Handle)
DllCall("Advapi32\CloseServiceHandle"
, "Uint", sc_Handle)
Return
}
|
Is there any idea what is wrong? Same user program with [Sleep] works fine with ServiceEx.exe mentioned at http://www.autohotkey.com/forum/topic50111.html&highlight=service
Help is kindly appreciated. |
|