Installer un service Windows .NET sans InstallUtil.exe

J’ai un service Windows standard .NET écrit en C #.

Peut-il s’installer sans utiliser InstallUtil? Dois-je utiliser la classe d’installation de service? Comment devrais-je l’utiliser?

Je veux pouvoir appeler ce qui suit:

MyService.exe -install 

Et cela aura le même effet que d’appeler:

 InstallUtil MyService.exe 

Oui, c’est tout à fait possible (c’est-à-dire que je fais exactement cela); il vous suffit de référencer la bonne DLL (System.ServiceProcess.dll) et d’append une classe d’installation …

Voici un exemple:

 [RunInstaller(true)] public sealed class MyServiceInstallerProcess : ServiceProcessInstaller { public MyServiceInstallerProcess() { this.Account = ServiceAccount.NetworkService; } } [RunInstaller(true)] public sealed class MyServiceInstaller : ServiceInstaller { public MyServiceInstaller() { this.Description = "Service Description"; this.DisplayName = "Service Name"; this.ServiceName = "ServiceName"; this.StartType = System.ServiceProcess.ServiceStartMode.Automatic; } } static void Install(bool undo, ssortingng[] args) { try { Console.WriteLine(undo ? "uninstalling" : "installing"); using (AssemblyInstaller inst = new AssemblyInstaller(typeof(Program).Assembly, args)) { IDictionary state = new Hashtable(); inst.UseNewContext = true; try { if (undo) { inst.Uninstall(state); } else { inst.Install(state); inst.Commit(state); } } catch { try { inst.Rollback(state); } catch { } throw; } } } catch (Exception ex) { Console.Error.WriteLine(ex.Message); } } 

Jetez un oeil à la méthode InstallHelper de la classe ManagedInstaller . Vous pouvez installer un service en utilisant:

 ssortingng[] args; ManagedInstallerClass.InstallHelper(args); 

C’est exactement ce que fait InstallUtil. Les arguments sont les mêmes que pour InstallUtil.

Les avantages de cette méthode sont qu’elle n’implique aucun problème dans le registre et qu’elle utilise le même mécanisme que InstallUtil.

Vous pouvez toujours vous rabattre sur les bons anciens appels WinAPI, bien que la quantité de travail nécessaire ne soit pas sortingviale. Il n’est pas nécessaire que les services .NET soient installés via un mécanisme compatible avec .NET.

À installer:

  • Ouvrez le gestionnaire de services via OpenSCManager .
  • Appelez CreateService pour enregistrer le service.
  • Appelez éventuellement ChangeServiceConfig2 pour définir une description.
  • Fermez les CloseServiceHandle service et de gestionnaire de services avec CloseServiceHandle .

Pour désinstaller:

  • Ouvrez le gestionnaire de services via OpenSCManager .
  • Ouvrez le service à l’aide d’ OpenService .
  • Supprimez le service en appelant DeleteService sur le handle renvoyé par OpenService .
  • Fermez les CloseServiceHandle service et de gestionnaire de services avec CloseServiceHandle .

La principale raison pour laquelle je préfère cela à l’aide de ServiceInstaller / ServiceProcessInstaller est que vous pouvez enregistrer le service avec vos propres arguments de ligne de commande personnalisés. Par exemple, vous pouvez l’enregistrer en tant que "MyApp.exe -service" , puis, si l’utilisateur exécute votre application sans aucun argument, vous pouvez lui proposer une interface utilisateur pour installer / supprimer le service.

L’exécution de Reflector sur ServiceInstaller peut renseigner les détails manquants dans cette brève explication.

PS Il est clair que cela n’aura pas “le même effet que d’appeler: InstallUtil MyService.exe” – en particulier, vous ne pourrez pas désinstaller avec InstallUtil. Mais il semble que ce ne soit peut-être pas une exigence ssortingcte pour vous.

Voici une classe que j’utilise pour écrire des services. J’ai généralement un écran interactif qui s’affiche lorsque le service n’est pas appelé. À partir de là, j’utilise le cours si nécessaire. Il permet plusieurs instances nommées sur la même machine, par exemple le champ InstanceID.

Exemple d’appel

  IntegratedServiceInstaller Inst = new IntegratedServiceInstaller(); Inst.Install("MySvc", "My Sample Service", "Service that executes something", _InstanceID, // System.ServiceProcess.ServiceAccount.LocalService, // this is more secure, but only available in XP and above and WS-2003 and above System.ServiceProcess.ServiceAccount.LocalSystem, // this is required for WS-2000 System.ServiceProcess.ServiceStartMode.Automatic); if (controller == null) { controller = new System.ServiceProcess.ServiceController(Ssortingng.Format("MySvc_{0}", _InstanceID), "."); } if (controller.Status == System.ServiceProcess.ServiceControllerStatus.Running) { Start_Stop.Text = "Stop Service"; Start_Stop_Debugging.Enabled = false; } else { Start_Stop.Text = "Start Service"; Start_Stop_Debugging.Enabled = true; } 

La classe elle-même

 using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using Microsoft.Win32; namespace MySvc { class IntegratedServiceInstaller { public void Install(Ssortingng ServiceName, Ssortingng DisplayName, Ssortingng Description, Ssortingng InstanceID, System.ServiceProcess.ServiceAccount Account, System.ServiceProcess.ServiceStartMode StartMode) { //http://www.theblacksparrow.com/ System.ServiceProcess.ServiceProcessInstaller ProcessInstaller = new System.ServiceProcess.ServiceProcessInstaller(); ProcessInstaller.Account = Account; System.ServiceProcess.ServiceInstaller SINST = new System.ServiceProcess.ServiceInstaller(); System.Configuration.Install.InstallContext Context = new System.Configuration.Install.InstallContext(); ssortingng processPath = Process.GetCurrentProcess().MainModule.FileName; if (processPath != null && processPath.Length > 0) { System.IO.FileInfo fi = new System.IO.FileInfo(processPath); Ssortingng path = Ssortingng.Format("/assemblypath={0}", fi.FullName); Ssortingng[] cmdline = { path }; Context = new System.Configuration.Install.InstallContext("", cmdline); } SINST.Context = Context; SINST.DisplayName = Ssortingng.Format("{0} - {1}", DisplayName, InstanceID); SINST.Description = Ssortingng.Format("{0} - {1}", Description, InstanceID); SINST.ServiceName = Ssortingng.Format("{0}_{1}", ServiceName, InstanceID); SINST.StartType = StartMode; SINST.Parent = ProcessInstaller; // http://bytes.com/forum/thread527221.html SINST.ServicesDependedOn = new Ssortingng[] { "Spooler", "Netlogon", "Netman" }; System.Collections.Specialized.ListDictionary state = new System.Collections.Specialized.ListDictionary(); SINST.Install(state); // http://www.dotnet247.com/247reference/msgs/43/219565.aspx using (RegistryKey oKey = Registry.LocalMachine.OpenSubKey(Ssortingng.Format(@"SYSTEM\CurrentControlSet\Services\{0}_{1}", ServiceName, InstanceID), true)) { try { Object sValue = oKey.GetValue("ImagePath"); oKey.SetValue("ImagePath", sValue); } catch (Exception Ex) { System.Windows.Forms.MessageBox.Show(Ex.Message); } } } public void Uninstall(Ssortingng ServiceName, Ssortingng InstanceID) { //http://www.theblacksparrow.com/ System.ServiceProcess.ServiceInstaller SINST = new System.ServiceProcess.ServiceInstaller(); System.Configuration.Install.InstallContext Context = new System.Configuration.Install.InstallContext("c:\\install.log", null); SINST.Context = Context; SINST.ServiceName = Ssortingng.Format("{0}_{1}", ServiceName, InstanceID); SINST.Uninstall(null); } } } 

Les exemples ci-dessus n’ont pas vraiment fonctionné pour moi et le lien vers le forum en tant que solution n ° 1 est difficile à comprendre. Voici une classe que j’ai écrite (en partie), et l’autre partie est fusionnée à partir de ce lien que j’ai trouvé enterré quelque part

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.ServiceProcess; using System.Runtime.InteropServices; namespace SystemControl { class Services { #region "Environment Variables" public static ssortingng GetEnvironment(ssortingng name, bool ExpandVariables=true) { if ( ExpandVariables ) { return System.Environment.GetEnvironmentVariable( name ); } else { return (ssortingng)Microsoft.Win32.Registry.LocalMachine.OpenSubKey( @"SYSTEM\CurrentControlSet\Control\Session Manager\Environment\" ).GetValue( name, "", Microsoft.Win32.RegistryValueOptions.DoNotExpandEnvironmentNames ); } } public static void SetEnvironment( ssortingng name, ssortingng value ) { System.Environment.SetEnvironmentVariable(name, value); } #endregion #region "ServiceCalls Native" public static ServiceController[] List { get { return ServiceController.GetServices(); } } public static void Start( ssortingng serviceName, int timeoutMilliseconds ) { ServiceController service=new ServiceController( serviceName ); try { TimeSpan timeout=TimeSpan.FromMilliseconds( timeoutMilliseconds ); service.Start(); service.WaitForStatus( ServiceControllerStatus.Running, timeout ); } catch { // ... } } public static void Stop( ssortingng serviceName, int timeoutMilliseconds ) { ServiceController service=new ServiceController( serviceName ); try { TimeSpan timeout=TimeSpan.FromMilliseconds( timeoutMilliseconds ); service.Stop(); service.WaitForStatus( ServiceControllerStatus.Stopped, timeout ); } catch { // ... } } public static void Restart( ssortingng serviceName, int timeoutMilliseconds ) { ServiceController service=new ServiceController( serviceName ); try { int millisec1=Environment.TickCount; TimeSpan timeout=TimeSpan.FromMilliseconds( timeoutMilliseconds ); service.Stop(); service.WaitForStatus( ServiceControllerStatus.Stopped, timeout ); // count the rest of the timeout int millisec2=Environment.TickCount; timeout=TimeSpan.FromMilliseconds( timeoutMilliseconds-( millisec2-millisec1 ) ); service.Start(); service.WaitForStatus( ServiceControllerStatus.Running, timeout ); } catch { // ... } } public static bool IsInstalled( ssortingng serviceName ) { // get list of Windows services ServiceController[] services=ServiceController.GetServices(); // try to find service name foreach ( ServiceController service in services ) { if ( service.ServiceName==serviceName ) return true; } return false; } #endregion #region "ServiceCalls API" private const int STANDARD_RIGHTS_REQUIRED = 0xF0000; private const int SERVICE_WIN32_OWN_PROCESS = 0x00000010; [Flags] public enum ServiceManagerRights { Connect = 0x0001, CreateService = 0x0002, EnumerateService = 0x0004, Lock = 0x0008, QueryLockStatus = 0x0010, ModifyBootConfig = 0x0020, StandardRightsRequired = 0xF0000, AllAccess = (StandardRightsRequired | Connect | CreateService | EnumerateService | Lock | QueryLockStatus | ModifyBootConfig) } [Flags] public enum ServiceRights { QueryConfig = 0x1, ChangeConfig = 0x2, QueryStatus = 0x4, EnumerateDependants = 0x8, Start = 0x10, Stop = 0x20, PauseContinue = 0x40, Interrogate = 0x80, UserDefinedControl = 0x100, Delete = 0x00010000, StandardRightsRequired = 0xF0000, AllAccess = (StandardRightsRequired | QueryConfig | ChangeConfig | QueryStatus | EnumerateDependants | Start | Stop | PauseContinue | Interrogate | UserDefinedControl) } public enum ServiceBootFlag { Start = 0x00000000, SystemStart = 0x00000001, AutoStart = 0x00000002, DemandStart = 0x00000003, Disabled = 0x00000004 } public enum ServiceState { Unknown = -1, // The state cannot be (has not been) resortingeved. NotFound = 0, // The service is not known on the host server. Stop = 1, // The service is NET stopped. Run = 2, // The service is NET started. Stopping = 3, Starting = 4, } public enum ServiceControl { Stop = 0x00000001, Pause = 0x00000002, Continue = 0x00000003, Interrogate = 0x00000004, Shutdown = 0x00000005, ParamChange = 0x00000006, NetBindAdd = 0x00000007, NetBindRemove = 0x00000008, NetBindEnable = 0x00000009, NetBindDisable = 0x0000000A } public enum ServiceError { Ignore = 0x00000000, Normal = 0x00000001, Severe = 0x00000002, Critical = 0x00000003 } [StructLayout(LayoutKind.Sequential)] private class SERVICE_STATUS { public int dwServiceType = 0; public ServiceState dwCurrentState = 0; public int dwControlsAccepted = 0; public int dwWin32ExitCode = 0; public int dwServiceSpecificExitCode = 0; public int dwCheckPoint = 0; public int dwWaitHint = 0; } [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerA")] private static extern IntPtr OpenSCManager(ssortingng lpMachineName, ssortingng lpDatabaseName, ServiceManagerRights dwDesiredAccess); [DllImport("advapi32.dll", EntryPoint = "OpenServiceA", CharSet = CharSet.Ansi)] private static extern IntPtr OpenService(IntPtr hSCManager, ssortingng lpServiceName, ServiceRights dwDesiredAccess); [DllImport("advapi32.dll", EntryPoint = "CreateServiceA")] private static extern IntPtr CreateService(IntPtr hSCManager, ssortingng lpServiceName, ssortingng lpDisplayName, ServiceRights dwDesiredAccess, int dwServiceType, ServiceBootFlag dwStartType, ServiceError dwErrorControl, ssortingng lpBinaryPathName, ssortingng lpLoadOrderGroup, IntPtr lpdwTagId, ssortingng lpDependencies, ssortingng lp, ssortingng lpPassword); [DllImport("advapi32.dll")] private static extern int CloseServiceHandle(IntPtr hSCObject); [DllImport("advapi32.dll")] private static extern int QueryServiceStatus(IntPtr hService, SERVICE_STATUS lpServiceStatus); [DllImport("advapi32.dll", SetLastError = true)] private static extern int DeleteService(IntPtr hService); [DllImport("advapi32.dll")] private static extern int ControlService(IntPtr hService, ServiceControl dwControl, SERVICE_STATUS lpServiceStatus); [DllImport("advapi32.dll", EntryPoint = "StartServiceA")] private static extern int StartService(IntPtr hService, int dwNumServiceArgs, int lpServiceArgVectors); ///  /// Takes a service name and sortinges to stop and then uninstall the windows serviceError ///  /// The windows service name to uninstall public static void Uninstall(ssortingng ServiceName) { IntPtr scman = OpenSCManager(ServiceManagerRights.Connect); try { IntPtr service = OpenService(scman, ServiceName, ServiceRights.StandardRightsRequired | ServiceRights.Stop | ServiceRights.QueryStatus); if (service == IntPtr.Zero) { throw new ApplicationException("Service not installed."); } try { StopService(service); int ret = DeleteService(service); if (ret == 0) { int error = Marshal.GetLastWin32Error(); throw new ApplicationException("Could not delete service " + error); } } finally { CloseServiceHandle(service); } } finally { CloseServiceHandle(scman); } } ///  /// Accepts a service name and returns true if the service with that service name exists ///  /// The service name that we will check for existence /// True if that service exists false otherwise public static bool ServiceIsInstalled(ssortingng ServiceName) { IntPtr scman = OpenSCManager(ServiceManagerRights.Connect); try { IntPtr service = OpenService(scman, ServiceName, ServiceRights.QueryStatus); if (service == IntPtr.Zero) return false; CloseServiceHandle(service); return true; } finally { CloseServiceHandle(scman); } } ///  /// Takes a service name, a service display name and the path to the service executable and installs / starts the windows service. ///  /// The service name that this service will have /// The display name that this service will have /// The path to the executable of the service public static void InstallAndStart(ssortingng ServiceName, ssortingng DisplayName, ssortingng FileName) { IntPtr scman = OpenSCManager(ServiceManagerRights.Connect | ServiceManagerRights.CreateService); try { IntPtr service = OpenService(scman, ServiceName, ServiceRights.QueryStatus | ServiceRights.Start); if (service == IntPtr.Zero) { service = CreateService(scman, ServiceName, DisplayName, ServiceRights.QueryStatus | ServiceRights.Start, SERVICE_WIN32_OWN_PROCESS, ServiceBootFlag.AutoStart, ServiceError.Normal, FileName, null, IntPtr.Zero, null, null, null); } if (service == IntPtr.Zero) { throw new ApplicationException("Failed to install service."); } try { StartService(service); } finally { CloseServiceHandle(service); } } finally { CloseServiceHandle(scman); } } ///  /// Takes a service name and starts it ///  /// The service name public static void StartService(ssortingng Name) { IntPtr scman = OpenSCManager(ServiceManagerRights.Connect); try { IntPtr hService = OpenService(scman, Name, ServiceRights.QueryStatus | ServiceRights.Start); if (hService == IntPtr.Zero) { throw new ApplicationException("Could not open service."); } try { StartService(hService); } finally { CloseServiceHandle(hService); } } finally { CloseServiceHandle(scman); } } ///  /// Stops the provided windows service ///  /// The service name that will be stopped public static void StopService(ssortingng Name) { IntPtr scman = OpenSCManager(ServiceManagerRights.Connect); try { IntPtr hService = OpenService(scman, Name, ServiceRights.QueryStatus | ServiceRights.Stop); if (hService == IntPtr.Zero) { throw new ApplicationException("Could not open service."); } try { StopService(hService); } finally { CloseServiceHandle(hService); } } finally { CloseServiceHandle(scman); } } ///  /// Stars the provided windows service ///  /// The handle to the windows service private static void StartService(IntPtr hService) { SERVICE_STATUS status = new SERVICE_STATUS(); StartService(hService, 0, 0); WaitForServiceStatus(hService, ServiceState.Starting, ServiceState.Run); } ///  /// Stops the provided windows service ///  /// The handle to the windows service private static void StopService(IntPtr hService) { SERVICE_STATUS status = new SERVICE_STATUS(); ControlService(hService, ServiceControl.Stop, status); WaitForServiceStatus(hService, ServiceState.Stopping, ServiceState.Stop); } ///  /// Takes a service name and returns the ServiceState of the corresponding service ///  /// The service name that we will check for his ServiceState /// The ServiceState of the service we wanted to check public static ServiceState GetServiceStatus(ssortingng ServiceName) { IntPtr scman = OpenSCManager(ServiceManagerRights.Connect); try { IntPtr hService = OpenService(scman, ServiceName, ServiceRights.QueryStatus); if (hService == IntPtr.Zero) { return ServiceState.NotFound; } try { return GetServiceStatus(hService); } finally { CloseServiceHandle(scman); } } finally { CloseServiceHandle(scman); } } ///  /// Gets the service state by using the handle of the provided windows service ///  /// The handle to the service /// The ServiceState of the service private static ServiceState GetServiceStatus(IntPtr hService) { SERVICE_STATUS ssStatus = new SERVICE_STATUS(); if (QueryServiceStatus(hService, ssStatus) == 0) { throw new ApplicationException("Failed to query service status."); } return ssStatus.dwCurrentState; } ///  /// Returns true when the service status has been changes from wait status to desired status /// ,this method waits around 10 seconds for this operation. ///  /// The handle to the service /// The current state of the service /// The desired state of the service /// bool if the service has successfully changed states within the allowed timeline private static bool WaitForServiceStatus(IntPtr hService, ServiceState WaitStatus, ServiceState DesiredStatus) { SERVICE_STATUS ssStatus = new SERVICE_STATUS(); int dwOldCheckPoint; int dwStartTickCount; QueryServiceStatus(hService, ssStatus); if (ssStatus.dwCurrentState == DesiredStatus) return true; dwStartTickCount = Environment.TickCount; dwOldCheckPoint = ssStatus.dwCheckPoint; while (ssStatus.dwCurrentState == WaitStatus) { // Do not wait longer than the wait hint. A good interval is // one tenth the wait hint, but no less than 1 second and no // more than 10 seconds. int dwWaitTime = ssStatus.dwWaitHint / 10; if (dwWaitTime < 1000) dwWaitTime = 1000; else if (dwWaitTime > 10000) dwWaitTime = 10000; System.Threading.Thread.Sleep(dwWaitTime); // Check the status again. if (QueryServiceStatus(hService, ssStatus) == 0) break; if (ssStatus.dwCheckPoint > dwOldCheckPoint) { // The service is making progress. dwStartTickCount = Environment.TickCount; dwOldCheckPoint = ssStatus.dwCheckPoint; } else { if (Environment.TickCount - dwStartTickCount > ssStatus.dwWaitHint) { // No progress made within the wait hint break; } } } return (ssStatus.dwCurrentState == DesiredStatus); } ///  /// Opens the service manager ///  /// The service manager rights /// the handle to the service manager private static IntPtr OpenSCManager(ServiceManagerRights Rights) { IntPtr scman = OpenSCManager(null, null, Rights); if (scman == IntPtr.Zero) { throw new ApplicationException("Could not connect to service control manager."); } return scman; } #endregion } } 

Pour installer un service, exécutez la commande InstallAndStart comme suit:

  SystemControl.InstallAndStart( "apache", "Apache Web Server", @"""c:\apache\bin\httpd.exe"" -k runservice" ); 

Assurez-vous que le compte qui exécute le programme est autorisé à installer des services. Vous pouvez toujours “Exécuter en tant qu’administrateur” sur le programme.

J’ai également inclus plusieurs commandes pour un access non api qui n’installe pas ou ne supprime pas de services, mais vous pouvez les lister et en contrôler plusieurs (démarrer, arrêter, redémarrer). Vous n’avez vraiment besoin que d’élever les permissions pour installer ou supprimer des services.

Il existe quelques commandes pour obtenir et définir des variables d’environnement, telles que OPENSSL_CONF ou TEMP . Pour la plupart, les parameters et les noms de méthode doivent être assez explicites.

En cas d’installation d’une application de ligne de commande en tant que service Windows, essayez l’utilitaire ‘ NSSM ‘. Détails ServerFault connexes trouvés ici .

Processus QProc = new Process ();

QProc.StartInfo.FileName = “cmd”;

QProc.StartInfo.Arguments = “/ c InstallUtil” + “\” “+ filefullPath +” \ “”;

QProc.StartInfo.WorkingDirectory = Environment.GetEnvironmentVariable (“windir”) + @ “\ Microsoft.NET \ Framework \ v2.0.50727 \”;

  QProc.StartInfo.UseShellExecute = false; // QProc.StartInfo.CreateNoWindow = true; QProc.StartInfo.RedirectStandardOutput = true; QProc.Start(); //QProc.WaitForExit(); QProc.Close();