Skip to content

Commit 991c4fe

Browse files
authored
Fix exception and enable event logging when service has no name (#67827)
* Increase timeouts that are sporadically being hit * Add logging to TestServiceInstaller * Update ServiceTypeOptions to match header * Rename SERVICE_ constants to match header * Remove defunct comment * Add failing test * Remove event log noise * Make test pass * more apos * spacing * feedback * Avoid overwriting last error * feedback
1 parent a29a334 commit 991c4fe

File tree

9 files changed

+121
-79
lines changed

9 files changed

+121
-79
lines changed

src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ServiceProcessOptions.cs

+18-19
Original file line numberDiff line numberDiff line change
@@ -65,25 +65,24 @@ internal static partial class ServiceOptions
6565

6666
internal static partial class ServiceTypeOptions
6767
{
68-
internal const int SERVICE_TYPE_ADAPTER = 0x00000004;
69-
internal const int SERVICE_TYPE_FILE_SYSTEM_DRIVER = 0x00000002;
70-
internal const int SERVICE_TYPE_INTERACTIVE_PROCESS = 0x00000100;
71-
internal const int SERVICE_TYPE_KERNEL_DRIVER = 0x00000001;
72-
internal const int SERVICE_TYPE_RECOGNIZER_DRIVER = 0x00000008;
73-
internal const int SERVICE_TYPE_WIN32_OWN_PROCESS = 0x00000010;
74-
internal const int SERVICE_TYPE_WIN32_SHARE_PROCESS = 0x00000020;
75-
internal const int SERVICE_TYPE_WIN32 =
76-
SERVICE_TYPE_WIN32_OWN_PROCESS |
77-
SERVICE_TYPE_WIN32_SHARE_PROCESS;
78-
internal const int SERVICE_TYPE_DRIVER =
79-
SERVICE_TYPE_KERNEL_DRIVER |
80-
SERVICE_TYPE_FILE_SYSTEM_DRIVER |
81-
SERVICE_TYPE_RECOGNIZER_DRIVER;
82-
internal const int SERVICE_TYPE_ALL =
83-
SERVICE_TYPE_WIN32 |
84-
SERVICE_TYPE_ADAPTER |
85-
SERVICE_TYPE_DRIVER |
86-
SERVICE_TYPE_INTERACTIVE_PROCESS;
68+
internal const int SERVICE_KERNEL_DRIVER = 0x00000001;
69+
internal const int SERVICE_FILE_SYSTEM_DRIVER = 0x00000002;
70+
internal const int SERVICE_ADAPTER = 0x00000004;
71+
internal const int SERVICE_RECOGNIZER_DRIVER = 0x00000008;
72+
73+
internal const int SERVICE_DRIVER =
74+
SERVICE_KERNEL_DRIVER |
75+
SERVICE_FILE_SYSTEM_DRIVER |
76+
SERVICE_RECOGNIZER_DRIVER;
77+
78+
internal const int SERVICE_WIN32_OWN_PROCESS = 0x00000010;
79+
internal const int SERVICE_WIN32_SHARE_PROCESS = 0x00000020;
80+
81+
internal const int SERVICE_WIN32 =
82+
SERVICE_WIN32_OWN_PROCESS |
83+
SERVICE_WIN32_SHARE_PROCESS;
84+
85+
internal const int SERVICE_INTERACTIVE_PROCESS = 0x00000100;
8786
}
8887

8988
internal static partial class ServiceAccessOptions

src/libraries/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceBase.cs

+13-21
Original file line numberDiff line numberDiff line change
@@ -577,22 +577,8 @@ private unsafe void DeferredShutdown()
577577

578578
/// <summary>
579579
/// <para>When implemented in a derived class, <see cref='System.ServiceProcess.ServiceBase.OnCustomCommand'/>
580-
/// executes when a custom command is passed to
581-
/// the service. Specifies the actions to take when
580+
/// executes when a custom command is passed to the service. Specifies the actions to take when
582581
/// a command with the specified parameter value occurs.</para>
583-
/// <note type="rnotes">
584-
/// Previously had "Passed to the
585-
/// service by
586-
/// the SCM", but the SCM doesn't pass custom commands. Do we want to indicate an
587-
/// agent here? Would it be the ServiceController, or is there another way to pass
588-
/// the int into the service? I thought that the SCM did pass it in, but
589-
/// otherwise ignored it since it was an int it doesn't recognize. I was under the
590-
/// impression that the difference was that the SCM didn't have default processing, so
591-
/// it transmitted it without examining it or trying to performs its own
592-
/// default behavior on it. Please correct where my understanding is wrong in the
593-
/// second paragraph below--what, if any, contact does the SCM have with a
594-
/// custom command?
595-
/// </note>
596582
/// </summary>
597583
protected virtual void OnCustomCommand(int command)
598584
{
@@ -698,11 +684,11 @@ private void Initialize(bool multipleServices)
698684

699685
if (!multipleServices)
700686
{
701-
_status.serviceType = ServiceTypeOptions.SERVICE_TYPE_WIN32_OWN_PROCESS;
687+
_status.serviceType = ServiceTypeOptions.SERVICE_WIN32_OWN_PROCESS;
702688
}
703689
else
704690
{
705-
_status.serviceType = ServiceTypeOptions.SERVICE_TYPE_WIN32_SHARE_PROCESS;
691+
_status.serviceType = ServiceTypeOptions.SERVICE_WIN32_SHARE_PROCESS;
706692
}
707693

708694
_status.currentState = ServiceControlStatus.STATE_START_PENDING;
@@ -885,10 +871,15 @@ public unsafe void ServiceMainCallback(int argCount, IntPtr argPointer)
885871

886872
if (argCount > 0)
887873
{
888-
char** argsAsPtr = (char**)argPointer.ToPointer();
874+
char** argsAsPtr = (char**)argPointer;
875+
876+
// The first arg is always the service name. We don't want to pass that in,
877+
// but we can use it to set the service name on ourselves if we don't already know it.
878+
if (string.IsNullOrEmpty(_serviceName))
879+
{
880+
_serviceName = Marshal.PtrToStringUni((IntPtr)(*argsAsPtr))!;
881+
}
889882

890-
//Lets read the arguments
891-
// the first arg is always the service name. We don't want to pass that in.
892883
args = new string[argCount - 1];
893884

894885
for (int index = 0; index < args.Length; ++index)
@@ -954,7 +945,8 @@ public unsafe void ServiceMainCallback(int argCount, IntPtr argPointer)
954945
statusOK = SetServiceStatus(_statusHandle, pStatus);
955946
if (!statusOK)
956947
{
957-
WriteLogEntry(SR.Format(SR.StartFailed, new Win32Exception().Message), EventLogEntryType.Error);
948+
string errorMessage = new Win32Exception().Message;
949+
WriteLogEntry(SR.Format(SR.StartFailed, errorMessage), EventLogEntryType.Error);
958950
_status.currentState = ServiceControlStatus.STATE_STOPPED;
959951
SetServiceStatus(_statusHandle, pStatus);
960952
}

src/libraries/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceController.cs

+21-16
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ public class ServiceController : Component
1919
private string _machineName; // Never null
2020
private const string DefaultMachineName = ".";
2121

22+
// Note that ServiceType currently does not include all of SERVICE_TYPE_ALL; see see Interop.Advapi32.ServiceTypeOptions
23+
private const int AllServiceTypes = (int)(ServiceType.Adapter | ServiceType.FileSystemDriver | ServiceType.InteractiveProcess |
24+
ServiceType.KernelDriver | ServiceType.RecognizerDriver | ServiceType.Win32OwnProcess |
25+
ServiceType.Win32ShareProcess);
26+
2227
private string? _name;
2328
private string? _eitherName;
2429
private string? _displayName;
@@ -37,7 +42,7 @@ public class ServiceController : Component
3742
public ServiceController()
3843
{
3944
_machineName = DefaultMachineName;
40-
_type = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_ALL;
45+
_type = AllServiceTypes;
4146
}
4247

4348
/// <summary>
@@ -65,7 +70,7 @@ public ServiceController(string name, string machineName)
6570

6671
_machineName = machineName;
6772
_eitherName = name;
68-
_type = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_ALL;
73+
_type = AllServiceTypes;
6974
}
7075

7176
private ServiceController(string machineName, Interop.Advapi32.ENUM_SERVICE_STATUS status)
@@ -310,7 +315,7 @@ public unsafe ServiceController[] ServicesDependedOn
310315
{
311316
success = Interop.Advapi32.QueryServiceConfig(serviceHandle, bufPtr, bytesNeeded, out bytesNeeded);
312317
if (!success)
313-
throw new Win32Exception(Marshal.GetLastWin32Error());
318+
throw new Win32Exception();
314319

315320
Interop.Advapi32.QUERY_SERVICE_CONFIG config = new Interop.Advapi32.QUERY_SERVICE_CONFIG();
316321
Marshal.PtrToStructure(bufPtr, config);
@@ -389,7 +394,7 @@ public ServiceStartMode StartType
389394
{
390395
success = Interop.Advapi32.QueryServiceConfig(serviceHandle, bufPtr, bytesNeeded, out bytesNeeded);
391396
if (!success)
392-
throw new Win32Exception(Marshal.GetLastWin32Error());
397+
throw new Win32Exception();
393398

394399
Interop.Advapi32.QUERY_SERVICE_CONFIG config = new Interop.Advapi32.QUERY_SERVICE_CONFIG();
395400
Marshal.PtrToStructure(bufPtr, config);
@@ -467,7 +472,7 @@ public void Close()
467472

468473
_statusGenerated = false;
469474
_startTypeInitialized = false;
470-
_type = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_ALL;
475+
_type = AllServiceTypes;
471476
}
472477

473478
/// <summary>
@@ -491,7 +496,7 @@ private unsafe void GenerateStatus()
491496
Interop.Advapi32.SERVICE_STATUS svcStatus = default;
492497
bool success = Interop.Advapi32.QueryServiceStatus(serviceHandle, &svcStatus);
493498
if (!success)
494-
throw new Win32Exception(Marshal.GetLastWin32Error());
499+
throw new Win32Exception();
495500

496501
_commandsAccepted = svcStatus.controlsAccepted;
497502
_status = (ServiceControllerStatus)svcStatus.currentState;
@@ -632,7 +637,7 @@ private static SafeServiceHandle GetDataBaseHandleWithAccess(string machineName,
632637

633638
if (databaseHandle.IsInvalid)
634639
{
635-
Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
640+
Exception inner = new Win32Exception();
636641
throw new InvalidOperationException(SR.Format(SR.OpenSC, machineName), inner);
637642
}
638643

@@ -669,7 +674,7 @@ public static ServiceController[] GetDevices()
669674
/// <returns>Set of service controllers</returns>
670675
public static ServiceController[] GetDevices(string machineName)
671676
{
672-
return GetServicesOfType(machineName, Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_DRIVER);
677+
return GetServicesOfType(machineName, Interop.Advapi32.ServiceTypeOptions.SERVICE_DRIVER);
673678
}
674679

675680
/// <summary>
@@ -684,7 +689,7 @@ private SafeServiceHandle GetServiceHandle(int desiredAccess)
684689
var serviceHandle = new SafeServiceHandle(Interop.Advapi32.OpenService(_serviceManagerHandle, ServiceName, desiredAccess));
685690
if (serviceHandle.IsInvalid)
686691
{
687-
Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
692+
Exception inner = new Win32Exception();
688693
throw new InvalidOperationException(SR.Format(SR.OpenService, ServiceName, _machineName), inner);
689694
}
690695

@@ -707,7 +712,7 @@ public static ServiceController[] GetServices()
707712
/// <returns></returns>
708713
public static ServiceController[] GetServices(string machineName)
709714
{
710-
return GetServicesOfType(machineName, Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_WIN32);
715+
return GetServicesOfType(machineName, Interop.Advapi32.ServiceTypeOptions.SERVICE_WIN32);
711716
}
712717

713718
/// <summary>
@@ -718,7 +723,7 @@ public static ServiceController[] GetServices(string machineName)
718723
/// <returns></returns>
719724
private static Interop.Advapi32.ENUM_SERVICE_STATUS_PROCESS[] GetServicesInGroup(string machineName, string group)
720725
{
721-
return GetServices(machineName, Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_WIN32, group, status => status);
726+
return GetServices(machineName, Interop.Advapi32.ServiceTypeOptions.SERVICE_WIN32, group, status => status);
722727
}
723728

724729
/// <summary>
@@ -804,7 +809,7 @@ public unsafe void Pause()
804809

805810
if (!result)
806811
{
807-
Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
812+
Exception inner = new Win32Exception();
808813
throw new InvalidOperationException(SR.Format(SR.PauseService, ServiceName, _machineName), inner);
809814
}
810815
}
@@ -819,7 +824,7 @@ public unsafe void Continue()
819824
bool result = Interop.Advapi32.ControlService(serviceHandle, Interop.Advapi32.ControlOptions.CONTROL_CONTINUE, &status);
820825
if (!result)
821826
{
822-
Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
827+
Exception inner = new Win32Exception();
823828
throw new InvalidOperationException(SR.Format(SR.ResumeService, ServiceName, _machineName), inner);
824829
}
825830
}
@@ -835,7 +840,7 @@ public unsafe void ExecuteCommand(int command)
835840
bool result = Interop.Advapi32.ControlService(serviceHandle, command, &status);
836841
if (!result)
837842
{
838-
Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
843+
Exception inner = new Win32Exception();
839844
throw new InvalidOperationException(SR.Format(SR.ControlService, ServiceName, MachineName), inner);
840845
}
841846
}
@@ -891,7 +896,7 @@ public void Start(string[] args!!)
891896
bool result = Interop.Advapi32.StartService(serviceHandle, args.Length, argPtrsHandle.AddrOfPinnedObject());
892897
if (!result)
893898
{
894-
Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
899+
Exception inner = new Win32Exception();
895900
throw new InvalidOperationException(SR.Format(SR.CannotStart, ServiceName, _machineName), inner);
896901
}
897902
}
@@ -952,7 +957,7 @@ unsafe void Stop(bool stopDependentServices)
952957
bool result = Interop.Advapi32.ControlService(serviceHandle, Interop.Advapi32.ControlOptions.CONTROL_STOP, &status);
953958
if (!result)
954959
{
955-
Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
960+
Exception inner = new Win32Exception();
956961
throw new InvalidOperationException(SR.Format(SR.StopService, ServiceName, _machineName), inner);
957962
}
958963
}

src/libraries/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceType.cs

+8-7
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ namespace System.ServiceProcess
66
[Flags]
77
public enum ServiceType
88
{
9-
Adapter = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_ADAPTER,
10-
FileSystemDriver = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_FILE_SYSTEM_DRIVER,
11-
InteractiveProcess = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_INTERACTIVE_PROCESS,
12-
KernelDriver = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_KERNEL_DRIVER,
13-
RecognizerDriver = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_RECOGNIZER_DRIVER,
14-
Win32OwnProcess = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_WIN32_OWN_PROCESS,
15-
Win32ShareProcess = Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_WIN32_SHARE_PROCESS
9+
// Does not currently represent all of the Win32 values; see Interop.Advapi32.ServiceTypeOptions
10+
Adapter = Interop.Advapi32.ServiceTypeOptions.SERVICE_ADAPTER,
11+
FileSystemDriver = Interop.Advapi32.ServiceTypeOptions.SERVICE_FILE_SYSTEM_DRIVER,
12+
InteractiveProcess = Interop.Advapi32.ServiceTypeOptions.SERVICE_INTERACTIVE_PROCESS,
13+
KernelDriver = Interop.Advapi32.ServiceTypeOptions.SERVICE_KERNEL_DRIVER,
14+
RecognizerDriver = Interop.Advapi32.ServiceTypeOptions.SERVICE_RECOGNIZER_DRIVER,
15+
Win32OwnProcess = Interop.Advapi32.ServiceTypeOptions.SERVICE_WIN32_OWN_PROCESS,
16+
Win32ShareProcess = Interop.Advapi32.ServiceTypeOptions.SERVICE_WIN32_SHARE_PROCESS
1617
}
1718
}

src/libraries/System.ServiceProcess.ServiceController/tests/SafeServiceControllerTests.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,12 @@ public static void GetDevices()
8181
ServiceController[] devices = ServiceController.GetDevices();
8282
Assert.True(devices.Length != 0);
8383

84-
const ServiceType SERVICE_TYPE_DRIVER =
84+
const ServiceType SERVICE_DRIVER =
8585
ServiceType.FileSystemDriver |
8686
ServiceType.KernelDriver |
8787
ServiceType.RecognizerDriver;
8888

89-
Assert.All(devices, device => Assert.NotEqual(0, (int)(device.ServiceType & SERVICE_TYPE_DRIVER)));
89+
Assert.All(devices, device => Assert.NotEqual(0, (int)(device.ServiceType & SERVICE_DRIVER)));
9090
}
9191

9292
[Fact]

src/libraries/System.ServiceProcess.ServiceController/tests/ServiceBaseTests.cs

+15
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,21 @@ public void PropagateExceptionFromOnStart()
223223
testService.DeleteTestServices();
224224
}
225225

226+
[ConditionalFact(nameof(IsElevatedAndSupportsEventLogs))]
227+
public void NoServiceNameOnServiceBase()
228+
{
229+
// When installing a service, you must supply a non empty name.
230+
// When a service starts itself (using StartServiceCtrlDispatcher) it's legal to pass an empty string for the name.
231+
string serviceName = "NoServiceNameOnServiceBase";
232+
var testService = new TestServiceProvider(serviceName);
233+
234+
// Ensure it has successfully written to the event log,
235+
// indicating it figured out its own name.
236+
Assert.True(EventLog.SourceExists(serviceName));
237+
238+
testService.DeleteTestServices();
239+
}
240+
226241
private ServiceController ConnectToServer()
227242
{
228243
TestServiceProvider.DebugTrace("ServiceBaseTests.ConnectToServer: connecting");

src/libraries/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/Program.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ static int Main(string[] args)
6565
}
6666
else
6767
{
68-
Console.WriteLine("EROOR: Invalid Service verb. Only suppot create or delete.");
68+
Console.WriteLine("EROOR: Invalid Service verb. Only support create or delete.");
6969
return 2;
7070
}
7171
}

src/libraries/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestService.cs

+14-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ public class TestService : ServiceBase
2222
public TestService(string serviceName, Exception throwException = null)
2323
{
2424
DebugTrace("TestService " + ServiceName + ": Ctor");
25-
this.ServiceName = serviceName;
25+
26+
if (serviceName != "NoServiceNameOnServiceBase")
27+
{
28+
this.ServiceName = serviceName;
29+
}
2630

2731
// Enable all the events
2832
this.CanPauseAndContinue = true;
@@ -101,7 +105,15 @@ protected override void OnStop()
101105
base.OnStop();
102106
// We may be stopping because the test has completed and we're cleaning up the test service so there's no client at all, so don't waitForConnect.
103107
// Tests that verify "Stop" itself should ensure the client connection has completed before calling stop, by waiting on some other message from the pipe first.
104-
WriteStreamAsync(PipeMessageByteCode.Stop, waitForConnect:false).Wait();
108+
try
109+
{
110+
WriteStreamAsync(PipeMessageByteCode.Stop, waitForConnect:false).Wait();
111+
}
112+
catch (AggregateException ae) when (ae.InnerException.GetType() == typeof(InvalidOperationException))
113+
{
114+
// Some tests don't bother to connect to the pipe, and just stop the service to clean up.
115+
// Don't log this exception into the event log.
116+
}
105117
}
106118

107119
public async Task WriteStreamAsync(PipeMessageByteCode code, int command = 0, bool waitForConnect = true)

0 commit comments

Comments
 (0)