PageRenderTime 49ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://gitlab.com/0072016/0072016-corefx-
C# | 856 lines | 690 code | 102 blank | 64 comment | 92 complexity | be00810d5254767c44bf52c18fae5893 MD5 | raw file
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using Microsoft.Win32.SafeHandles;
  5. using System.Text;
  6. using System.Runtime.InteropServices;
  7. using System.ComponentModel;
  8. using System.Diagnostics;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Threading;
  12. using System.Globalization;
  13. using System.Security;
  14. namespace System.ServiceProcess
  15. {
  16. /// This class represents an NT service. It allows you to connect to a running or stopped service
  17. /// and manipulate it or get information about it.
  18. public class ServiceController : IDisposable
  19. {
  20. private readonly string _machineName;
  21. private readonly ManualResetEvent _waitForStatusSignal = new ManualResetEvent(false);
  22. private const string DefaultMachineName = ".";
  23. private string _name;
  24. private string _eitherName;
  25. private string _displayName;
  26. private int _commandsAccepted;
  27. private bool _statusGenerated;
  28. private bool _startTypeInitialized;
  29. private int _type;
  30. private bool _disposed;
  31. private SafeServiceHandle _serviceManagerHandle;
  32. private ServiceControllerStatus _status;
  33. private ServiceController[] _dependentServices;
  34. private ServiceController[] _servicesDependedOn;
  35. private ServiceStartMode _startType;
  36. private const int SERVICENAMEMAXLENGTH = 80;
  37. private const int DISPLAYNAMEBUFFERSIZE = 256;
  38. /// Creates a ServiceController object, based on service name.
  39. public ServiceController(string name)
  40. : this(name, DefaultMachineName)
  41. {
  42. }
  43. /// Creates a ServiceController object, based on machine and service name.
  44. public ServiceController(string name, string machineName)
  45. {
  46. if (!CheckMachineName(machineName))
  47. throw new ArgumentException(SR.Format(SR.BadMachineName, machineName));
  48. if (string.IsNullOrEmpty(name))
  49. throw new ArgumentException(SR.Format(SR.InvalidParameter, "name", name));
  50. _machineName = machineName;
  51. _eitherName = name;
  52. _type = Interop.mincore.ServiceTypeOptions.SERVICE_TYPE_ALL;
  53. }
  54. /// Used by the GetServices and GetDevices methods. Avoids duplicating work by the static
  55. /// methods and our own GenerateInfo().
  56. private ServiceController(string machineName, Interop.mincore.ENUM_SERVICE_STATUS status)
  57. {
  58. if (!CheckMachineName(machineName))
  59. throw new ArgumentException(SR.Format(SR.BadMachineName, machineName));
  60. _machineName = machineName;
  61. _name = status.serviceName;
  62. _displayName = status.displayName;
  63. _commandsAccepted = status.controlsAccepted;
  64. _status = (ServiceControllerStatus)status.currentState;
  65. _type = status.serviceType;
  66. _statusGenerated = true;
  67. }
  68. /// Used by the GetServicesInGroup method.
  69. private ServiceController(string machineName, Interop.mincore.ENUM_SERVICE_STATUS_PROCESS status)
  70. {
  71. if (!CheckMachineName(machineName))
  72. throw new ArgumentException(SR.Format(SR.BadMachineName, machineName));
  73. _machineName = machineName;
  74. _name = status.serviceName;
  75. _displayName = status.displayName;
  76. _commandsAccepted = status.controlsAccepted;
  77. _status = (ServiceControllerStatus)status.currentState;
  78. _type = status.serviceType;
  79. _statusGenerated = true;
  80. }
  81. /// Tells if the service referenced by this object can be paused.
  82. public bool CanPauseAndContinue
  83. {
  84. get
  85. {
  86. GenerateStatus();
  87. return (_commandsAccepted & Interop.mincore.AcceptOptions.ACCEPT_PAUSE_CONTINUE) != 0;
  88. }
  89. }
  90. /// Tells if the service is notified when system shutdown occurs.
  91. public bool CanShutdown
  92. {
  93. get
  94. {
  95. GenerateStatus();
  96. return (_commandsAccepted & Interop.mincore.AcceptOptions.ACCEPT_SHUTDOWN) != 0;
  97. }
  98. }
  99. /// Tells if the service referenced by this object can be stopped.
  100. public bool CanStop
  101. {
  102. get
  103. {
  104. GenerateStatus();
  105. return (_commandsAccepted & Interop.mincore.AcceptOptions.ACCEPT_STOP) != 0;
  106. }
  107. }
  108. /// The descriptive name shown for this service in the Service applet.
  109. public string DisplayName
  110. {
  111. get
  112. {
  113. if (_displayName == null)
  114. GenerateNames();
  115. return _displayName;
  116. }
  117. }
  118. /// The set of services that depend on this service. These are the services that will be stopped if
  119. /// this service is stopped.
  120. public ServiceController[] DependentServices
  121. {
  122. get
  123. {
  124. if (_dependentServices == null)
  125. {
  126. IntPtr serviceHandle = GetServiceHandle(Interop.mincore.ServiceOptions.SERVICE_ENUMERATE_DEPENDENTS);
  127. try
  128. {
  129. // figure out how big a buffer we need to get the info
  130. int bytesNeeded = 0;
  131. int numEnumerated = 0;
  132. bool result = Interop.mincore.EnumDependentServices(serviceHandle, Interop.mincore.ServiceState.SERVICE_STATE_ALL, IntPtr.Zero, 0,
  133. ref bytesNeeded, ref numEnumerated);
  134. if (result)
  135. {
  136. _dependentServices = Array.Empty<ServiceController>();
  137. return _dependentServices;
  138. }
  139. int lastError = Marshal.GetLastWin32Error();
  140. if (lastError != Interop.mincore.Errors.ERROR_MORE_DATA)
  141. throw new Win32Exception(lastError);
  142. // allocate the buffer
  143. IntPtr enumBuffer = Marshal.AllocHGlobal((IntPtr)bytesNeeded);
  144. try
  145. {
  146. // get all the info
  147. result = Interop.mincore.EnumDependentServices(serviceHandle, Interop.mincore.ServiceState.SERVICE_STATE_ALL, enumBuffer, bytesNeeded,
  148. ref bytesNeeded, ref numEnumerated);
  149. if (!result)
  150. throw new Win32Exception();
  151. // for each of the entries in the buffer, create a new ServiceController object.
  152. _dependentServices = new ServiceController[numEnumerated];
  153. for (int i = 0; i < numEnumerated; i++)
  154. {
  155. Interop.mincore.ENUM_SERVICE_STATUS status = new Interop.mincore.ENUM_SERVICE_STATUS();
  156. IntPtr structPtr = (IntPtr)((long)enumBuffer + (i * Marshal.SizeOf<Interop.mincore.ENUM_SERVICE_STATUS>()));
  157. Marshal.PtrToStructure(structPtr, status);
  158. _dependentServices[i] = new ServiceController(_machineName, status);
  159. }
  160. }
  161. finally
  162. {
  163. Marshal.FreeHGlobal(enumBuffer);
  164. }
  165. }
  166. finally
  167. {
  168. Interop.mincore.CloseServiceHandle(serviceHandle);
  169. }
  170. }
  171. return _dependentServices;
  172. }
  173. }
  174. /// The name of the machine on which this service resides.
  175. public string MachineName
  176. {
  177. get
  178. {
  179. return _machineName;
  180. }
  181. }
  182. /// Returns the short name of the service referenced by this object.
  183. public string ServiceName
  184. {
  185. get
  186. {
  187. if (_name == null)
  188. GenerateNames();
  189. return _name;
  190. }
  191. }
  192. public unsafe ServiceController[] ServicesDependedOn
  193. {
  194. get
  195. {
  196. if (_servicesDependedOn != null)
  197. return _servicesDependedOn;
  198. IntPtr serviceHandle = GetServiceHandle(Interop.mincore.ServiceOptions.SERVICE_QUERY_CONFIG);
  199. try
  200. {
  201. int bytesNeeded = 0;
  202. bool success = Interop.mincore.QueryServiceConfig(serviceHandle, IntPtr.Zero, 0, out bytesNeeded);
  203. if (success)
  204. {
  205. _servicesDependedOn = Array.Empty<ServiceController>();
  206. return _servicesDependedOn;
  207. }
  208. int lastError = Marshal.GetLastWin32Error();
  209. if (lastError != Interop.mincore.Errors.ERROR_INSUFFICIENT_BUFFER)
  210. throw new Win32Exception(lastError);
  211. // get the info
  212. IntPtr bufPtr = Marshal.AllocHGlobal((IntPtr)bytesNeeded);
  213. try
  214. {
  215. success = Interop.mincore.QueryServiceConfig(serviceHandle, bufPtr, bytesNeeded, out bytesNeeded);
  216. if (!success)
  217. throw new Win32Exception(Marshal.GetLastWin32Error());
  218. Interop.mincore.QUERY_SERVICE_CONFIG config = new Interop.mincore.QUERY_SERVICE_CONFIG();
  219. Marshal.PtrToStructure(bufPtr, config);
  220. Dictionary<string, ServiceController> dependencyHash = null;
  221. char* dependencyChar = config.lpDependencies;
  222. if (dependencyChar != null)
  223. {
  224. // lpDependencies points to the start of multiple null-terminated strings. The list is
  225. // double-null terminated.
  226. int length = 0;
  227. dependencyHash = new Dictionary<string, ServiceController>();
  228. while (*(dependencyChar + length) != '\0')
  229. {
  230. length++;
  231. if (*(dependencyChar + length) == '\0')
  232. {
  233. string dependencyNameStr = new string(dependencyChar, 0, length);
  234. dependencyChar = dependencyChar + length + 1;
  235. length = 0;
  236. if (dependencyNameStr.StartsWith("+", StringComparison.Ordinal))
  237. {
  238. // this entry is actually a service load group
  239. Interop.mincore.ENUM_SERVICE_STATUS_PROCESS[] loadGroup = GetServicesInGroup(_machineName, dependencyNameStr.Substring(1));
  240. foreach (Interop.mincore.ENUM_SERVICE_STATUS_PROCESS groupMember in loadGroup)
  241. {
  242. if (!dependencyHash.ContainsKey(groupMember.serviceName))
  243. dependencyHash.Add(groupMember.serviceName, new ServiceController(MachineName, groupMember));
  244. }
  245. }
  246. else
  247. {
  248. if (!dependencyHash.ContainsKey(dependencyNameStr))
  249. dependencyHash.Add(dependencyNameStr, new ServiceController(dependencyNameStr, MachineName));
  250. }
  251. }
  252. }
  253. }
  254. if (dependencyHash != null)
  255. {
  256. _servicesDependedOn = new ServiceController[dependencyHash.Count];
  257. dependencyHash.Values.CopyTo(_servicesDependedOn, 0);
  258. }
  259. else
  260. {
  261. _servicesDependedOn = Array.Empty<ServiceController>();
  262. }
  263. return _servicesDependedOn;
  264. }
  265. finally
  266. {
  267. Marshal.FreeHGlobal(bufPtr);
  268. }
  269. }
  270. finally
  271. {
  272. Interop.mincore.CloseServiceHandle(serviceHandle);
  273. }
  274. }
  275. }
  276. public ServiceStartMode StartType
  277. {
  278. get
  279. {
  280. if (_startTypeInitialized)
  281. return _startType;
  282. IntPtr serviceHandle = IntPtr.Zero;
  283. try
  284. {
  285. serviceHandle = GetServiceHandle(Interop.mincore.ServiceOptions.SERVICE_QUERY_CONFIG);
  286. int bytesNeeded = 0;
  287. bool success = Interop.mincore.QueryServiceConfig(serviceHandle, IntPtr.Zero, 0, out bytesNeeded);
  288. int lastError = Marshal.GetLastWin32Error();
  289. if (lastError != Interop.mincore.Errors.ERROR_INSUFFICIENT_BUFFER)
  290. throw new Win32Exception(lastError);
  291. // get the info
  292. IntPtr bufPtr = IntPtr.Zero;
  293. try
  294. {
  295. bufPtr = Marshal.AllocHGlobal((IntPtr)bytesNeeded);
  296. success = Interop.mincore.QueryServiceConfig(serviceHandle, bufPtr, bytesNeeded, out bytesNeeded);
  297. if (!success)
  298. throw new Win32Exception(Marshal.GetLastWin32Error());
  299. Interop.mincore.QUERY_SERVICE_CONFIG config = new Interop.mincore.QUERY_SERVICE_CONFIG();
  300. Marshal.PtrToStructure(bufPtr, config);
  301. _startType = (ServiceStartMode)config.dwStartType;
  302. _startTypeInitialized = true;
  303. }
  304. finally
  305. {
  306. if (bufPtr != IntPtr.Zero)
  307. Marshal.FreeHGlobal(bufPtr);
  308. }
  309. }
  310. finally
  311. {
  312. if (serviceHandle != IntPtr.Zero)
  313. Interop.mincore.CloseServiceHandle(serviceHandle);
  314. }
  315. return _startType;
  316. }
  317. }
  318. public SafeHandle ServiceHandle
  319. {
  320. get
  321. {
  322. return new SafeServiceHandle(GetServiceHandle(Interop.mincore.ServiceOptions.SERVICE_ALL_ACCESS));
  323. }
  324. }
  325. /// Gets the status of the service referenced by this object, e.g., Running, Stopped, etc.
  326. public ServiceControllerStatus Status
  327. {
  328. get
  329. {
  330. GenerateStatus();
  331. return _status;
  332. }
  333. }
  334. /// Gets the type of service that this object references.
  335. public ServiceType ServiceType
  336. {
  337. get
  338. {
  339. GenerateStatus();
  340. return (ServiceType)_type;
  341. }
  342. }
  343. private static bool CheckMachineName(string value)
  344. {
  345. return !string.IsNullOrWhiteSpace(value) && value.IndexOf('\\') == -1;
  346. }
  347. private void Close()
  348. {
  349. }
  350. public void Dispose()
  351. {
  352. Dispose(true);
  353. }
  354. /// Disconnects this object from the service and frees any allocated resources.
  355. protected virtual void Dispose(bool disposing)
  356. {
  357. if (_serviceManagerHandle != null)
  358. {
  359. _serviceManagerHandle.Dispose();
  360. _serviceManagerHandle = null;
  361. }
  362. _statusGenerated = false;
  363. _startTypeInitialized = false;
  364. _type = Interop.mincore.ServiceTypeOptions.SERVICE_TYPE_ALL;
  365. _disposed = true;
  366. }
  367. private unsafe void GenerateStatus()
  368. {
  369. if (!_statusGenerated)
  370. {
  371. IntPtr serviceHandle = GetServiceHandle(Interop.mincore.ServiceOptions.SERVICE_QUERY_STATUS);
  372. try
  373. {
  374. Interop.mincore.SERVICE_STATUS svcStatus = new Interop.mincore.SERVICE_STATUS();
  375. bool success = Interop.mincore.QueryServiceStatus(serviceHandle, &svcStatus);
  376. if (!success)
  377. throw new Win32Exception(Marshal.GetLastWin32Error());
  378. _commandsAccepted = svcStatus.controlsAccepted;
  379. _status = (ServiceControllerStatus)svcStatus.currentState;
  380. _type = svcStatus.serviceType;
  381. _statusGenerated = true;
  382. }
  383. finally
  384. {
  385. Interop.mincore.CloseServiceHandle(serviceHandle);
  386. }
  387. }
  388. }
  389. private unsafe void GenerateNames()
  390. {
  391. if (_machineName.Length == 0)
  392. throw new ArgumentException(SR.NoMachineName);
  393. IntPtr databaseHandle = IntPtr.Zero;
  394. IntPtr memory = IntPtr.Zero;
  395. int bytesNeeded;
  396. int servicesReturned;
  397. int resumeHandle = 0;
  398. try
  399. {
  400. databaseHandle = GetDataBaseHandleWithEnumerateAccess(_machineName);
  401. Interop.mincore.EnumServicesStatusEx(
  402. databaseHandle,
  403. Interop.mincore.ServiceControllerOptions.SC_ENUM_PROCESS_INFO,
  404. Interop.mincore.ServiceTypeOptions.SERVICE_TYPE_WIN32,
  405. Interop.mincore.StatusOptions.STATUS_ALL,
  406. IntPtr.Zero,
  407. 0,
  408. out bytesNeeded,
  409. out servicesReturned,
  410. ref resumeHandle,
  411. null);
  412. memory = Marshal.AllocHGlobal(bytesNeeded);
  413. Interop.mincore.EnumServicesStatusEx(
  414. databaseHandle,
  415. Interop.mincore.ServiceControllerOptions.SC_ENUM_PROCESS_INFO,
  416. Interop.mincore.ServiceTypeOptions.SERVICE_TYPE_WIN32,
  417. Interop.mincore.StatusOptions.STATUS_ALL,
  418. memory,
  419. bytesNeeded,
  420. out bytesNeeded,
  421. out servicesReturned,
  422. ref resumeHandle,
  423. null);
  424. // Since the service name of one service cannot be equal to the
  425. // service or display name of another service, we can safely
  426. // loop through all services checking if either the service or
  427. // display name matches the user given name. If there is a
  428. // match, then we've found the service.
  429. for (int i = 0; i < servicesReturned; i++)
  430. {
  431. IntPtr structPtr = (IntPtr)((long)memory + (i * Marshal.SizeOf<Interop.mincore.ENUM_SERVICE_STATUS_PROCESS>()));
  432. Interop.mincore.ENUM_SERVICE_STATUS_PROCESS status = new Interop.mincore.ENUM_SERVICE_STATUS_PROCESS();
  433. Marshal.PtrToStructure(structPtr, status);
  434. if (string.Equals(_eitherName, status.serviceName, StringComparison.OrdinalIgnoreCase) ||
  435. string.Equals(_eitherName, status.displayName, StringComparison.OrdinalIgnoreCase))
  436. {
  437. if (_name == null)
  438. {
  439. _name = status.serviceName;
  440. }
  441. if (_displayName == null)
  442. {
  443. _displayName = status.displayName;
  444. }
  445. _eitherName = string.Empty;
  446. return;
  447. }
  448. }
  449. throw new InvalidOperationException(SR.Format(SR.NoService, _eitherName, _machineName));
  450. }
  451. finally
  452. {
  453. Marshal.FreeHGlobal(memory);
  454. if (databaseHandle != IntPtr.Zero)
  455. {
  456. Interop.mincore.CloseServiceHandle(databaseHandle);
  457. }
  458. }
  459. }
  460. private static IntPtr GetDataBaseHandleWithAccess(string machineName, int serviceControlManagerAccess)
  461. {
  462. IntPtr databaseHandle = IntPtr.Zero;
  463. if (machineName.Equals(DefaultMachineName) || machineName.Length == 0)
  464. {
  465. databaseHandle = Interop.mincore.OpenSCManager(null, null, serviceControlManagerAccess);
  466. }
  467. else
  468. {
  469. databaseHandle = Interop.mincore.OpenSCManager(machineName, null, serviceControlManagerAccess);
  470. }
  471. if (databaseHandle == IntPtr.Zero)
  472. {
  473. Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
  474. throw new InvalidOperationException(SR.Format(SR.OpenSC, machineName), inner);
  475. }
  476. return databaseHandle;
  477. }
  478. private void GetDataBaseHandleWithConnectAccess()
  479. {
  480. if (_disposed)
  481. {
  482. throw new ObjectDisposedException(GetType().Name);
  483. }
  484. // get a handle to SCM with connect access and store it in serviceManagerHandle field.
  485. if (_serviceManagerHandle == null)
  486. {
  487. _serviceManagerHandle = new SafeServiceHandle(GetDataBaseHandleWithAccess(_machineName, Interop.mincore.ServiceControllerOptions.SC_MANAGER_CONNECT));
  488. }
  489. }
  490. private static IntPtr GetDataBaseHandleWithEnumerateAccess(string machineName)
  491. {
  492. return GetDataBaseHandleWithAccess(machineName, Interop.mincore.ServiceControllerOptions.SC_MANAGER_ENUMERATE_SERVICE);
  493. }
  494. /// Gets all the device-driver services on the local machine.
  495. public static ServiceController[] GetDevices()
  496. {
  497. return GetDevices(DefaultMachineName);
  498. }
  499. /// Gets all the device-driver services in the machine specified.
  500. public static ServiceController[] GetDevices(string machineName)
  501. {
  502. return GetServicesOfType(machineName, Interop.mincore.ServiceTypeOptions.SERVICE_TYPE_DRIVER);
  503. }
  504. /// Opens a handle for the current service. The handle must be closed with
  505. /// a call to Interop.CloseServiceHandle().
  506. private IntPtr GetServiceHandle(int desiredAccess)
  507. {
  508. GetDataBaseHandleWithConnectAccess();
  509. IntPtr serviceHandle = Interop.mincore.OpenService(_serviceManagerHandle.DangerousGetHandle(), ServiceName, desiredAccess);
  510. if (serviceHandle == IntPtr.Zero)
  511. {
  512. Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
  513. throw new InvalidOperationException(SR.Format(SR.OpenService, ServiceName, _machineName), inner);
  514. }
  515. return serviceHandle;
  516. }
  517. /// Gets the services (not including device-driver services) on the local machine.
  518. public static ServiceController[] GetServices()
  519. {
  520. return GetServices(DefaultMachineName);
  521. }
  522. /// Gets the services (not including device-driver services) on the machine specified.
  523. public static ServiceController[] GetServices(string machineName)
  524. {
  525. return GetServicesOfType(machineName, Interop.mincore.ServiceTypeOptions.SERVICE_TYPE_WIN32);
  526. }
  527. /// Helper function for ServicesDependedOn.
  528. private static Interop.mincore.ENUM_SERVICE_STATUS_PROCESS[] GetServicesInGroup(string machineName, string group)
  529. {
  530. return GetServices<Interop.mincore.ENUM_SERVICE_STATUS_PROCESS>(machineName, Interop.mincore.ServiceTypeOptions.SERVICE_TYPE_WIN32, group, status => { return status; });
  531. }
  532. /// Helper function for GetDevices and GetServices.
  533. private static ServiceController[] GetServicesOfType(string machineName, int serviceType)
  534. {
  535. if (!CheckMachineName(machineName))
  536. throw new ArgumentException(SR.Format(SR.BadMachineName, machineName));
  537. return GetServices<ServiceController>(machineName, serviceType, null, status => { return new ServiceController(machineName, status); });
  538. }
  539. /// Helper for GetDevices, GetServices, and ServicesDependedOn
  540. private static T[] GetServices<T>(string machineName, int serviceType, string group, Func<Interop.mincore.ENUM_SERVICE_STATUS_PROCESS, T> selector)
  541. {
  542. IntPtr databaseHandle = IntPtr.Zero;
  543. IntPtr memory = IntPtr.Zero;
  544. int bytesNeeded;
  545. int servicesReturned;
  546. int resumeHandle = 0;
  547. T[] services;
  548. try
  549. {
  550. databaseHandle = GetDataBaseHandleWithEnumerateAccess(machineName);
  551. Interop.mincore.EnumServicesStatusEx(
  552. databaseHandle,
  553. Interop.mincore.ServiceControllerOptions.SC_ENUM_PROCESS_INFO,
  554. serviceType,
  555. Interop.mincore.StatusOptions.STATUS_ALL,
  556. IntPtr.Zero,
  557. 0,
  558. out bytesNeeded,
  559. out servicesReturned,
  560. ref resumeHandle,
  561. group);
  562. memory = Marshal.AllocHGlobal((IntPtr)bytesNeeded);
  563. //
  564. // Get the set of services
  565. //
  566. Interop.mincore.EnumServicesStatusEx(
  567. databaseHandle,
  568. Interop.mincore.ServiceControllerOptions.SC_ENUM_PROCESS_INFO,
  569. serviceType,
  570. Interop.mincore.StatusOptions.STATUS_ALL,
  571. memory,
  572. bytesNeeded,
  573. out bytesNeeded,
  574. out servicesReturned,
  575. ref resumeHandle,
  576. group);
  577. //
  578. // Go through the block of memory it returned to us and select the results
  579. //
  580. services = new T[servicesReturned];
  581. for (int i = 0; i < servicesReturned; i++)
  582. {
  583. IntPtr structPtr = (IntPtr)((long)memory + (i * Marshal.SizeOf<Interop.mincore.ENUM_SERVICE_STATUS_PROCESS>()));
  584. Interop.mincore.ENUM_SERVICE_STATUS_PROCESS status = new Interop.mincore.ENUM_SERVICE_STATUS_PROCESS();
  585. Marshal.PtrToStructure(structPtr, status);
  586. services[i] = selector(status);
  587. }
  588. }
  589. finally
  590. {
  591. Marshal.FreeHGlobal(memory);
  592. if (databaseHandle != IntPtr.Zero)
  593. {
  594. Interop.mincore.CloseServiceHandle(databaseHandle);
  595. }
  596. }
  597. return services;
  598. }
  599. /// Suspends a service's operation.
  600. public unsafe void Pause()
  601. {
  602. IntPtr serviceHandle = GetServiceHandle(Interop.mincore.ServiceOptions.SERVICE_PAUSE_CONTINUE);
  603. try
  604. {
  605. Interop.mincore.SERVICE_STATUS status = new Interop.mincore.SERVICE_STATUS();
  606. bool result = Interop.mincore.ControlService(serviceHandle, Interop.mincore.ControlOptions.CONTROL_PAUSE, &status);
  607. if (!result)
  608. {
  609. Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
  610. throw new InvalidOperationException(SR.Format(SR.PauseService, ServiceName, _machineName), inner);
  611. }
  612. }
  613. finally
  614. {
  615. Interop.mincore.CloseServiceHandle(serviceHandle);
  616. }
  617. }
  618. /// Continues a service after it has been paused.
  619. public unsafe void Continue()
  620. {
  621. IntPtr serviceHandle = GetServiceHandle(Interop.mincore.ServiceOptions.SERVICE_PAUSE_CONTINUE);
  622. try
  623. {
  624. Interop.mincore.SERVICE_STATUS status = new Interop.mincore.SERVICE_STATUS();
  625. bool result = Interop.mincore.ControlService(serviceHandle, Interop.mincore.ControlOptions.CONTROL_CONTINUE, &status);
  626. if (!result)
  627. {
  628. Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
  629. throw new InvalidOperationException(SR.Format(SR.ResumeService, ServiceName, _machineName), inner);
  630. }
  631. }
  632. finally
  633. {
  634. Interop.mincore.CloseServiceHandle(serviceHandle);
  635. }
  636. }
  637. /// Refreshes all property values.
  638. public void Refresh()
  639. {
  640. _statusGenerated = false;
  641. _startTypeInitialized = false;
  642. _dependentServices = null;
  643. _servicesDependedOn = null;
  644. }
  645. /// Starts the service.
  646. public void Start()
  647. {
  648. Start(Array.Empty<string>());
  649. }
  650. /// Starts a service in the machine specified.
  651. public void Start(string[] args)
  652. {
  653. if (args == null)
  654. throw new ArgumentNullException(nameof(args));
  655. IntPtr serviceHandle = GetServiceHandle(Interop.mincore.ServiceOptions.SERVICE_START);
  656. try
  657. {
  658. IntPtr[] argPtrs = new IntPtr[args.Length];
  659. int i = 0;
  660. try
  661. {
  662. for (i = 0; i < args.Length; i++)
  663. {
  664. if (args[i] == null)
  665. throw new ArgumentNullException(SR.ArgsCantBeNull, nameof(args));
  666. argPtrs[i] = Marshal.StringToHGlobalUni(args[i]);
  667. }
  668. }
  669. catch
  670. {
  671. for (int j = 0; j < i; j++)
  672. Marshal.FreeHGlobal(argPtrs[i]);
  673. throw;
  674. }
  675. GCHandle argPtrsHandle = new GCHandle();
  676. try
  677. {
  678. argPtrsHandle = GCHandle.Alloc(argPtrs, GCHandleType.Pinned);
  679. bool result = Interop.mincore.StartService(serviceHandle, args.Length, (IntPtr)argPtrsHandle.AddrOfPinnedObject());
  680. if (!result)
  681. {
  682. Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
  683. throw new InvalidOperationException(SR.Format(SR.CannotStart, ServiceName, _machineName), inner);
  684. }
  685. }
  686. finally
  687. {
  688. for (i = 0; i < args.Length; i++)
  689. Marshal.FreeHGlobal(argPtrs[i]);
  690. if (argPtrsHandle.IsAllocated)
  691. argPtrsHandle.Free();
  692. }
  693. }
  694. finally
  695. {
  696. Interop.mincore.CloseServiceHandle(serviceHandle);
  697. }
  698. }
  699. /// Stops the service. If any other services depend on this one for operation,
  700. /// they will be stopped first. The DependentServices property lists this set
  701. /// of services.
  702. public unsafe void Stop()
  703. {
  704. IntPtr serviceHandle = GetServiceHandle(Interop.mincore.ServiceOptions.SERVICE_STOP);
  705. try
  706. {
  707. // Before stopping this service, stop all the dependent services that are running.
  708. // (It's OK not to cache the result of getting the DependentServices property because it caches on its own.)
  709. for (int i = 0; i < DependentServices.Length; i++)
  710. {
  711. ServiceController currentDependent = DependentServices[i];
  712. currentDependent.Refresh();
  713. if (currentDependent.Status != ServiceControllerStatus.Stopped)
  714. {
  715. currentDependent.Stop();
  716. currentDependent.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 30));
  717. }
  718. }
  719. Interop.mincore.SERVICE_STATUS status = new Interop.mincore.SERVICE_STATUS();
  720. bool result = Interop.mincore.ControlService(serviceHandle, Interop.mincore.ControlOptions.CONTROL_STOP, &status);
  721. if (!result)
  722. {
  723. Exception inner = new Win32Exception(Marshal.GetLastWin32Error());
  724. throw new InvalidOperationException(SR.Format(SR.StopService, ServiceName, _machineName), inner);
  725. }
  726. }
  727. finally
  728. {
  729. Interop.mincore.CloseServiceHandle(serviceHandle);
  730. }
  731. }
  732. /// Waits infinitely until the service has reached the given status.
  733. public void WaitForStatus(ServiceControllerStatus desiredStatus)
  734. {
  735. WaitForStatus(desiredStatus, TimeSpan.MaxValue);
  736. }
  737. /// Waits until the service has reached the given status or until the specified time
  738. /// has expired
  739. public void WaitForStatus(ServiceControllerStatus desiredStatus, TimeSpan timeout)
  740. {
  741. if (!Enum.IsDefined(typeof(ServiceControllerStatus), desiredStatus))
  742. throw new ArgumentException(SR.Format(SR.InvalidEnumArgument, "desiredStatus", (int)desiredStatus, typeof(ServiceControllerStatus)));
  743. DateTime start = DateTime.UtcNow;
  744. Refresh();
  745. while (Status != desiredStatus)
  746. {
  747. if (DateTime.UtcNow - start > timeout)
  748. throw new System.ServiceProcess.TimeoutException(SR.Timeout);
  749. _waitForStatusSignal.WaitOne(250);
  750. Refresh();
  751. }
  752. }
  753. }
  754. }