NTService

Share your advanced PureBasic knowledge/code with the community.
reisve
User
User
Posts: 86
Joined: Sat Nov 08, 2008 4:52 pm
Location: Luanda, Angola

NTService

Post by reisve »

I've been strugling with examples and libs to get a service working "properly", specialy the notify part of it. So I get a source of a service I found here and adapted it. It is working fine, reporting the service status changing correctely. (cleanned up the code a little bit too :) )

I left the code as I tested it, so you can changed (or improve it) as you wish.

The only thing untested is the ErrorHandling procedure. Couldn't find a way to produce a failure in the API functions Calls.

Another Piece of code related with services, is the creation/remove/start/stop a service from an external application (a setup App?). On this one I changed the constant names to not conflict with some allready existent libs. But you can change them back. Also commented out the constants not used, so if you want can remove them to make the code smaller

I hope This will help some one

The service

Code: Select all

; Apr. 12, 2003 
; Converted to PB by Richard Eikeland
; Jan. 3, 2009
; Changed by Virginio Reis (reisve) 
; This code is posted as is with out any waranties. 
; 
#SERVICE_WIN32_OWN_PROCESS = $10 
#SERVICE_WIN32_SHARE_PROCESS = $20 
#SERVICE_WIN32 = #SERVICE_WIN32_OWN_PROCESS + #SERVICE_WIN32_SHARE_PROCESS 
#SERVICE_ACCEPT_STOP = $1 
#SERVICE_ACCEPT_PAUSE_CONTINUE = $2 
#SERVICE_ACCEPT_SHUTDOWN = $4 
#SC_MANAGER_CONNECT = $1 
#SC_MANAGER_CREATE_SERVICE = $2 
#SC_MANAGER_ENUMERATE_SERVICE = $4 
#SC_MANAGER_LOCK = $8 
#SC_MANAGER_QUERY_LOCK_STATUS = $10 
#SC_MANAGER_MODIFY_BOOT_CONFIG = $20 

#STANDARD_RIGHTS_REQUIRED = $F0000 
#SERVICE_QUERY_CONFIG = $1 
#SERVICE_CHANGE_CONFIG = $2 
#SERVICE_QUERY_STATUS = $4 
#SERVICE_ENUMERATE_DEPENDENTS = $8 
#SERVICE_START = $10 
#SERVICE_STOP = $20 
#SERVICE_PAUSE_CONTINUE = $40 
#SERVICE_INTERROGATE = $80 
#SERVICE_USER_DEFINED_CONTROL = $100 
#SERVICE_ALL_ACCESS = #STANDARD_RIGHTS_REQUIRED | #SERVICE_QUERY_CONFIG | #SERVICE_CHANGE_CONFIG | #SERVICE_QUERY_STATUS | #SERVICE_ENUMERATE_DEPENDENTS | #SERVICE_START | #SERVICE_STOP | #SERVICE_PAUSE_CONTINUE | #SERVICE_INTERROGATE |#SERVICE_USER_DEFINED_CONTROL 

#SERVICE_DEMAND_START = $3 
#SERVICE_ERROR_NORMAL = $1 

;- SERVICE_CONTROL 
#SERVICE_CONTROL_STOP = $1 
#SERVICE_CONTROL_PAUSE = $2 
#SERVICE_CONTROL_CONTINUE = $3 
#SERVICE_CONTROL_INTERROGATE = $4 
#SERVICE_CONTROL_SHUTDOWN = $5 


;-SERVICE_STATE 
#SERVICE_STOPPED = $1 
#SERVICE_START_PENDING = $2 
#SERVICE_STOP_PENDING = $3 
#SERVICE_RUNNING = $4 
#SERVICE_CONTINUE_PENDING = $5 
#SERVICE_PAUSE_PENDING = $6 
#SERVICE_PAUSED = $7 

Global ServiceStatus.SERVICE_STATUS
Global hServiceStatus.l
Global SERVICE_NAME.s
Global Finish.l
Global sThread.l
Global rThread.l
Global UserFunction
Global NotifyFunction

Declare Handler(fdwControl.l) 
Declare ServiceMain(dwArgc.l, lpszArgv.l) 

Procedure ServiceFunction()
    OpenFile(0, "c:\MyTestSvc.txt")
    FileSeek (0, Lof(0) )
    WriteStringN(0, "Service Function is Running")
    CloseFile(0)
EndProcedure

Procedure ServiceNotify(Parameter)
    OpenFile(0, "c:\MyTestSvc.txt")
    FileSeek (0, Lof(0) )
    WriteStringN(0, "Service Status - " + Str(Parameter))
    CloseFile(0)
EndProcedure

Procedure ErrorHandler()
    Result = GetLastError_() 
    OpenFile(0, "c:\MyTestSvc.txt")
    FileSeek (0, Lof(0) )
    WriteStringN(0, "Error Ocurred - " + Str(Result))
    CloseFile(0)

    Finish = 1
    ServiceStatus\dwCurrentState = #SERVICE_STOP_PENDING 
    SetServiceStatus_(hServiceStatus, ServiceStatus) 
    ServiceStatus\dwCurrentState = #SERVICE_STOPPED 
    SetServiceStatus_(hServiceStatus, ServiceStatus) 
EndProcedure

Procedure RunService(svcName.s, usrFunction, nfyFunction) 
    hSCManager.l 
    hService.l 
    ServiceTableEntry.SERVICE_TABLE_ENTRY 
    b.l 
    cmd.s 
    SERVICE_NAME =  svcName
    UserFunction = usrFunction
    NotifyFunction = nfyFunction
    *sname.s = SERVICE_NAME 
    ServiceTableEntry\lpServiceName = @SERVICE_NAME 
    ServiceTableEntry\lpServiceProc = @ServiceMain() 
    b = StartServiceCtrlDispatcher_(@ServiceTableEntry) 
    If b = 0 : ErrorHandler() : EndIf
    Repeat : Until Finish =1 : End
EndProcedure 

Procedure Handler(fdwControl.l) 
    b.l 
    id.l
    Select fdwControl 
        Case #SERVICE_CONTROL_PAUSE 
            ServiceStatus\dwCurrentState = #SERVICE_PAUSED 
        Case #SERVICE_CONTROL_CONTINUE 
            ServiceStatus\dwCurrentState = #SERVICE_RUNNING 
        Case #SERVICE_CONTROL_STOP 
            ServiceStatus\dwWin32ExitCode = 0 
            ServiceStatus\dwCurrentState = #SERVICE_STOP_PENDING 
            ServiceStatus\dwCheckPoint = 0 
            ServiceStatus\dwWaitHint = 0 
            b = SetServiceStatus_(hServiceStatus, ServiceStatus) 
            If b = 0 : ErrorHandler() : EndIf
            Finish = 1 
            ServiceStatus\dwCurrentState = #SERVICE_STOPPED 
        Case #SERVICE_CONTROL_INTERROGATE 
    EndSelect 
    sThread = CreateThread_(0, 0, NotifyFunction, ServiceStatus\dwCurrentState, 0, @id )
    If sThread = #Null : ErrorHandler() : EndIf
    WaitThread(sThread)
    b = SetServiceStatus_(hServiceStatus, ServiceStatus) 
    If b = 0: ErrorHandler() : EndIf
EndProcedure 

Procedure ServiceMain(dwArgc.l, lpszArgv.l) 
    b.l
    id.l
    ServiceStatus\dwServiceType = #SERVICE_WIN32_OWN_PROCESS 
    ServiceStatus\dwCurrentState = #SERVICE_START_PENDING 
    ServiceStatus\dwControlsAccepted = #SERVICE_ACCEPT_STOP | #SERVICE_ACCEPT_PAUSE_CONTINUE | #SERVICE_ACCEPT_SHUTDOWN 
    ServiceStatus\dwWin32ExitCode = 0 
    ServiceStatus\dwServiceSpecificExitCode = 0 
    ServiceStatus\dwCheckPoint = 0 
    ServiceStatus\dwWaitHint = 0 
    hServiceStatus = RegisterServiceCtrlHandler_(SERVICE_NAME, @Handler()) 
    ServiceStatus\dwCurrentState = #SERVICE_START_PENDING 
    b = SetServiceStatus_(hServiceStatus, ServiceStatus) 
    If b = 0 : ErrorHandler() : EndIf
    hThread = CreateThread_(0, 0, UserFunction, 0, 0, @id)
    If hThread = #Null : ErrorHandler() : EndIf
    ServiceStatus\dwCurrentState = #SERVICE_RUNNING 
    rThread = CreateThread_(0, 0, NotifyFunction, ServiceStatus\dwCurrentState, 0, @id)
    If rThread = #Null : ErrorHandler() : EndIf
    WaitThread(rThread)
    b = SetServiceStatus_(hServiceStatus, ServiceStatus) 
    If b = 0 : ErrorHandler() : EndIf
EndProcedure 

RunService("MyTestSvc", @ServiceFunction(), @ServiceNotify()) 
Create/remove/start/stop a service

Code: Select all

; ** Access Rights for SCM
#SCM_ALL_ACCESS = $F003F                ;Includes STANDARD_RIGHTS_REQUIRED, in addition To all access rights in this table. 
;#SCM_CREATE_SERVICE = $0002             ;Required To call the CreateService function To create a service object And add it To the database. 
;#SCM_CONNECT = $0001                    ;Required To connect To the service Control manager. 
;#SCM_ENUMERATE_SERVICE = $0004          ;Required To call the EnumServicesStatusEx function To list the services that are in the database. 
;#SCM_LOCK = $0008                       ;Required To call the LockServiceDatabase function To acquire a lock on the database. 
;#SCM_MODIFY_BOOT_CONFIG = $0020         ;Required To call the NotifyBootConfigStatus function. 
;#SCM_QUERY_LOCK_STATUS = $0010 

; ** Access Rights for Services
#SVC_ALL_ACCESS = $F01FF                ; Includes STANDARD_RIGHTS_REQUIRED in addition To all access rights in This table. 
;#SVC_CHANGE_CONFIG = $0002              ; Required To call The ChangeServiceConfig Or ChangeServiceConfig2 function To change The service configuration. Because This grants The caller The right To change The executable file that The system runs, it should be granted only To administrators.  
;#SVC_ENUMERATE_DEPENDENTS = $0008       ; Required To call The EnumDependentServices function To enumerate all The services dependent on The service. 
;#SVC_INTERROGATE = $0080                ; Required To call The ControlService function To ask The service To report its Status immediately. 
;#SVC_PAUSE_CONTINUE = $0040             ; Required To call The ControlService function To pause Or Continue The service. 
;#SVC_QUERY_CONFIG = $0001               ; Required To call The QueryServiceConfig And QueryServiceConfig2 functions To query The service configuration. 
;#SVC_QUERY_STATUS = $0004               ; Required To call The QueryServiceStatusEx function To ask The service Control manager about The Status of The service. 
;#SVC_START = $0010                      ; Required To call The StartService function To start The service. 
;#SVC_STOP = $0020                       ; Required To call The ControlService function To stop The service. 

; ** Create Service service types
;#SVC_FILE_SYSTEM_DRIVER = $00000002     ; File system driver service.
;#SVC_KERNEL_DRIVER = $00000001          ; driver service.
#SVC_WIN32_OWN_PROCESS = $00000010      ; service that runs in its own process.
;#SVC_WIN32_SHARE_PROCESS = $00000020    ; service that shares a process With one Or more other services
#SVC_INTERACTIVE_PROCESS = $00000100    ; The service can interact With The desktop. 
    
; ** Service Start Type
#SVC_AUTO_START = $00000002             ; A service started automatically by The service Control manager during system startup
;#SVC_BOOT_START = $00000000             ; A device driver started by The system loader. This Value is valid only For driver services.
;#SVC_DEMAND_START = $00000003           ; A service started by The service Control manager when A process calls The StartService function.
;#SVC_DISABLED = $00000004               ; A service that cannot be started. Attempts To start The service Result in The error code ERROR_SVC_DISABLED.
;#SVC_SYSTEM_START = $00000001           ; A device driver started by the IoInitSystem function. This value is valid only for driver services.

; ** Create Service error control
;#SVC_ERROR_CRITICAL = $00000003         ; The startup program logs The error in The event log, If possible. 
                                        ; If The last-known-good configuration is being started, The startup operation fails. 
                                        ; Otherwise, The system is restarted With The last-known good configuration.
;#SVC_ERROR_IGNORE = $00000000           ; The startup program ignores The error And continues The startup operation.
#SVC_ERROR_NORMAL = $00000001           ; The startup program logs The error in The event log but continues The startup operation.
;#SVC_ERROR_SEVERE = $00000002           ; The startup program logs The error in The event log. If The last-known-good configuration is being started, 
                                        ; The startup operation continues. Otherwise, The system is restarted With The last-known-good configuration.
                    
; ** Service Control Codes
;#SVC_CONTROL_CONTINUE = $00000003       ; Notifies a paused service that it should resume. The hService handle must have The SERVICE_PAUSE_CONTINUE access right.
;#SVC_CONTROL_PAUSE = $00000002          ; Notifies a service that it should pause. The hService handle must have The SERVICE_PAUSE_CONTINUE access right.
#SVC_CONTROL_STOP = $00000001           ; Notifies a service that it should stop. The hService handle must have The SERVICE_STOP access right. 
        
        
; ** Service Status
;#SERVICE_CONTINUE_PENDING = $00000005   ; The service Continue is pending.
;#SERVICE_PAUSE_PENDING = $00000006      ; The service pause is pending.
;#SERVICE_PAUSED = $00000007             ; The service is paused.
#SERVICE_RUNNING = $00000004            ; The service is running.
;#SERVICE_START_PENDING = $00000002      ; The service is Starting.
;#SERVICE_STOP_PENDING = $00000003       ; The service is stopping.
#SERVICE_STOPPED = $00000001            ; The service is Not running.

; ** Error Codes
;#ERROR_ACCESS_DENIED = $5               ; The requested access was denied.
;#ERROR_CIRCULAR_DEPENDENCY = $423       ; A circular service dependency was specified.
;#ERROR_DUPLICATE_SERVICE_NAME = $436    ; The display name already exists in The service Control manager database either As A service name Or As another display name.
;#ERROR_INVALID_HANDLE = $6              ; The specified handle is invalid on closing SCM handler.
;#ERROR_INVALID_NAME = $7B               ; The specified service name is invalid.
;#ERROR_INVALID_PARAMETER = $57          ; A parameter that was specified is invalid.
;#ERROR_INVALID_SERVICE_ACCOUNT = $421   ; The user account name specified in The lpServiceStartName parameter does Not exist.
;#ERROR_SERVICE_EXISTS = $431            ; The specified service already exists in This database.
;#ERROR_ACCESS_DENIED = $5               ; The requested access was denied.
;#ERROR_INVALID_HANDLE = $6              ; The specified handle is invalid on closing SCM handler.
;#ERROR_PATH_NOT_FOUND = $3              ; The service binary file could Not be found.
;#ERROR_SERVICE_ALREADY_RUNNING = $420   ; An instance of The service is already running.
;#ERROR_SERVICE_DATABASE_LOCKED = $41F   ; The database is locked.
;#ERROR_SERVICE_DEPENDENCY_DELETED = $433 ; The service depends on a service that does Not exist Or has been marked For deletion.
;#ERROR_SERVICE_DEPENDENCY_FAIL = $42C   ; The service depends on another service that has failed To start.
;#ERROR_SERVICE_DISABLED = $422          ; The service has been disabled.
;#ERROR_SERVICE_LOGON_FAILED = $42D      ; The service did Not start due To a logon failure. This error occurs If The service is configured To run under An account that does Not have The "Log on as a service" right.
;#ERROR_SERVICE_MARKED_FOR_DELETE = $430 ; The service has been marked For deletion.
;#ERROR_SERVICE_NO_THREAD = $41E         ; A thread could Not be created For The service.
;#ERROR_SERVICE_REQUEST_TIMEOUT = $41D   ; The process For The service was started, but it did Not call StartServiceCtrlDispatcher, Or The thread that called StartServiceCtrlDispatcher may be blocked in a Control handler function.
;#ERROR_DATABASE_DOES_NOT_EXIST = $429   ; The specified database does Not exist.
;#ERROR_INVALID_PARAMETER =$57           ; A specified parameter is invalid.

Structure Status
    dwServiceType.l  
    dwCurrentState.l 
    dwControlsAccepted.l  
    dwWin32ExitCode.l  
    dwServiceSpecificExitCode.l  
    dwCheckPoint.l  
    dwWaitHint.l
EndStructure
service.Status
    
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                        Vars                              ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Global lpMachineName = #Null
Global lpDatabaseName = #Null
Global scmDesiredAccess = #SCM_ALL_ACCESS
Global lpServiceName.s = "MyTestSvc"
Global lpDisplayName.s = "MyTestSvc Display Name"
Global svcDesiredAccess = #SVC_ALL_ACCESS
Global dwServiceType = #SVC_WIN32_OWN_PROCESS|#SVC_INTERACTIVE_PROCESS
Global dwStartType = #SVC_AUTO_START
Global dwErrorControl = #SVC_ERROR_NORMAL
Global lpBinaryPathName.s = Chr(34) + "c:\program files\nettax\interface\MyTestSvc.exe" + Chr(34)
Global lpLoadOrderGroup = #Null
Global lpdwTagId = #Null
Global lpDependencies = #Null
Global lpServiceStartName = #Null
Global lpPassword = #Null
Global dwNumServiceArgs = #Null
Global lpServiceArgVectors = #Null
Global dwControl = #SVC_CONTROL_STOP

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                        Processing Code                   ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Procedure StartSvc()
    scmHandler = OpenSCManager_(lpMachineName, lpDatabaseName, scmDesiredAccess)
    If scmHandler = #Null
        LastError = GetLastError_()
        MessageRequester("ERROR", "Open SCM failed with error " + Str(LastError))
    Else 
        svcHandler = OpenService_(scmHandler, lpServiceName, svcDesiredAccess)
        If svcHandler = #Null
            LastError = GetLastError_()
            MessageRequester("ERROR", "Open Service failed with error " + Str(LastError))
        Else
            Result = QueryServiceStatus_(svcHandler, service.Status)
            If Result = 0
                LastError = GetLastError_()
                MessageRequester("", "Query failed with error " + Str(LastError))
            Else
                If Not service\dwCurrentState = #SERVICE_RUNNING 
                    Result = StartService_(svcHandler, dwNumServiceArgs, lpServiceArgVectors)
                    If Result = 0
                        LastError = GetLastError_()
                        MessageRequester("ERROR", "Start Service failed with error " + Str(LastError))
                    Else
                        WatchDog = 0
                        Repeat
                            QueryServiceStatus_(svcHandler, service.Status)
                            Delay(1000)
                            WatchDog = WatchDog +1
                        Until service\dwCurrentState = #SERVICE_RUNNING Or WatchDog => 10
                        If service\dwCurrentState <> #SERVICE_RUNNING And WatchDog => 10
                            MessageRequester("ERROR", "Could not Start the Service")
                        EndIf
                    EndIf
                EndIf
            EndIf
        EndIf
        Result = CloseServiceHandle_(svcHandler)
    EndIf
    Result = CloseServiceHandle_(scmHandler)
EndProcedure

Procedure CreateSvc()
    scmHandler = OpenSCManager_(lpMachineName, lpDatabaseName, scmDesiredAccess)
    If scmHandler = #Null
        LastError = GetLastError_()
        MessageRequester("ERROR", "Open SCM failed with error " + Str(LastError))
    Else 
        svcHandler = CreateService_(scmHandler, lpServiceName, lpDisplayName, svcDesiredAccess, dwServiceType, dwStartType, dwErrorControl, lpBinaryPathName, lpLoadOrderGroup, lpdwTagId, lpDependencies, lpServiceStartName, lpPassword)
        If svcHandler = #Null
            LastError = GetLastError_()
            MessageRequester("ERROR", "Create Service failed with error " + Str(LastError))
        Else
            StartSvc()
        EndIf    
        Result = CloseServiceHandle_(svcHandler)
    EndIf
    Result = CloseServiceHandle_(scmHandler)
EndProcedure

Procedure StopSvc()
    scmHandler = OpenSCManager_(lpMachineName, lpDatabaseName, scmDesiredAccess)
    If scmHandler = #Null
        LastError = GetLastError_()
        MessageRequester("ERROR", "Open SCM failed with error " + Str(LastError))
    Else 
        svcHandler = OpenService_(scmHandler, lpServiceName, svcDesiredAccess)
        If svcHandler = #Null
            LastError = GetLastError_()
            MessageRequester("ERROR", "Open Service failed with error " + Str(LastError))
        Else
            Result = QueryServiceStatus_(svcHandler, service.Status)
            If Result = 0
                LastError = GetLastError_()
                MessageRequester("", "Query failed with error " + Str(LastError))
            Else
                If Not service\dwCurrentState = #SERVICE_STOPPED
                    Result = ControlService_(svcHandler, dwControl, service.Status)
                    If Result = 0
                        LastError = GetLastError_()
                        MessageRequester("", "Control Service failed with error " + Str(LastError))
                    Else
                        WatchDog = 0
                        Repeat
                            QueryServiceStatus_(svcHandler, service.Status)
                            Delay(1000)
                            WatchDog = WatchDog +1
                        Until service\dwCurrentState = #SERVICE_STOPPED Or WatchDog => 10
                        If service\dwCurrentState <> #SERVICE_STOPPED And WatchDog => 10
                            MessageRequester("ERROR", "Could not Stop the Service")
                        EndIf
                    EndIf
                EndIf
            EndIf
        EndIf
        Result = CloseServiceHandle_(svcHandler)
    EndIf
    Result = CloseServiceHandle_(scmHandler)
EndProcedure

Procedure RemoveSvc()
    scmHandler = OpenSCManager_(lpMachineName, lpDatabaseName, scmDesiredAccess)
    If scmHandler = #Null
        LastError = GetLastError_()
        MessageRequester("ERROR", "Open SCM failed with error " + Str(LastError))
    Else 
        svcHandler = OpenService_(scmHandler, lpServiceName, svcDesiredAccess)
        If svcHandler = #Null
            LastError = GetLastError_()
            MessageRequester("ERROR", "Open Service failed with error " + Str(LastError))
        Else
            StopSvc()
        EndIf
        Result = DeleteService_(svcHandler)
        If Result = 0
            LastError = GetLastError_()
            MessageRequester("", "Delete Service failed with error " + Str(LastError))
        EndIf
        Result = CloseServiceHandle_(svcHandler)
    EndIf
    Result = CloseServiceHandle_(scmHandler)
EndProcedure

CreateSvc()
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

Very nice, I'll have to play around with this later.

One note though, where possible you should use the proper WinAPI constants, reason for this is that when/if constants are added to PureBasic the programmers can just delete these lines from the source thus reducing the size of the source code.
reisve
User
User
Posts: 86
Joined: Sat Nov 08, 2008 4:52 pm
Location: Luanda, Angola

Post by reisve »

Thanks

I was thinking in the procedures names and endup doing it for the costantes too. Will post an updated version, which will include also a way to set the service description when creating a service
reisve
User
User
Posts: 86
Joined: Sat Nov 08, 2008 4:52 pm
Location: Luanda, Angola

Post by reisve »

Updated. when create a service, writes a desvription of the service

Create/remove/start/stop

Code: Select all

; ** Access Rights for SCM
#SC_MANAGER_ALL_ACCESS = $F003F                ;Includes STANDARD_RIGHTS_REQUIRED, in addition To all access rights in this table. 
;#SC_MANAGER_CREATE_SERVICE = $0002             ;Required To call the CreateService function To create a service object And add it To the database. 
;#SC_MANAGER_CONNECT = $0001                    ;Required To connect To the service Control manager. 
;#SC_MANAGER_ENUMERATE_SERVICE = $0004          ;Required To call the EnumServicesStatusEx function To list the services that are in the database. 
;#SC_MANAGER_LOCK = $0008                       ;Required To call the LockServiceDatabase function To acquire a lock on the database. 
;#SC_MANAGER_MODIFY_BOOT_CONFIG = $0020         ;Required To call the NotifyBootConfigStatus function. 
;#SC_MANAGER_QUERY_LOCK_STATUS = $0010 

; ** Access Rights for Services
#SERVICE_ALL_ACCESS = $F01FF                ; Includes STANDARD_RIGHTS_REQUIRED in addition To all access rights in This table. 
;#SERVICE_CHANGE_CONFIG = $0002              ; Required To call The ChangeServiceConfig Or ChangeServiceConfig2 function To change The service configuration. Because This grants The caller The right To change The executable file that The system runs, it should be granted only To administrators.  
;#SERVICE_ENUMERATE_DEPENDENTS = $0008       ; Required To call The EnumDependentServices function To enumerate all The services dependent on The service. 
;#SERVICE_INTERROGATE = $0080                ; Required To call The ControlService function To ask The service To report its Status immediately. 
;#SERVICE_PAUSE_CONTINUE = $0040             ; Required To call The ControlService function To pause Or Continue The service. 
;#SERVICE_QUERY_CONFIG = $0001               ; Required To call The QueryServiceConfig And QueryServiceConfig2 functions To query The service configuration. 
;#SERVICE_QUERY_STATUS = $0004               ; Required To call The QueryServiceStatusEx function To ask The service Control manager about The Status of The service. 
;#SERVICE_START = $0010                      ; Required To call The StartService function To start The service. 
;#SERVICE_STOP = $0020                       ; Required To call The ControlService function To stop The service. 

; ** Create Service service types
;#SERVICE_FILE_SYSTEM_DRIVER = $00000002     ; File system driver service.
;#SERVICE_KERNEL_DRIVER = $00000001          ; driver service.
#SERVICE_WIN32_OWN_PROCESS = $00000010      ; service that runs in its own process.
;#SERVICE_WIN32_SHARE_PROCESS = $00000020    ; service that shares a process With one Or more other services
#SERVICE_INTERACTIVE_PROCESS = $00000100    ; The service can interact With The desktop. 
    
; ** Service Start Type
#SERVICE_AUTO_START = $00000002             ; A service started automatically by The service Control manager during system startup
;#SERVICE_BOOT_START = $00000000             ; A device driver started by The system loader. This Value is valid only For driver services.
;#SERVICE_DEMAND_START = $00000003           ; A service started by The service Control manager when A process calls The StartService function.
;#SERVICE_DISABLED = $00000004               ; A service that cannot be started. Attempts To start The service Result in The error code ERROR_SVC_DISABLED.
;#SERVICE_SYSTEM_START = $00000001           ; A device driver started by the IoInitSystem function. This value is valid only for driver services.

; ** Create Service error control
;#SERVICE_ERROR_CRITICAL = $00000003         ; The startup program logs The error in The event log, If possible. 
                                        ; If The last-known-good configuration is being started, The startup operation fails. 
                                        ; Otherwise, The system is restarted With The last-known good configuration.
;#SERVICE_ERROR_IGNORE = $00000000           ; The startup program ignores The error And continues The startup operation.
#SERVICE_ERROR_NORMAL = $00000001           ; The startup program logs The error in The event log but continues The startup operation.
;#SERVICE_ERROR_SEVERE = $00000002           ; The startup program logs The error in The event log. If The last-known-good configuration is being started, 
                                        ; The startup operation continues. Otherwise, The system is restarted With The last-known-good configuration.
                    
; ** Service Control Codes
#SERVICE_CONTROL_CONTINUE = $00000003       ; Notifies a paused service that it should resume. The hService handle must have The SERVICE_PAUSE_CONTINUE access right.
#SERVICE_CONTROL_PAUSE = $00000002          ; Notifies a service that it should pause. The hService handle must have The SERVICE_PAUSE_CONTINUE access right.
#SERVICE_CONTROL_STOP = $00000001           ; Notifies a service that it should stop. The hService handle must have The SERVICE_STOP access right. 
#SERVICE_CONTROL_ALL = #SERVICE_CONTROL_CONTINUE| #SERVICE_CONTROL_PAUSE| #SERVICE_CONTROL_STOP        
        
; ** Service Status
;#SERVICE_CONTINUE_PENDING = $00000005   ; The service Continue is pending.
;#SERVICE_PAUSE_PENDING = $00000006      ; The service pause is pending.
;#SERVICE_PAUSED = $00000007             ; The service is paused.
#SERVICE_RUNNING = $00000004            ; The service is running.
;#SERVICE_START_PENDING = $00000002      ; The service is Starting.
;#SERVICE_STOP_PENDING = $00000003       ; The service is stopping.
#SERVICE_STOPPED = $00000001            ; The service is Not running.

; ** Error Codes
;#ERROR_ACCESS_DENIED = $5               ; The requested access was denied.
;#ERROR_CIRCULAR_DEPENDENCY = $423       ; A circular service dependency was specified.
;#ERROR_DUPLICATE_SERVICE_NAME = $436    ; The display name already exists in The service Control manager database either As A service name Or As another display name.
;#ERROR_INVALID_HANDLE = $6              ; The specified handle is invalid on closing SCM handler.
;#ERROR_INVALID_NAME = $7B               ; The specified service name is invalid.
;#ERROR_INVALID_PARAMETER = $57          ; A parameter that was specified is invalid.
;#ERROR_INVALID_SERVICE_ACCOUNT = $421   ; The user account name specified in The lpServiceStartName parameter does Not exist.
;#ERROR_SERVICE_EXISTS = $431            ; The specified service already exists in This database.
;#ERROR_ACCESS_DENIED = $5               ; The requested access was denied.
;#ERROR_INVALID_HANDLE = $6              ; The specified handle is invalid on closing SCM handler.
;#ERROR_PATH_NOT_FOUND = $3              ; The service binary file could Not be found.
;#ERROR_SERVICE_ALREADY_RUNNING = $420   ; An instance of The service is already running.
;#ERROR_SERVICE_DATABASE_LOCKED = $41F   ; The database is locked.
;#ERROR_SERVICE_DEPENDENCY_DELETED = $433 ; The service depends on a service that does Not exist Or has been marked For deletion.
;#ERROR_SERVICE_DEPENDENCY_FAIL = $42C   ; The service depends on another service that has failed To start.
;#ERROR_SERVICE_DISABLED = $422          ; The service has been disabled.
;#ERROR_SERVICE_LOGON_FAILED = $42D      ; The service did Not start due To a logon failure. This error occurs If The service is configured To run under An account that does Not have The "Log on as a service" right.
;#ERROR_SERVICE_MARKED_FOR_DELETE = $430 ; The service has been marked For deletion.
;#ERROR_SERVICE_NO_THREAD = $41E         ; A thread could Not be created For The service.
;#ERROR_SERVICE_REQUEST_TIMEOUT = $41D   ; The process For The service was started, but it did Not call StartServiceCtrlDispatcher, Or The thread that called StartServiceCtrlDispatcher may be blocked in a Control handler function.
;#ERROR_DATABASE_DOES_NOT_EXIST = $429   ; The specified database does Not exist.
;#ERROR_INVALID_PARAMETER =$57           ; A specified parameter is invalid.

; ** Registry Access Rights
#KEY_WRITE = $20006                     ; Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, And KEY_CREATE_SUB_KEY access rights.
 
Structure Status
    dwServiceType.l  
    dwCurrentState.l 
    dwControlsAccepted.l  
    dwWin32ExitCode.l  
    dwServiceSpecificExitCode.l  
    dwCheckPoint.l  
    dwWaitHint.l
EndStructure
service.Status
    
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                        Default Vars                      ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Global lpMachineName = #Null
Global lpDatabaseName = #Null
Global scmDesiredAccess = #SC_MANAGER_ALL_ACCESS
Global svcDesiredAccess = #SERVICE_ALL_ACCESS
Global dwServiceType = #SERVICE_WIN32_OWN_PROCESS|#SERVICE_INTERACTIVE_PROCESS
Global dwStartType = #SERVICE_AUTO_START
Global dwErrorControl = #SERVICE_ERROR_NORMAL
Global lpLoadOrderGroup = #Null
Global lpdwTagId = #Null
Global lpDependencies = #Null
Global lpServiceStartName = #Null
Global lpPassword = #Null
Global svcDesiredAccess = #SERVICE_ALL_ACCESS
Global dwControl = #SERVICE_CONTROL_STOP
Global dwNumServiceArgs = #Null
Global lpServiceArgVectors = #Null

Global mainKey.l = #HKEY_LOCAL_MACHINE
Global subKey.s = "SYSTEM\CurrentControlSet\Services\"
Global descKey.s = "Description"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                        Processing Code                   ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Procedure StartSvc(svcName.s)
    scmHandler = OpenSCManager_(lpMachineName, lpDatabaseName, scmDesiredAccess)
    If scmHandler = #Null
        LastError = GetLastError_()
        MessageRequester("ERROR", "Open SCM failed with error " + Str(LastError))
    Else 
        svcHandler = OpenService_(scmHandler, svcName, svcDesiredAccess)
        If svcHandler = #Null
            LastError = GetLastError_()
            MessageRequester("ERROR", "Open Service failed with error " + Str(LastError))
        Else
            Result = QueryServiceStatus_(svcHandler, service.Status)
            If Result = 0
                LastError = GetLastError_()
                MessageRequester("", "Query failed with error " + Str(LastError))
            Else
                If Not service\dwCurrentState = #SERVICE_RUNNING 
                    Result = StartService_(svcHandler, dwNumServiceArgs, lpServiceArgVectors)
                    If Result = 0
                        LastError = GetLastError_()
                        MessageRequester("ERROR", "Start Service failed with error " + Str(LastError))
                    Else
                        WatchDog = 0
                        Repeat
                            QueryServiceStatus_(svcHandler, service.Status)
                            Delay(1000)
                            WatchDog = WatchDog +1
                        Until service\dwCurrentState = #SERVICE_RUNNING Or WatchDog => 10
                        If service\dwCurrentState <> #SERVICE_RUNNING And WatchDog => 10
                            MessageRequester("ERROR", "Could not Start the Service")
                        EndIf
                    EndIf
                EndIf
            EndIf
        EndIf
        Result = CloseServiceHandle_(svcHandler)
    EndIf
    Result = CloseServiceHandle_(scmHandler)
EndProcedure

Procedure CreateSvc(svcName.s, dspName.s, binPath.s, keyDesc.s)
    hKey.l = 0 
    scmHandler = OpenSCManager_(lpMachineName, lpDatabaseName, scmDesiredAccess)
    If scmHandler = #Null
        LastError = GetLastError_()
        MessageRequester("ERROR", "Open SCM failed with error " + Str(LastError))
    Else 
        svcHandler = CreateService_(scmHandler, svcName, dspName, svcDesiredAccess, dwServiceType, dwStartType, dwErrorControl, binPath, lpLoadOrderGroup, lpdwTagId, lpDependencies, lpServiceStartName, lpPassword)
        If svcHandler = #Null
            LastError = GetLastError_()
            MessageRequester("ERROR", "Create Service failed with error " + Str(LastError))
        Else
            StartSvc(svcName.s)
        EndIf    
        CloseServiceHandle_(svcHandler)
    EndIf
    CloseServiceHandle_(scmHandler)
    Result = RegOpenKeyEx_(mainKey, subKey + svcName, 0, #KEY_WRITE, @hKey)
    If Result <> #ERROR_SUCCESS
        MessageRequester("ERROR", "Create Service Description failed with error " + Str(Result))
    EndIf
    Result = RegSetValueEx_(hKey, descKey, 0, #REG_SZ, @keyDesc, Len(keyDesc))
    If Result <> #ERROR_SUCCESS
        MessageRequester("ERROR", "Writing Service Description failed with error " + Str(Result))
    EndIf
    Result = RegCloseKey_(hKey) 
    If Result <> #ERROR_SUCCESS
        MessageRequester("ERROR", "Closing Regitry Key failed with error " + Str(Result))
    EndIf
EndProcedure

Procedure StopSvc(svcName.s)
    scmHandler = OpenSCManager_(lpMachineName, lpDatabaseName, scmDesiredAccess)
    If scmHandler = #Null
        LastError = GetLastError_()
        MessageRequester("ERROR", "Open SCM failed with error " + Str(LastError))
    Else 
        svcHandler = OpenService_(scmHandler, svcName, svcDesiredAccess)
        If svcHandler = #Null
            LastError = GetLastError_()
            MessageRequester("ERROR", "Open Service failed with error " + Str(LastError))
        Else
            Result = QueryServiceStatus_(svcHandler, service.Status)
            If Result = 0
                LastError = GetLastError_()
                MessageRequester("", "Query failed with error " + Str(LastError))
            Else
                If Not service\dwCurrentState = #SERVICE_STOPPED
                    Result = ControlService_(svcHandler, dwControl, service.Status)
                    If Result = 0
                        LastError = GetLastError_()
                        MessageRequester("", "Control Service failed with error " + Str(LastError))
                    Else
                        WatchDog = 0
                        Repeat
                            QueryServiceStatus_(svcHandler, service.Status)
                            Delay(1000)
                            WatchDog = WatchDog +1
                        Until service\dwCurrentState = #SERVICE_STOPPED Or WatchDog => 10
                        If service\dwCurrentState <> #SERVICE_STOPPED And WatchDog => 10
                            MessageRequester("ERROR", "Could not Stop the Service")
                        EndIf
                    EndIf
                EndIf
            EndIf
        EndIf
        Result = CloseServiceHandle_(svcHandler)
    EndIf
    Result = CloseServiceHandle_(scmHandler)
EndProcedure

Procedure RemoveSvc(svcName.s)
    scmHandler = OpenSCManager_(lpMachineName, lpDatabaseName, scmDesiredAccess)
    If scmHandler = #Null
        LastError = GetLastError_()
        MessageRequester("ERROR", "Open SCM failed with error " + Str(LastError))
    Else 
        svcHandler = OpenService_(scmHandler, svcName, svcDesiredAccess)
        If svcHandler = #Null
            LastError = GetLastError_()
            MessageRequester("ERROR", "Open Service failed with error " + Str(LastError))
        Else
            StopSvc(svcName.s)
        EndIf
        Result = DeleteService_(svcHandler)
        If Result = 0
            LastError = GetLastError_()
            MessageRequester("", "Delete Service failed with error " + Str(LastError))
        EndIf
        Result = CloseServiceHandle_(svcHandler)
    EndIf
    Result = CloseServiceHandle_(scmHandler)
EndProcedure

; ** Use it as follow (uncomment the commands as needed) and replace te vars with values

svcName.s = "MyTestSvc"
dspName.s = "MyTestSvc - Display Name"
binPath.s = Chr(34) + "c:\program files\nettax\interface\nettaxsvc.exe" + Chr(34)
keyDesc.s = "Description of the service"

; ** Create Service
;CreateSvc(svcName, dspName, binPath, keyDesc)  ; ServiceName, ServiceDisplayName, PathToServiceExecutable

; ** Remove Service
;RemoveSvc(svcName)                             ; ServiceName

; ** Stop Service
;StopSvc(svcName)                               ; ServiceName

; ** Start Service
;StartSvc(svcName)                              ; ServiceName 
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Post by SFSxOI »

Very nice, thank you :)
Post Reply