未验证 提交 ec271296 编写于 作者: F Francesco Saltori 提交者: GitHub

Implement ServiceController.Stop(bool) overload (#52519)

* Implement ServiceController.Stop(bool) overload with test

* Add basic docs for new ServiceController.Stop overload

* Name boolean parameter
Co-authored-by: NIlya <darpa@yandex.ru>

* Add a test for a valid use of ServiceController.Stop(bool)

* Run tests for the new Stop(bool) overload only on .NETCoreApp

* Add a test for manually stopping a service and its dependents with Stop(false)

* Expose new Stop(bool) overload only for .NET Core 3.1+

* Target netcoreapp3.1
Co-authored-by: NIlya <darpa@yandex.ru>
Co-authored-by: NViktor Hofer <viktor.hofer@microsoft.com>
上级 dfcaf417
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);netstandard2.0;net461</TargetFrameworks>
<TargetFrameworks>$(NetCoreAppCurrent);netcoreapp3.1;netstandard2.0;net461</TargetFrameworks>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Compile Include="System.ServiceProcess.ServiceController.cs" Condition="'$(TargetFramework)' != 'net461'" />
<Compile Include="System.ServiceProcess.ServiceController.net461.cs" Condition="'$(TargetFramework)' == 'net461'" />
<Compile Include="System.ServiceProcess.ServiceController.netcoreapp.cs" Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(LibrariesProjectRoot)System.Diagnostics.EventLog\ref\System.Diagnostics.EventLog.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<Reference Include="System.Runtime" />
<Reference Include="System.ComponentModel.TypeConverter" />
<Reference Include="System.ComponentModel.Primitives" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<Reference Include="System.ServiceProcess" />
</ItemGroup>
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// ------------------------------------------------------------------------------
// Changes to this file must follow the https://aka.ms/api-review process.
// ------------------------------------------------------------------------------
namespace System.ServiceProcess
{
public partial class ServiceController : System.ComponentModel.Component
{
public void Stop(bool stopDependentServices) { }
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netstandard2.0-windows;netstandard2.0;net461-windows</TargetFrameworks>
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0-windows;netstandard2.0;net461-windows</TargetFrameworks>
<NoWarn>$(NoWarn);CA2249</NoWarn>
<Nullable>enable</Nullable>
</PropertyGroup>
......@@ -89,6 +89,9 @@
<Reference Include="System.Threading.Thread" />
<Reference Include="System.Threading.ThreadPool" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith('netcoreapp3.1'))">
<Reference Include="System.ComponentModel.TypeConverter" />
</ItemGroup>
<ItemGroup Condition="'$(IsPartialFacadeAssembly)' == 'true'">
<Reference Include="System.ServiceProcess" />
</ItemGroup>
......
......@@ -912,19 +912,42 @@ public void Start(string[] args)
/// they will be stopped first. The DependentServices property lists this set
/// of services.
/// </summary>
public unsafe void Stop()
public void Stop()
{
Stop(stopDependentServices: true);
}
/// <summary>
/// Stops the service and optionally any services that are dependent on this service.
/// </summary>
/// <remarks>
/// If any other services depend on this one, you need to either pass <c>true</c> for
/// <paramref name="stopDependentServices"/> or stop them manually before calling this method.
/// </remarks>
/// <param name="stopDependentServices">
/// <c>true</c> to stop all running dependent services together with the service; <c>false</c> to stop only the service.
/// </param>
#if NETCOREAPP3_1_OR_GREATER
public
#else
private
#endif
unsafe void Stop(bool stopDependentServices)
{
using SafeServiceHandle serviceHandle = GetServiceHandle(Interop.Advapi32.ServiceOptions.SERVICE_STOP);
// Before stopping this service, stop all the dependent services that are running.
// (It's OK not to cache the result of getting the DependentServices property because it caches on its own.)
for (int i = 0; i < DependentServices.Length; i++)
if (stopDependentServices)
{
ServiceController currentDependent = DependentServices[i];
currentDependent.Refresh();
if (currentDependent.Status != ServiceControllerStatus.Stopped)
// Before stopping this service, stop all the dependent services that are running.
// (It's OK not to cache the result of getting the DependentServices property because it caches on its own.)
for (int i = 0; i < DependentServices.Length; i++)
{
currentDependent.Stop();
currentDependent.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 30));
ServiceController currentDependent = DependentServices[i];
currentDependent.Refresh();
if (currentDependent.Status != ServiceControllerStatus.Stopped)
{
currentDependent.Stop();
currentDependent.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 30));
}
}
}
......
......@@ -7,7 +7,7 @@ namespace System.ServiceProcess.Tests
{
[OuterLoop(/* Modifies machine state */)]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Persistent issues starting test service on NETFX")]
public class ServiceControllerTests : IDisposable
public partial class ServiceControllerTests : IDisposable
{
private const int connectionTimeout = 30000;
private readonly TestServiceProvider _testService;
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Xunit;
namespace System.ServiceProcess.Tests
{
[OuterLoop(/* Modifies machine state */)]
public partial class ServiceControllerTests : IDisposable
{
[ConditionalFact(nameof(IsProcessElevated))]
public void Stop_FalseArg_WithDependentServices_ThrowsInvalidOperationException()
{
var controller = new ServiceController(_testService.TestServiceName);
controller.WaitForStatus(ServiceControllerStatus.Running, _testService.ControlTimeout);
Assert.Throws<InvalidOperationException>(() => controller.Stop(stopDependentServices: false));
}
[ConditionalFact(nameof(IsProcessElevated))]
public void Stop_TrueArg_WithDependentServices_StopsTheServiceAndItsDependents()
{
var controller = new ServiceController(_testService.TestServiceName);
controller.WaitForStatus(ServiceControllerStatus.Running, _testService.ControlTimeout);
controller.Stop(stopDependentServices: true);
controller.WaitForStatus(ServiceControllerStatus.Stopped, _testService.ControlTimeout);
Assert.Equal(ServiceControllerStatus.Stopped, controller.Status);
Assert.All(controller.DependentServices, service => Assert.Equal(ServiceControllerStatus.Stopped, service.Status));
}
[ConditionalFact(nameof(IsProcessElevated))]
public void StopTheServiceAndItsDependentsManually()
{
var controller = new ServiceController(_testService.TestServiceName);
controller.WaitForStatus(ServiceControllerStatus.Running, _testService.ControlTimeout);
foreach (var dependentService in controller.DependentServices)
{
dependentService.Stop(stopDependentServices: false);
dependentService.WaitForStatus(ServiceControllerStatus.Stopped, _testService.ControlTimeout);
}
controller.Stop(stopDependentServices: false);
controller.WaitForStatus(ServiceControllerStatus.Stopped, _testService.ControlTimeout);
Assert.Equal(ServiceControllerStatus.Stopped, controller.Status);
Assert.All(controller.DependentServices, service => Assert.Equal(ServiceControllerStatus.Stopped, service.Status));
}
}
}
......@@ -9,6 +9,9 @@
<Compile Include="ServiceControllerTests.cs" />
<Compile Include="ServiceProcessDescriptionAttributeTests.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
<Compile Include="ServiceControllerTests.netcoreapp.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="System.ServiceProcess.ServiceController.TestService\System.ServiceProcess.ServiceController.TestService.csproj" />
</ItemGroup>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册