MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4#if !UNIX // Not built on Unix5 6using System;7using System.Collections.Generic;8using System.ComponentModel; // Win32Exception9using System.Diagnostics.CodeAnalysis;10using System.Management.Automation;11using System.Management.Automation.Internal;12using System.Runtime.InteropServices; // Marshal, DllImport13using System.Runtime.Serialization;14using System.Security.AccessControl;15using System.ServiceProcess;16using Dbg = System.Management.Automation.Diagnostics;17using DWORD = System.UInt32;18using NakedWin32Handle = System.IntPtr;19 20namespace Microsoft.PowerShell.Commands21{22 #region ServiceBaseCommand23 24 /// <summary>25 /// This class implements the base for service commands.26 /// </summary>27 public abstract class ServiceBaseCommand : Cmdlet28 {29 #region Internal30 31 /// <summary>32 /// Confirm that the operation should proceed.33 /// </summary>34 /// <param name="service">Service object to be acted on.</param>35 /// <returns>True if operation should continue, false otherwise.</returns>36 protected bool ShouldProcessServiceOperation(ServiceController service)37 {38 return ShouldProcessServiceOperation(39 service.DisplayName,40 service.ServiceName);41 }42 43 /// <summary>44 /// Confirm that the operation should proceed.45 /// </summary>46 /// <param name="displayName">Display name of service to be acted on.</param>47 /// <param name="serviceName">Service name of service to be acted on.</param>48 /// <returns>True if operation should continue, false otherwise.</returns>49 protected bool ShouldProcessServiceOperation(50 string displayName, string serviceName)51 {52 string name = StringUtil.Format(ServiceResources.ServiceNameForConfirmation,53 displayName,54 serviceName);55 56 return ShouldProcess(name);57 }58 59 /// <summary>60 /// Writes a non-terminating error.61 /// </summary>62 /// <param name="service"></param>63 /// <param name="innerException"></param>64 /// <param name="errorId"></param>65 /// <param name="errorMessage"></param>66 /// <param name="category"></param>67 internal void WriteNonTerminatingError(68 ServiceController service,69 Exception innerException,70 string errorId,71 string errorMessage,72 ErrorCategory category)73 {74 WriteNonTerminatingError(75 service.ServiceName,76 service.DisplayName,77 service,78 innerException,79 errorId,80 errorMessage,81 category);82 }83 84 /// <summary>85 /// Writes a non-terminating error.86 /// </summary>87 /// <param name="serviceName"></param>88 /// <param name="displayName"></param>89 /// <param name="targetObject"></param>90 /// <param name="innerException"></param>91 /// <param name="errorId"></param>92 /// <param name="errorMessage"></param>93 /// <param name="category"></param>94 internal void WriteNonTerminatingError(95 string serviceName,96 string displayName,97 object targetObject,98 Exception innerException,99 string errorId,100 string errorMessage,101 ErrorCategory category)102 {103 string message = StringUtil.Format(errorMessage,104 serviceName,105 displayName,106 (innerException == null) ? category.ToString() : innerException.Message);107 108 var exception = new ServiceCommandException(message, innerException);109 exception.ServiceName = serviceName;110 111 WriteError(new ErrorRecord(exception, errorId, category, targetObject));112 }113 114 internal void SetServiceSecurityDescriptor(115 ServiceController service,116 string securityDescriptorSddl,117 NakedWin32Handle hService)118 {119 var rawSecurityDescriptor = new RawSecurityDescriptor(securityDescriptorSddl);120 RawAcl rawDiscretionaryAcl = rawSecurityDescriptor.DiscretionaryAcl;121 var discretionaryAcl = new DiscretionaryAcl(false, false, rawDiscretionaryAcl);122 123 byte[] rawDacl = new byte[discretionaryAcl.BinaryLength];124 discretionaryAcl.GetBinaryForm(rawDacl, 0);125 rawSecurityDescriptor.DiscretionaryAcl = new RawAcl(rawDacl, 0);126 byte[] securityDescriptorByte = new byte[rawSecurityDescriptor.BinaryLength];127 rawSecurityDescriptor.GetBinaryForm(securityDescriptorByte, 0);128 129 bool status = NativeMethods.SetServiceObjectSecurity(130 hService,131 SecurityInfos.DiscretionaryAcl,132 securityDescriptorByte);133 134 if (!status)135 {136 int lastError = Marshal.GetLastWin32Error();137 Win32Exception exception = new(lastError);138 bool accessDenied = exception.NativeErrorCode == NativeMethods.ERROR_ACCESS_DENIED;139 WriteNonTerminatingError(140 service,141 exception,142 nameof(ServiceResources.CouldNotSetServiceSecurityDescriptorSddl),143 StringUtil.Format(ServiceResources.CouldNotSetServiceSecurityDescriptorSddl, service.ServiceName, exception.Message),144 accessDenied ? ErrorCategory.PermissionDenied : ErrorCategory.InvalidOperation);145 }146 }147 #endregion Internal148 }149 #endregion ServiceBaseCommand150 151 #region MultipleServiceCommandBase152 153 /// <summary>154 /// This class implements the base for service commands which can155 /// operate on multiple services.156 /// </summary>157 public abstract class MultipleServiceCommandBase : ServiceBaseCommand158 {159 #region Parameters160 161 /// <summary>162 /// The various process selection modes.163 /// </summary>164 internal enum SelectionMode165 {166 /// <summary>167 /// Select all services.168 /// </summary>169 Default = 0,170 /// <summary>171 /// Select services matching the supplied names.172 /// </summary>173 DisplayName = 1,174 /// <summary>175 /// Select services based on pipeline input.176 /// </summary>177 InputObject = 2,178 /// <summary>179 /// Select services by Service name.180 /// </summary>181 ServiceName = 3182 }183 /// <summary>184 /// Holds the selection mode setting.185 /// </summary>186 internal SelectionMode selectionMode;187 188 /// <remarks>189 /// The ServiceName parameter is declared in subclasses,190 /// since it is optional for GetService and mandatory otherwise.191 /// </remarks>192 internal string[] serviceNames = null;193 194 /// <summary>195 /// Gets/sets an array of display names for services.196 /// </summary>197 [Parameter(ParameterSetName = "DisplayName", Mandatory = true)]198 public string[] DisplayName199 {200 get201 {202 return displayNames;203 }204 205 set206 {207 displayNames = value;208 selectionMode = SelectionMode.DisplayName;209 }210 }211 212 internal string[] displayNames = null;213 214 /// <summary>215 /// Lets you include particular services. Services not matching216 /// one of these (if specified) are excluded.217 /// These are interpreted as either ServiceNames or DisplayNames218 /// according to the parameter set.219 /// </summary>220 [Parameter]221 [ValidateNotNullOrEmpty]222 public string[] Include223 {224 get225 {226 return include;227 }228 229 set230 {231 include = value;232 }233 }234 235 internal string[] include = null;236 237 /// <summary>238 /// Lets you exclude particular services. Services matching239 /// one of these (if specified) are excluded.240 /// These are interpreted as either ServiceNames or DisplayNames241 /// according to the parameter set.242 /// </summary>243 [Parameter]244 [ValidateNotNullOrEmpty]245 public string[] Exclude246 {247 get248 {249 return exclude;250 }251 252 set253 {254 exclude = value;255 }256 }257 258 internal string[] exclude = null;259 260 // 1054295-2004/12/01-JonN This also works around 1054295.261 /// <summary>262 /// If the input is a stream of [collections of]263 /// ServiceController objects, we bypass the ServiceName and264 /// DisplayName parameters and read the ServiceControllers265 /// directly. This allows us to deal with services which266 /// have wildcard characters in their name (servicename or267 /// displayname).268 /// </summary>269 /// <value>ServiceController objects</value>270 [Parameter(ParameterSetName = "InputObject", ValueFromPipeline = true)]271 [ValidateNotNullOrEmpty]272 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]273 public ServiceController[] InputObject274 {275 get276 {277 return _inputObject;278 }279 280 set281 {282 _inputObject = value;283 selectionMode = SelectionMode.InputObject;284 }285 }286 287 private ServiceController[] _inputObject = null;288 #endregion Parameters289 290 #region Internal291 292 /// <summary>293 /// Gets an array of all services.294 /// </summary>295 /// <value>296 /// An array of <see cref="ServiceController"/> components that represents all the service resources.297 /// </value>298 /// <exception cref="System.Security.SecurityException">299 /// MSDN does not document the list of exceptions,300 /// but it is reasonable to expect that SecurityException is301 /// among them. Errors here will terminate the cmdlet.302 /// </exception>303 internal ServiceController[] AllServices => _allServices ??= ServiceController.GetServices();304 305 private ServiceController[] _allServices;306 307 internal ServiceController GetOneService(string nameOfService)308 {309 Dbg.Assert(!WildcardPattern.ContainsWildcardCharacters(nameOfService), "Caller should verify that nameOfService doesn't contain wildcard characters");310 311 try312 {313 var sc = new ServiceController(nameOfService);314 // This will throw if the service doesn't exist315 var unused = sc.Status;316 317 // No exception, then this is an existing, valid service. Return it.318 return sc;319 }320 catch (InvalidOperationException) { }321 catch (ArgumentException) { }322 323 return null;324 }325 326 /// <summary>327 /// Retrieve the list of all services matching the ServiceName,328 /// DisplayName, Include and Exclude parameters, sorted by ServiceName.329 /// </summary>330 /// <returns></returns>331 internal List<ServiceController> MatchingServices()332 {333 List<ServiceController> matchingServices;334 switch (selectionMode)335 {336 case SelectionMode.DisplayName:337 matchingServices = MatchingServicesByDisplayName();338 break;339 case SelectionMode.InputObject:340 matchingServices = MatchingServicesByInput();341 break;342 default:343 matchingServices = MatchingServicesByServiceName();344 break;345 }346 // 2004/12/16 Note that the services will be sorted347 // before being stopped. JimTru confirms that this is OK.348 matchingServices.Sort(ServiceComparison);349 return matchingServices;350 }351 352 // sort by servicename353 private static int ServiceComparison(ServiceController x, ServiceController y)354 {355 return string.Compare(x.ServiceName, y.ServiceName, StringComparison.OrdinalIgnoreCase);356 }357 358 /// <summary>359 /// Retrieves the list of all services matching the ServiceName,360 /// Include and Exclude parameters.361 /// Generates a non-terminating error for each specified362 /// service name which is not found even though it contains363 /// no wildcards.364 /// </summary>365 /// <returns></returns>366 /// <remarks>367 /// We do not use the ServiceController(string serviceName)368 /// constructor variant, since the resultant369 /// ServiceController.ServiceName is the provided serviceName370 /// even when that differs from the real ServiceName by case.371 /// </remarks>372 private List<ServiceController> MatchingServicesByServiceName()373 {374 List<ServiceController> matchingServices = new();375 376 if (serviceNames == null)377 {378 foreach (ServiceController service in AllServices)379 {380 IncludeExcludeAdd(matchingServices, service, false);381 }382 383 return matchingServices;384 }385 386 foreach (string pattern in serviceNames)387 {388 bool found = false;389 390 if (WildcardPattern.ContainsWildcardCharacters(pattern))391 {392 WildcardPattern wildcard = WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase);393 foreach (ServiceController service in AllServices)394 {395 if (!wildcard.IsMatch(service.ServiceName))396 continue;397 found = true;398 IncludeExcludeAdd(matchingServices, service, true);399 }400 }401 else402 {403 ServiceController service = GetOneService(pattern);404 if (service != null)405 {406 found = true;407 IncludeExcludeAdd(matchingServices, service, true);408 }409 }410 411 if (!found && !WildcardPattern.ContainsWildcardCharacters(pattern))412 {413 WriteNonTerminatingError(414 pattern,415 string.Empty,416 pattern,417 null,418 "NoServiceFoundForGivenName",419 ServiceResources.NoServiceFoundForGivenName,420 ErrorCategory.ObjectNotFound);421 }422 }423 424 return matchingServices;425 }426 427 /// <summary>428 /// Retrieves the list of all services matching the DisplayName,429 /// Include and Exclude parameters.430 /// Generates a non-terminating error for each specified431 /// display name which is not found even though it contains432 /// no wildcards.433 /// </summary>434 /// <returns></returns>435 private List<ServiceController> MatchingServicesByDisplayName()436 {437 List<ServiceController> matchingServices = new();438 if (DisplayName == null)439 {440 Diagnostics.Assert(false, "null DisplayName");441 throw PSTraceSource.NewInvalidOperationException();442 }443 444 foreach (string pattern in DisplayName)445 {446 WildcardPattern wildcard =447 WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase);448 bool found = false;449 foreach (ServiceController service in AllServices)450 {451 if (!wildcard.IsMatch(service.DisplayName))452 continue;453 found = true;454 IncludeExcludeAdd(matchingServices, service, true);455 }456 457 if (!found && !WildcardPattern.ContainsWildcardCharacters(pattern))458 {459 WriteNonTerminatingError(460 string.Empty,461 pattern,462 pattern,463 null,464 "NoServiceFoundForGivenDisplayName",465 ServiceResources.NoServiceFoundForGivenDisplayName,466 ErrorCategory.ObjectNotFound);467 }468 }469 470 return matchingServices;471 }472 473 /// <summary>474 /// Retrieves the list of all services matching the InputObject,475 /// Include and Exclude parameters.476 /// </summary>477 /// <returns></returns>478 private List<ServiceController> MatchingServicesByInput()479 {480 List<ServiceController> matchingServices = new();481 if (InputObject == null)482 {483 Diagnostics.Assert(false, "null InputObject");484 throw PSTraceSource.NewInvalidOperationException();485 }486 487 foreach (ServiceController service in InputObject)488 {489 service.Refresh();490 IncludeExcludeAdd(matchingServices, service, false);491 }492 493 return matchingServices;494 }495 496 /// <summary>497 /// Add <paramref name="service"/> to <paramref name="list"/>,498 /// but only if it passes the Include and Exclude filters (if present)499 /// and (if <paramref name="checkDuplicates"/>) if it is not500 /// already on <paramref name="list"/>.501 /// </summary>502 /// <param name="list">List of services.</param>503 /// <param name="service">Service to add to list.</param>504 /// <param name="checkDuplicates">Check list for duplicates.</param>505 private void IncludeExcludeAdd(506 List<ServiceController> list,507 ServiceController service,508 bool checkDuplicates)509 {510 if (include != null && !Matches(service, include))511 return;512 if (exclude != null && Matches(service, exclude))513 return;514 if (checkDuplicates)515 {516 foreach (ServiceController sc in list)517 {518 if (sc.ServiceName == service.ServiceName &&519 sc.MachineName == service.MachineName)520 {521 return;522 }523 }524 }525 526 list.Add(service);527 }528 529 /// <summary>530 /// Check whether <paramref name="service"/> matches the list of531 /// patterns in <paramref name="matchList"/>.532 /// </summary>533 /// <param name="service"></param>534 /// <param name="matchList"></param>535 /// <returns></returns>536 private bool Matches(ServiceController service, string[] matchList)537 {538 if (matchList == null)539 throw PSTraceSource.NewArgumentNullException(nameof(matchList));540 string serviceID = (selectionMode == SelectionMode.DisplayName)541 ? service.DisplayName542 : service.ServiceName;543 foreach (string pattern in matchList)544 {545 WildcardPattern wildcard = WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase);546 if (wildcard.IsMatch(serviceID))547 return true;548 }549 550 return false;551 }552 #endregion Internal553 554 }555 #endregion MultipleServiceCommandBase556 557 #region GetServiceCommand558 /// <summary>559 /// This class implements the get-service command.560 /// </summary>561 [Cmdlet(VerbsCommon.Get, "Service", DefaultParameterSetName = "Default",562 HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096496", RemotingCapability = RemotingCapability.SupportedByCommand)]563 [OutputType(typeof(ServiceController))]564 public sealed class GetServiceCommand : MultipleServiceCommandBase565 {566 #region Parameters567 /// <summary>568 /// Gets/sets an array of service names.569 /// </summary>570 /// <remarks>571 /// The ServiceName parameter is declared in subclasses,572 /// since it is optional for GetService and mandatory otherwise.573 /// </remarks>574 [Parameter(Position = 0, ParameterSetName = "Default", ValueFromPipelineByPropertyName = true, ValueFromPipeline = true)]575 [ValidateNotNullOrEmpty()]576 [Alias("ServiceName")]577 public string[] Name578 {579 get580 {581 return serviceNames;582 }583 584 set585 {586 serviceNames = value;587 selectionMode = SelectionMode.ServiceName;588 }589 }590 591 /// <summary>592 /// This returns the DependentServices of the specified service.593 /// </summary>594 [Parameter]595 [Alias("DS")]596 public SwitchParameter DependentServices { get; set; }597 598 /// <summary>599 /// This returns the ServicesDependedOn of the specified service.600 /// </summary>601 [Parameter]602 [Alias("SDO", "ServicesDependedOn")]603 public SwitchParameter RequiredServices { get; set; }604 605 #endregion Parameters606 607 #region Overrides608 /// <summary>609 /// Write the service objects.610 /// </summary>611 protected override void ProcessRecord()612 {613 nint scManagerHandle = nint.Zero;614 if (!DependentServices && !RequiredServices)615 {616 // As Get-Service only works on local services we get this once617 // to retrieve extra properties added by PowerShell.618 scManagerHandle = NativeMethods.OpenSCManagerW(619 lpMachineName: null,620 lpDatabaseName: null,621 dwDesiredAccess: NativeMethods.SC_MANAGER_CONNECT);622 if (scManagerHandle == nint.Zero)623 {624 Win32Exception exception = new();625 string message = StringUtil.Format(ServiceResources.FailToOpenServiceControlManager, exception.Message);626 ServiceCommandException serviceException = new ServiceCommandException(message, exception);627 ErrorRecord err = new ErrorRecord(628 serviceException,629 "FailToOpenServiceControlManager",630 ErrorCategory.PermissionDenied,631 null);632 ThrowTerminatingError(err);633 }634 }635 636 try637 {638 foreach (ServiceController service in MatchingServices())639 {640 if (!DependentServices.IsPresent && !RequiredServices.IsPresent)641 {642 WriteObject(AddProperties(scManagerHandle, service));643 }644 else645 {646 if (DependentServices.IsPresent)647 {648 foreach (ServiceController dependantserv in service.DependentServices)649 {650 WriteObject(dependantserv);651 }652 }653 654 if (RequiredServices.IsPresent)655 {656 foreach (ServiceController servicedependedon in service.ServicesDependedOn)657 {658 WriteObject(servicedependedon);659 }660 }661 }662 }663 }664 finally665 {666 if (scManagerHandle != nint.Zero)667 {668 bool succeeded = NativeMethods.CloseServiceHandle(scManagerHandle);669 Diagnostics.Assert(succeeded, "SCManager handle close failed");670 }671 }672 }673 674 #endregion Overrides675 676#nullable enable677 678 /// <summary>679 /// Writes a verbose message when a service property query fails.680 /// </summary>681 /// <param name="serviceName">Name of the service.</param>682 /// <param name="propertyName">Name of the property that failed to be queried.</param>683 private void WriteServicePropertyError(string serviceName, string propertyName)684 {685 Win32Exception e = new(Marshal.GetLastWin32Error());686 WriteVerbose(687 StringUtil.Format(688 ServiceResources.CouldNotGetServiceProperty,689 serviceName,690 propertyName,691 e.Message));692 }693 694 /// <summary>695 /// Adds UserName, Description, BinaryPathName, DelayedAutoStart and StartupType to a ServiceController object.696 /// </summary>697 /// <param name="scManagerHandle">Handle to the local SCManager instance.</param>698 /// <param name="service"></param>699 /// <returns>ServiceController as PSObject with UserName, Description and StartupType added.</returns>700 private PSObject AddProperties(nint scManagerHandle, ServiceController service)701 {702 NakedWin32Handle hService = nint.Zero;703 704 // As these are optional values, a failure due to permissions or705 // other problem is ignored and the properties are set to null.706 bool? isDelayedAutoStart = null;707 string? binPath = null;708 string? description = null;709 string? startName = null;710 ServiceStartupType startupType = ServiceStartupType.InvalidValue;711 try712 {713 // We don't use service.ServiceHandle as that requests714 // SERVICE_ALL_ACCESS when we only need SERVICE_QUERY_CONFIG.715 hService = NativeMethods.OpenServiceW(716 scManagerHandle,717 service.ServiceName,718 NativeMethods.SERVICE_QUERY_CONFIG719 );720 if (hService != nint.Zero)721 {722 if (NativeMethods.QueryServiceConfig2(723 hService,724 NativeMethods.SERVICE_CONFIG_DESCRIPTION,725 out NativeMethods.SERVICE_DESCRIPTIONW descriptionInfo))726 {727 description = descriptionInfo.lpDescription;728 }729 else730 {731 WriteServicePropertyError(service.ServiceName, nameof(NativeMethods.SERVICE_DESCRIPTIONW));732 }733 734 if (NativeMethods.QueryServiceConfig2(735 hService,736 NativeMethods.SERVICE_CONFIG_DELAYED_AUTO_START_INFO,737 out NativeMethods.SERVICE_DELAYED_AUTO_START_INFO autostartInfo))738 {739 isDelayedAutoStart = autostartInfo.fDelayedAutostart;740 }741 else742 {743 WriteServicePropertyError(service.ServiceName, nameof(NativeMethods.SERVICE_DELAYED_AUTO_START_INFO));744 }745 746 if (NativeMethods.QueryServiceConfig(747 hService,748 out NativeMethods.QUERY_SERVICE_CONFIG serviceInfo))749 {750 binPath = serviceInfo.lpBinaryPathName;751 startName = serviceInfo.lpServiceStartName;752 if (isDelayedAutoStart.HasValue)753 {754 startupType = NativeMethods.GetServiceStartupType(755 (ServiceStartMode)serviceInfo.dwStartType,756 isDelayedAutoStart.Value);757 }758 }759 else760 {761 WriteServicePropertyError(service.ServiceName, nameof(NativeMethods.QUERY_SERVICE_CONFIG));762 }763 }764 else765 {766 // handle when OpenServiceW itself fails:767 WriteServicePropertyError(service.ServiceName, nameof(NativeMethods.SERVICE_QUERY_CONFIG));768 }769 }770 finally771 {772 if (hService != IntPtr.Zero)773 {774 bool succeeded = NativeMethods.CloseServiceHandle(hService);775 Diagnostics.Assert(succeeded, "Failed to close service handle");776 }777 }778 779 PSObject serviceAsPSObj = PSObject.AsPSObject(service);780 PSNoteProperty noteProperty = new("UserName", startName);781 serviceAsPSObj.Properties.Add(noteProperty, true);782 serviceAsPSObj.TypeNames.Insert(0, "System.Service.ServiceController#UserName");783 784 noteProperty = new PSNoteProperty("Description", description);785 serviceAsPSObj.Properties.Add(noteProperty, true);786 serviceAsPSObj.TypeNames.Insert(0, "System.Service.ServiceController#Description");787 788 noteProperty = new PSNoteProperty("DelayedAutoStart", isDelayedAutoStart);789 serviceAsPSObj.Properties.Add(noteProperty, true);790 serviceAsPSObj.TypeNames.Insert(0, "System.Service.ServiceController#DelayedAutoStart");791 792 noteProperty = new PSNoteProperty("BinaryPathName", binPath);793 serviceAsPSObj.Properties.Add(noteProperty, true);794 serviceAsPSObj.TypeNames.Insert(0, "System.Service.ServiceController#BinaryPathName");795 796 noteProperty = new PSNoteProperty("StartupType", startupType);797 serviceAsPSObj.Properties.Add(noteProperty, true);798 serviceAsPSObj.TypeNames.Insert(0, "System.Service.ServiceController#StartupType");799 800 return serviceAsPSObj;801 }802 }803#nullable disable804 #endregion GetServiceCommand805 806 #region ServiceOperationBaseCommand807 /// <summary>808 /// This class implements the base for service commands which actually809 /// act on the service(s).810 /// </summary>811 public abstract class ServiceOperationBaseCommand : MultipleServiceCommandBase812 {813 #region Parameters814 /// <summary>815 /// Gets/sets an array of service names.816 /// </summary>817 /// <remarks>818 /// The ServiceName parameter is declared in subclasses,819 /// since it is optional for GetService and mandatory otherwise.820 /// </remarks>821 [Parameter(Position = 0, ParameterSetName = "Default", Mandatory = true, ValueFromPipelineByPropertyName = true, ValueFromPipeline = true)]822 [Alias("ServiceName")]823 public string[] Name824 {825 get826 {827 return serviceNames;828 }829 830 set831 {832 serviceNames = value;833 selectionMode = SelectionMode.ServiceName;834 }835 }836 837 /// <summary>838 /// Service controller objects.839 /// </summary>840 [Parameter(Position = 0, Mandatory = true, ParameterSetName = "InputObject", ValueFromPipeline = true)]841 [ValidateNotNullOrEmpty]842 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]843 public new ServiceController[] InputObject844 {845 get846 {847 return base.InputObject;848 }849 850 set851 {852 base.InputObject = value;853 }854 }855 856 /// <summary>857 /// Specifies whether to write the objects successfully operated upon858 /// to the success stream.859 /// </summary>860 [Parameter]861 public SwitchParameter PassThru { get; set; }862 863 #endregion Parameters864 865 #region Internal866 /// <summary>867 /// Waits forever for the service to reach the desired status, but868 /// writes a string to WriteWarning every 2 seconds.869 /// </summary>870 /// <param name="serviceController">Service on which to operate.</param>871 /// <param name="targetStatus">Desired status.</param>872 /// <param name="pendingStatus">873 /// This is the expected status while the operation is incomplete.874 /// If the service is in some other state, this means that the875 /// operation failed.876 /// </param>877 /// <param name="resourceIdPending">878 /// resourceId for a string to be written to verbose stream879 /// every 2 seconds880 /// </param>881 /// <param name="errorId">882 /// errorId for a nonterminating error if operation fails883 /// </param>884 /// <param name="errorMessage">885 /// errorMessage for a nonterminating error if operation fails886 /// </param>887 /// <returns>True if action succeeded.</returns>888 /// <exception cref="PipelineStoppedException">889 /// WriteWarning will throw this if the pipeline has been stopped.890 /// This means that the delay between hitting CTRL-C and stopping891 /// the cmdlet should be 2 seconds at most.892 /// </exception>893 internal bool DoWaitForStatus(894 ServiceController serviceController,895 ServiceControllerStatus targetStatus,896 ServiceControllerStatus pendingStatus,897 string resourceIdPending,898 string errorId,899 string errorMessage)900 {901 while (true)902 {903 try904 {905 // ServiceController.Start will return before the service is actually started906 // This API will wait forever907 serviceController.WaitForStatus(908 targetStatus,909 new TimeSpan(20000000) // 2 seconds910 );911 return true; // service reached target status912 }913 catch (System.ServiceProcess.TimeoutException) // still waiting914 {915 if (serviceController.Status != pendingStatus916 // NTRAID#Windows Out Of Band Releases-919945-2005/09/27-JonN917 // Close window where service could complete at918 // just the wrong time919 && serviceController.Status != targetStatus)920 {921 WriteNonTerminatingError(serviceController, null,922 errorId,923 errorMessage,924 ErrorCategory.OpenError);925 return false;926 }927 928 string message = StringUtil.Format(resourceIdPending,929 serviceController.ServiceName,930 serviceController.DisplayName931 );932 // will throw PipelineStoppedException if user hit CTRL-C933 WriteWarning(message);934 }935 }936 }937 938 /// <summary>939 /// This will start the service.940 /// </summary>941 /// <param name="serviceController">Service to start.</param>942 /// <returns>True if-and-only-if the service was started.</returns>943 internal bool DoStartService(ServiceController serviceController)944 {945 Exception exception = null;946 try947 {948 serviceController.Start();949 }950 catch (Win32Exception e)951 {952 if (e.NativeErrorCode != NativeMethods.ERROR_SERVICE_ALREADY_RUNNING)953 exception = e;954 }955 catch (InvalidOperationException e)956 {957 if (e.InnerException is not Win32Exception eInner958 || eInner.NativeErrorCode != NativeMethods.ERROR_SERVICE_ALREADY_RUNNING)959 {960 exception = e;961 }962 }963 964 if (exception != null)965 {966 // This service refused to accept the start command,967 // so write a non-terminating error.968 WriteNonTerminatingError(serviceController,969 exception,970 "CouldNotStartService",971 ServiceResources.CouldNotStartService,972 ErrorCategory.OpenError);973 return false;974 }975 976 // ServiceController.Start will return977 // before the service is actually started.978 if (!DoWaitForStatus(979 serviceController,980 ServiceControllerStatus.Running,981 ServiceControllerStatus.StartPending,982 ServiceResources.StartingService,983 "StartServiceFailed",984 ServiceResources.StartServiceFailed))985 {986 return false;987 }988 989 return true;990 }991 992 /// <summary>993 /// This will stop the service.994 /// </summary>995 /// <param name="serviceController">Service to stop.</param>996 /// <param name="force">Stop dependent services.</param>997 /// <param name="waitForServiceToStop"></param>998 /// <returns>True if-and-only-if the service was stopped.</returns>999 internal List<ServiceController> DoStopService(ServiceController serviceController, bool force, bool waitForServiceToStop)1000 {1001 // Ignore ServiceController.CanStop. CanStop will be set false1002 // if the service is not running, but this is not an error.1003 1004 List<ServiceController> stoppedServices = new();1005 ServiceController[] dependentServices = null;1006 1007 try1008 {1009 dependentServices = serviceController.DependentServices;1010 }1011 catch (Win32Exception e)1012 {1013 WriteNonTerminatingError(serviceController, e,1014 "CouldNotAccessDependentServices",1015 ServiceResources.CouldNotAccessDependentServices,1016 ErrorCategory.InvalidOperation);1017 }1018 catch (InvalidOperationException e)1019 {1020 WriteNonTerminatingError(serviceController, e,1021 "CouldNotAccessDependentServices",1022 ServiceResources.CouldNotAccessDependentServices,1023 ErrorCategory.InvalidOperation);1024 }1025 1026 if (!force)1027 {1028 if ((dependentServices != null)1029 && (dependentServices.Length > 0))1030 {1031 // Check if all dependent services are stopped1032 if (!HaveAllDependentServicesStopped(dependentServices))1033 {1034 // This service has dependent services1035 // and the force flag is not specified.1036 // Add a non-critical error for it.1037 WriteNonTerminatingError(serviceController,1038 null,1039 "ServiceHasDependentServices",1040 ServiceResources.ServiceHasDependentServices,1041 ErrorCategory.InvalidOperation);1042 1043 return stoppedServices;1044 }1045 }1046 }1047 1048 if (dependentServices != null)1049 {1050 foreach (ServiceController service in dependentServices)1051 {1052 if ((service.Status == ServiceControllerStatus.Running ||1053 service.Status == ServiceControllerStatus.StartPending) &&1054 service.CanStop)1055 {1056 stoppedServices.Add(service);1057 }1058 }1059 }1060 1061 Exception exception = null;1062 try1063 {1064 serviceController.Stop();1065 }1066 catch (Win32Exception e)1067 {1068 if (e.NativeErrorCode != NativeMethods.ERROR_SERVICE_NOT_ACTIVE)1069 exception = e;1070 }1071 catch (InvalidOperationException e)1072 {1073 if (e.InnerException is not Win32Exception eInner1074 || eInner.NativeErrorCode != NativeMethods.ERROR_SERVICE_NOT_ACTIVE)1075 {1076 exception = e;1077 }1078 }1079 1080 if (exception != null)1081 {1082 // This service refused to accept the stop command,1083 // so write a non-terminating error.1084 WriteNonTerminatingError(serviceController,1085 exception,1086 "CouldNotStopService",1087 ServiceResources.CouldNotStopService,1088 ErrorCategory.CloseError);1089 RemoveNotStoppedServices(stoppedServices);1090 return stoppedServices;1091 }1092 1093 // ServiceController.Stop will return1094 // before the service is actually stopped.1095 if (waitForServiceToStop)1096 {1097 if (!DoWaitForStatus(1098 serviceController,1099 ServiceControllerStatus.Stopped,1100 ServiceControllerStatus.StopPending,1101 ServiceResources.StoppingService,1102 "StopServiceFailed",1103 ServiceResources.StopServiceFailed))1104 {1105 RemoveNotStoppedServices(stoppedServices);1106 return stoppedServices;1107 }1108 }1109 1110 RemoveNotStoppedServices(stoppedServices);1111 if ((serviceController.Status.Equals(ServiceControllerStatus.Stopped)) || (serviceController.Status.Equals(ServiceControllerStatus.StopPending)))1112 {1113 stoppedServices.Add(serviceController);1114 }1115 1116 return stoppedServices;1117 }1118 1119 /// <summary>1120 /// Check if all dependent services are stopped.1121 /// </summary>1122 /// <param name="dependentServices"></param>1123 /// <returns>1124 /// True if all dependent services are stopped1125 /// False if not all dependent services are stopped1126 /// </returns>1127 private static bool HaveAllDependentServicesStopped(ServiceController[] dependentServices)1128 {1129 return Array.TrueForAll(dependentServices, static service => service.Status == ServiceControllerStatus.Stopped);1130 }1131 1132 /// <summary>1133 /// This removes all services that are not stopped from a list of services.1134 /// </summary>1135 /// <param name="services">A list of services.</param>1136 internal void RemoveNotStoppedServices(List<ServiceController> services)1137 {1138 // You shall not modify a collection during enumeration.1139 services.RemoveAll(service =>1140 service.Status != ServiceControllerStatus.Stopped &&1141 service.Status != ServiceControllerStatus.StopPending);1142 }1143 1144 /// <summary>1145 /// This will pause the service.1146 /// </summary>1147 /// <param name="serviceController">Service to pause.</param>1148 /// <returns>True if-and-only-if the service was paused.</returns>1149 internal bool DoPauseService(ServiceController serviceController)1150 {1151 Exception exception = null;1152 bool serviceNotRunning = false;1153 try1154 {1155 serviceController.Pause();1156 }1157 catch (Win32Exception e)1158 {1159 if (e.NativeErrorCode == NativeMethods.ERROR_SERVICE_NOT_ACTIVE)1160 {1161 serviceNotRunning = true;1162 }1163 1164 exception = e;1165 }1166 catch (InvalidOperationException e)1167 {1168 if (e.InnerException is Win32Exception eInner1169 && eInner.NativeErrorCode == NativeMethods.ERROR_SERVICE_NOT_ACTIVE)1170 {1171 serviceNotRunning = true;1172 }1173 1174 exception = e;1175 }1176 1177 if (exception != null)1178 {1179 // This service refused to accept the pause command,1180 // so write a non-terminating error.1181 string resourceIdAndErrorId = ServiceResources.CouldNotSuspendService;1182 if (serviceNotRunning)1183 {1184 WriteNonTerminatingError(serviceController,1185 exception,1186 "CouldNotSuspendServiceNotRunning",1187 ServiceResources.CouldNotSuspendServiceNotRunning,1188 ErrorCategory.CloseError);1189 }1190 else if (!serviceController.CanPauseAndContinue)1191 {1192 WriteNonTerminatingError(serviceController,1193 exception,1194 "CouldNotSuspendServiceNotSupported",1195 ServiceResources.CouldNotSuspendServiceNotSupported,1196 ErrorCategory.CloseError);1197 }1198 1199 WriteNonTerminatingError(serviceController,1200 exception,