Page 1 of 1

ChangeServiceConfig2

Posted: Wed Jun 05, 2013 2:15 pm
by TeddyLM
Hi there,

i try to use ChangeServiceConfig2 to change the settings of a service to restart on crash.
Although i run it with higher privilege (on Win7 64bit) and have the permission to change directly in the registry, the function keeps returning 'Access denied'.
Any idea what i'm doing wrong?
Thanks for the help.

Code: Select all

Enumeration     ;SC_ACTION_TYPE 
    #SC_ACTION_NONE         ;0 - no action.
    #SC_ACTION_RESTART      ;1 - restart the service.
    #SC_ACTION_REBOOT       ;2 - reboot the computer.
    #SC_ACTION_RUN_COMMAND  ;3 - run a command.
EndEnumeration

Enumeration 1    ;dwInfoLevel = Configuration information to be changed
    #SERVICE_CONFIG_DESCRIPTION                 ;1 - The lpInfo parameter is a pointer To a SERVICE_DESCRIPTION Structure.
    #SERVICE_CONFIG_FAILURE_ACTIONS             ;2 - The lpInfo parameter is a pointer To a SERVICE_FAILURE_ACTIONS Structure.
    #SERVICE_CONFIG_DELAYED_AUTO_START_INFO     ;3 - The lpInfo parameter is a pointer To a SERVICE_DELAYED_AUTO_START_INFO Structure. (XP not supported)
    #SERVICE_CONFIG_FAILURE_ACTIONS_FLAG        ;4 - The lpInfo parameter is a pointer To a SERVICE_FAILURE_ACTIONS_FLAG Structure. (XP not supported)
    #SERVICE_CONFIG_SERVICE_SID_INFO            ;5 - The lpInfo parameter is a pointer To a SERVICE_SID_INFO Structure.
    #SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO    ;6 - The lpInfo parameter is a pointer To a SERVICE_REQUIRED_PRIVILEGES_INFO Structure. (XP not supported)
    #SERVICE_CONFIG_PRESHUTDOWN_INFO            ;7 - The lpInfo parameter is a pointer To a SERVICE_PRESHUTDOWN_INFO Structure. (XP not supported)
    #SERVICE_CONFIG_TRIGGER_INFO                ;8 - The lpInfo parameter is a pointer To a SERVICE_TRIGGER_INFO Structure. (XP not supported) 
    #SERVICE_CONFIG_PREFERRED_NODE              ;9 - The lpInfo parameter is a pointer To a SERVICE_PREFERRED_NODE_INFO Structure. (XP not supported)
EndEnumeration

Structure SC_ACTION
    type.l                      ;SC_ACTION_TYPE
    delay.l                     ;(in milliseconds)
EndStructure

Structure SERVICE_FAILURE_ACTIONS
    dwResetPeriod.l             ;The time after which to reset the failure count to zero if there are no failures, in seconds. Specify INFINITE to indicate that this value should never be reset.
    lpRebootMsg.l               ;The message to be broadcast before rebooting in response to the SC_ACTION_REBOOT service controller action (#NULL = message is unchanged, "" = no message is broadcast)
    lpCommand.l                 ;The command line of the process for the CreateProcess function to execute in response to the SC_ACTION_RUN_COMMAND service controller action
    cActions.l                  ;Number of elements in the lpsaActions array
    *lpsaActions                ;Pointer to an array of SC_ACTION structures
EndStructure

Prototype.l ProtoChange(hService.l, dwInfoLevel.l, sfa.l)

;===================================
Procedure.s GetLastError() 
    Msg$ = ""
    error.l = GetLastError_() 
    If error        
        *Buffer = AllocateMemory(255)
        If *Buffer
            FormatMessage_ (#FORMAT_MESSAGE_FROM_SYSTEM, #Null, error, 0, *Buffer, 255, #Null) 
            Msg$ = PeekS(*Buffer) 
            FreeMemory(*Buffer) 
        EndIf
    EndIf    
    ProcedureReturn Msg$
EndProcedure
;===================================
Computername$ = ""
Servicename$ = "MozillaMaintenance"                     ;<-- enter an existing service here
LibNr.i = OpenLibrary(#PB_Any,"advapi32.dll")
If LibNr
    CompilerIf #PB_Compiler_Unicode 
        ChangeServiceConfig2.ProtoChange = GetFunction(LibNr, "ChangeServiceConfig2W")
    CompilerElse
        ChangeServiceConfig2.ProtoChange = GetFunction(LibNr, "ChangeServiceConfig2A")
    CompilerEndIf
    ;
    hSCObject.i = OpenSCManager_(Computername$, 0, #SC_MANAGER_ALL_ACCESS)
    If hSCObject
        hService.i = OpenService_(hSCObject, Servicename$, #SERVICE_ALL_ACCESS)    
        If hService
            ;
            Dim sca.SC_ACTION(3)
            sca(0)\type = #SC_ACTION_RESTART
            sca(0)\delay = 2000
            sca(1)\type = #SC_ACTION_NONE
            sca(1)\delay = 0
            sca(2)\type = #SC_ACTION_NONE
            sca(2)\delay = 0
            ;
            sfa.SERVICE_FAILURE_ACTIONS
            sfa\dwResetPeriod = #INFINITE
            sfa\lpRebootMsg = #Null
            sfa\lpCommand = 0
            sfa\cActions = 3
            sfa\lpsaActions = sca()
            ;
            If ChangeServiceConfig2(hService, #SERVICE_CONFIG_FAILURE_ACTIONS, sfa)
                Debug "OK"
            Else
                Debug "Unable to change the configuration (" + GetLastError() + ")"
            EndIf    
            ;
            CloseServiceHandle_(hService)
        Else
            Debug "Unable to open the service (" + GetLastError() + ")"
        EndIf    
        CloseServiceHandle_(hSCObject)
    Else
        Debug "Unable to call the service manager (" + GetLastError() + ")"
    EndIf
    CloseLibrary(LibNr) 
EndIf

Re: ChangeServiceConfig2

Posted: Wed Jun 05, 2013 11:10 pm
by luis
MSDN wrote: If the service controller handles the SC_ACTION_RESTART action, hService must have the SERVICE_START access right.
Try

Code: Select all

hService.l = OpenService_(hSCObject, Servicename$, #SERVICE_CHANGE_CONFIG | #SERVICE_START)

Re: ChangeServiceConfig2

Posted: Thu Jun 06, 2013 7:27 am
by TeddyLM
Hi luis
shame on me.
I realised that i called OpenService with an Access right for the Service Control Manager (#SC_MANAGER_ALL_ACCESS = $F003F) and not for the Service (#SERVICE_ALL_ACCESS = $F01FF).
The answer was in front of me all the time ! :oops:

Thanks for the suggestion.

Re: ChangeServiceConfig2

Posted: Thu Jun 06, 2013 7:35 am
by TeddyLM
I corrected the code.
It now works for me (tested on Win7 64 bit).

Re: ChangeServiceConfig2

Posted: Thu Jun 06, 2013 8:00 am
by PureGuy
TeddyLM you use long for handles: LibNr.l, hSCObject.l, hService.l
If you compile your code as native x64 you will get problems, better change them to .i.

Re: ChangeServiceConfig2

Posted: Thu Jun 06, 2013 9:23 am
by TeddyLM
You are right, it is better that way, although i'm not sure it would be problematic.

Microsoft writes:
...
• 64-bit versions of Windows use 32-bit handles for interoperability. When sharing a handle between 32-bit and 64-bit applications, only the lower 32 bits are significant, so it is safe to truncate the handle (when passing it from 64-bit to 32-bit) or sign-extend the handle (when passing it from 32-bit to 64-bit). Handles that can be shared include handles to user objects such as windows (HWND), handles to GDI objects such as pens and brushes (HBRUSH and HPEN), and handles to named objects such as mutexes, semaphores, and file handles.
...

Re: ChangeServiceConfig2

Posted: Thu Jun 06, 2013 12:36 pm
by luis
Often it's not a problem with handles but you never know when you'll meet the exception, so better drop the habit to use .l unless really needed (for example inside API structures).

Moreover I believe #PB_Any can return values larger than 32 bits on x64

IMHO .l should be banned and used only when needed, this way it will be kind of an implicit comment too: "see ? this is .l for a reason, it's not any integer ... it really needs to be this size !"