Tiens je viens de retomber sur mon propre thread. Comme j’ai trouvé la réponse, je la mets ici, peut-être que ça pourra servir à quelqu’un.
Je me suis aidé de l’excellent site PInvoke.net et en particulier de cette page.
J’ai ajouté dessus une version quasi-identique à celle que j’ai implémenté.
C’est une classe utilitaire qui permet à la fois de récupérer et de modifier les infos d’un service existant.
Je la copie-colle ici :
[codebox]namespace Services
{
///
/// Some wrappers around Win32 calls dealing with services.
///
public class ServiceConfigurator
{
[StructLayout(LayoutKind.Sequential)]
private struct QueryServiceConfigStruct
{
public int serviceType;
public int startType;
public int errorControl;
public IntPtr binaryPathName;
public IntPtr loadOrderGroup;
public int tagID;
public IntPtr dependencies;
public IntPtr startName;
public IntPtr displayName;
}
public struct ServiceInfo
{
public int serviceType;
public int startType;
public int errorControl;
public string binaryPathName;
public string loadOrderGroup;
public int tagID;
public string dependencies;
public string startName;
public string displayName;
}
#region constants
private enum SCManagerAccess : int
{
GENERIC_ALL = 0x10000000
}
private enum ServiceAccess : int
{
QUERY_CONFIG = 0x1,
CHANGE_CONFIG = 0x2,
}
private const int SERVICE_NO_CHANGE = 0xFFFF;
#endregion
#region DllImports
[DllImport("advapi32.dll",
SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr OpenSCManager(
[MarshalAs(UnmanagedType.LPTStr)]
string machineName,
[MarshalAs(UnmanagedType.LPTStr)]
string databaseName,
int desiredAccess);
[DllImport("advapi32.dll",
SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr OpenService(
IntPtr scManager,
[MarshalAs(UnmanagedType.LPTStr)]
string serviceName,
int desiredAccess);
[DllImport("advapi32.dll",
SetLastError = true, CharSet = CharSet.Auto)]
private static extern int ChangeServiceConfig(
IntPtr service,
int serviceType,
int startType,
int errorControl,
[MarshalAs(UnmanagedType.LPTStr)]
string binaryPathName,
[MarshalAs(UnmanagedType.LPTStr)]
string loadOrderGroup,
IntPtr tagID,
[MarshalAs(UnmanagedType.LPTStr)]
string dependencies,
[MarshalAs(UnmanagedType.LPTStr)]
string startName,
[MarshalAs(UnmanagedType.LPTStr)]
string password,
[MarshalAs(UnmanagedType.LPTStr)]
string displayName);
[DllImport("advapi32.dll",
SetLastError = true, CharSet = CharSet.Auto)]
private static extern int QueryServiceConfig(
IntPtr service,
IntPtr queryServiceConfig,
int bufferSize,
ref int bytesNeeded);
#endregion
public static ServiceInfo GetServiceInfo(string ServiceName)
{
if (ServiceName.Equals(""))
throw new NullReferenceException("ServiceName must contain a valid service name.");
IntPtr scManager = OpenSCManager(".", null,
(int)SCManagerAccess.GENERIC_ALL);
if (scManager.ToInt32() <= 0)
throw new Win32Exception();
IntPtr service = OpenService(scManager,
ServiceName, (int)ServiceAccess.QUERY_CONFIG);
if (service.ToInt32() <= 0)
throw new NullReferenceException();
int bytesNeeded = 5;
QueryServiceConfigStruct qscs = new QueryServiceConfigStruct();
IntPtr qscPtr = Marshal.AllocCoTaskMem(0);
int retCode = QueryServiceConfig(service, qscPtr,
0, ref bytesNeeded);
if (retCode == 0 && bytesNeeded == 0)
{
throw new Win32Exception();
}
else
{
qscPtr = Marshal.AllocCoTaskMem(bytesNeeded);
retCode = QueryServiceConfig(service, qscPtr,
bytesNeeded, ref bytesNeeded);
if (retCode == 0)
{
throw new Win32Exception();
}
qscs.binaryPathName = IntPtr.Zero;
qscs.dependencies = IntPtr.Zero;
qscs.displayName = IntPtr.Zero;
qscs.loadOrderGroup = IntPtr.Zero;
qscs.startName = IntPtr.Zero;
qscs = (QueryServiceConfigStruct)
Marshal.PtrToStructure(qscPtr,
new QueryServiceConfigStruct().GetType());
}
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.binaryPathName =
Marshal.PtrToStringAuto(qscs.binaryPathName);
serviceInfo.dependencies =
Marshal.PtrToStringAuto(qscs.dependencies);
serviceInfo.displayName =
Marshal.PtrToStringAuto(qscs.displayName);
serviceInfo.loadOrderGroup =
Marshal.PtrToStringAuto(qscs.loadOrderGroup);
serviceInfo.startName =
Marshal.PtrToStringAuto(qscs.startName);
serviceInfo.errorControl = qscs.errorControl;
serviceInfo.serviceType = qscs.serviceType;
serviceInfo.startType = qscs.startType;
serviceInfo.tagID = qscs.tagID;
Marshal.FreeCoTaskMem(qscPtr);
return serviceInfo;
}
public static void ChangeAccount(string ServiceName,
string Username, string Password)
{
ServiceInfo serviceInfo = GetServiceInfo(ServiceName);
IntPtr scManager = OpenSCManager(".", null,
(int)SCManagerAccess.GENERIC_ALL);
if (scManager.ToInt32() <= 0)
throw new Win32Exception();
IntPtr service = OpenService(scManager,
ServiceName, (int)ServiceAccess.CHANGE_CONFIG);
if (service.ToInt32() <= 0)
throw new Win32Exception();
if (ChangeServiceConfig(service, serviceInfo.serviceType,
serviceInfo.startType, serviceInfo.errorControl,
serviceInfo.binaryPathName, serviceInfo.loadOrderGroup,
IntPtr.Zero, serviceInfo.dependencies,
Username, Password, serviceInfo.displayName) == 0)
{
throw new Win32Exception();
}
}
}
}[/codebox]
Moins compliqué que ce que je pensais les P/Invoke finalement. Par contre ma solution ne marchera que sur des machines Windows… en attendant que le framework soit amélioré pour prendre en compte ces options.