mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Remove the code that spins up our own assembly load context. Keep the code that registers our `Resolve` method to the default loader's `Resolving` event, so that we can continue to do special assembly resolution as needed (such as the GAC probing logic needed for consuming FullCLR PS modules). Essentially, the assembly `Microsoft.PowerShell.CoreCLR.AssemblyLoadContext.dll` is not needed anymore, the remaining code should be moved to S.M.A.dll. However, that will break DSC and other native hosts that are hosting powershell. So this assembly is kept for now.
2418 lines
92 KiB
C#
2418 lines
92 KiB
C#
/********************************************************************++
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
--********************************************************************/
|
|
#if !UNIX // Not built on Unix
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ServiceProcess;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using Dbg = System.Management.Automation.Diagnostics;
|
|
using System.Management.Automation;
|
|
using System.Management.Automation.Internal;
|
|
using System.ComponentModel; // Win32Exception
|
|
using System.Runtime.Serialization;
|
|
using System.Runtime.InteropServices; // Marshal, DllImport
|
|
using System.Security.Permissions;
|
|
using NakedWin32Handle = System.IntPtr;
|
|
using DWORD = System.UInt32;
|
|
|
|
namespace Microsoft.PowerShell.Commands
|
|
{
|
|
#region ServiceBaseCommand
|
|
|
|
/// <summary>
|
|
/// This class implements the base for service commands
|
|
/// </summary>
|
|
public abstract class ServiceBaseCommand : Cmdlet
|
|
{
|
|
#region Internal
|
|
|
|
/// <summary>
|
|
/// Confirm that the operation should proceed
|
|
/// </summary>
|
|
/// <param name="service">service object to be acted on</param>
|
|
/// <returns> true if operation should continue, false otherwise </returns>
|
|
protected bool ShouldProcessServiceOperation(ServiceController service)
|
|
{
|
|
return ShouldProcessServiceOperation(
|
|
service.DisplayName,
|
|
service.ServiceName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Confirm that the operation should proceed
|
|
/// </summary>
|
|
/// <param name="displayName">display name of service to be acted on</param>
|
|
/// <param name="serviceName">service name of service to be acted on</param>
|
|
/// <returns> true if operation should continue, false otherwise </returns>
|
|
protected bool ShouldProcessServiceOperation(
|
|
string displayName, string serviceName)
|
|
{
|
|
string name = StringUtil.Format(ServiceResources.ServiceNameForConfirmation,
|
|
displayName,
|
|
serviceName);
|
|
|
|
return ShouldProcess(name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a non-terminating error.
|
|
/// </summary>
|
|
/// <param name="service"></param>
|
|
/// <param name="innerException"></param>
|
|
/// <param name="errorId"></param>
|
|
/// <param name="errorMessage"></param>
|
|
/// <param name="category"></param>
|
|
internal void WriteNonTerminatingError(
|
|
ServiceController service,
|
|
Exception innerException,
|
|
string errorId,
|
|
string errorMessage,
|
|
ErrorCategory category)
|
|
{
|
|
WriteNonTerminatingError(
|
|
service.ServiceName,
|
|
service.DisplayName,
|
|
service,
|
|
innerException,
|
|
errorId,
|
|
errorMessage,
|
|
category);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a non-terminating error.
|
|
/// </summary>
|
|
/// <param name="serviceName"></param>
|
|
/// <param name="displayName"></param>
|
|
/// <param name="targetObject"></param>
|
|
/// <param name="innerException"></param>
|
|
/// <param name="errorId"></param>
|
|
/// <param name="errorMessage"></param>
|
|
/// <param name="category"></param>
|
|
internal void WriteNonTerminatingError(
|
|
string serviceName,
|
|
string displayName,
|
|
object targetObject,
|
|
Exception innerException,
|
|
string errorId,
|
|
string errorMessage,
|
|
ErrorCategory category)
|
|
{
|
|
string message = StringUtil.Format(errorMessage,
|
|
serviceName,
|
|
displayName,
|
|
(null == innerException) ? "" : innerException.Message
|
|
);
|
|
ServiceCommandException exception =
|
|
new ServiceCommandException(message, innerException);
|
|
exception.ServiceName = serviceName;
|
|
|
|
if (innerException != null)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
}
|
|
|
|
WriteError(new ErrorRecord(exception, errorId, category, targetObject));
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Writes a non-terminating error on computer name.
|
|
/// </summary>
|
|
/// <param name="service"></param>
|
|
/// <param name="computername"></param>
|
|
/// <param name="innerException"></param>
|
|
/// <param name="errorId"></param>
|
|
/// <param name="errorMessage"></param>
|
|
/// <param name="category"></param>
|
|
internal void WriteNonTerminatingError(
|
|
ServiceController service,
|
|
string computername,
|
|
Exception innerException,
|
|
string errorId,
|
|
string errorMessage,
|
|
ErrorCategory category)
|
|
{
|
|
WriteNonTerminatingError(
|
|
service.ServiceName,
|
|
computername,
|
|
service,
|
|
innerException,
|
|
errorId,
|
|
errorMessage,
|
|
category);
|
|
}
|
|
|
|
#endregion Internal
|
|
} // class ServiceBaseCommand
|
|
#endregion ServiceBaseCommand
|
|
|
|
#region MultipleServiceCommandBase
|
|
|
|
/// <summary>
|
|
/// This class implements the base for service commands which can
|
|
/// operate on multiple services
|
|
/// </summary>
|
|
public abstract class MultipleServiceCommandBase : ServiceBaseCommand
|
|
{
|
|
#region Parameters
|
|
|
|
/// <summary>
|
|
/// The various process selection modes
|
|
/// </summary>
|
|
internal enum SelectionMode
|
|
{
|
|
/// <summary>
|
|
/// Select all services
|
|
/// </summary>
|
|
Default = 0,
|
|
/// <summary>
|
|
/// Select services matching the supplied names
|
|
/// </summary>
|
|
DisplayName = 1,
|
|
/// <summary>
|
|
/// Select services based on pipeline input
|
|
/// </summary>
|
|
InputObject = 2,
|
|
/// <summary>
|
|
/// Select services by Service name
|
|
/// </summary>
|
|
ServiceName = 3
|
|
};
|
|
/// <summary>
|
|
/// Holds the selection mode setting.
|
|
/// </summary>
|
|
internal SelectionMode selectionMode;
|
|
|
|
/// <remarks>
|
|
/// The ServiceName parameter is declared in subclasses,
|
|
/// since it is optional for GetService and mandatory otherwise.
|
|
/// </remarks>
|
|
internal string[] serviceNames = null;
|
|
|
|
/// <summary>
|
|
/// gets/sets an array of display names for services
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = "DisplayName", Mandatory = true)]
|
|
public string[] DisplayName
|
|
{
|
|
get
|
|
{
|
|
return displayNames;
|
|
}
|
|
set
|
|
{
|
|
displayNames = value;
|
|
selectionMode = SelectionMode.DisplayName;
|
|
}
|
|
}
|
|
internal string[] displayNames = null;
|
|
|
|
/// <summary>
|
|
/// Lets you include particular services. Services not matching
|
|
/// one of these (if specified) are excluded.
|
|
/// These are interpreted as either ServiceNames or DisplayNames
|
|
/// according to the parameter set.
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateNotNullOrEmpty]
|
|
public string[] Include
|
|
{
|
|
get
|
|
{
|
|
return include;
|
|
}
|
|
set
|
|
{
|
|
include = value;
|
|
}
|
|
}
|
|
internal string[] include = null;
|
|
|
|
/// <summary>
|
|
/// Lets you exclude particular services. Services matching
|
|
/// one of these (if specified) are excluded.
|
|
/// These are interpreted as either ServiceNames or DisplayNames
|
|
/// according to the parameter set.
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateNotNullOrEmpty]
|
|
public string[] Exclude
|
|
{
|
|
get
|
|
{
|
|
return exclude;
|
|
}
|
|
set
|
|
{
|
|
exclude = value;
|
|
}
|
|
}
|
|
internal string[] exclude = null;
|
|
|
|
// 1054295-2004/12/01-JonN This also works around 1054295.
|
|
/// <summary>
|
|
/// If the input is a stream of [collections of]
|
|
/// ServiceController objects, we bypass the ServiceName and
|
|
/// DisplayName parameters and read the ServiceControllers
|
|
/// directly. This allows us to deal with services which
|
|
/// have wildcard characters in their name (servicename or
|
|
/// displayname).
|
|
/// </summary>
|
|
/// <value>ServiceController objects</value>
|
|
[Parameter(ParameterSetName = "InputObject", ValueFromPipeline = true)]
|
|
[ValidateNotNullOrEmpty]
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public ServiceController[] InputObject
|
|
{
|
|
get
|
|
{
|
|
return _inputObject;
|
|
}
|
|
set
|
|
{
|
|
_inputObject = value;
|
|
selectionMode = SelectionMode.InputObject;
|
|
}
|
|
}
|
|
private ServiceController[] _inputObject = null;
|
|
#endregion Parameters
|
|
|
|
#region Internal
|
|
|
|
/// <summary>
|
|
/// Retrieve the master list of all services
|
|
/// </summary>
|
|
/// <value></value>
|
|
/// <exception cref="System.Security.SecurityException">
|
|
/// MSDN does not document the list of exceptions,
|
|
/// but it is reasonable to expect that SecurityException is
|
|
/// among them. Errors here will terminate the cmdlet.
|
|
/// </exception>
|
|
internal ServiceController[] AllServices
|
|
{
|
|
get
|
|
{
|
|
if (null == _allServices)
|
|
{
|
|
List<ServiceController> services = new List<ServiceController>();
|
|
|
|
if (SuppliedComputerName.Length > 0)
|
|
{
|
|
foreach (string computerName in SuppliedComputerName)
|
|
{
|
|
services.AddRange(ServiceController.GetServices(computerName));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
services.AddRange(ServiceController.GetServices());
|
|
}
|
|
|
|
_allServices = services.ToArray();
|
|
}
|
|
return _allServices;
|
|
}
|
|
}
|
|
private ServiceController[] _allServices = null;
|
|
|
|
private void AddIfValidService(IList<ServiceController> listOfValidServices, string nameOfService, string computerName)
|
|
{
|
|
try
|
|
{
|
|
ServiceController sc = new ServiceController(nameOfService, computerName);
|
|
ServiceControllerStatus tmp = sc.Status; // this will throw if the service doesn't exist
|
|
|
|
// no exceptions = this is an existing, valid service = add
|
|
listOfValidServices.Add(sc);
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
}
|
|
}
|
|
|
|
internal List<ServiceController> OneService(string nameOfService)
|
|
{
|
|
Dbg.Assert(!WildcardPattern.ContainsWildcardCharacters(nameOfService), "Caller should verify that nameOfService doesn't contain wildcard characters");
|
|
|
|
List<ServiceController> services = new List<ServiceController>();
|
|
if (SuppliedComputerName.Length > 0)
|
|
{
|
|
foreach (string computerName in SuppliedComputerName)
|
|
{
|
|
AddIfValidService(services, nameOfService, computerName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AddIfValidService(services, nameOfService, ".");
|
|
}
|
|
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The computer from which to retrieve processes.
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
protected string[] SuppliedComputerName { get; set; } = Utils.EmptyArray<string>();
|
|
|
|
/// <summary>
|
|
/// Retrieve the list of all services matching the ServiceName,
|
|
/// DisplayName, Include and Exclude parameters, sorted by ServiceName.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal List<ServiceController> MatchingServices()
|
|
{
|
|
List<ServiceController> matchingServices;
|
|
switch (selectionMode)
|
|
{
|
|
case SelectionMode.DisplayName:
|
|
matchingServices = MatchingServicesByDisplayName();
|
|
break;
|
|
case SelectionMode.InputObject:
|
|
matchingServices = MatchingServicesByInput();
|
|
break;
|
|
default:
|
|
matchingServices = MatchingServicesByServiceName();
|
|
break;
|
|
}
|
|
// 2004/12/16 Note that the services will be sorted
|
|
// before being stopped. JimTru confirms that this is OK.
|
|
matchingServices.Sort(ServiceComparison);
|
|
return matchingServices;
|
|
} // MatchingServices
|
|
|
|
// sort by servicename
|
|
private static int ServiceComparison(ServiceController x, ServiceController y)
|
|
{
|
|
return String.Compare(x.ServiceName, y.ServiceName, StringComparison.CurrentCultureIgnoreCase);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves the list of all services matching the ServiceName,
|
|
/// Include and Exclude parameters.
|
|
/// Generates a non-terminating error for each specified
|
|
/// service name which is not found even though it contains
|
|
/// no wildcards.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks>
|
|
/// We do not use the ServiceController(string serviceName)
|
|
/// constructor variant, since the resultant
|
|
/// ServiceController.ServiceName is the provided serviceName
|
|
/// even when that differs from the real ServiceName by case.
|
|
/// </remarks>
|
|
private List<ServiceController> MatchingServicesByServiceName()
|
|
{
|
|
List<ServiceController> matchingServices = new List<ServiceController>();
|
|
|
|
if (null == serviceNames)
|
|
{
|
|
foreach (ServiceController service in AllServices)
|
|
{
|
|
IncludeExcludeAdd(matchingServices, service, false);
|
|
}
|
|
return matchingServices;
|
|
}
|
|
|
|
foreach (string pattern in serviceNames)
|
|
{
|
|
bool found = false;
|
|
|
|
if (WildcardPattern.ContainsWildcardCharacters(pattern))
|
|
{
|
|
WildcardPattern wildcard = WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase);
|
|
foreach (ServiceController service in AllServices)
|
|
{
|
|
if (!wildcard.IsMatch(service.ServiceName))
|
|
continue;
|
|
found = true;
|
|
IncludeExcludeAdd(matchingServices, service, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (ServiceController service in OneService(pattern))
|
|
{
|
|
found = true;
|
|
IncludeExcludeAdd(matchingServices, service, true);
|
|
}
|
|
}
|
|
|
|
if (!found && !WildcardPattern.ContainsWildcardCharacters(pattern))
|
|
{
|
|
WriteNonTerminatingError(
|
|
pattern,
|
|
"",
|
|
pattern,
|
|
null,
|
|
"NoServiceFoundForGivenName",
|
|
ServiceResources.NoServiceFoundForGivenName,
|
|
ErrorCategory.ObjectNotFound);
|
|
}
|
|
}
|
|
return matchingServices;
|
|
} // MatchingServicesByServiceName
|
|
|
|
/// <summary>
|
|
/// Retrieves the list of all services matching the DisplayName,
|
|
/// Include and Exclude parameters.
|
|
/// Generates a non-terminating error for each specified
|
|
/// display name which is not found even though it contains
|
|
/// no wildcards.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private List<ServiceController> MatchingServicesByDisplayName()
|
|
{
|
|
List<ServiceController> matchingServices = new List<ServiceController>();
|
|
if (null == DisplayName)
|
|
{
|
|
Diagnostics.Assert(false, "null DisplayName");
|
|
throw PSTraceSource.NewInvalidOperationException();
|
|
}
|
|
foreach (string pattern in DisplayName)
|
|
{
|
|
WildcardPattern wildcard =
|
|
WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase);
|
|
bool found = false;
|
|
foreach (ServiceController service in AllServices)
|
|
{
|
|
if (!wildcard.IsMatch(service.DisplayName))
|
|
continue;
|
|
found = true;
|
|
IncludeExcludeAdd(matchingServices, service, true);
|
|
}
|
|
if (!found && !WildcardPattern.ContainsWildcardCharacters(pattern))
|
|
{
|
|
WriteNonTerminatingError(
|
|
"",
|
|
pattern,
|
|
pattern,
|
|
null,
|
|
"NoServiceFoundForGivenDisplayName",
|
|
ServiceResources.NoServiceFoundForGivenDisplayName,
|
|
ErrorCategory.ObjectNotFound);
|
|
}
|
|
}
|
|
return matchingServices;
|
|
} // MatchingServicesByDisplayName
|
|
|
|
/// <summary>
|
|
/// Retrieves the list of all services matching the InputObject,
|
|
/// Include and Exclude parameters.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private List<ServiceController> MatchingServicesByInput()
|
|
{
|
|
List<ServiceController> matchingServices = new List<ServiceController>();
|
|
if (null == InputObject)
|
|
{
|
|
Diagnostics.Assert(false, "null InputObject");
|
|
throw PSTraceSource.NewInvalidOperationException();
|
|
}
|
|
foreach (ServiceController service in InputObject)
|
|
{
|
|
service.Refresh();
|
|
IncludeExcludeAdd(matchingServices, service, false);
|
|
}
|
|
return matchingServices;
|
|
} // MatchingServicesByInput
|
|
|
|
/// <summary>
|
|
/// Add <paramref name="service"/> to <paramref name="list"/>,
|
|
/// but only if it passes the Include and Exclude filters (if present)
|
|
/// and (if <paramref name="checkDuplicates"/>) if it is not
|
|
/// already on <paramref name="list"/>.
|
|
/// </summary>
|
|
/// <param name="list">list of services</param>
|
|
/// <param name="service">service to add to list</param>
|
|
/// <param name="checkDuplicates">check list for duplicates</param>
|
|
private void IncludeExcludeAdd(
|
|
List<ServiceController> list,
|
|
ServiceController service,
|
|
bool checkDuplicates)
|
|
{
|
|
if (null != include && !Matches(service, include))
|
|
return;
|
|
if (null != exclude && Matches(service, exclude))
|
|
return;
|
|
if (checkDuplicates)
|
|
{
|
|
foreach (ServiceController sc in list)
|
|
{
|
|
if (sc.ServiceName == service.ServiceName &&
|
|
sc.MachineName == service.MachineName)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
list.Add(service);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check whether <paramref name="service"/> matches the list of
|
|
/// patterns in <paramref name="matchList"/>.
|
|
/// </summary>
|
|
/// <param name="service"></param>
|
|
/// <param name="matchList"></param>
|
|
/// <returns></returns>
|
|
private bool Matches(ServiceController service, string[] matchList)
|
|
{
|
|
if (null == matchList)
|
|
throw PSTraceSource.NewArgumentNullException("matchList");
|
|
string serviceID = (selectionMode == SelectionMode.DisplayName)
|
|
? service.DisplayName
|
|
: service.ServiceName;
|
|
foreach (string pattern in matchList)
|
|
{
|
|
WildcardPattern wildcard = WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase);
|
|
if (wildcard.IsMatch(serviceID))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#endregion Internal
|
|
|
|
} // class MultipleServiceCommandBase
|
|
#endregion MultipleServiceCommandBase
|
|
|
|
#region GetServiceCommand
|
|
/// <summary>
|
|
/// This class implements the get-service command
|
|
/// </summary>
|
|
[Cmdlet(VerbsCommon.Get, "Service", DefaultParameterSetName = "Default",
|
|
HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113332", RemotingCapability = RemotingCapability.SupportedByCommand)]
|
|
[OutputType(typeof(ServiceController))]
|
|
public sealed class GetServiceCommand : MultipleServiceCommandBase
|
|
{
|
|
#region Parameters
|
|
/// <summary>
|
|
/// gets/sets an array of service names
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The ServiceName parameter is declared in subclasses,
|
|
/// since it is optional for GetService and mandatory otherwise.
|
|
/// </remarks>
|
|
[Parameter(Position = 0, ParameterSetName = "Default", ValueFromPipelineByPropertyName = true, ValueFromPipeline = true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[Alias("ServiceName")]
|
|
public string[] Name
|
|
{
|
|
get
|
|
{
|
|
return serviceNames;
|
|
}
|
|
set
|
|
{
|
|
serviceNames = value;
|
|
selectionMode = SelectionMode.ServiceName;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// gets/sets the destination computer name
|
|
/// </summary>
|
|
[Parameter(
|
|
Mandatory = false,
|
|
ValueFromPipelineByPropertyName = true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[Alias("Cn")]
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public string[] ComputerName
|
|
{
|
|
get
|
|
{
|
|
return SuppliedComputerName;
|
|
}
|
|
set
|
|
{
|
|
SuppliedComputerName = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This returns the DependentServices of the specified service.
|
|
/// </summary>
|
|
[Parameter]
|
|
[Alias("DS")]
|
|
public SwitchParameter DependentServices { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
/// This returns the ServicesDependedOn of the specified service.
|
|
/// </summary>
|
|
[Parameter]
|
|
[Alias("SDO", "ServicesDependedOn")]
|
|
public SwitchParameter RequiredServices { get; set; }
|
|
|
|
#endregion Parameters
|
|
|
|
#region Overrides
|
|
/// <summary>
|
|
/// Write the service objects
|
|
/// </summary>
|
|
protected override void ProcessRecord()
|
|
{
|
|
foreach (ServiceController service in MatchingServices())
|
|
{
|
|
if (!DependentServices.IsPresent && !RequiredServices.IsPresent)
|
|
{
|
|
WriteObject(service);
|
|
}
|
|
else
|
|
{
|
|
if (DependentServices.IsPresent)
|
|
{
|
|
foreach (ServiceController dependantserv in service.DependentServices)
|
|
{
|
|
WriteObject(dependantserv);
|
|
}
|
|
}
|
|
if (RequiredServices.IsPresent)
|
|
{
|
|
foreach (ServiceController servicedependedon in service.ServicesDependedOn)
|
|
{
|
|
WriteObject(servicedependedon);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion Overrides
|
|
}
|
|
#endregion GetServiceCommand
|
|
|
|
#region ServiceOperationBaseCommand
|
|
/// <summary>
|
|
/// This class implements the base for service commands which actually
|
|
/// act on the service(s).
|
|
/// </summary>
|
|
public abstract class ServiceOperationBaseCommand : MultipleServiceCommandBase
|
|
{
|
|
#region Parameters
|
|
/// <summary>
|
|
/// gets/sets an array of service names
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The ServiceName parameter is declared in subclasses,
|
|
/// since it is optional for GetService and mandatory otherwise.
|
|
/// </remarks>
|
|
[Parameter(Position = 0, ParameterSetName = "Default", Mandatory = true, ValueFromPipelineByPropertyName = true, ValueFromPipeline = true)]
|
|
[Alias("ServiceName")]
|
|
public string[] Name
|
|
{
|
|
get
|
|
{
|
|
return serviceNames;
|
|
}
|
|
set
|
|
{
|
|
serviceNames = value;
|
|
selectionMode = SelectionMode.ServiceName;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Service controller objects
|
|
/// </summary>
|
|
[Parameter(Position = 0, Mandatory = true, ParameterSetName = "InputObject", ValueFromPipeline = true)]
|
|
[ValidateNotNullOrEmpty]
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public new ServiceController[] InputObject
|
|
{
|
|
get
|
|
{
|
|
return base.InputObject;
|
|
}
|
|
set
|
|
{
|
|
base.InputObject = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies whether to write the objects successfully operated upon
|
|
/// to the success stream.
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter PassThru { get; set; }
|
|
|
|
#endregion Parameters
|
|
|
|
#region Internal
|
|
/// <summary>
|
|
/// Waits forever for the service to reach the desired status, but
|
|
/// writes a string to WriteWarning every 2 seconds.
|
|
/// </summary>
|
|
/// <param name="serviceController">service on which to operate</param>
|
|
/// <param name="targetStatus">desired status</param>
|
|
/// <param name="pendingStatus">
|
|
/// This is the expected status while the operation is incomplete.
|
|
/// If the service is in some other state, this means that the
|
|
/// operation failed.
|
|
/// </param>
|
|
/// <param name="resourceIdPending">
|
|
/// resourceId for a string to be written to verbose stream
|
|
/// every 2 seconds
|
|
/// </param>
|
|
/// <param name="errorId">
|
|
/// errorId for a nonterminating error if operation fails
|
|
/// </param>
|
|
/// <param name="errorMessage">
|
|
/// errorMessage for a nonterminating error if operation fails
|
|
/// </param>
|
|
/// <returns>true if action succeeded</returns>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// WriteWarning will throw this if the pipeline has been stopped.
|
|
/// This means that the delay between hitting CTRL-C and stopping
|
|
/// the cmdlet should be 2 seconds at most.
|
|
/// </exception>
|
|
internal bool DoWaitForStatus(
|
|
ServiceController serviceController,
|
|
ServiceControllerStatus targetStatus,
|
|
ServiceControllerStatus pendingStatus,
|
|
string resourceIdPending,
|
|
string errorId,
|
|
string errorMessage)
|
|
{
|
|
do
|
|
{
|
|
try
|
|
{
|
|
// ServiceController.Start will return before the service is actually started
|
|
// This API will wait forever
|
|
serviceController.WaitForStatus(
|
|
targetStatus,
|
|
new TimeSpan(20000000) // 2 seconds
|
|
);
|
|
return true; // service reached target status
|
|
}
|
|
catch (System.ServiceProcess.TimeoutException) // still waiting
|
|
{
|
|
if (serviceController.Status != pendingStatus
|
|
// NTRAID#Windows Out Of Band Releases-919945-2005/09/27-JonN
|
|
// Close window where service could complete at
|
|
// just the wrong time
|
|
&& serviceController.Status != targetStatus)
|
|
{
|
|
WriteNonTerminatingError(serviceController, null,
|
|
errorId,
|
|
errorMessage,
|
|
ErrorCategory.OpenError);
|
|
return false;
|
|
}
|
|
string message = StringUtil.Format(resourceIdPending,
|
|
serviceController.ServiceName,
|
|
serviceController.DisplayName
|
|
);
|
|
// will throw PipelineStoppedException if user hit CTRL-C
|
|
WriteWarning(message);
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This will start the service.
|
|
/// </summary>
|
|
/// <param name="serviceController">service to start</param>
|
|
/// <returns>true iff the service was started</returns>
|
|
internal bool DoStartService(ServiceController serviceController)
|
|
{
|
|
Exception exception = null;
|
|
try
|
|
{
|
|
serviceController.Start();
|
|
}
|
|
catch (Win32Exception e)
|
|
{
|
|
if (NativeMethods.ERROR_SERVICE_ALREADY_RUNNING != e.NativeErrorCode)
|
|
exception = e;
|
|
}
|
|
catch (InvalidOperationException e)
|
|
{
|
|
Win32Exception eInner = e.InnerException as Win32Exception;
|
|
if (null == eInner
|
|
|| NativeMethods.ERROR_SERVICE_ALREADY_RUNNING != eInner.NativeErrorCode)
|
|
{
|
|
exception = e;
|
|
}
|
|
}
|
|
if (null != exception)
|
|
{
|
|
// This service refused to accept the start command,
|
|
// so write a non-terminating error.
|
|
WriteNonTerminatingError(serviceController,
|
|
exception,
|
|
"CouldNotStartService",
|
|
ServiceResources.CouldNotStartService,
|
|
ErrorCategory.OpenError);
|
|
return false;
|
|
}
|
|
|
|
// ServiceController.Start will return
|
|
// before the service is actually started.
|
|
if (!DoWaitForStatus(
|
|
serviceController,
|
|
ServiceControllerStatus.Running,
|
|
ServiceControllerStatus.StartPending,
|
|
ServiceResources.StartingService,
|
|
"StartServiceFailed",
|
|
ServiceResources.StartServiceFailed))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This will stop the service.
|
|
/// </summary>
|
|
/// <param name="serviceController">service to stop</param>
|
|
/// <param name="force">stop dependent services</param>
|
|
/// <param name="waitForServiceToStop"></param>
|
|
/// <returns>true iff the service was stopped</returns>
|
|
internal List<ServiceController> DoStopService(ServiceController serviceController, bool force, bool waitForServiceToStop)
|
|
{
|
|
// Ignore ServiceController.CanStop. CanStop will be set false
|
|
// if the service is not running, but this is not an error.
|
|
|
|
List<ServiceController> stoppedServices = new List<ServiceController>();
|
|
ServiceController[] dependentServices = null;
|
|
|
|
try
|
|
{
|
|
dependentServices = serviceController.DependentServices;
|
|
}
|
|
catch (Win32Exception e)
|
|
{
|
|
WriteNonTerminatingError(serviceController, e,
|
|
"CouldNotAccessDependentServices",
|
|
ServiceResources.CouldNotAccessDependentServices,
|
|
ErrorCategory.InvalidOperation);
|
|
}
|
|
catch (InvalidOperationException e)
|
|
{
|
|
WriteNonTerminatingError(serviceController, e,
|
|
"CouldNotAccessDependentServices",
|
|
ServiceResources.CouldNotAccessDependentServices,
|
|
ErrorCategory.InvalidOperation);
|
|
}
|
|
|
|
if (!force)
|
|
{
|
|
if ((dependentServices != null)
|
|
&& (dependentServices.Length > 0))
|
|
{
|
|
// Check if all dependent services are stopped
|
|
if (!HaveAllDependentServicesStopped(dependentServices))
|
|
{
|
|
// This service has dependent services
|
|
// and the force flag is not specified.
|
|
// Add a non-critical error for it.
|
|
WriteNonTerminatingError(serviceController,
|
|
null,
|
|
"ServiceHasDependentServices",
|
|
ServiceResources.ServiceHasDependentServices,
|
|
ErrorCategory.InvalidOperation);
|
|
|
|
return stoppedServices;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dependentServices != null)
|
|
{
|
|
foreach (ServiceController service in dependentServices)
|
|
{
|
|
if ((service.Status == ServiceControllerStatus.Running ||
|
|
service.Status == ServiceControllerStatus.StartPending) &&
|
|
service.CanStop)
|
|
{
|
|
stoppedServices.Add(service);
|
|
}
|
|
}
|
|
}
|
|
|
|
Exception exception = null;
|
|
try
|
|
{
|
|
serviceController.Stop();
|
|
}
|
|
catch (Win32Exception e)
|
|
{
|
|
if (NativeMethods.ERROR_SERVICE_NOT_ACTIVE != e.NativeErrorCode)
|
|
exception = e;
|
|
}
|
|
catch (InvalidOperationException e)
|
|
{
|
|
Win32Exception eInner =
|
|
e.InnerException as Win32Exception;
|
|
if (null == eInner
|
|
|| NativeMethods.ERROR_SERVICE_NOT_ACTIVE != eInner.NativeErrorCode)
|
|
{
|
|
exception = e;
|
|
}
|
|
}
|
|
if (null != exception)
|
|
{
|
|
// This service refused to accept the stop command,
|
|
// so write a non-terminating error.
|
|
WriteNonTerminatingError(serviceController,
|
|
exception,
|
|
"CouldNotStopService",
|
|
ServiceResources.CouldNotStopService,
|
|
ErrorCategory.CloseError);
|
|
RemoveNotStoppedServices(stoppedServices);
|
|
return stoppedServices;
|
|
}
|
|
|
|
// ServiceController.Stop will return
|
|
// before the service is actually stopped.
|
|
if (waitForServiceToStop)
|
|
{
|
|
if (!DoWaitForStatus(
|
|
serviceController,
|
|
ServiceControllerStatus.Stopped,
|
|
ServiceControllerStatus.StopPending,
|
|
ServiceResources.StoppingService,
|
|
"StopServiceFailed",
|
|
ServiceResources.StopServiceFailed))
|
|
{
|
|
RemoveNotStoppedServices(stoppedServices);
|
|
return stoppedServices;
|
|
}
|
|
}
|
|
|
|
RemoveNotStoppedServices(stoppedServices);
|
|
if ((serviceController.Status.Equals(ServiceControllerStatus.Stopped)) || (serviceController.Status.Equals(ServiceControllerStatus.StopPending)))
|
|
{
|
|
stoppedServices.Add(serviceController);
|
|
}
|
|
|
|
|
|
return stoppedServices;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if all dependent services are stopped
|
|
/// </summary>
|
|
/// <param name="dependentServices"></param>
|
|
/// <returns>
|
|
/// True if all dependent services are stopped
|
|
/// False if not all dependent services are stopped
|
|
/// </returns>
|
|
private bool HaveAllDependentServicesStopped(ICollection<ServiceController> dependentServices)
|
|
{
|
|
foreach (ServiceController service in dependentServices)
|
|
{
|
|
if (service.Status != ServiceControllerStatus.Stopped)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This removes all services that are not stopped from a list of services
|
|
/// </summary>
|
|
/// <param name="services">a list of services</param>
|
|
internal void RemoveNotStoppedServices(List<ServiceController> services)
|
|
{
|
|
foreach (ServiceController service in services)
|
|
{
|
|
if (service.Status != ServiceControllerStatus.Stopped &&
|
|
service.Status != ServiceControllerStatus.StopPending)
|
|
{
|
|
services.Remove(service);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This will pause the service.
|
|
/// </summary>
|
|
/// <param name="serviceController">service to pause</param>
|
|
/// <returns>true iff the service was paused</returns>
|
|
internal bool DoPauseService(ServiceController serviceController)
|
|
{
|
|
Exception exception = null;
|
|
bool serviceNotRunning = false;
|
|
try
|
|
{
|
|
serviceController.Pause();
|
|
}
|
|
catch (Win32Exception e)
|
|
{
|
|
if (NativeMethods.ERROR_SERVICE_NOT_ACTIVE == e.NativeErrorCode)
|
|
{
|
|
serviceNotRunning = true;
|
|
}
|
|
exception = e;
|
|
}
|
|
catch (InvalidOperationException e)
|
|
{
|
|
Win32Exception eInner = e.InnerException as Win32Exception;
|
|
if (null != eInner
|
|
&& NativeMethods.ERROR_SERVICE_NOT_ACTIVE == eInner.NativeErrorCode)
|
|
{
|
|
serviceNotRunning = true;
|
|
}
|
|
exception = e;
|
|
}
|
|
if (null != exception)
|
|
{
|
|
// This service refused to accept the pause command,
|
|
// so write a non-terminating error.
|
|
string resourceIdAndErrorId = ServiceResources.CouldNotSuspendService;
|
|
if (serviceNotRunning)
|
|
{
|
|
WriteNonTerminatingError(serviceController,
|
|
exception,
|
|
"CouldNotSuspendServiceNotRunning",
|
|
ServiceResources.CouldNotSuspendServiceNotRunning,
|
|
ErrorCategory.CloseError);
|
|
}
|
|
else if (!serviceController.CanPauseAndContinue)
|
|
{
|
|
WriteNonTerminatingError(serviceController,
|
|
exception,
|
|
"CouldNotSuspendServiceNotSupported",
|
|
ServiceResources.CouldNotSuspendServiceNotSupported,
|
|
ErrorCategory.CloseError);
|
|
}
|
|
|
|
WriteNonTerminatingError(serviceController,
|
|
exception,
|
|
"CouldNotSuspendService",
|
|
ServiceResources.CouldNotSuspendService,
|
|
ErrorCategory.CloseError);
|
|
|
|
return false;
|
|
}
|
|
|
|
// ServiceController.Pause will return
|
|
// before the service is actually paused.
|
|
if (!DoWaitForStatus(
|
|
serviceController,
|
|
ServiceControllerStatus.Paused,
|
|
ServiceControllerStatus.PausePending,
|
|
ServiceResources.SuspendingService,
|
|
"SuspendServiceFailed",
|
|
ServiceResources.SuspendServiceFailed))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This will resume the service.
|
|
/// </summary>
|
|
/// <param name="serviceController">service to resume</param>
|
|
/// <returns>true iff the service was resumed</returns>
|
|
internal bool DoResumeService(ServiceController serviceController)
|
|
{
|
|
Exception exception = null;
|
|
bool serviceNotRunning = false;
|
|
try
|
|
{
|
|
serviceController.Continue();
|
|
}
|
|
catch (Win32Exception e)
|
|
{
|
|
if (NativeMethods.ERROR_SERVICE_NOT_ACTIVE == e.NativeErrorCode)
|
|
{
|
|
serviceNotRunning = true;
|
|
}
|
|
exception = e;
|
|
}
|
|
catch (InvalidOperationException e)
|
|
{
|
|
Win32Exception eInner = e.InnerException as Win32Exception;
|
|
if (null != eInner
|
|
&& NativeMethods.ERROR_SERVICE_NOT_ACTIVE == eInner.NativeErrorCode)
|
|
{
|
|
serviceNotRunning = true;
|
|
}
|
|
exception = e;
|
|
}
|
|
if (null != exception)
|
|
{
|
|
// This service refused to accept the continue command,
|
|
// so write a non-terminating error.
|
|
if (serviceNotRunning)
|
|
{
|
|
WriteNonTerminatingError(serviceController,
|
|
exception,
|
|
"CouldNotResumeServiceNotRunning",
|
|
ServiceResources.CouldNotResumeServiceNotRunning,
|
|
ErrorCategory.CloseError);
|
|
}
|
|
else if (!serviceController.CanPauseAndContinue)
|
|
{
|
|
WriteNonTerminatingError(serviceController,
|
|
exception,
|
|
"CouldNotResumeServiceNotSupported",
|
|
ServiceResources.CouldNotResumeServiceNotSupported,
|
|
ErrorCategory.CloseError);
|
|
}
|
|
|
|
WriteNonTerminatingError(serviceController,
|
|
exception,
|
|
"CouldNotResumeService",
|
|
ServiceResources.CouldNotResumeService,
|
|
ErrorCategory.CloseError);
|
|
|
|
return false;
|
|
}
|
|
|
|
// ServiceController.Continue will return
|
|
// before the service is actually continued.
|
|
if (!DoWaitForStatus(
|
|
serviceController,
|
|
ServiceControllerStatus.Running,
|
|
ServiceControllerStatus.ContinuePending,
|
|
ServiceResources.ResumingService,
|
|
"ResumeServiceFailed",
|
|
ServiceResources.ResumeServiceFailed))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endregion Internal
|
|
}
|
|
#endregion ServiceOperationBaseCommand
|
|
|
|
#region StopServiceCommand
|
|
|
|
/// <summary>
|
|
/// This class implements the stop-service command
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Note that the services will be sorted before being stopped.
|
|
/// PM confirms that this is OK.
|
|
/// </remarks>
|
|
[Cmdlet(VerbsLifecycle.Stop, "Service", DefaultParameterSetName = "InputObject", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113414")]
|
|
[OutputType(typeof(ServiceController))]
|
|
public sealed class StopServiceCommand : ServiceOperationBaseCommand
|
|
{
|
|
/// <summary>
|
|
/// Specifies whether to force a service to stop
|
|
/// even if it has dependent services.
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter Force { get; set; }
|
|
|
|
/// <summary>
|
|
/// Specifies whether to wait for a service to reach the stopped state before returning
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter NoWait { get; set; }
|
|
|
|
/// <summary>
|
|
/// Stop the services.
|
|
/// It is a non-terminating error if -Force is not specified and
|
|
/// the service has dependent services, whether or not they
|
|
/// are running.
|
|
/// It is a non-terminating error if the service stop operation fails.
|
|
/// </summary>
|
|
protected override void ProcessRecord()
|
|
{
|
|
foreach (ServiceController serviceController in MatchingServices())
|
|
{
|
|
// confirm the operation first
|
|
// this is always false if WhatIf is set
|
|
if (!ShouldProcessServiceOperation(serviceController))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
List<ServiceController> stoppedServices = DoStopService(serviceController, Force, !NoWait);
|
|
|
|
if (PassThru && stoppedServices.Count > 0)
|
|
{
|
|
foreach (ServiceController service in stoppedServices)
|
|
{
|
|
WriteObject(service);
|
|
}
|
|
}
|
|
}
|
|
} // ProcessRecord
|
|
} // StopServiceCommand
|
|
#endregion StopServiceCommand
|
|
|
|
#region StartServiceCommand
|
|
/// <summary>
|
|
/// This class implements the start-service command
|
|
/// </summary>
|
|
[Cmdlet(VerbsLifecycle.Start, "Service", DefaultParameterSetName = "InputObject", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113406")]
|
|
[OutputType(typeof(ServiceController))]
|
|
public sealed class StartServiceCommand : ServiceOperationBaseCommand
|
|
{
|
|
/// <summary>
|
|
/// Start the services
|
|
/// </summary>
|
|
protected override void ProcessRecord()
|
|
{
|
|
foreach (ServiceController serviceController in MatchingServices())
|
|
{
|
|
// confirm the operation first
|
|
// this is always false if WhatIf is set
|
|
if (!ShouldProcessServiceOperation(serviceController))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (DoStartService(serviceController))
|
|
{
|
|
if (PassThru)
|
|
WriteObject(serviceController);
|
|
}
|
|
}
|
|
} // ProcessRecord
|
|
} // class StartServiceCommand
|
|
#endregion StartServiceCommand
|
|
|
|
#region SuspendServiceCommand
|
|
/// <summary>
|
|
/// This class implements the suspend-service command
|
|
/// </summary>
|
|
[Cmdlet(VerbsLifecycle.Suspend, "Service", DefaultParameterSetName = "InputObject", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113416")]
|
|
[OutputType(typeof(ServiceController))]
|
|
public sealed class SuspendServiceCommand : ServiceOperationBaseCommand
|
|
{
|
|
/// <summary>
|
|
/// Start the services
|
|
/// </summary>
|
|
protected override void ProcessRecord()
|
|
{
|
|
foreach (ServiceController serviceController in MatchingServices())
|
|
{
|
|
// confirm the operation first
|
|
// this is always false if WhatIf is set
|
|
if (!ShouldProcessServiceOperation(serviceController))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (DoPauseService(serviceController))
|
|
{
|
|
if (PassThru)
|
|
WriteObject(serviceController);
|
|
}
|
|
}
|
|
} // ProcessRecord
|
|
} // class SuspendServiceCommand
|
|
#endregion SuspendServiceCommand
|
|
|
|
#region ResumeServiceCommand
|
|
/// <summary>
|
|
/// This class implements the resume-service command
|
|
/// </summary>
|
|
[Cmdlet(VerbsLifecycle.Resume, "Service", DefaultParameterSetName = "InputObject", SupportsShouldProcess = true,
|
|
HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113386")]
|
|
[OutputType(typeof(ServiceController))]
|
|
public sealed class ResumeServiceCommand : ServiceOperationBaseCommand
|
|
{
|
|
/// <summary>
|
|
/// Start the services
|
|
/// </summary>
|
|
protected override void ProcessRecord()
|
|
{
|
|
foreach (ServiceController serviceController in MatchingServices())
|
|
{
|
|
// confirm the operation first
|
|
// this is always false if WhatIf is set
|
|
if (!ShouldProcessServiceOperation(serviceController))
|
|
{
|
|
continue;
|
|
}
|
|
if (DoResumeService(serviceController))
|
|
{
|
|
if (PassThru)
|
|
WriteObject(serviceController);
|
|
}
|
|
}
|
|
} // ProcessRecord
|
|
} // class ResumeServiceCommand
|
|
#endregion ResumeServiceCommand
|
|
|
|
#region RestartServiceCommand
|
|
|
|
/// <summary>
|
|
/// This class implements the restart-service command
|
|
/// </summary>
|
|
[Cmdlet(VerbsLifecycle.Restart, "Service", DefaultParameterSetName = "InputObject", SupportsShouldProcess = true,
|
|
HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113385")]
|
|
[OutputType(typeof(ServiceController))]
|
|
public sealed class RestartServiceCommand : ServiceOperationBaseCommand
|
|
{
|
|
/// <summary>
|
|
/// Specifies whether to force a service to stop
|
|
/// even if it has dependent services.
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter Force { get; set; }
|
|
|
|
/// <summary>
|
|
/// Stop and restart the services.
|
|
/// It is a non-terminating error if the service is running,
|
|
/// -Force is not specified and the service has dependent services,
|
|
/// whether or not the dependent services are running.
|
|
/// It is a non-terminating error if the service stop operation fails.
|
|
/// </summary>
|
|
protected override void ProcessRecord()
|
|
{
|
|
foreach (ServiceController serviceController in MatchingServices())
|
|
{
|
|
// confirm the operation first
|
|
// this is always false if WhatIf is set
|
|
if (!ShouldProcessServiceOperation(serviceController))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//Set the NoWait parameter to false since we are not adding this switch to this cmdlet.
|
|
List<ServiceController> stoppedServices = DoStopService(serviceController, Force, true);
|
|
|
|
if (stoppedServices.Count > 0)
|
|
{
|
|
foreach (ServiceController service in stoppedServices)
|
|
{
|
|
if (DoStartService(service))
|
|
{
|
|
if (PassThru)
|
|
WriteObject(service);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // ProcessRecord
|
|
} // RestartServiceCommand
|
|
#endregion RestartServiceCommand
|
|
|
|
#region SetServiceCommand
|
|
|
|
/// <summary>
|
|
/// This class implements the set-service command
|
|
/// </summary>
|
|
[Cmdlet(VerbsCommon.Set, "Service", SupportsShouldProcess = true, DefaultParameterSetName = "Name",
|
|
HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113399", RemotingCapability = RemotingCapability.SupportedByCommand)]
|
|
[OutputType(typeof(ServiceController))]
|
|
public class SetServiceCommand : ServiceOperationBaseCommand
|
|
{
|
|
#region Parameters
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "ComputerName".
|
|
/// Set the properties of service running on the list of computer names
|
|
/// specified. The default is the local computer.
|
|
/// Type the NETBIOS name, an IP address, or a fully-qualified domain name of
|
|
/// one or more remote computers. To indicate the local computer, use the
|
|
/// computer name, "localhost" or a dot (.). When the computer is in a different
|
|
/// domain than the user, the fully-qualified domain name is required.
|
|
/// </summary>
|
|
[Parameter(ValueFromPipelineByPropertyName = true)]
|
|
[ValidateNotNullOrEmpty]
|
|
[Alias("cn")]
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public String[] ComputerName { get; set; } = new string[] { "." };
|
|
|
|
/// <summary>
|
|
/// service name
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "Name")]
|
|
[Alias("ServiceName", "SN")]
|
|
public new String Name
|
|
{
|
|
get { return serviceName; }
|
|
set
|
|
{
|
|
serviceName = value;
|
|
}
|
|
}
|
|
internal String serviceName = null;
|
|
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "DisplayName".
|
|
/// Specifies a new display name for the cmdlet.
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateNotNullOrEmpty]
|
|
[Alias("DN")]
|
|
public new string DisplayName
|
|
{
|
|
get { return displayName; }
|
|
set
|
|
{
|
|
displayName = value;
|
|
}
|
|
}
|
|
internal string displayName = null;
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "Description".
|
|
/// Specifies a new description for the service.
|
|
/// The service description appears in Services in Computer Management.
|
|
/// Description is not a property of the ServiceController object that
|
|
/// Get-Service retrieve
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateNotNullOrEmpty]
|
|
public string Description
|
|
{
|
|
get { return description; }
|
|
set
|
|
{
|
|
description = value;
|
|
}
|
|
}
|
|
internal string description = null;
|
|
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "StartupType".
|
|
/// Changes the starting mode of the service. Valid values for StartupType are:
|
|
/// -- Automatic: Start when the system starts.
|
|
/// -- Manual : Starts only when started by a user or program.
|
|
/// -- Disabled : Can
|
|
/// </summary>
|
|
[Parameter]
|
|
[Alias("StartMode", "SM", "ST")]
|
|
[ValidateNotNullOrEmpty]
|
|
public ServiceStartMode StartupType
|
|
{
|
|
get { return startupType; }
|
|
set
|
|
{
|
|
startupType = value;
|
|
}
|
|
}
|
|
// We set the initial value to an invalid value so that we can
|
|
// distinguish when this is and is not set.
|
|
internal ServiceStartMode startupType = (ServiceStartMode)(-1);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "Status".
|
|
/// This specifies what state the service should be in (e.g. Running, Stopped,
|
|
/// Paused). If it is already in that state, do nothing. If it is not, do the
|
|
/// appropriate action to bring about the desired result (start/stop/suspend the
|
|
/// service) and issue an error if this cannot be achieved.
|
|
/// Status can be Paused , Running and Stopped
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateSetAttribute(new string[] { "Running", "Stopped", "Paused" })]
|
|
public string Status
|
|
{
|
|
get { return serviceStatus; }
|
|
set
|
|
{
|
|
serviceStatus = value;
|
|
}
|
|
}
|
|
internal string serviceStatus = null;
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "InputObject".
|
|
/// Specifies ServiceController object representing the services to be stopped.
|
|
/// Enter a variable that contains the objects or type a command or expression
|
|
/// that gets the objects.
|
|
/// </summary>
|
|
[Parameter(ValueFromPipeline = true,
|
|
ParameterSetName = "InputObject")]
|
|
public new ServiceController InputObject { get; set; }
|
|
|
|
/// <summary>
|
|
/// This is not a parameter for this cmdlet.
|
|
/// </summary>
|
|
//This has been shadowed from base class and removed parameter tag to fix gcm "Set-Service" -syntax
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public new string[] Include
|
|
{
|
|
get
|
|
{
|
|
return include;
|
|
}
|
|
set
|
|
{
|
|
include = null;
|
|
}
|
|
}
|
|
internal new string[] include = null;
|
|
|
|
/// <summary>
|
|
/// This is not a parameter for this cmdlet.
|
|
/// </summary>
|
|
//This has been shadowed from base class and removed parameter tag to fix gcm "Set-Service" -syntax
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public new string[] Exclude
|
|
{
|
|
get
|
|
{
|
|
return exclude;
|
|
}
|
|
set
|
|
{
|
|
exclude = null;
|
|
}
|
|
}
|
|
internal new string[] exclude = null;
|
|
#endregion Parameters
|
|
|
|
#region Overrides
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
[ArchitectureSensitive]
|
|
protected override void ProcessRecord()
|
|
{
|
|
ServiceController service = null;
|
|
string ServiceComputerName = null;
|
|
foreach (string computer in ComputerName)
|
|
{
|
|
bool objServiceShouldBeDisposed = false;
|
|
try
|
|
{
|
|
if (_ParameterSetName.Equals("InputObject", StringComparison.OrdinalIgnoreCase) && InputObject != null)
|
|
{
|
|
service = InputObject;
|
|
Name = service.ServiceName;
|
|
ServiceComputerName = service.MachineName;
|
|
//computer = service.MachineName;
|
|
objServiceShouldBeDisposed = false;
|
|
}
|
|
else
|
|
{
|
|
ServiceComputerName = computer;
|
|
service = new ServiceController(serviceName, ServiceComputerName);
|
|
objServiceShouldBeDisposed = true;
|
|
}
|
|
Diagnostics.Assert(!String.IsNullOrEmpty(Name), "null ServiceName");
|
|
// "new ServiceController" will succeed even if
|
|
// there is no such service. This checks whether
|
|
// the service actually exists.
|
|
|
|
string unusedByDesign = service.DisplayName;
|
|
}
|
|
catch (ArgumentException ex)
|
|
{
|
|
//cannot use WriteNonterminatingError as service is null
|
|
ErrorRecord er = new ErrorRecord(ex, "ArgumentException", ErrorCategory.ObjectNotFound, computer);
|
|
WriteError(er);
|
|
continue;
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
//cannot use WriteNonterminatingError as service is null
|
|
ErrorRecord er = new ErrorRecord(ex, "InvalidOperationException", ErrorCategory.ObjectNotFound, computer);
|
|
WriteError(er);
|
|
continue;
|
|
}
|
|
|
|
try // In finally we ensure dispose, if object not pipelined.
|
|
{
|
|
// confirm the operation first
|
|
// this is always false if WhatIf is set
|
|
if (!ShouldProcessServiceOperation(service))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
NakedWin32Handle hScManager = IntPtr.Zero;
|
|
NakedWin32Handle hService = IntPtr.Zero;
|
|
try
|
|
{
|
|
hScManager = NativeMethods.OpenSCManagerW(
|
|
ServiceComputerName,
|
|
null,
|
|
NativeMethods.SC_MANAGER_CONNECT
|
|
);
|
|
if (IntPtr.Zero == hScManager)
|
|
{
|
|
int lastError = Marshal.GetLastWin32Error();
|
|
Win32Exception exception = new Win32Exception(lastError);
|
|
WriteNonTerminatingError(
|
|
service,
|
|
ServiceComputerName,
|
|
exception,
|
|
"ComputerAccessDenied",
|
|
ServiceResources.ComputerAccessDenied,
|
|
ErrorCategory.PermissionDenied);
|
|
continue;
|
|
}
|
|
hService = NativeMethods.OpenServiceW(
|
|
hScManager,
|
|
Name,
|
|
NativeMethods.SERVICE_CHANGE_CONFIG
|
|
);
|
|
if (IntPtr.Zero == hService)
|
|
{
|
|
int lastError = Marshal.GetLastWin32Error();
|
|
Win32Exception exception = new Win32Exception(lastError);
|
|
WriteNonTerminatingError(
|
|
service,
|
|
exception,
|
|
"CouldNotSetService",
|
|
ServiceResources.CouldNotSetService,
|
|
ErrorCategory.PermissionDenied);
|
|
continue;
|
|
}
|
|
|
|
// modify startup type or display name
|
|
if (!String.IsNullOrEmpty(DisplayName)
|
|
|| (ServiceStartMode)(-1) != StartupType)
|
|
{
|
|
DWORD dwStartType = NativeMethods.SERVICE_NO_CHANGE;
|
|
switch (StartupType)
|
|
{
|
|
case ServiceStartMode.Automatic:
|
|
dwStartType = NativeMethods.SERVICE_AUTO_START;
|
|
break;
|
|
case ServiceStartMode.Manual:
|
|
dwStartType = NativeMethods.SERVICE_DEMAND_START;
|
|
break;
|
|
case ServiceStartMode.Disabled:
|
|
dwStartType = NativeMethods.SERVICE_DISABLED;
|
|
break;
|
|
default:
|
|
Diagnostics.Assert(
|
|
((ServiceStartMode)(-1)) == StartupType,
|
|
"bad StartupType");
|
|
break;
|
|
}
|
|
bool succeeded = NativeMethods.ChangeServiceConfigW(
|
|
hService,
|
|
NativeMethods.SERVICE_NO_CHANGE,
|
|
dwStartType,
|
|
NativeMethods.SERVICE_NO_CHANGE,
|
|
null,
|
|
null,
|
|
IntPtr.Zero,
|
|
null,
|
|
null,
|
|
IntPtr.Zero,
|
|
DisplayName
|
|
);
|
|
if (!succeeded)
|
|
{
|
|
int lastError = Marshal.GetLastWin32Error();
|
|
Win32Exception exception = new Win32Exception(lastError);
|
|
WriteNonTerminatingError(
|
|
service,
|
|
exception,
|
|
"CouldNotSetService",
|
|
ServiceResources.CouldNotSetService,
|
|
ErrorCategory.PermissionDenied);
|
|
continue;
|
|
}
|
|
} // modify startup type or display name
|
|
|
|
NativeMethods.SERVICE_DESCRIPTIONW sd = new NativeMethods.SERVICE_DESCRIPTIONW();
|
|
sd.lpDescription = Description;
|
|
int size = Marshal.SizeOf(sd);
|
|
IntPtr buffer = Marshal.AllocCoTaskMem(size);
|
|
Marshal.StructureToPtr(sd, buffer, false);
|
|
|
|
bool status = NativeMethods.ChangeServiceConfig2W(
|
|
hService,
|
|
NativeMethods.SERVICE_CONFIG_DESCRIPTION,
|
|
buffer);
|
|
|
|
if (!status)
|
|
{
|
|
int lastError = Marshal.GetLastWin32Error();
|
|
Win32Exception exception = new Win32Exception(lastError);
|
|
WriteNonTerminatingError(
|
|
service,
|
|
exception,
|
|
"CouldNotSetServiceDescription",
|
|
ServiceResources.CouldNotSetServiceDescription,
|
|
ErrorCategory.PermissionDenied);
|
|
}
|
|
|
|
|
|
|
|
//Addition by v-ramch Mar 11 2008
|
|
//if Status parameter specified do the necessary action
|
|
//to bring about the desired result
|
|
|
|
if (!string.IsNullOrEmpty(Status))
|
|
{
|
|
if (Status.Equals("Running", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (!service.Status.Equals(ServiceControllerStatus.Running))
|
|
{
|
|
if (service.Status.Equals(ServiceControllerStatus.Paused))
|
|
//resume service
|
|
DoResumeService(service);
|
|
else
|
|
//start service
|
|
DoStartService(service);
|
|
}
|
|
}
|
|
else if (Status.Equals("Stopped", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
if (!service.Status.Equals(ServiceControllerStatus.Stopped))
|
|
{
|
|
//check for the dependent services as set-service dont have force parameter
|
|
ServiceController[] dependentServices = service.DependentServices;
|
|
|
|
if ((dependentServices != null) && (dependentServices.Length > 0))
|
|
{
|
|
WriteNonTerminatingError(service, null, "ServiceHasDependentServicesNoForce", ServiceResources.ServiceHasDependentServicesNoForce, ErrorCategory.InvalidOperation);
|
|
continue;
|
|
}
|
|
|
|
ServiceController[] servicedependedon = service.ServicesDependedOn;
|
|
|
|
if ((servicedependedon != null) && (servicedependedon.Length > 0))
|
|
{
|
|
WriteNonTerminatingError(service, null, "ServiceIsDependentOnNoForce", ServiceResources.ServiceIsDependentOnNoForce, ErrorCategory.InvalidOperation);
|
|
continue;
|
|
}
|
|
//stop service,give the force parameter always true as we have already checked for the dependent services
|
|
//Specify NoWait parameter as always false since we are not adding this switch to this cmdlet
|
|
DoStopService(service, true, true);
|
|
}
|
|
}
|
|
else if (Status.Equals("Paused", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
if (!service.Status.Equals(ServiceControllerStatus.Paused))
|
|
//pause service
|
|
DoPauseService(service);
|
|
}
|
|
}
|
|
if (PassThru.IsPresent)
|
|
{
|
|
//to display the service,refreshing the service would not show the display name after updating
|
|
ServiceController displayservice = new ServiceController(Name, ServiceComputerName);
|
|
WriteObject(displayservice);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (IntPtr.Zero != hService)
|
|
{
|
|
bool succeeded = NativeMethods.CloseServiceHandle(hService);
|
|
if (!succeeded)
|
|
{
|
|
int lastError = Marshal.GetLastWin32Error();
|
|
Win32Exception exception = new Win32Exception(lastError);
|
|
WriteNonTerminatingError(
|
|
service,
|
|
exception,
|
|
"CouldNotSetServiceDescription",
|
|
ServiceResources.CouldNotSetServiceDescription,
|
|
ErrorCategory.PermissionDenied);
|
|
}
|
|
}
|
|
|
|
if (IntPtr.Zero != hScManager)
|
|
{
|
|
bool succeeded = NativeMethods.CloseServiceHandle(hScManager);
|
|
if (!succeeded)
|
|
{
|
|
int lastError = Marshal.GetLastWin32Error();
|
|
Win32Exception exception = new Win32Exception(lastError);
|
|
WriteNonTerminatingError(
|
|
service,
|
|
exception,
|
|
"CouldNotSetServiceDescription",
|
|
ServiceResources.CouldNotSetServiceDescription,
|
|
ErrorCategory.PermissionDenied);
|
|
}
|
|
}
|
|
} // finally
|
|
} //End try
|
|
finally
|
|
{
|
|
if (objServiceShouldBeDisposed)
|
|
{
|
|
service.Dispose();
|
|
}
|
|
}
|
|
}//end for
|
|
}
|
|
#endregion Overrides
|
|
|
|
|
|
} // class SetServiceCommand
|
|
#endregion SetServiceCommand
|
|
|
|
#region NewServiceCommand
|
|
/// <summary>
|
|
/// This class implements the set-service command
|
|
/// </summary>
|
|
[Cmdlet(VerbsCommon.New, "Service", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113359")]
|
|
[OutputType(typeof(ServiceController))]
|
|
public class NewServiceCommand : ServiceBaseCommand
|
|
{
|
|
#region Parameters
|
|
/// <summary>
|
|
/// Name of the service to create
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter(Position = 0, Mandatory = true)]
|
|
[Alias("ServiceName")]
|
|
public string Name
|
|
{
|
|
get { return serviceName; }
|
|
set { serviceName = value; }
|
|
}
|
|
internal string serviceName = null;
|
|
|
|
/// <summary>
|
|
/// The executable which implements this service
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter(Position = 1, Mandatory = true)]
|
|
public string BinaryPathName
|
|
{
|
|
get { return binaryPathName; }
|
|
set { binaryPathName = value; }
|
|
}
|
|
internal string binaryPathName = null;
|
|
|
|
/// <summary>
|
|
/// DisplayName of the service to create
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter]
|
|
[ValidateNotNullOrEmpty]
|
|
public string DisplayName
|
|
{
|
|
get { return displayName; }
|
|
set { displayName = value; }
|
|
}
|
|
internal string displayName = null;
|
|
|
|
/// <summary>
|
|
/// Description of the service to create
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter]
|
|
[ValidateNotNullOrEmpty]
|
|
public string Description
|
|
{
|
|
get { return description; }
|
|
set { description = value; }
|
|
}
|
|
internal string description = null;
|
|
|
|
/// <summary>
|
|
/// Should the service start automatically?
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter]
|
|
public ServiceStartMode StartupType
|
|
{
|
|
get { return startupType; }
|
|
set { startupType = value; }
|
|
}
|
|
internal ServiceStartMode startupType = ServiceStartMode.Automatic;
|
|
|
|
/// <summary>
|
|
/// Account under which the service should run
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter]
|
|
[Credential()]
|
|
public PSCredential Credential
|
|
{
|
|
get { return credential; }
|
|
set { credential = value; }
|
|
}
|
|
internal PSCredential credential = null;
|
|
|
|
/// <summary>
|
|
/// Other services on which the new service depends
|
|
/// </summary>
|
|
/// <value></value>
|
|
[Parameter]
|
|
public string[] DependsOn
|
|
{
|
|
get { return dependsOn; }
|
|
set { dependsOn = value; }
|
|
}
|
|
internal string[] dependsOn = null;
|
|
#endregion Parameters
|
|
|
|
#region Overrides
|
|
/// <summary>
|
|
/// Create the service
|
|
/// </summary>
|
|
[ArchitectureSensitive]
|
|
protected override void BeginProcessing()
|
|
{
|
|
Diagnostics.Assert(!String.IsNullOrEmpty(Name),
|
|
"null ServiceName");
|
|
Diagnostics.Assert(!String.IsNullOrEmpty(BinaryPathName),
|
|
"null BinaryPathName");
|
|
|
|
// confirm the operation first
|
|
// this is always false if WhatIf is set
|
|
if (!ShouldProcessServiceOperation(DisplayName ?? "", Name))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Connect to the service controller
|
|
NakedWin32Handle hScManager = IntPtr.Zero;
|
|
NakedWin32Handle hService = IntPtr.Zero;
|
|
IntPtr password = IntPtr.Zero;
|
|
try
|
|
{
|
|
hScManager = NativeMethods.OpenSCManagerW(
|
|
null,
|
|
null,
|
|
NativeMethods.SC_MANAGER_CONNECT | NativeMethods.SC_MANAGER_CREATE_SERVICE
|
|
);
|
|
if (IntPtr.Zero == hScManager)
|
|
{
|
|
int lastError = Marshal.GetLastWin32Error();
|
|
Win32Exception exception = new Win32Exception(lastError);
|
|
WriteNonTerminatingError(
|
|
Name,
|
|
DisplayName,
|
|
Name,
|
|
exception,
|
|
"CouldNotNewService",
|
|
ServiceResources.CouldNotNewService,
|
|
ErrorCategory.PermissionDenied);
|
|
return;
|
|
}
|
|
DWORD dwStartType = NativeMethods.SERVICE_AUTO_START;
|
|
switch (StartupType)
|
|
{
|
|
case ServiceStartMode.Automatic:
|
|
dwStartType = NativeMethods.SERVICE_AUTO_START;
|
|
break;
|
|
case ServiceStartMode.Manual:
|
|
dwStartType = NativeMethods.SERVICE_DEMAND_START;
|
|
break;
|
|
case ServiceStartMode.Disabled:
|
|
dwStartType = NativeMethods.SERVICE_DISABLED;
|
|
break;
|
|
default:
|
|
Diagnostics.Assert(
|
|
((ServiceStartMode)(-1)) == StartupType,
|
|
"bad StartupType");
|
|
break;
|
|
}
|
|
|
|
// set up the double-null-terminated lpDependencies parameter
|
|
IntPtr lpDependencies = IntPtr.Zero;
|
|
if (null != DependsOn)
|
|
{
|
|
int numchars = 1; // final null
|
|
foreach (string dependedOn in DependsOn)
|
|
{
|
|
numchars += dependedOn.Length + 1;
|
|
}
|
|
char[] doubleNullArray = new char[numchars];
|
|
int pos = 0;
|
|
foreach (string dependedOn in DependsOn)
|
|
{
|
|
Array.Copy(
|
|
dependedOn.ToCharArray(), 0,
|
|
doubleNullArray, pos,
|
|
dependedOn.Length
|
|
);
|
|
pos += dependedOn.Length;
|
|
doubleNullArray[pos++] = (char)0; // null terminator
|
|
}
|
|
doubleNullArray[pos++] = (char)0; // double-null terminator
|
|
Diagnostics.Assert(pos == numchars, "lpDependencies build error");
|
|
lpDependencies = Marshal.AllocHGlobal(
|
|
numchars * Marshal.SystemDefaultCharSize);
|
|
Marshal.Copy(doubleNullArray, 0, lpDependencies, numchars);
|
|
}
|
|
|
|
// set up the Credential parameter
|
|
string username = null;
|
|
if (null != Credential)
|
|
{
|
|
username = Credential.UserName;
|
|
password = Marshal.SecureStringToCoTaskMemUnicode(Credential.Password);
|
|
}
|
|
|
|
// Create the service
|
|
hService = NativeMethods.CreateServiceW(
|
|
hScManager,
|
|
Name,
|
|
DisplayName,
|
|
NativeMethods.SERVICE_CHANGE_CONFIG,
|
|
NativeMethods.SERVICE_WIN32_OWN_PROCESS,
|
|
dwStartType,
|
|
NativeMethods.SERVICE_ERROR_NORMAL,
|
|
BinaryPathName,
|
|
null,
|
|
null,
|
|
lpDependencies,
|
|
username,
|
|
password
|
|
);
|
|
if (IntPtr.Zero == hService)
|
|
{
|
|
int lastError = Marshal.GetLastWin32Error();
|
|
Win32Exception exception = new Win32Exception(lastError);
|
|
WriteNonTerminatingError(
|
|
Name,
|
|
DisplayName,
|
|
Name,
|
|
exception,
|
|
"CouldNotNewService",
|
|
ServiceResources.CouldNotNewService,
|
|
ErrorCategory.PermissionDenied);
|
|
return;
|
|
}
|
|
|
|
// Set the service description
|
|
NativeMethods.SERVICE_DESCRIPTIONW sd = new NativeMethods.SERVICE_DESCRIPTIONW();
|
|
sd.lpDescription = Description;
|
|
int size = Marshal.SizeOf(sd);
|
|
IntPtr buffer = Marshal.AllocCoTaskMem(size);
|
|
Marshal.StructureToPtr(sd, buffer, false);
|
|
|
|
bool succeeded = NativeMethods.ChangeServiceConfig2W(
|
|
hService,
|
|
NativeMethods.SERVICE_CONFIG_DESCRIPTION,
|
|
buffer);
|
|
|
|
if (!succeeded)
|
|
{
|
|
int lastError = Marshal.GetLastWin32Error();
|
|
Win32Exception exception = new Win32Exception(lastError);
|
|
WriteNonTerminatingError(
|
|
Name,
|
|
DisplayName,
|
|
Name,
|
|
exception,
|
|
"CouldNotNewServiceDescription",
|
|
ServiceResources.CouldNotNewServiceDescription,
|
|
ErrorCategory.PermissionDenied);
|
|
}
|
|
|
|
// write the ServiceController for the new service
|
|
using (ServiceController service =
|
|
new ServiceController(Name)) // ensure dispose
|
|
{
|
|
WriteObject(service);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (IntPtr.Zero != password)
|
|
{
|
|
Marshal.ZeroFreeCoTaskMemUnicode(password);
|
|
}
|
|
|
|
if (IntPtr.Zero != hService)
|
|
{
|
|
bool succeeded = NativeMethods.CloseServiceHandle(hService);
|
|
if (!succeeded)
|
|
{
|
|
int lastError = Marshal.GetLastWin32Error();
|
|
Win32Exception exception = new Win32Exception(lastError);
|
|
WriteNonTerminatingError(
|
|
Name,
|
|
DisplayName,
|
|
Name,
|
|
exception,
|
|
"CouldNotNewServiceDescription",
|
|
ServiceResources.CouldNotNewServiceDescription,
|
|
ErrorCategory.PermissionDenied);
|
|
}
|
|
}
|
|
|
|
if (IntPtr.Zero != hScManager)
|
|
{
|
|
bool succeeded = NativeMethods.CloseServiceHandle(hScManager);
|
|
if (!succeeded)
|
|
{
|
|
int lastError = Marshal.GetLastWin32Error();
|
|
Win32Exception exception = new Win32Exception(lastError);
|
|
WriteNonTerminatingError(
|
|
Name,
|
|
DisplayName,
|
|
Name,
|
|
exception,
|
|
"CouldNotNewServiceDescription",
|
|
ServiceResources.CouldNotNewServiceDescription,
|
|
ErrorCategory.PermissionDenied);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion Overrides
|
|
} // class NewServiceCommand
|
|
#endregion NewServiceCommand
|
|
|
|
#region ServiceCommandException
|
|
/// <summary>
|
|
/// Non-terminating errors occurring in the service noun commands
|
|
/// </summary>
|
|
[Serializable]
|
|
public class ServiceCommandException : SystemException
|
|
{
|
|
#region ctors
|
|
/// <summary>
|
|
/// unimplemented standard constructor
|
|
/// </summary>
|
|
/// <returns> doesn't return </returns>
|
|
public ServiceCommandException()
|
|
: base()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// standard constructor
|
|
/// </summary>
|
|
/// <param name="message"></param>
|
|
/// <returns> constructed object </returns>
|
|
public ServiceCommandException(string message)
|
|
: base(message)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// standard constructor
|
|
/// </summary>
|
|
/// <param name="message"></param>
|
|
/// <param name="innerException"></param>
|
|
public ServiceCommandException(string message, Exception innerException)
|
|
: base(message, innerException)
|
|
{
|
|
}
|
|
#endregion ctors
|
|
|
|
#region Serialization
|
|
/// <summary>
|
|
/// serialization constructor
|
|
/// </summary>
|
|
/// <param name="info"></param>
|
|
/// <param name="context"></param>
|
|
/// <returns> constructed object </returns>
|
|
protected ServiceCommandException(SerializationInfo info, StreamingContext context)
|
|
: base(info, context)
|
|
{
|
|
if (info == null)
|
|
{
|
|
throw new ArgumentNullException("info");
|
|
}
|
|
|
|
_serviceName = info.GetString("ServiceName");
|
|
}
|
|
/// <summary>
|
|
/// Serializer
|
|
/// </summary>
|
|
/// <param name="info"> serialization information </param>
|
|
/// <param name="context"> streaming context </param>
|
|
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
|
|
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
|
{
|
|
if (info == null)
|
|
{
|
|
throw new ArgumentNullException("info");
|
|
}
|
|
|
|
base.GetObjectData(info, context);
|
|
info.AddValue("ServiceName", _serviceName);
|
|
}
|
|
#endregion Serialization
|
|
|
|
#region Properties
|
|
/// <summary>
|
|
/// Name of the service which could not be found or operated upon
|
|
/// </summary>
|
|
/// <value></value>
|
|
public string ServiceName
|
|
{
|
|
get { return _serviceName; }
|
|
set { _serviceName = value; }
|
|
}
|
|
private string _serviceName = String.Empty;
|
|
#endregion Properties
|
|
} // class ServiceCommandException
|
|
#endregion ServiceCommandException
|
|
|
|
#region NativeMethods
|
|
internal static class NativeMethods
|
|
{
|
|
// from winuser.h
|
|
internal const int ERROR_SERVICE_ALREADY_RUNNING = 1056;
|
|
internal const int ERROR_SERVICE_NOT_ACTIVE = 1062;
|
|
internal const DWORD SC_MANAGER_CONNECT = 1;
|
|
internal const DWORD SC_MANAGER_CREATE_SERVICE = 2;
|
|
internal const DWORD SERVICE_QUERY_CONFIG = 1;
|
|
internal const DWORD SERVICE_CHANGE_CONFIG = 2;
|
|
internal const DWORD SERVICE_NO_CHANGE = 0xffffffff;
|
|
internal const DWORD SERVICE_AUTO_START = 0x2;
|
|
internal const DWORD SERVICE_DEMAND_START = 0x3;
|
|
internal const DWORD SERVICE_DISABLED = 0x4;
|
|
internal const DWORD SERVICE_CONFIG_DESCRIPTION = 1;
|
|
internal const DWORD SERVICE_WIN32_OWN_PROCESS = 0x10;
|
|
internal const DWORD SERVICE_ERROR_NORMAL = 1;
|
|
|
|
// from winnt.h
|
|
[DllImport(PinvokeDllNames.OpenSCManagerWDllName, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal static extern
|
|
NakedWin32Handle OpenSCManagerW(
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpMachineName,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpDatabaseName,
|
|
DWORD dwDesiredAccess
|
|
);
|
|
|
|
[DllImport(PinvokeDllNames.OpenServiceWDllName, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal static extern
|
|
NakedWin32Handle OpenServiceW(
|
|
NakedWin32Handle hSCManager,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpServiceName,
|
|
DWORD dwDesiredAccess
|
|
);
|
|
|
|
[DllImport(PinvokeDllNames.CloseServiceHandleDllName, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal static extern
|
|
bool CloseServiceHandle(
|
|
NakedWin32Handle hSCManagerOrService
|
|
);
|
|
|
|
[DllImport(PinvokeDllNames.ChangeServiceConfigWDllName, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal static extern
|
|
bool ChangeServiceConfigW(
|
|
NakedWin32Handle hService,
|
|
DWORD dwServiceType,
|
|
DWORD dwStartType,
|
|
DWORD dwErrorControl,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpBinaryPathName,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpLoadOrderGroup,
|
|
IntPtr lpdwTagId,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpDependencies,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpServiceStartName,
|
|
[In] IntPtr lpPassword,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpDisplayName
|
|
);
|
|
|
|
[DllImport(PinvokeDllNames.ChangeServiceConfig2WDllName, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal static extern
|
|
bool ChangeServiceConfig2W(
|
|
NakedWin32Handle hService,
|
|
DWORD dwInfoLevel,
|
|
IntPtr lpInfo
|
|
);
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
internal struct SERVICE_DESCRIPTIONW
|
|
{
|
|
[MarshalAs(UnmanagedType.LPWStr)]
|
|
internal string lpDescription;
|
|
};
|
|
|
|
[DllImport(PinvokeDllNames.CreateServiceWDllName, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal static extern
|
|
NakedWin32Handle CreateServiceW(
|
|
NakedWin32Handle hSCManager,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpServiceName,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpDisplayName,
|
|
DWORD dwDesiredAccess,
|
|
DWORD dwServiceType,
|
|
DWORD dwStartType,
|
|
DWORD dwErrorControl,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpBinaryPathName,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpLoadOrderGroup,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpdwTagId,
|
|
[In] IntPtr lpDependencies,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpServiceStartName,
|
|
[In] IntPtr lpPassword
|
|
);
|
|
|
|
/// <summary>
|
|
/// CreateJobObject API creates or opens a job object.
|
|
/// </summary>
|
|
/// <param name="lpJobAttributes">
|
|
/// A pointer to a SECURITY_ATTRIBUTES structure that specifies the security descriptor for the
|
|
/// job object and determines whether child processes can inherit the returned handle.
|
|
/// If lpJobAttributes is NULL, the job object gets a default security descriptor
|
|
/// and the handle cannot be inherited.
|
|
/// </param>
|
|
/// <param name="lpName">
|
|
/// The name of the job.
|
|
/// </param>
|
|
/// <returns>
|
|
/// If the function succeeds, the return value is a handle to the job object.
|
|
/// If the object existed before the function call, the function
|
|
/// returns a handle to the existing job object.
|
|
/// </returns>
|
|
[DllImport(PinvokeDllNames.CreateJobObjectDllName, CharSet = CharSet.Unicode)]
|
|
internal static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string lpName);
|
|
|
|
/// <summary>
|
|
/// AssignProcessToJobObject API is used to assign a process to an existing job object.
|
|
/// </summary>
|
|
/// <param name="hJob">
|
|
/// A handle to the job object to which the process will be associated.
|
|
/// </param>
|
|
/// <param name="hProcess">
|
|
/// A handle to the process to associate with the job object.
|
|
/// </param>
|
|
/// <returns>If the function succeeds, the return value is nonzero.
|
|
/// If the function fails, the return value is zero.
|
|
/// </returns>
|
|
[DllImport(PinvokeDllNames.AssignProcessToJobObjectDllName, CharSet = CharSet.Unicode)]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
internal static extern bool AssignProcessToJobObject(SafeHandle hJob, IntPtr hProcess);
|
|
|
|
/// <summary>
|
|
/// Retrieves job state information from the job object.
|
|
/// </summary>
|
|
/// <param name="hJob">
|
|
/// A handle to the job whose information is being queried.
|
|
/// </param>
|
|
/// <param name="JobObjectInfoClass">
|
|
/// The information class for the limits to be queried.
|
|
/// </param>
|
|
/// <param name="lpJobObjectInfo">
|
|
/// The limit or job state information.
|
|
/// </param>
|
|
/// <param name="cbJobObjectLength">
|
|
/// The count of the job information being queried, in bytes.
|
|
/// </param>
|
|
/// <param name="lpReturnLength">
|
|
/// A pointer to a variable that receives the length of
|
|
/// data written to the structure pointed to by the lpJobObjectInfo parameter.
|
|
/// </param>
|
|
/// <returns>If the function succeeds, the return value is nonzero.
|
|
/// If the function fails, the return value is zero.
|
|
/// </returns>
|
|
[DllImport(PinvokeDllNames.QueryInformationJobObjectDllName, EntryPoint = "QueryInformationJobObject", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
public static extern bool QueryInformationJobObject(SafeHandle hJob, int JobObjectInfoClass,
|
|
ref JOBOBJECT_BASIC_PROCESS_ID_LIST lpJobObjectInfo,
|
|
int cbJobObjectLength, IntPtr lpReturnLength);
|
|
}
|
|
#endregion NativeMethods
|
|
}
|
|
|
|
#endif // Not built on Unix
|