[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.
The Unicode version here (no problems with that)
[DllImport("Advapi32.dll", EntryPoint="ChangeServiceConfigW", CharSet=CharSet.Unicode, ExactSpelling=true, SetLastError=true)]
internal static extern bool ChangeServiceConfig(
System.Runtime.InteropServices.SafeHandle hService,
[MarshalAs(UnmanagedType.U4)]
System.ServiceProcess.ServiceType dwServiceType,
[MarshalAs(UnmanagedType.U4)]
System.ServiceProcess.ServiceStartMode dwStartType, int dwErrorControl,
[In, MarshalAs(UnmanagedType.LPWStr)] string lpBinaryPathName,
[In, MarshalAs(UnmanagedType.LPWStr)] string lpLoadOrderGroup,
int lpdwTagId, //is out-opt specify 0
[In, MarshalAs(UnmanagedType.LPWStr)] string lpDependencies,
[In, MarshalAs(UnmanagedType.LPWStr)]
string lpServiceStartName,
[In, MarshalAs(UnmanagedType.LPWStr)]string lpPassWord, [In, MarshalAs(UnmanagedType.LPWStr)]string lpDisplayName);
See warning and usage below. [http://hi7tech.com/blog/microsoft-corporation/]
<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
None.
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);
To use this function, make sure you call OpenService with the SERVICE_CHANGE_CONFIG flag. Look at OpenService and QueryServiceConfig for more examples.
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.
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.
Remember that the cofiguration settings will only be in effect the next time the service is started.
For those who are tired about searching and not found code running. This code snipet is functional, you only need to copy paste.
<Flags()> Public Enum ACCESS_MASK : Uint32
DELETE = &H10000
READ_CONTROL = &H20000
WRITE_DAC = &H40000
WRITE_OWNER = &H80000
SYNCHRONIZE = &H100000
STANDARD_RIGHTS_REQUIRED = &HF0000
STANDARD_RIGHTS_READ = &H20000
STANDARD_RIGHTS_WRITE = &H20000
STANDARD_RIGHTS_EXECUTE = &H20000
STANDARD_RIGHTS_ALL = &H1F0000
SPECIFIC_RIGHTS_ALL = &HFFFF
ACCESS_SYSTEM_SECURITY = &H1000000
MAXIMUM_ALLOWED = &H2000000
GENERIC_READ = &H80000000
GENERIC_WRITE = &H40000000
GENERIC_EXECUTE = &H20000000
GENERIC_ALL = &H10000000
DESKTOP_READOBJECTS = &H1
DESKTOP_CREATEWINDOW = &H2
DESKTOP_CREATEMENU = &H4
DESKTOP_HOOKCONTROL = &H8
DESKTOP_JOURNALRECORD = &H10
DESKTOP_JOURNALPLAYBACK = &H20
DESKTOP_ENUMERATE = &H40
DESKTOP_WRITEOBJECTS = &H80
DESKTOP_SWITCHDESKTOP = &H100
WINSTA_ENUMDESKTOPS = &H1
WINSTA_READATTRIBUTES = &H2
WINSTA_ACCESSCLIPBOARD = &H4
WINSTA_CREATEDESKTOP = &H8
WINSTA_WRITEATTRIBUTES = &H10
WINSTA_ACCESSGLOBALATOMS = &H20
WINSTA_EXITWINDOWS = &H40
WINSTA_ENUMERATE = &H100
WINSTA_READSCREEN = &H200
WINSTA_ALL_ACCESS = &H37F
End Enum
<Flags()> _
Public Enum SCM_ACCESS : UInt
''' <summary>
''' Required to connect to the service control manager.
''' </summary>
SC_MANAGER_CONNECT = &H1
''' <summary>
''' Required to call the CreateService function to create a service
''' object and add it to the database.
''' </summary>
SC_MANAGER_CREATE_SERVICE = &H2
''' <summary>
''' Required to call the EnumServicesStatusEx function to list the
''' services that are in the database.
''' </summary>
SC_MANAGER_ENUMERATE_SERVICE = &H4
''' <summary>
''' Required to call the LockServiceDatabase function to acquire a
''' lock on the database.
''' </summary>
SC_MANAGER_LOCK = &H8
''' <summary>
''' Required to call the QueryServiceLockStatus function to retrieve
''' the lock status information for the database.
''' </summary>
SC_MANAGER_QUERY_LOCK_STATUS = &H10
''' <summary>
''' Required to call the NotifyBootConfigStatus function.
''' </summary>
SC_MANAGER_MODIFY_BOOT_CONFIG = &H20
''' <summary>
''' Includes STANDARD_RIGHTS_REQUIRED, in addition to all access
''' rights in this table.
''' </summary>
SC_MANAGER_ALL_ACCESS = ACCESS_MASK.STANDARD_RIGHTS_REQUIRED Or _
SC_MANAGER_CONNECT Or _
SC_MANAGER_CREATE_SERVICE Or _
SC_MANAGER_ENUMERATE_SERVICE Or _
SC_MANAGER_LOCK Or _
SC_MANAGER_QUERY_LOCK_STATUS Or _
SC_MANAGER_MODIFY_BOOT_CONFIG
GENERIC_READ = ACCESS_MASK.STANDARD_RIGHTS_READ Or _
SC_MANAGER_ENUMERATE_SERVICE Or _
SC_MANAGER_QUERY_LOCK_STATUS
GENERIC_WRITE = ACCESS_MASK.STANDARD_RIGHTS_WRITE Or _
SC_MANAGER_CREATE_SERVICE Or _
SC_MANAGER_MODIFY_BOOT_CONFIG
GENERIC_EXECUTE = ACCESS_MASK.STANDARD_RIGHTS_EXECUTE Or _
SC_MANAGER_CONNECT Or SC_MANAGER_LOCK
GENERIC_ALL = SC_MANAGER_ALL_ACCESS
End Enum
<DllImport("advapi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function OpenSCManager(ByVal machineName As String, ByVal databaseName As String, ByVal desiredAccess As Int32) As IntPtr
End Function
<DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function OpenService(ByVal hSCManager As IntPtr, ByVal lpServiceName As String, ByVal dwDesiredAccess As Int32) As IntPtr
End Function
<DllImport("AdvApi32", CharSet:=CharSet.Auto, entrypoint:="ChangeServiceConfigA")> _
Private Shared Function ChangeServiceConfig(ByVal hService As Integer, ByVal dwServiceType As UInt32, ByVal dwStartType As UInt32, ByVal dwErrorControl As UInt32, 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
Private Const SERVICE_NO_CHANGE As UInteger = &HFFFFFFFFUI
Private Const SERVICE_QUERY_CONFIG As UInteger = &H1UI
Private Const SERVICE_CHANGE_CONFIG As UInteger = &H2UI
''' <summary>
''' Changes the service startup method
''' </summary>
''' <param name="ServiceName">Indicates the name of the service, as it is shown in the service manager</param>
''' <param name="Mode">Indicates the mode you wish to stablish as startup</param>
Private Sub ChangeStartUp(ByVal ServiceName As String, ByVal Mode As System.ServiceProcess.ServiceStartMode)
Dim databaseHandle As IntPtr = OpenSCManager(Nothing, Nothing, SCM_ACCESS.SC_MANAGER_ALL_ACCESS)
If databaseHandle = IntPtr.Zero Then Throw New System.Runtime.InteropServices.ExternalException("Open Service Manager Error")
Dim m_pServiceHandle As IntPtr = OpenService(databaseHandle, ServiceName, SERVICE_QUERY_CONFIG Or SERVICE_CHANGE_CONFIG)
If m_pServiceHandle = IntPtr.Zero Then Throw New System.Runtime.InteropServices.ExternalException("Open Service Error")
If Not ChangeServiceConfig(m_pServiceHandle, SERVICE_NO_CHANGE, CUInt(Mode), SERVICE_NO_CHANGE, Nothing, Nothing, IntPtr.Zero, Nothing, Nothing, Nothing, Nothing) Then
Throw New System.Runtime.InteropServices.ExternalException("Could not change password ")
End If
End Sub
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();
char[] rawEmptyArray = "".ToCharArray(); // To revoke dependent Services
(In case of doubt, check rawArray in the debugger first!)
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);
}