Severity: Warning
File: src/Servy.Core/Native/NativeMethods.cs, lines 149-173
NativeMethods declares the Win32 service-status struct twice with two different names but byte-identical layouts:
/// <summary>Represents the status of a service.</summary>
[StructLayout(LayoutKind.Sequential)]
public struct ServiceStatus
{
public int dwServiceType;
public int dwCurrentState;
public int dwControlsAccepted;
public int dwWin32ExitCode;
public int dwServiceSpecificExitCode;
public int dwCheckPoint;
public int dwWaitHint;
}
/// <summary>Internal structure for service status reports.</summary>
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_STATUS
{
public int dwServiceType;
public int dwCurrentState;
public int dwControlsAccepted;
public int dwWin32ExitCode;
public int dwServiceSpecificExitCode;
public int dwCheckPoint;
public int dwWaitHint;
}
Both wrap the same Win32 SERVICE_STATUS ABI. Different P/Invoke signatures use different names:
ServiceManager.cs:568 — var status = new NativeMethods.ServiceStatus(); _windowsServiceApi.ControlService(serviceHandle, SERVICE_CONTROL_STOP, ref status);
Service.cs:554 and Service.cs:2068 — SERVICE_STATUS status = new SERVICE_STATUS { ... }; (passed to SetServiceStatus via P/Invoke).
Two problems compound:
-
DRY violation — identical struct declared twice. Adding a field, attribute, or [StructLayout] tweak requires maintaining two copies and remembering both call paths use it. A subtle layout drift between them would silently corrupt SCM communication.
-
Naming collision — Servy.Core.Enums.ServiceStatus is the public service-state enum used in 30+ places across Servy.Core.DTOs, Servy.Manager.ViewModels, StatusConverter, etc. NativeMethods.ServiceStatus is a completely unrelated P/Invoke struct. Reading ServiceManager.cs:568 in isolation makes you double-take: "is new NativeMethods.ServiceStatus() calling the enum or a struct?" Even resharper-style "go to definition" lands on whichever appears first. The PascalCase struct should not exist alongside the enum of the same simple name.
Suggested fix:
Pick one struct — the SERVICE_STATUS (uppercase, Win32-style) is the convention used by P/Invoke definitions everywhere else and avoids the enum collision. Then:
- Delete
NativeMethods.ServiceStatus.
- Update
IWindowsServiceApi.ControlService and WindowsServiceApi.ControlService (and any other refs) to take ref SERVICE_STATUS.
- Update
ServiceManager.cs:568 to new SERVICE_STATUS() (or just default(SERVICE_STATUS)).
If [ExcludeFromCodeCoverage] is the reason no test caught the duplication, that's an additional argument for consolidation.
Severity: Warning
File:
src/Servy.Core/Native/NativeMethods.cs, lines 149-173NativeMethodsdeclares the Win32 service-status struct twice with two different names but byte-identical layouts:Both wrap the same Win32
SERVICE_STATUSABI. Different P/Invoke signatures use different names:ServiceManager.cs:568—var status = new NativeMethods.ServiceStatus(); _windowsServiceApi.ControlService(serviceHandle, SERVICE_CONTROL_STOP, ref status);Service.cs:554andService.cs:2068—SERVICE_STATUS status = new SERVICE_STATUS { ... };(passed toSetServiceStatusvia P/Invoke).Two problems compound:
DRY violation — identical struct declared twice. Adding a field, attribute, or
[StructLayout]tweak requires maintaining two copies and remembering both call paths use it. A subtle layout drift between them would silently corrupt SCM communication.Naming collision —
Servy.Core.Enums.ServiceStatusis the public service-state enum used in 30+ places acrossServy.Core.DTOs,Servy.Manager.ViewModels,StatusConverter, etc.NativeMethods.ServiceStatusis a completely unrelated P/Invoke struct. ReadingServiceManager.cs:568in isolation makes you double-take: "isnew NativeMethods.ServiceStatus()calling the enum or a struct?" Even resharper-style "go to definition" lands on whichever appears first. The PascalCase struct should not exist alongside the enum of the same simple name.Suggested fix:
Pick one struct — the
SERVICE_STATUS(uppercase, Win32-style) is the convention used by P/Invoke definitions everywhere else and avoids the enum collision. Then:NativeMethods.ServiceStatus.IWindowsServiceApi.ControlServiceandWindowsServiceApi.ControlService(and any other refs) to takeref SERVICE_STATUS.ServiceManager.cs:568tonew SERVICE_STATUS()(or justdefault(SERVICE_STATUS)).If
[ExcludeFromCodeCoverage]is the reason no test caught the duplication, that's an additional argument for consolidation.