Bug Description
ServiceController.GetServices() returns an array of ServiceController objects that each implement IDisposable and hold an underlying Windows service handle. In GetServices(), these objects are projected into DTOs but never disposed, leaking handles on every call.
Actual Behavior
// WindowsServiceApi.cs lines 146-154
public IEnumerable<WindowsServiceInfo> GetServices()
{
return ServiceController.GetServices()
.Select(s => new WindowsServiceInfo
{
ServiceName = s.ServiceName,
DisplayName = s.DisplayName
});
}
Each ServiceController in the array holds an unmanaged handle. Because the LINQ projection is deferred and no disposal occurs, handles leak until the GC finalizer runs.
Suggested Fix
Materialize the query and dispose each ServiceController explicitly:
public IEnumerable<WindowsServiceInfo> GetServices()
{
var controllers = ServiceController.GetServices();
try
{
return controllers.Select(s => new WindowsServiceInfo
{
ServiceName = s.ServiceName,
DisplayName = s.DisplayName
}).ToList();
}
finally
{
foreach (var sc in controllers)
sc.Dispose();
}
}
Environment
- File:
src/Servy.Core/Services/WindowsServiceApi.cs
- Lines: 146-154
Bug Description
ServiceController.GetServices()returns an array ofServiceControllerobjects that each implementIDisposableand hold an underlying Windows service handle. InGetServices(), these objects are projected into DTOs but never disposed, leaking handles on every call.Actual Behavior
Each
ServiceControllerin the array holds an unmanaged handle. Because the LINQ projection is deferred and no disposal occurs, handles leak until the GC finalizer runs.Suggested Fix
Materialize the query and dispose each
ServiceControllerexplicitly:Environment
src/Servy.Core/Services/WindowsServiceApi.cs