public static void Run(string[] _args) { if (_args.Length > 0) { var d = new ServiceDescriptor(); Win32Services svc = new WmiRoot().GetCollection <Win32Services>(); Console.WriteLine("Looking for service..."); Win32Service s = svc.Select(d.Id); var args = new List <string>(Array.AsReadOnly(_args)); if (args[0] == "/redirect") { // Redirect output // One might ask why we support this when the caller // can redirect the output easily. The answer is for supporting UAC. // On UAC-enabled Windows such as Vista, SCM operation requires // elevated privileges, thus winsw.exe needs to be launched // accordingly. This in turn limits what the caller can do, // and among other things it makes it difficult for the caller // to read stdout/stderr. Thus redirection becomes handy. var f = new FileStream(args[1], FileMode.Create); var w = new StreamWriter(f); w.AutoFlush = true; Console.SetOut(w); Console.SetError(w); var safeHandle = f.SafeFileHandle; SetStdHandle(-11, safeHandle.DangerousGetHandle()); // set stdout SetStdHandle(-12, safeHandle.DangerousGetHandle()); // set stder args = args.GetRange(2, args.Count - 2); } args[0] = args[0].ToLower(); if (args[0] == "install") { svc.Create( d.Id, d.Caption, "\"" + ServiceDescriptor.ExecutablePath + "\"", WMI.ServiceType.OwnProcess, ErrorControl.UserNotified, StartMode.Automatic, d.Interactive, d.ServiceDependencies); // update the description /* Somehow this doesn't work, even though it doesn't report an error * Win32Service s = svc.Select(d.Id); * s.Description = d.Description; * s.Commit(); */ // so using a classic method to set the description. Ugly. Registry.LocalMachine.OpenSubKey("System").OpenSubKey("CurrentControlSet").OpenSubKey("Services") .OpenSubKey(d.Id, true).SetValue("Description", d.Description); Console.WriteLine("Installed"); } if (args[0] == "uninstall") { if (s == null) { return; // there's no such service, so consider it already uninstalled } try { s.Delete(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion) { return; // it's already uninstalled, so consider it a success } throw e; } Console.WriteLine("Uninstalled"); } if (args[0] == "start") { Console.WriteLine("Requesting start"); if (s == null) { ThrowNoSuchService(); } s.StartService(); } if (args[0] == "stop") { Console.WriteLine("Requesting stop"); if (s == null) { ThrowNoSuchService(); } if (s.Started) { s.StopService(); Console.WriteLine("Stopped."); } else { Console.WriteLine("Not running."); } } if (args[0] == "restart") { Console.WriteLine("Requesting restart"); if (s == null) { ThrowNoSuchService(); } if (s.Started) { s.StopService(); } while (s.Started) { // XXX: Should give up after a while, and display error-message. Thread.Sleep(1000); s = svc.Select(d.Id); } s.StartService(); } if (args[0] == "status") { if (s == null) { Console.WriteLine("NonExistent"); } else if (s.Started) { Console.WriteLine("Started"); } else { Console.WriteLine("Stopped"); } } if (args[0] == "test") { WrapperService wsvc = new WrapperService(); wsvc.OnStart(args.ToArray()); Thread.Sleep(1000); wsvc.OnStop(); } if (args[0] == "version") { Version version = new Version(); Console.WriteLine("Compiled on " + version.version); } if (args[0] == "specs") { Dictionary <string, string> specs = new Dictionary <string, string>() { { "argument", d.Arguments }, { "beeponshutdown", d.BeepOnShutdown.ToString() }, { "name", d.Caption }, { "description", d.Description }, { "executable", d.Executable }, { "startargument", d.Startarguments }, { "stopexecutable", d.StopExecutable }, { "stopargument", d.Stoparguments }, { "id", d.Id }, { "interactive", d.Interactive.ToString() }, { "logpath", d.LogDirectory }, { "sleeptime", d.SleepTime.ToString() }, { "waithint", d.WaitHint.ToString() }, }; foreach (KeyValuePair <string, string> spec in specs) { Console.WriteLine(spec.Key + ": " + spec.Value); } Console.WriteLine("env:"); foreach (var k in d.EnvironmentVariables) { Console.WriteLine("\t" + k.Key + "=" + k.Value); } Console.WriteLine("downloads:"); foreach (var k in d.Downloads) { Console.WriteLine("\tFrom: " + k.From + "\tTo: " + k.To); } Console.WriteLine("depend:"); foreach (var k in d.ServiceDependencies) { Console.WriteLine("\t" + k); } Console.WriteLine("log:"); Console.WriteLine("\tmode: " + d.LogHandler.logmode()); if (d.LogHandler.GetType() == typeof(TimeBasedRollingLogAppender)) { Console.WriteLine("\tpattern: " + ((TimeBasedRollingLogAppender)d.LogHandler).Pattern); Console.WriteLine("\tperiod: " + ((TimeBasedRollingLogAppender)d.LogHandler).Period); } else if (d.LogHandler.GetType() == typeof(SizeBasedRollingLogAppender)) { Console.WriteLine("\tkeepFiles: " + ((SizeBasedRollingLogAppender)d.LogHandler).FilesToKeep); Console.WriteLine("\tsizeThreshold: " + ((SizeBasedRollingLogAppender)d.LogHandler).SizeThreshold); } } return; } ServiceBase.Run(new WrapperService()); }
// ReSharper disable once InconsistentNaming /// <summary> /// Runs the wrapper. /// </summary> /// <param name="_args">Arguments. If empty, WinSW will behave in the service mode. Otherwise - CLI mode</param> /// <param name="descriptor">Service descriptor. If null, it will be initialized within the method. /// In such case configs will be loaded from the XML Configuration File.</param> /// <exception cref="Exception">Any unhandled exception</exception> public static void Run(string[] _args, ServiceDescriptor descriptor = null) { bool isCLIMode = _args.Length > 0; // If descriptor is not specified, initialize the new one (and load configs from there) var d = descriptor ?? new ServiceDescriptor(); // Configure the wrapper-internal logging. // STDIN and STDOUT of the child process will be handled independently. InitLoggers(d, isCLIMode); if (isCLIMode) // CLI mode, in-service mode otherwise { Log.Debug("Starting ServiceWrapper in the CLI mode"); // Get service info for the future use Win32Services svc = new WmiRoot().GetCollection <Win32Services>(); Win32Service s = svc.Select(d.Id); var args = new List <string>(Array.AsReadOnly(_args)); if (args[0] == "/redirect") { // Redirect output // One might ask why we support this when the caller // can redirect the output easily. The answer is for supporting UAC. // On UAC-enabled Windows such as Vista, SCM operation requires // elevated privileges, thus winsw.exe needs to be launched // accordingly. This in turn limits what the caller can do, // and among other things it makes it difficult for the caller // to read stdout/stderr. Thus redirection becomes handy. var f = new FileStream(args[1], FileMode.Create); var w = new StreamWriter(f) { AutoFlush = true }; Console.SetOut(w); Console.SetError(w); var handle = f.SafeFileHandle; Kernel32.SetStdHandle(-11, handle); // set stdout Kernel32.SetStdHandle(-12, handle); // set stder args = args.GetRange(2, args.Count - 2); } args[0] = args[0].ToLower(); if (args[0] == "install") { Log.Info("Installing the service with id '" + d.Id + "'"); // Check if the service exists if (s != null) { Console.WriteLine("Service with id '" + d.Id + "' already exists"); Console.WriteLine("To install the service, delete the existing one or change service Id in the configuration file"); throw new Exception("Installation failure: Service with id '" + d.Id + "' already exists"); } string username = null, password = null; bool setallowlogonasaserviceright = false; if (args.Count > 1 && args[1] == "/p") { // we expected username/password on stdin Console.Write("Username: "******"Password: "******"Set Account rights to allow log on as a service (y/n)?: "); var keypressed = Console.ReadKey(); Console.WriteLine(); if (keypressed.Key == ConsoleKey.Y) { setallowlogonasaserviceright = true; } } else { if (d.HasServiceAccount()) { username = d.ServiceAccountUser; password = d.ServiceAccountPassword; setallowlogonasaserviceright = d.AllowServiceAcountLogonRight; } } if (setallowlogonasaserviceright) { LogonAsAService.AddLogonAsAServiceRight(username); } svc.Create( d.Id, d.Caption, "\"" + d.ExecutablePath + "\"", ServiceType.OwnProcess, ErrorControl.UserNotified, d.StartMode, d.Interactive, username, password, d.ServiceDependencies); // update the description /* Somehow this doesn't work, even though it doesn't report an error * Win32Service s = svc.Select(d.Id); * s.Description = d.Description; * s.Commit(); */ // so using a classic method to set the description. Ugly. Registry.LocalMachine.OpenSubKey("System").OpenSubKey("CurrentControlSet").OpenSubKey("Services") .OpenSubKey(d.Id, true).SetValue("Description", d.Description); var actions = d.FailureActions; var isDelayedAutoStart = d.StartMode == StartMode.Automatic && d.DelayedAutoStart; if (actions.Count > 0 || isDelayedAutoStart) { using (ServiceManager scm = new ServiceManager()) { using (Service sc = scm.Open(d.Id)) { // Delayed auto start if (isDelayedAutoStart) { sc.SetDelayedAutoStart(true); } // Set the failure actions if (actions.Count > 0) { sc.ChangeConfig(d.ResetFailureAfter, actions); } } } } return; } if (args[0] == "uninstall") { Log.Info("Uninstalling the service with id '" + d.Id + "'"); if (s == null) { Log.Warn("The service with id '" + d.Id + "' does not exist. Nothing to uninstall"); return; // there's no such service, so consider it already uninstalled } if (s.Started) { // We could fail the opeartion here, but it would be an incompatible change. // So it is just a warning Log.Warn("The service with id '" + d.Id + "' is running. It may be impossible to uninstall it"); } try { s.Delete(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion) { Log.Error("Failed to uninstall the service with id '" + d.Id + "'" + ". It has been marked for deletion."); // TODO: change the default behavior to Error? return; // it's already uninstalled, so consider it a success } else { Log.Fatal("Failed to uninstall the service with id '" + d.Id + "'. WMI Error code is '" + e.ErrorCode + "'"); } throw e; } return; } if (args[0] == "start") { Log.Info("Starting the service with id '" + d.Id + "'"); if (s == null) { ThrowNoSuchService(); } s.StartService(); return; } if (args[0] == "stop") { Log.Info("Stopping the service with id '" + d.Id + "'"); if (s == null) { ThrowNoSuchService(); } s.StopService(); return; } if (args[0] == "restart") { Log.Info("Restarting the service with id '" + d.Id + "'"); if (s == null) { ThrowNoSuchService(); } if (s.Started) { s.StopService(); } while (s.Started) { Thread.Sleep(1000); s = svc.Select(d.Id); } s.StartService(); return; } if (args[0] == "restart!") { Log.Info("Restarting the service with id '" + d.Id + "'"); // run restart from another process group. see README.md for why this is useful. STARTUPINFO si = new STARTUPINFO(); PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); bool result = Kernel32.CreateProcess(null, d.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, 0x200 /*CREATE_NEW_PROCESS_GROUP*/, IntPtr.Zero, null, ref si, out pi); if (!result) { throw new Exception("Failed to invoke restart: " + Marshal.GetLastWin32Error()); } return; } if (args[0] == "status") { Log.Debug("User requested the status of the process with id '" + d.Id + "'"); if (s == null) { Console.WriteLine("NonExistent"); } else if (s.Started) { Console.WriteLine("Started"); } else { Console.WriteLine("Stopped"); } return; } if (args[0] == "test") { WrapperService wsvc = new WrapperService(d); wsvc.OnStart(args.ToArray()); Thread.Sleep(1000); wsvc.OnStop(); return; } if (args[0] == "testwait") { WrapperService wsvc = new WrapperService(d); wsvc.OnStart(args.ToArray()); Console.WriteLine("Press any key to stop the service..."); Console.Read(); wsvc.OnStop(); return; } if (args[0] == "help" || args[0] == "--help" || args[0] == "-h" || args[0] == "-?" || args[0] == "/?") { printHelp(); return; } if (args[0] == "version") { printVersion(); return; } Console.WriteLine("Unknown command: " + args[0]); printAvailableCommandsInfo(); throw new Exception("Unknown command: " + args[0]); } else { Log.Info("Starting ServiceWrapper in the service mode"); } Run(new WrapperService(d)); }
public static void Run(string[] args) { if (args.Length > 0) { var d = new ServiceDescriptor(); Win32Services svc = new WmiRoot().GetCollection<Win32Services>(); Win32Service s = svc.Select(d.Id); args[0] = args[0].ToLower(); if (args[0] == "install") { svc.Create( d.Id, d.Caption, "\"" + ServiceDescriptor.ExecutablePath + "\"", WMI.ServiceType.OwnProcess, ErrorControl.UserNotified, StartMode.Automatic, d.Interactive, d.ServiceDependencies); // update the description /* Somehow this doesn't work, even though it doesn't report an error Win32Service s = svc.Select(d.Id); s.Description = d.Description; s.Commit(); */ // so using a classic method to set the description. Ugly. Registry.LocalMachine.OpenSubKey("System").OpenSubKey("CurrentControlSet").OpenSubKey("Services") .OpenSubKey(d.Id, true).SetValue("Description", d.Description); } if (args[0] == "uninstall") { if (s == null) return; // there's no such service, so consider it already uninstalled try { s.Delete(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion) return; // it's already uninstalled, so consider it a success throw e; } } if (args[0] == "start") { if (s == null) ThrowNoSuchService(); s.StartService(); } if (args[0] == "stop") { if (s == null) ThrowNoSuchService(); s.StopService(); } if (args[0] == "restart") { if (s == null) ThrowNoSuchService(); if (s.Started) s.StopService(); while (s.Started) { Thread.Sleep(1000); s = svc.Select(d.Id); } s.StartService(); } if (args[0] == "status") { if (s == null) Console.WriteLine("NonExistent"); else if (s.Started) Console.WriteLine("Started"); else Console.WriteLine("Stopped"); } if (args[0] == "test") { WrapperService wsvc = new WrapperService(); wsvc.OnStart(args); Thread.Sleep(1000); wsvc.OnStop(); } return; } ServiceBase.Run(new WrapperService()); }
// ReSharper disable once InconsistentNaming /// <summary> /// Runs the wrapper. /// </summary> /// <param name="_args">Arguments.</param> /// <param name="descriptor">Service descriptor. If null, it will be initialized within the method. /// In such case configs will be loaded from the XML Configuration File.</param> /// <exception cref="Exception">Any unhandled exception</exception> public static void Run(string[] _args, ServiceDescriptor?descriptor = null) { bool inCliMode = _args.Length > 0; // If descriptor is not specified, initialize the new one (and load configs from there) descriptor ??= new ServiceDescriptor(); // Configure the wrapper-internal logging. // STDIN and STDOUT of the child process will be handled independently. InitLoggers(descriptor, inCliMode); if (!inCliMode) { Log.Debug("Starting WinSW in the service mode"); Run(new WrapperService(descriptor)); return; } Log.Debug("Starting WinSW in the CLI mode"); if (_args.Length == 0) { printHelp(); return; } // Get service info for the future use Win32Services svc = new WmiRoot().GetCollection <Win32Services>(); Win32Service? s = svc.Select(descriptor.Id); var args = new List <string>(Array.AsReadOnly(_args)); if (args[0] == "/redirect") { // Redirect output // One might ask why we support this when the caller // can redirect the output easily. The answer is for supporting UAC. // On UAC-enabled Windows such as Vista, SCM operation requires // elevated privileges, thus winsw.exe needs to be launched // accordingly. This in turn limits what the caller can do, // and among other things it makes it difficult for the caller // to read stdout/stderr. Thus redirection becomes handy. var f = new FileStream(args[1], FileMode.Create); var w = new StreamWriter(f) { AutoFlush = true }; Console.SetOut(w); Console.SetError(w); var handle = f.SafeFileHandle; Kernel32.SetStdHandle(-11, handle); // set stdout Kernel32.SetStdHandle(-12, handle); // set stder args = args.GetRange(2, args.Count - 2); } args[0] = args[0].ToLower(); if (args[0] == "install") { Log.Info("Installing the service with id '" + descriptor.Id + "'"); // Check if the service exists if (s != null) { Console.WriteLine("Service with id '" + descriptor.Id + "' already exists"); Console.WriteLine("To install the service, delete the existing one or change service Id in the configuration file"); throw new Exception("Installation failure: Service with id '" + descriptor.Id + "' already exists"); } string?username = null; string?password = null; bool setallowlogonasaserviceright = false; // This variable is very readable. if (args.Count > 1 && args[1] == "/p") { // we expected username/password on stdin Console.Write("Username: "******"Password: "******"Set Account rights to allow log on as a service (y/n)?: "); var keypressed = Console.ReadKey(); Console.WriteLine(); if (keypressed.Key == ConsoleKey.Y) { setallowlogonasaserviceright = true; } } else { if (descriptor.HasServiceAccount()) { username = descriptor.ServiceAccountUser; password = descriptor.ServiceAccountPassword; setallowlogonasaserviceright = descriptor.AllowServiceAcountLogonRight; } } if (setallowlogonasaserviceright) { LogonAsAService.AddLogonAsAServiceRight(username !); } svc.Create( descriptor.Id, descriptor.Caption, "\"" + descriptor.ExecutablePath + "\"", ServiceType.OwnProcess, ErrorControl.UserNotified, descriptor.StartMode.ToString(), descriptor.Interactive, username, password, descriptor.ServiceDependencies); using ServiceManager scm = new ServiceManager(); using Service sc = scm.Open(descriptor.Id); sc.SetDescription(descriptor.Description); var actions = descriptor.FailureActions; if (actions.Count > 0) { sc.SetFailureActions(descriptor.ResetFailureAfter, actions); } var isDelayedAutoStart = descriptor.StartMode == StartMode.Automatic && descriptor.DelayedAutoStart; if (isDelayedAutoStart) { sc.SetDelayedAutoStart(true); } if (descriptor.SecurityDescriptor != null) { // throws ArgumentException RawSecurityDescriptor rawSecurityDescriptor = new RawSecurityDescriptor(descriptor.SecurityDescriptor); byte[] securityDescriptorBytes = new byte[rawSecurityDescriptor.BinaryLength]; rawSecurityDescriptor.GetBinaryForm(securityDescriptorBytes, 0); _ = Advapi32.SetServiceObjectSecurity(sc.Handle, SecurityInfos.DiscretionaryAcl, securityDescriptorBytes); } return; } if (args[0] == "uninstall") { Log.Info("Uninstalling the service with id '" + descriptor.Id + "'"); if (s is null) { Log.Warn("The service with id '" + descriptor.Id + "' does not exist. Nothing to uninstall"); return; // there's no such service, so consider it already uninstalled } if (s.Started) { // We could fail the opeartion here, but it would be an incompatible change. // So it is just a warning Log.Warn("The service with id '" + descriptor.Id + "' is running. It may be impossible to uninstall it"); } try { s.Delete(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion) { Log.Error("Failed to uninstall the service with id '" + descriptor.Id + "'" + ". It has been marked for deletion."); // TODO: change the default behavior to Error? return; // it's already uninstalled, so consider it a success } else { Log.Fatal("Failed to uninstall the service with id '" + descriptor.Id + "'. WMI Error code is '" + e.ErrorCode + "'"); } throw e; } return; } if (args[0] == "start") { Log.Info("Starting the service with id '" + descriptor.Id + "'"); if (s is null) { ThrowNoSuchService(); } try { s.StartService(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceAlreadyRunning) { Log.Info($"The service with ID '{descriptor.Id}' has already been started"); } else { throw; } } return; } if (args[0] == "stop") { Log.Info("Stopping the service with id '" + descriptor.Id + "'"); if (s is null) { ThrowNoSuchService(); } try { s.StopService(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceCannotAcceptControl) { Log.Info($"The service with ID '{descriptor.Id}' is not running"); } else { throw; } } return; } if (args[0] == "restart") { Log.Info("Restarting the service with id '" + descriptor.Id + "'"); if (s is null) { ThrowNoSuchService(); } if (s.Started) { s.StopService(); } while (s.Started) { Thread.Sleep(1000); s = svc.Select(descriptor.Id) !; } s.StartService(); return; } if (args[0] == "restart!") { Log.Info("Restarting the service with id '" + descriptor.Id + "'"); // run restart from another process group. see README.md for why this is useful. bool result = Kernel32.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, Kernel32.CREATE_NEW_PROCESS_GROUP, IntPtr.Zero, null, default, out _);
public static void Run(string[] argsArray, IWinSWConfiguration?descriptor = null) { bool inConsoleMode = argsArray.Length > 0; // If descriptor is not specified, initialize the new one (and load configs from there) descriptor ??= GetServiceDescriptor(); // Configure the wrapper-internal logging. // STDOUT and STDERR of the child process will be handled independently. InitLoggers(descriptor, inConsoleMode); if (!inConsoleMode) { Log.Debug("Starting WinSW in service mode"); ServiceBase.Run(new WrapperService(descriptor)); return; } Log.Debug("Starting WinSW in console mode"); if (argsArray.Length == 0) { PrintHelp(); return; } // Get service info for the future use IWin32Services svcs = new WmiRoot().GetCollection <IWin32Services>(); IWin32Service? svc = svcs.Select(descriptor.Id); var args = new List <string>(Array.AsReadOnly(argsArray)); if (args[0] == "/redirect") { var f = new FileStream(args[1], FileMode.Create); var w = new StreamWriter(f) { AutoFlush = true }; Console.SetOut(w); Console.SetError(w); var handle = f.SafeFileHandle; _ = Kernel32.SetStdHandle(-11, handle); // set stdout _ = Kernel32.SetStdHandle(-12, handle); // set stder args = args.GetRange(2, args.Count - 2); } bool elevated; if (args[0] == "/elevated") { elevated = true; _ = ConsoleApis.FreeConsole(); _ = ConsoleApis.AttachConsole(ConsoleApis.ATTACH_PARENT_PROCESS); args = args.GetRange(1, args.Count - 1); } else if (Environment.OSVersion.Version.Major == 5) { // Windows XP elevated = true; } else { elevated = IsProcessElevated(); } switch (args[0].ToLower()) { case "install": Install(); return; case "uninstall": Uninstall(); return; case "start": Start(); return; case "stop": Stop(); return; case "stopwait": StopWait(); return; case "restart": Restart(); return; case "restart!": RestartSelf(); return; case "status": Status(); return; case "test": Test(); return; case "testwait": TestWait(); return; case "help": case "--help": case "-h": case "-?": case "/?": PrintHelp(); return; case "version": PrintVersion(); return; default: Console.WriteLine("Unknown command: " + args[0]); PrintAvailableCommands(); #pragma warning disable S112 // General exceptions should never be thrown throw new Exception("Unknown command: " + args[0]); #pragma warning restore S112 // General exceptions should never be thrown } void Install() { if (!elevated) { Elevate(); return; } Log.Info("Installing the service with id '" + descriptor.Id + "'"); // Check if the service exists if (svc != null) { Console.WriteLine("Service with id '" + descriptor.Id + "' already exists"); Console.WriteLine("To install the service, delete the existing one or change service Id in the configuration file"); #pragma warning disable S112 // General exceptions should never be thrown throw new Exception("Installation failure: Service with id '" + descriptor.Id + "' already exists"); #pragma warning restore S112 // General exceptions should never be thrown } string?username = null; string?password = null; bool allowServiceLogonRight = false; if (args.Count > 1 && args[1] == "/p") { Console.Write("Username: "******"Password: "******"Set Account rights to allow log on as a service (y/n)?: "); var keypressed = Console.ReadKey(); Console.WriteLine(); if (keypressed.Key == ConsoleKey.Y) { allowServiceLogonRight = true; } } else { if (descriptor.ServiceAccount.HasServiceAccount()) { username = descriptor.ServiceAccount.ServiceAccountUser; password = descriptor.ServiceAccount.ServiceAccountPassword; allowServiceLogonRight = descriptor.ServiceAccount.AllowServiceAcountLogonRight; } } if (allowServiceLogonRight) { Security.AddServiceLogonRight(descriptor.ServiceAccount.ServiceAccountDomain !, descriptor.ServiceAccount.ServiceAccountName !); } svcs.Create( descriptor.Id, descriptor.Caption, "\"" + descriptor.ExecutablePath + "\"", ServiceType.OwnProcess, ErrorControl.UserNotified, descriptor.StartMode.ToString(), descriptor.Interactive, username, password, descriptor.ServiceDependencies); using ServiceManager scm = ServiceManager.Open(); using Service sc = scm.OpenService(descriptor.Id); sc.SetDescription(descriptor.Description); SC_ACTION[] actions = descriptor.FailureActions; if (actions.Length > 0) { sc.SetFailureActions(descriptor.ResetFailureAfter, actions); } bool isDelayedAutoStart = descriptor.StartMode == StartMode.Automatic && descriptor.DelayedAutoStart; if (isDelayedAutoStart) { sc.SetDelayedAutoStart(true); } string?securityDescriptor = descriptor.SecurityDescriptor; if (securityDescriptor != null) { // throws ArgumentException sc.SetSecurityDescriptor(new RawSecurityDescriptor(securityDescriptor)); } string eventLogSource = descriptor.Id; if (!EventLog.SourceExists(eventLogSource)) { EventLog.CreateEventSource(eventLogSource, "Application"); } } void Uninstall() { if (!elevated) { Elevate(); return; } Log.Info("Uninstalling the service with id '" + descriptor.Id + "'"); if (svc is null) { Log.Warn("The service with id '" + descriptor.Id + "' does not exist. Nothing to uninstall"); return; // there's no such service, so consider it already uninstalled } if (svc.Started) { // We could fail the opeartion here, but it would be an incompatible change. // So it is just a warning Log.Warn("The service with id '" + descriptor.Id + "' is running. It may be impossible to uninstall it"); } try { svc.Delete(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion) { Log.Error("Failed to uninstall the service with id '" + descriptor.Id + "'" + ". It has been marked for deletion."); // TODO: change the default behavior to Error? return; // it's already uninstalled, so consider it a success } else { Log.Fatal("Failed to uninstall the service with id '" + descriptor.Id + "'. WMI Error code is '" + e.ErrorCode + "'"); } throw; } } void Start() { if (!elevated) { Elevate(); return; } Log.Info("Starting the service with id '" + descriptor.Id + "'"); if (svc is null) { ThrowNoSuchService(); } try { svc.StartService(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceAlreadyRunning) { Log.Info($"The service with ID '{descriptor.Id}' has already been started"); } else { throw; } } } void Stop() { if (!elevated) { Elevate(); return; } Log.Info("Stopping the service with id '" + descriptor.Id + "'"); if (svc is null) { ThrowNoSuchService(); } try { svc.StopService(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceCannotAcceptControl) { Log.Info($"The service with ID '{descriptor.Id}' is not running"); } else { throw; } } } void StopWait() { if (!elevated) { Elevate(); return; } Log.Info("Stopping the service with id '" + descriptor.Id + "'"); if (svc is null) { ThrowNoSuchService(); } if (svc.Started) { svc.StopService(); } while (svc != null && svc.Started) { Log.Info("Waiting the service to stop..."); Thread.Sleep(1000); svc = svcs.Select(descriptor.Id); } Log.Info("The service stopped."); } void Restart() { if (!elevated) { Elevate(); return; } Log.Info("Restarting the service with id '" + descriptor.Id + "'"); if (svc is null) { ThrowNoSuchService(); } if (svc.Started) { svc.StopService(); } while (svc.Started) { Thread.Sleep(1000); svc = svcs.Select(descriptor.Id) !; } svc.StartService(); } void RestartSelf() { if (!elevated) { throw new UnauthorizedAccessException("Access is denied."); } Log.Info("Restarting the service with id '" + descriptor.Id + "'"); // run restart from another process group. see README.md for why this is useful. bool result = ProcessApis.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, ProcessApis.CREATE_NEW_PROCESS_GROUP, IntPtr.Zero, null, default, out _);
public static void Run(string[] _args) { if (_args.Length > 0) { var d = new ServiceDescriptor(); Win32Services svc = new WmiRoot().GetCollection<Win32Services>(); Win32Service s = svc.Select(d.Id); var args = new List<string>(Array.AsReadOnly(_args)); if (args[0] == "/redirect") { // Redirect output // One might ask why we support this when the caller // can redirect the output easily. The answer is for supporting UAC. // On UAC-enabled Windows such as Vista, SCM operation requires // elevated privileges, thus winsw.exe needs to be launched // accordingly. This in turn limits what the caller can do, // and among other things it makes it difficult for the caller // to read stdout/stderr. Thus redirection becomes handy. var f = new FileStream(args[1], FileMode.Create); var w = new StreamWriter(f); w.AutoFlush = true; Console.SetOut(w); Console.SetError(w); var handle = f.Handle; SetStdHandle(-11, handle); // set stdout SetStdHandle(-12, handle); // set stder args = args.GetRange(2, args.Count - 2); } args[0] = args[0].ToLower(); if (args[0] == "install") { svc.Create( d.Id, d.Caption, "\""+ServiceDescriptor.ExecutablePath+"\"", WMI.ServiceType.OwnProcess, ErrorControl.UserNotified, StartMode.Automatic, d.Interactive, d.ServiceDependencies); // update the description /* Somehow this doesn't work, even though it doesn't report an error Win32Service s = svc.Select(d.Id); s.Description = d.Description; s.Commit(); */ // so using a classic method to set the description. Ugly. Registry.LocalMachine.OpenSubKey("System").OpenSubKey("CurrentControlSet").OpenSubKey("Services") .OpenSubKey(d.Id, true).SetValue("Description", d.Description); } if (args[0] == "uninstall") { if (s == null) return; // there's no such service, so consider it already uninstalled try { s.Delete(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion) return; // it's already uninstalled, so consider it a success throw e; } } if (args[0] == "start") { if (s == null) ThrowNoSuchService(); s.StartService(); } if (args[0] == "stop") { if (s == null) ThrowNoSuchService(); s.StopService(); } if (args[0] == "restart") { if (s == null) ThrowNoSuchService(); if(s.Started) s.StopService(); while (s.Started) { Thread.Sleep(1000); s = svc.Select(d.Id); } s.StartService(); } if (args[0] == "status") { if (s == null) Console.WriteLine("NonExistent"); else if (s.Started) Console.WriteLine("Started"); else Console.WriteLine("Stopped"); } if (args[0] == "test") { WrapperService wsvc = new WrapperService(); wsvc.OnStart(args.ToArray()); Thread.Sleep(1000); wsvc.OnStop(); } return; } ServiceBase.Run(new WrapperService()); }
// ReSharper disable once InconsistentNaming public static void Run(string[] _args, ServiceDescriptor descriptor = null) { if (_args.Length > 0) { var d = descriptor ?? new ServiceDescriptor(); var svc = new WmiRoot().GetCollection<Win32Services>(); var s = svc.Select(d.Id); var args = new List<string>(Array.AsReadOnly(_args)); if (args[0] == "/redirect") { // Redirect output // One might ask why we support this when the caller // can redirect the output easily. The answer is for supporting UAC. // On UAC-enabled Windows such as Vista, SCM operation requires // elevated privileges, thus winsw.exe needs to be launched // accordingly. This in turn limits what the caller can do, // and among other things it makes it difficult for the caller // to read stdout/stderr. Thus redirection becomes handy. var f = new FileStream(args[1], FileMode.Create); var w = new StreamWriter(f) {AutoFlush = true}; Console.SetOut(w); Console.SetError(w); var handle = f.Handle; Kernel32.SetStdHandle(-11, handle); // set stdout Kernel32.SetStdHandle(-12, handle); // set stder args = args.GetRange(2, args.Count - 2); } args[0] = args[0].ToLower(); if (args[0] == "install") { // Check if the service exists if (s != null) { Console.WriteLine("Service with id '" + d.Id + "' already exists"); Console.WriteLine( "To install the service, delete the existing one or change service Id in the configuration file"); throw new Exception("Installation failure: Service with id '" + d.Id + "' already exists"); } string username = null, password = null; var setallowlogonasaserviceright = false; if (args.Count > 1 && args[1] == "/p") { // we expected username/password on stdin Console.Write("Username: "******"Password: "******"Set Account rights to allow log on as a service (y/n)?: "); var keypressed = Console.ReadKey(); Console.WriteLine(); if (keypressed.Key == ConsoleKey.Y) { setallowlogonasaserviceright = true; } } else { if (d.HasServiceAccount()) { username = d.ServiceAccountUser; password = d.ServiceAccountPassword; setallowlogonasaserviceright = d.AllowServiceAcountLogonRight; } } if (setallowlogonasaserviceright) { LogonAsAService.AddLogonAsAServiceRight(username); } svc.Create( d.Id, d.Caption, "\"" + d.ExecutablePath + "\"", ServiceType.OwnProcess, ErrorControl.UserNotified, d.StartMode, d.Interactive, username, password, d.ServiceDependencies); // update the description /* Somehow this doesn't work, even though it doesn't report an error Win32Service s = svc.Select(d.Id); s.Description = d.Description; s.Commit(); */ // so using a classic method to set the description. Ugly. Registry.LocalMachine.OpenSubKey("System").OpenSubKey("CurrentControlSet").OpenSubKey("Services") .OpenSubKey(d.Id, true).SetValue("Description", d.Description); var actions = d.FailureActions; if (actions.Count > 0) { // set the failure actions using (var scm = new ServiceManager()) { using (var sc = scm.Open(d.Id)) { sc.ChangeConfig(d.ResetFailureAfter, actions); } } } return; } if (args[0] == "uninstall") { if (s == null) { Console.WriteLine("Warning! The service with id '" + d.Id + "' does not exist. Nothing to uninstall"); return; // there's no such service, so consider it already uninstalled } try { s.Delete(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion) return; // it's already uninstalled, so consider it a success throw e; } return; } if (args[0] == "start") { if (s == null) ThrowNoSuchService(); s.StartService(); return; } if (args[0] == "stop") { if (s == null) ThrowNoSuchService(); s.StopService(); return; } if (args[0] == "restart") { if (s == null) ThrowNoSuchService(); if (s.Started) s.StopService(); while (s.Started) { Thread.Sleep(1000); s = svc.Select(d.Id); } s.StartService(); return; } if (args[0] == "restart!") { // run restart from another process group. see README.md for why this is useful. var si = new STARTUPINFO(); var pi = new PROCESS_INFORMATION(); var result = Kernel32.CreateProcess(null, d.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, 0x200 /*CREATE_NEW_PROCESS_GROUP*/, IntPtr.Zero, null, ref si, out pi); if (!result) { throw new Exception("Failed to invoke restart: " + Marshal.GetLastWin32Error()); } return; } if (args[0] == "status") { if (s == null) Console.WriteLine("NonExistent"); else if (s.Started) Console.WriteLine("Started"); else Console.WriteLine("Stopped"); return; } if (args[0] == "test") { var wsvc = new WrapperService(); wsvc.OnStart(args.ToArray()); Thread.Sleep(1000); wsvc.OnStop(); return; } if (args[0] == "help" || args[0] == "--help" || args[0] == "-h" || args[0] == "-?" || args[0] == "/?") { printHelp(); return; } if (args[0] == "version") { printVersion(); return; } Console.WriteLine("Unknown command: " + args[0]); printAvailableCommandsInfo(); throw new Exception("Unknown command: " + args[0]); } Run(new WrapperService()); }
public static void Run(string[] _args) { if (_args.Length > 0) { var d = new ServiceDescriptor(); Win32Services svc = new WmiRoot().GetCollection<Win32Services>(); Console.WriteLine("Looking for service..."); Win32Service s = svc.Select(d.Id); var args = new List<string>(Array.AsReadOnly(_args)); if (args[0] == "/redirect") { // Redirect output // One might ask why we support this when the caller // can redirect the output easily. The answer is for supporting UAC. // On UAC-enabled Windows such as Vista, SCM operation requires // elevated privileges, thus winsw.exe needs to be launched // accordingly. This in turn limits what the caller can do, // and among other things it makes it difficult for the caller // to read stdout/stderr. Thus redirection becomes handy. var f = new FileStream(args[1], FileMode.Create); var w = new StreamWriter(f); w.AutoFlush = true; Console.SetOut(w); Console.SetError(w); var safeHandle = f.SafeFileHandle; SetStdHandle(-11, safeHandle.DangerousGetHandle()); // set stdout SetStdHandle(-12, safeHandle.DangerousGetHandle()); // set stder args = args.GetRange(2, args.Count - 2); } args[0] = args[0].ToLower(); if (args[0] == "install") { svc.Create( d.Id, d.Caption, "\""+ServiceDescriptor.ExecutablePath+"\"", WMI.ServiceType.OwnProcess, ErrorControl.UserNotified, StartMode.Automatic, d.Interactive, d.ServiceDependencies); // update the description /* Somehow this doesn't work, even though it doesn't report an error Win32Service s = svc.Select(d.Id); s.Description = d.Description; s.Commit(); */ // so using a classic method to set the description. Ugly. Registry.LocalMachine.OpenSubKey("System").OpenSubKey("CurrentControlSet").OpenSubKey("Services") .OpenSubKey(d.Id, true).SetValue("Description", d.Description); Console.WriteLine("Installed"); } if (args[0] == "uninstall") { if (s == null) return; // there's no such service, so consider it already uninstalled try { s.Delete(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion) return; // it's already uninstalled, so consider it a success throw e; } Console.WriteLine("Uninstalled"); } if (args[0] == "start") { Console.WriteLine("Requesting start"); if (s == null) ThrowNoSuchService(); s.StartService(); } if (args[0] == "stop") { Console.WriteLine("Requesting stop"); if (s == null) ThrowNoSuchService(); if (s.Started) { s.StopService(); Console.WriteLine("Stopped."); } else { Console.WriteLine("Not running."); } } if (args[0] == "restart") { Console.WriteLine("Requesting restart"); if (s == null) ThrowNoSuchService(); if(s.Started) s.StopService(); while (s.Started) { // XXX: Should give up after a while, and display error-message. Thread.Sleep(1000); s = svc.Select(d.Id); } s.StartService(); } if (args[0] == "status") { if (s == null) Console.WriteLine("NonExistent"); else if (s.Started) Console.WriteLine("Started"); else Console.WriteLine("Stopped"); } if (args[0] == "test") { WrapperService wsvc = new WrapperService(); wsvc.OnStart(args.ToArray()); Thread.Sleep(1000); wsvc.OnStop(); } if (args[0] == "version") { Version version = new Version(); Console.WriteLine("Compiled on " + version.version); } if (args[0] == "specs") { Dictionary<string, string> specs = new Dictionary<string, string>() { {"argument", d.Arguments}, {"beeponshutdown", d.BeepOnShutdown.ToString()}, {"name", d.Caption}, {"description", d.Description}, {"executable", d.Executable}, {"startargument", d.Startarguments}, {"stopexecutable", d.StopExecutable}, {"stopargument", d.Stoparguments}, {"id", d.Id}, {"interactive", d.Interactive.ToString()}, {"logpath", d.LogDirectory}, {"sleeptime", d.SleepTime.ToString()}, {"waithint", d.WaitHint.ToString()}, }; foreach (KeyValuePair<string, string> spec in specs) { Console.WriteLine(spec.Key + ": " + spec.Value); } Console.WriteLine("env:"); foreach (var k in d.EnvironmentVariables) { Console.WriteLine("\t" + k.Key + "=" + k.Value); } Console.WriteLine("downloads:"); foreach (var k in d.Downloads) { Console.WriteLine("\tFrom: " + k.From + "\tTo: " + k.To); } Console.WriteLine("depend:"); foreach (var k in d.ServiceDependencies) { Console.WriteLine("\t" + k); } Console.WriteLine("log:"); Console.WriteLine("\tmode: " + d.LogHandler.logmode()); if (d.LogHandler.GetType() == typeof(TimeBasedRollingLogAppender)) { Console.WriteLine("\tpattern: " + ((TimeBasedRollingLogAppender)d.LogHandler).Pattern); Console.WriteLine("\tperiod: " + ((TimeBasedRollingLogAppender)d.LogHandler).Period); } else if (d.LogHandler.GetType() == typeof(SizeBasedRollingLogAppender)) { Console.WriteLine("\tkeepFiles: " + ((SizeBasedRollingLogAppender)d.LogHandler).FilesToKeep); Console.WriteLine("\tsizeThreshold: " + ((SizeBasedRollingLogAppender)d.LogHandler).SizeThreshold); } } return; } ServiceBase.Run(new WrapperService()); }
public static void Run(string[] _args) { if (_args.Length > 0) { var d = new ServiceDescriptor(); Win32Services svc = new WmiRoot().GetCollection <Win32Services>(); Win32Service s = svc.Select(d.Id); var args = new List <string>(Array.AsReadOnly(_args)); if (args[0] == "/redirect") { // Redirect output // One might ask why we support this when the caller // can redirect the output easily. The answer is for supporting UAC. // On UAC-enabled Windows such as Vista, SCM operation requires // elevated privileges, thus winsw.exe needs to be launched // accordingly. This in turn limits what the caller can do, // and among other things it makes it difficult for the caller // to read stdout/stderr. Thus redirection becomes handy. var f = new FileStream(args[1], FileMode.Create); var w = new StreamWriter(f); w.AutoFlush = true; Console.SetOut(w); Console.SetError(w); var handle = f.Handle; Kernel32.SetStdHandle(-11, handle); // set stdout Kernel32.SetStdHandle(-12, handle); // set stder args = args.GetRange(2, args.Count - 2); } args[0] = args[0].ToLower(); if (args[0] == "install") { string username = null, password = null; if (args.Count > 1 && args[1] == "/p") { // we expected username/password on stdin Console.Write("Username: "******"Password: "******"\"" + d.ExecutablePath + "\"", WMI.ServiceType.OwnProcess, ErrorControl.UserNotified, StartMode.Automatic, d.Interactive, username, password, d.ServiceDependencies); // update the description /* Somehow this doesn't work, even though it doesn't report an error * Win32Service s = svc.Select(d.Id); * s.Description = d.Description; * s.Commit(); */ // so using a classic method to set the description. Ugly. Registry.LocalMachine.OpenSubKey("System").OpenSubKey("CurrentControlSet").OpenSubKey("Services") .OpenSubKey(d.Id, true).SetValue("Description", d.Description); var actions = d.FailureActions; if (actions.Count > 0) {// set the failure actions using (ServiceManager scm = new ServiceManager()) { using (Service sc = scm.Open(d.Id)) { sc.ChangeConfig(d.ResetFailureAfter, actions); } } } } if (args[0] == "uninstall") { if (s == null) { return; // there's no such service, so consider it already uninstalled } try { s.Delete(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion) { return; // it's already uninstalled, so consider it a success } throw e; } } if (args[0] == "start") { if (s == null) { ThrowNoSuchService(); } s.StartService(); } if (args[0] == "stop") { if (s == null) { ThrowNoSuchService(); } s.StopService(); } if (args[0] == "restart") { if (s == null) { ThrowNoSuchService(); } if (s.Started) { s.StopService(); } while (s.Started) { Thread.Sleep(1000); s = svc.Select(d.Id); } s.StartService(); } if (args[0] == "restart!") { // run restart from another process group. see README.md for why this is useful. STARTUPINFO si = new STARTUPINFO(); PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); bool result = Kernel32.CreateProcess(null, d.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, 0x200 /*CREATE_NEW_PROCESS_GROUP*/, IntPtr.Zero, null, ref si, out pi); if (!result) { throw new Exception("Failed to invoke restart: " + Marshal.GetLastWin32Error()); } } if (args[0] == "status") { if (s == null) { Console.WriteLine("NonExistent"); } else if (s.Started) { Console.WriteLine("Started"); } else { Console.WriteLine("Stopped"); } } if (args[0] == "test") { WrapperService wsvc = new WrapperService(); wsvc.OnStart(args.ToArray()); Thread.Sleep(1000); wsvc.OnStop(); } return; } ServiceBase.Run(new WrapperService()); }
public static void Run(string[] args) { if (args.Length > 0) { var d = new ServiceDescriptor(); Win32Services svc = new WmiRoot().GetCollection <Win32Services>(); Win32Service s = svc.Select(d.Id); args[0] = args[0].ToLower(); if (args[0] == "install") { svc.Create( d.Id, d.Caption, "\"" + ServiceDescriptor.ExecutablePath + "\"", WMI.ServiceType.OwnProcess, ErrorControl.UserNotified, StartMode.Automatic, d.Interactive, d.ServiceDependencies); // update the description /* Somehow this doesn't work, even though it doesn't report an error * Win32Service s = svc.Select(d.Id); * s.Description = d.Description; * s.Commit(); */ // so using a classic method to set the description. Ugly. Registry.LocalMachine.OpenSubKey("System").OpenSubKey("CurrentControlSet").OpenSubKey("Services") .OpenSubKey(d.Id, true).SetValue("Description", d.Description); } if (args[0] == "uninstall") { if (s == null) { return; // there's no such service, so consider it already uninstalled } try { s.Delete(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion) { return; // it's already uninstalled, so consider it a success } throw e; } } if (args[0] == "start") { if (s == null) { ThrowNoSuchService(); } s.StartService(); } if (args[0] == "stop") { if (s == null) { ThrowNoSuchService(); } s.StopService(); } if (args[0] == "restart") { if (s == null) { ThrowNoSuchService(); } if (s.Started) { s.StopService(); } while (s.Started) { Thread.Sleep(1000); s = svc.Select(d.Id); } s.StartService(); } if (args[0] == "status") { if (s == null) { Console.WriteLine("NonExistent"); } else if (s.Started) { Console.WriteLine("Started"); } else { Console.WriteLine("Stopped"); } } if (args[0] == "test") { WrapperService wsvc = new WrapperService(); wsvc.OnStart(args); Thread.Sleep(1000); wsvc.OnStop(); } return; } ServiceBase.Run(new WrapperService()); }
public static void Run(string[] _args) { if (_args.Length > 0) { var d = new ServiceDescriptor(); Win32Services svc = new WmiRoot().GetCollection <Win32Services>(); Win32Service s = svc.Select(d.Id); var args = new List <string>(Array.AsReadOnly(_args)); if (args[0] == "/redirect") { // Redirect output // One might ask why we support this when the caller // can redirect the output easily. The answer is for supporting UAC. // On UAC-enabled Windows such as Vista, SCM operation requires // elevated privileges, thus winsw.exe needs to be launched // accordingly. This in turn limits what the caller can do, // and among other things it makes it difficult for the caller // to read stdout/stderr. Thus redirection becomes handy. var f = new FileStream(args[1], FileMode.Create); var w = new StreamWriter(f); w.AutoFlush = true; Console.SetOut(w); Console.SetError(w); var handle = f.Handle; SetStdHandle(-11, handle); // set stdout SetStdHandle(-12, handle); // set stder args = args.GetRange(2, args.Count - 2); } args[0] = args[0].ToLower(); if (args[0] == "install") { svc.Create( d.Id, d.Caption, "\"" + ServiceDescriptor.ExecutablePath + "\"", WMI.ServiceType.OwnProcess, ErrorControl.UserNotified, StartMode.Automatic, d.Interactive, d.ServiceDependencies); // update the description /* Somehow this doesn't work, even though it doesn't report an error * Win32Service s = svc.Select(d.Id); * s.Description = d.Description; * s.Commit(); */ // so using a classic method to set the description. Ugly. Registry.LocalMachine.OpenSubKey("System").OpenSubKey("CurrentControlSet").OpenSubKey("Services") .OpenSubKey(d.Id, true).SetValue("Description", d.Description); } if (args[0] == "uninstall") { if (s == null) { return; // there's no such service, so consider it already uninstalled } try { s.Delete(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion) { return; // it's already uninstalled, so consider it a success } throw e; } } if (args[0] == "start") { if (s == null) { ThrowNoSuchService(); } s.StartService(); } if (args[0] == "stop") { if (s == null) { ThrowNoSuchService(); } s.StopService(); } if (args[0] == "restart") { if (s == null) { ThrowNoSuchService(); } if (s.Started) { s.StopService(); } while (s.Started) { Thread.Sleep(1000); s = svc.Select(d.Id); } s.StartService(); } if (args[0] == "status") { if (s == null) { Console.WriteLine("NonExistent"); } else if (s.Started) { Console.WriteLine("Started"); } else { Console.WriteLine("Stopped"); } } if (args[0] == "test") { WrapperService wsvc = new WrapperService(); wsvc.OnStart(args.ToArray()); Thread.Sleep(1000); wsvc.OnStop(); } return; } ServiceBase.Run(new WrapperService()); }