ChangeServiceConfig (advapi32)
Last changed: -90.242.135.188

.
Summary
The ChangeServiceConfig function changes the configuration parameters of a service. To change the optional configuration parameters, use the ChangeServiceConfig2 function. The ChangeServiceConfig function changes the configuration information for the specified service in the service control manager database. You can obtain the current configuration information by using the QueryServiceConfig function.

C# Signature:

[DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern Boolean ChangeServiceConfig(IntPtr hService, UInt32 nServiceType, UInt32 nStartType, UInt32 nErrorControl, String lpBinaryPathName, String lpLoadOrderGroup, IntPtr lpdwTagId, String lpDependencies, String lpServiceStartName, String lpPassword, String lpDisplayName);

Correction:
[DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError = true)]
public static extern Boolean ChangeServiceConfig(IntPtr hService, UInt32 nServiceType, UInt32 nStartType, UInt32 nErrorControl, String lpBinaryPathName, String lpLoadOrderGroup, IntPtr lpdwTagId, [In] char[] lpDependencies, String lpServiceStartName, String lpPassword, String lpDisplayName);

In details:

[In] char[] lpDependencies: lpDependencies is marshaled as raw array of Unicode characters instead of string.

See warning and usage below.

VB Signature:

    <DllImport("AdvApi32", CharSet:=CharSet.Auto, entrypoint:="ChangeServiceConfigA")> _
    Public Function ChangeServiceConfig(ByVal hService As Integer, ByVal dwServiceType As ServiceType, ByVal dwStartType As ServiceStartType, ByVal dwErrorControl As ServiceErrorControl, ByVal lpBinaryPathName As String, ByVal lpLoadOrderGroup As String, ByVal lpdwTagId As Integer, ByVal lpDependencies As String, ByVal lpServiceStartName As String, ByVal lpPassword As String, ByVal lpDisplayName As String) As Boolean
    End Function

User-Defined Types:

None.

Alternative Managed API:

Do you know one? Please contribute it!

private const uint SERVICE_NO_CHANGE        = 0xffffffff; //this value is found in winsvc.h
private const uint SERVICE_QUERY_CONFIG        = 0x00000001;
private const uint SERVICE_CHANGE_CONFIG        = 0x00000002;
private const uint SERVICE_QUERY_STATUS        = 0x00000004;
private const uint SERVICE_ENUMERATE_DEPENDENTS    = 0x00000008;
private const uint SERVICE_START            = 0x00000010;
private const uint SERVICE_STOP             = 0x00000020;
private const uint SERVICE_PAUSE_CONTINUE           = 0x00000040;
private const uint SERVICE_INTERROGATE        = 0x00000080;
private const uint SERVICE_USER_DEFINED_CONTROL     = 0x00000100;
private const uint STANDARD_RIGHTS_REQUIRED         = 0x000F0000;
private const uint 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);

Notes:

To use this function, make sure you call OpenService with the SERVICE_CHANGE_CONFIG flag. Look at OpenService and QueryServiceConfig for more examples.

Remarks:

If the configuration is changed for a service that is running, with the exception of lpDisplayName, the changes do not take effect until the service is stopped.

Security Remarks:

Setting the lpServiceStartName parameter changes the logon account of the service. This can cause problems. If you have registered a service principal name (SPN), it would now be registered on the wrong account. Similarly, if you have used an ACE to grant access to a service, it would now grant access to the wrong account.

Tips & Tricks:

Remember that the cofiguration settings will only be in effect the next time the service is started.

Warning:

The lpDependencies parameters requires "a pointer to a double null-terminated array of null-separated names of services or load ordering groups", this means a TCHAR array in the following format:

DependentService1\0DependentService2\0DependentServiceN\0\0

This is different from a normal null-terminated string. First I tried to give a string from C# code which looked like this: "Service1\0Service2\0" or like this: "Service1\0Service2\0\0", and what happened was my process calling ChangeServiceConfig hung and even worse, my Windows XP system got corrupted in a way I had to reboot in Safe Mode to be able to run System Restore.

I don't know what the Marshaler is internally doing with these strings containing null characters, but ChangeServiceConfig was happy with the adapted prototype and an array given in this way:

char[] rawArray = "Service1\0Service2\0\0".ToCharArray();

(In case of doubt, check rawArray in the debugger first!)

Sample Code:

IntPtr databaseHandle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
if(databaseHandle == IntPtr.Zero)
    throw new System.Runtime.InteropServices.ExternalException("Open Service Manager Error");

m_pServiceHandle = OpenService(databaseHandle,  strServiceName, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
if(m_pServiceHandle == IntPtr.Zero)
    throw new System.Runtime.InteropServices.ExternalException("Open Service Error");

//This code is changing the password for the service.

if (!ChangeServiceConfig(m_pServiceHandle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, null, null,
    IntPtr.Zero, null, null, value, null))
{
    int nError = Marshal.GetLastWin32Error();
    Win32Exception win32Exception = new Win32Exception(nError);
    throw new System.Runtime.InteropServices.ExternalException("Could not change password : " + win32Exception.Message);
}

Documentation