static int Main(string[] args) { Thread.CurrentThread.Name = "Test Service"; if (args.Length == 1 || args.Length == 2) { TestService testService; if (args[0].StartsWith("PropagateExceptionFromOnStart")) { var expectedException = new InvalidOperationException("Fail on startup."); testService = new TestService(args[0], expectedException); try { ServiceBase.Run(testService); } catch (Exception actualException) { if (object.ReferenceEquals(expectedException, actualException)) { testService.WriteStreamAsync(PipeMessageByteCode.ExceptionThrown).Wait(); } else { throw actualException; } } } else if (args[0].StartsWith("LogWritten")) { testService = new TestService(args[0], throwException: null); testService.AutoLog = false; ServiceBase.Run(testService); } else { testService = new TestService(args[0]); ServiceBase.Run(testService); } return(0); } else if (args.Length == 3) { TestServiceInstaller testServiceInstaller = new TestServiceInstaller(); testServiceInstaller.ServiceName = args[0]; testServiceInstaller.DisplayName = args[1]; if (args[2] == "create") { testServiceInstaller.Install(); return(0); } else if (args[2] == "delete") { testServiceInstaller.RemoveService(); return(0); } else { Console.WriteLine("EROOR: Invalid Service verb. Only support create or delete."); return(2); } } Console.WriteLine($"usage: <ServiceName> <DisplayName> [create|delete]"); return(1); }
// Install and start the test service, after starting any prerequisite services it depends on public unsafe void Install() { string username = Username; string password = Password; if (ServiceCommandLine == null) { string processName = Process.GetCurrentProcess().MainModule.FileName; string entryPointName = System.Reflection.Assembly.GetEntryAssembly().Location; string arguments = ServiceName; // if process and entry point aren't the same then we are running hosted so pass // in the entrypoint as the first argument if (!string.Equals(processName, entryPointName, StringComparison.OrdinalIgnoreCase)) { arguments = $"\"{entryPointName}\" {arguments}"; } ServiceCommandLine = $"\"{processName}\" {arguments}"; } // Build servicesDependedOn string // These are prerequisite services that must be started before this service string servicesDependedOn = null; if (ServicesDependedOn.Length > 0) { StringBuilder buff = new StringBuilder(); for (int i = 0; i < ServicesDependedOn.Length; ++i) { //The servicesDependedOn need to be separated by a null buff.Append(ServicesDependedOn[i]); buff.Append('\0'); } // an extra null at the end indicates end of list. buff.Append('\0'); servicesDependedOn = buff.ToString(); } // Open the service manager using (var serviceManagerHandle = new SafeServiceHandle(Interop.Advapi32.OpenSCManager(null, null, Interop.Advapi32.ServiceControllerOptions.SC_MANAGER_ALL))) { if (serviceManagerHandle.IsInvalid) { throw new InvalidOperationException("Cannot open Service Control Manager"); } TestService.DebugTrace($"TestServiceInstaller: creating service '{ServiceName}' with prerequisite services {servicesDependedOn}"); // Install the service using (var serviceHandle = new SafeServiceHandle(Interop.Advapi32.CreateService(serviceManagerHandle, ServiceName, DisplayName, Interop.Advapi32.ServiceAccessOptions.ACCESS_TYPE_ALL, Interop.Advapi32.ServiceTypeOptions.SERVICE_WIN32_OWN_PROCESS, (int)StartType, Interop.Advapi32.ServiceStartErrorModes.ERROR_CONTROL_NORMAL, ServiceCommandLine, null, IntPtr.Zero, servicesDependedOn, username, password))) { if (serviceHandle.IsInvalid) { int errorCode = Marshal.GetLastWin32Error(); string errorMessage = new Win32Exception(errorCode).Message; throw new Win32Exception(errorCode, $"Cannot create service '{ServiceName}' with display name '{DisplayName}'. {errorMessage}"); } // A local variable in an unsafe method is already fixed -- so we don't need a "fixed { }" blocks to protect // across the p/invoke calls below. if (Description.Length != 0) { Interop.Advapi32.SERVICE_DESCRIPTION serviceDesc = new Interop.Advapi32.SERVICE_DESCRIPTION(); serviceDesc.description = Marshal.StringToHGlobalUni(Description); bool success = Interop.Advapi32.ChangeServiceConfig2(serviceHandle, Interop.Advapi32.ServiceConfigOptions.SERVICE_CONFIG_DESCRIPTION, ref serviceDesc); Marshal.FreeHGlobal(serviceDesc.description); if (!success) { int errorCode = Marshal.GetLastWin32Error(); string errorMessage = new Win32Exception(errorCode).Message; throw new Win32Exception(errorCode, $"Cannot set description on '{ServiceName}' with display name '{DisplayName}'. {errorMessage}"); } } // Start the service after creating it using (ServiceController svc = new ServiceController(ServiceName)) { if (svc.Status != ServiceControllerStatus.Running) { TestService.DebugTrace($"TestServiceInstaller: instructing ServiceController to start service '{ServiceName}'"); svc.Start(); if (!ServiceName.StartsWith("PropagateExceptionFromOnStart")) { svc.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(240)); } } else { TestService.DebugTrace($"TestServiceInstaller: service '{ServiceName}' already running"); } } } } }