diff --git a/build.psm1 b/build.psm1
index e060ee1437e..a2d807d4ab9 100644
--- a/build.psm1
+++ b/build.psm1
@@ -957,6 +957,7 @@ function Publish-PSTestTools {
$tools = @(
@{Path="${PSScriptRoot}/test/tools/TestExe";Output="testexe"}
@{Path="${PSScriptRoot}/test/tools/WebListener";Output="WebListener"}
+ @{Path="${PSScriptRoot}/test/tools/TestService";Output="TestService"}
)
$Options = Get-PSOptions -DefaultToNew
diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs
index 54e117cdd7b..e7be3d09b0b 100644
--- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs
+++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs
@@ -1532,6 +1532,15 @@ public string Status
}
internal string serviceStatus = null;
+ ///
+ /// The following is the definition of the input parameter "Force".
+ /// This parameter is useful only when parameter "Stop" is enabled.
+ /// If "Force" is enabled, it will also stop the dependent services.
+ /// If not, it will send an error when this service has dependent ones.
+ ///
+ [Parameter]
+ public SwitchParameter Force { get; set; }
+
///
/// This is not a parameter for this cmdlet.
///
@@ -1779,24 +1788,17 @@ protected override void ProcessRecord()
{
if (!service.Status.Equals(ServiceControllerStatus.Stopped))
{
- //check for the dependent services as set-service dont have force parameter
+ // Check for the dependent services as set-service dont have force parameter
ServiceController[] dependentServices = service.DependentServices;
- if ((dependentServices != null) && (dependentServices.Length > 0))
+ if ((!Force) && (dependentServices != null) && (dependentServices.Length > 0))
{
WriteNonTerminatingError(service, null, "ServiceHasDependentServicesNoForce", ServiceResources.ServiceHasDependentServicesNoForce, ErrorCategory.InvalidOperation);
return;
}
- ServiceController[] servicedependedon = service.ServicesDependedOn;
-
- if ((servicedependedon != null) && (servicedependedon.Length > 0))
- {
- WriteNonTerminatingError(service, null, "ServiceIsDependentOnNoForce", ServiceResources.ServiceIsDependentOnNoForce, ErrorCategory.InvalidOperation);
- return;
- }
// Stop service, pass 'true' to the force parameter as we have already checked for the dependent services.
- DoStopService(service, force: true, waitForServiceToStop: true);
+ DoStopService(service, Force, waitForServiceToStop: true);
}
}
else if (Status.Equals("Paused", StringComparison.CurrentCultureIgnoreCase))
diff --git a/src/Microsoft.PowerShell.Commands.Management/resources/ServiceResources.resx b/src/Microsoft.PowerShell.Commands.Management/resources/ServiceResources.resx
index 6218714a18c..7f8538e5d02 100644
--- a/src/Microsoft.PowerShell.Commands.Management/resources/ServiceResources.resx
+++ b/src/Microsoft.PowerShell.Commands.Management/resources/ServiceResources.resx
@@ -132,9 +132,6 @@
Cannot stop service '{1} ({0})' because it has dependent services.
-
- Cannot stop service '{1} ({0})' because it is dependent on other services.
-
Service '{1} ({0})' cannot be stopped due to the following error: {2}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1
index c2920d61235..5020e3dce45 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Service.Tests.ps1
@@ -12,12 +12,28 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW
net user $userName $testPass /add > $null
$password = ConvertTo-SecureString $testPass -AsPlainText -Force
$creds = [pscredential]::new(".\$userName", $password)
+
+ $testservicename1 = "testservice1"
+ $testservicename2 = "testservice2"
+ $svcbinaryname = "TestService"
+ $svccmd = Get-Command $svcbinaryname
+ $svccmd | Should -Not -BeNullOrEmpty
+ $svcfullpath = $svccmd.Path
+ $testservice1 = New-Service -BinaryPathName $svcfullpath -Name $testservicename1
+ $testservice1 | Should -Not -BeNullOrEmpty
+ $testservice2 = New-Service -BinaryPathName $svcfullpath -Name $testservicename2 -DependsOn $testservicename1
+ $testservice2 | Should -Not -BeNullOrEmpty
}
}
AfterAll {
$global:PSDefaultParameterValues = $originalDefaultParameterValues
if ($IsWindows) {
net user $userName /delete > $null
+
+ Stop-Service $testservicename2
+ Stop-Service $testservicename1
+ Remove-Service $testservicename2
+ Remove-Service $testservicename1
}
}
@@ -329,4 +345,32 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW
}
{ & $cmdlet @parameters } | Should -Throw -ErrorId $errorid
}
+
+ Context "Set-Service test cases on the services with dependent relationship" {
+ BeforeEach {
+ { Set-Service -Status Running $testservicename2 } | Should -Not -Throw
+ (Get-Service $testservicename1).Status | Should -BeExactly "Running"
+ (Get-Service $testservicename2).Status | Should -BeExactly "Running"
+ }
+
+ It "Set-Service can stop a service with dependency" {
+ $script = { Set-Service -Status Stopped $testservicename2 -ErrorAction Stop }
+ { & $script } | Should -Not -Throw
+ (Get-Service $testservicename2).Status | Should -BeExactly "Stopped"
+ }
+
+ It "Set-Service cannot stop a service with running dependent service" {
+ $script = { Set-Service -Status Stopped $testservicename1 -ErrorAction Stop }
+ { & $script } | Should -Throw
+ (Get-Service $testservicename1).Status | Should -BeExactly "Running"
+ (Get-Service $testservicename2).Status | Should -BeExactly "Running"
+ }
+
+ It "Set-Service can stop a service with running dependent service by parameter -Force" {
+ $script = { Set-Service -Status Stopped -Force $testservicename1 -ErrorAction Stop }
+ { & $script } | Should -Not -Throw
+ (Get-Service $testservicename1).Status | Should -BeExactly "Stopped"
+ (Get-Service $testservicename2).Status | Should -BeExactly "Stopped"
+ }
+ }
}
diff --git a/test/tools/OpenCover/OpenCover.psm1 b/test/tools/OpenCover/OpenCover.psm1
index 3ff2265742e..78502650761 100644
--- a/test/tools/OpenCover/OpenCover.psm1
+++ b/test/tools/OpenCover/OpenCover.psm1
@@ -667,7 +667,8 @@ function Invoke-OpenCover
$updatedEnvPath = "${PowerShellExeDirectory}\Modules;$TestToolsModulesPath"
$testToolsExePath = (Resolve-Path(Join-Path $TestPath -ChildPath "..\tools\TestExe\bin")).Path
- $updatedProcessEnvPath = "${testToolsExePath};${env:PATH}"
+ $testServiceExePath = (Resolve-Path(Join-Path $TestPath -ChildPath "..\tools\TestService\bin")).Path
+ $updatedProcessEnvPath = "${testServiceExePath};${testToolsExePath};${env:PATH}"
$startupArgs = "Set-ExecutionPolicy Bypass -Force -Scope Process; `$env:PSModulePath = '${updatedEnvPath}'; `$env:Path = '${updatedProcessEnvPath}';"
$targetArgs = "${startupArgs}", "Invoke-Pester","${TestPath}","-OutputFormat $PesterLogFormat"
diff --git a/test/tools/TestService/Program.cs b/test/tools/TestService/Program.cs
new file mode 100644
index 00000000000..f3a5c5d53b4
--- /dev/null
+++ b/test/tools/TestService/Program.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using System.ServiceProcess;
+
+namespace TestService
+{
+ static class Program
+ {
+ static void Main()
+ {
+ ServiceBase[] ServicesToRun;
+ ServicesToRun = new ServiceBase[]
+ {
+ new Service1()
+ };
+ ServiceBase.Run(ServicesToRun);
+ }
+ }
+}
diff --git a/test/tools/TestService/Service1.Designer.cs b/test/tools/TestService/Service1.Designer.cs
new file mode 100644
index 00000000000..b8210d468c9
--- /dev/null
+++ b/test/tools/TestService/Service1.Designer.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+namespace TestService
+{
+ partial class Service1
+ {
+ private System.ComponentModel.IContainer components = null;
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ private void InitializeComponent()
+ {
+ components = new System.ComponentModel.Container();
+ this.ServiceName = "Service1";
+ }
+ }
+}
diff --git a/test/tools/TestService/Service1.cs b/test/tools/TestService/Service1.cs
new file mode 100644
index 00000000000..36f680fa5f9
--- /dev/null
+++ b/test/tools/TestService/Service1.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using System.ServiceProcess;
+
+namespace TestService
+{
+ public partial class Service1 : ServiceBase
+ {
+ public Service1()
+ {
+ InitializeComponent();
+ }
+
+ protected override void OnStart(string[] args)
+ {
+ }
+
+ protected override void OnStop()
+ {
+ }
+ }
+}
diff --git a/test/tools/TestService/TestService.csproj b/test/tools/TestService/TestService.csproj
new file mode 100644
index 00000000000..2544875784f
--- /dev/null
+++ b/test/tools/TestService/TestService.csproj
@@ -0,0 +1,16 @@
+
+
+
+
+
+ Very tiny windows service to do service testing
+ TestService
+ Exe
+ win7-x86;win7-x64
+
+
+
+
+
+
+