AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Windows Service in AHK

 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Ask for Help
View previous topic :: View next topic  
Author Message
JHa



Joined: 11 Nov 2009
Posts: 1

PostPosted: Wed Nov 11, 2009 12:22 am    Post subject: Windows Service in AHK Reply with quote

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.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Ask for Help All times are GMT
Page 1 of 1

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group