Merge pull request #2333 from joelmartinez/docs-classic-fix
[mono.git] / mcs / class / System.ServiceProcess / System.ServiceProcess / Win32ServiceController.cs
1 //
2 // System.ServiceProcess.Win32ServiceController
3 //
4 // Author:
5 //      Gert Driesen (drieseng@users.sourceforge.net)
6 //
7 // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
8 //
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System.Collections;
31 using System.ComponentModel;
32 using System.Globalization;
33 using System.Runtime.InteropServices;
34 using System.Text;
35
36 namespace System.ServiceProcess
37 {
38         internal class Win32ServiceController : ServiceControllerImpl
39         {
40                 SERVICE_STATUS_PROCESS _status;
41
42                 public Win32ServiceController (ServiceController serviceController)
43                         : base (serviceController)
44                 {
45                 }
46
47                 public override bool CanPauseAndContinue {
48                         get {
49                                 if ((int) _status.dwServiceType == 0)
50                                         _status = GetServiceStatus (ServiceController.ServiceName,
51                                                 ServiceController.MachineName);
52                                 return (_status.dwControlsAccepted & SERVICE_CONTROL_ACCEPTED.SERVICE_ACCEPT_PAUSE_CONTINUE) != 0;
53                         }
54                 }
55
56                 public override bool CanShutdown {
57                         get {
58                                 if ((int) _status.dwServiceType == 0)
59                                         _status = GetServiceStatus (ServiceController.ServiceName,
60                                                 ServiceController.MachineName);
61                                 return (_status.dwControlsAccepted & SERVICE_CONTROL_ACCEPTED.SERVICE_ACCEPT_SHUTDOWN) != 0;
62                         }
63                 }
64
65                 public override bool CanStop {
66                         get {
67                                 if ((int) _status.dwServiceType == 0)
68                                         _status = GetServiceStatus (ServiceController.ServiceName,
69                                                 ServiceController.MachineName);
70                                 return (_status.dwControlsAccepted & SERVICE_CONTROL_ACCEPTED.SERVICE_ACCEPT_STOP) != 0;
71                         }
72                 }
73
74                 public override ServiceController [] DependentServices {
75                         get {
76                                 return GetDependentServices (ServiceController.ServiceName,
77                                         ServiceController.MachineName);
78                         }
79                 }
80
81                 public override string DisplayName {
82                         get {
83                                 string lookupName = ServiceController.ServiceName;
84
85                                 IntPtr scHandle = IntPtr.Zero;
86                                 try {
87                                         scHandle = OpenServiceControlManager (ServiceController.MachineName,
88                                                 SERVICE_MANAGER_RIGHTS.SC_MANAGER_CONNECT);
89                                         if (lookupName.Length == 0) {
90                                                 // if the service name is not available, then
91                                                 // assume the specified name is in fact already a display
92                                                 // name
93                                                 try {
94                                                         string serviceName = GetServiceName (scHandle, 
95                                                                 lookupName);
96                                                         ServiceController.InternalServiceName = serviceName;
97                                                         ServiceController.Name = string.Empty;
98                                                         return lookupName;
99                                                 } catch (Win32Exception) {
100                                                 }
101                                         }
102
103                                         if (ServiceController.InternalDisplayName.Length == 0)
104                                                 return GetServiceDisplayName (scHandle, lookupName,
105                                                         ServiceController.MachineName);
106                                         return ServiceController.InternalDisplayName;
107                                 } finally {
108                                         if (scHandle != IntPtr.Zero)
109                                                 CloseServiceHandle (scHandle);
110                                 }
111                         }
112                 }
113
114                 public override string ServiceName {
115                         get {
116                                 string lookupName = ServiceController.Name;
117                                 if (lookupName.Length == 0)
118                                         lookupName = ServiceController.InternalDisplayName;
119
120                                 IntPtr scHandle = IntPtr.Zero;
121                                 try {
122                                         scHandle = OpenServiceControlManager (ServiceController.MachineName,
123                                                 SERVICE_MANAGER_RIGHTS.SC_MANAGER_CONNECT);
124
125                                         // assume the specified name is in fact a display name
126                                         try {
127                                                 string serviceName = GetServiceName (scHandle, lookupName);
128                                                 ServiceController.InternalDisplayName = lookupName;
129                                                 ServiceController.Name = string.Empty;
130                                                 return serviceName;
131                                         } catch (Win32Exception) {
132                                         }
133
134                                         // instead of opening the service to verify whether it exists,
135                                         // we'll try to get its displayname and hereby avoid looking
136                                         // this up separately
137                                         string displayName = GetServiceDisplayName (scHandle,
138                                                 lookupName, ServiceController.MachineName);
139                                         ServiceController.InternalDisplayName = displayName;
140                                         ServiceController.Name = string.Empty;
141                                         return lookupName;
142                                 } finally {
143                                         if (scHandle != IntPtr.Zero)
144                                                 CloseServiceHandle (scHandle);
145                                 }
146                         }
147                 }
148
149                 public override ServiceController [] ServicesDependedOn {
150                         get {
151                                 return GetServiceDependencies (ServiceController.ServiceName,
152                                         ServiceController.MachineName);
153                         }
154                 }
155
156                 public override ServiceType ServiceType {
157                         get {
158                                 if ((int) _status.dwServiceType == 0)
159                                         _status = GetServiceStatus (ServiceController.ServiceName,
160                                                 ServiceController.MachineName);
161                                 return _status.dwServiceType;
162                         }
163                 }
164
165                 public override ServiceControllerStatus Status {
166                         get {
167                                 if ((int) _status.dwServiceType == 0)
168                                         _status = GetServiceStatus (ServiceController.ServiceName,
169                                                 ServiceController.MachineName);
170                                 return _status.dwCurrentState;
171                         }
172                 }
173
174                 public override void Close ()
175                 {
176                         // clear status cache
177                         _status.dwServiceType = 0;
178                 }
179
180                 public override void Continue ()
181                 {
182                         string serviceName = ServiceController.ServiceName;
183                         string machineName = ServiceController.MachineName;
184                         IntPtr scHandle = IntPtr.Zero;
185                         IntPtr svcHandle = IntPtr.Zero;
186
187                         try {
188                                 scHandle = OpenServiceControlManager (machineName,
189                                         SERVICE_MANAGER_RIGHTS.SC_MANAGER_CONNECT);
190
191                                 svcHandle = OpenService (scHandle, serviceName, SERVICE_RIGHTS.SERVICE_PAUSE_CONTINUE);
192                                 if (svcHandle == IntPtr.Zero)
193                                         throw CreateCannotOpenServiceException (serviceName,
194                                                 machineName);
195
196                                 SERVICE_STATUS status = new SERVICE_STATUS ();
197                                 if (!ControlService (svcHandle, SERVICE_CONTROL_TYPE.SERVICE_CONTROL_CONTINUE, ref status))
198                                         throw new InvalidOperationException (string.Format (
199                                                 CultureInfo.CurrentCulture, "Cannot resume {0} service"
200                                                 + " on computer '{1}'.", serviceName, machineName),
201                                                 new Win32Exception ());
202                         } finally {
203                                 if (svcHandle != IntPtr.Zero)
204                                         CloseServiceHandle (svcHandle);
205                                 if (scHandle != IntPtr.Zero)
206                                         CloseServiceHandle (scHandle);
207                         }
208                 }
209
210                 public override void Dispose (bool disposing)
211                 {
212                         // we're not keeping any handles open
213                 }
214
215                 public override void ExecuteCommand (int command)
216                 {
217                         string serviceName = ServiceController.ServiceName;
218                         string machineName = ServiceController.MachineName;
219                         IntPtr scHandle = IntPtr.Zero;
220                         IntPtr svcHandle = IntPtr.Zero;
221
222                         try {
223                                 scHandle = OpenServiceControlManager (machineName,
224                                         SERVICE_MANAGER_RIGHTS.SC_MANAGER_CONNECT);
225
226                                 // MSDN: the hService handle must have the SERVICE_USER_DEFINED_CONTROL
227                                 // access right
228                                 svcHandle = OpenService (scHandle, serviceName, SERVICE_RIGHTS.SERVICE_USER_DEFINED_CONTROL);
229                                 if (svcHandle == IntPtr.Zero)
230                                         throw CreateCannotOpenServiceException (serviceName,
231                                                 machineName);
232
233                                 SERVICE_STATUS status = new SERVICE_STATUS ();
234                                 if (!ControlService (svcHandle, (SERVICE_CONTROL_TYPE) command, ref status))
235                                         throw new InvalidOperationException (string.Format (
236                                                 CultureInfo.CurrentCulture, "Cannot control {0} service"
237                                                 + " on computer '{1}'.", serviceName, machineName),
238                                                 new Win32Exception ());
239                         } finally {
240                                 if (svcHandle != IntPtr.Zero)
241                                         CloseServiceHandle (svcHandle);
242                                 if (scHandle != IntPtr.Zero)
243                                         CloseServiceHandle (scHandle);
244                         }
245                 }
246
247                 public override ServiceController [] GetDevices ()
248                 {
249                         return GetServices (ServiceController.MachineName,
250                                 SERVICE_TYPE.SERVICE_DRIVER, null);
251                 }
252
253                 public override ServiceController [] GetServices ()
254                 {
255                         return GetServices (ServiceController.MachineName,
256                                 SERVICE_TYPE.SERVICE_WIN32, null);
257                 }
258
259                 public override void Pause ()
260                 {
261                         string serviceName = ServiceController.ServiceName;
262                         string machineName = ServiceController.MachineName;
263                         IntPtr scHandle = IntPtr.Zero;
264                         IntPtr svcHandle = IntPtr.Zero;
265
266                         try {
267                                 scHandle = OpenServiceControlManager (machineName,
268                                         SERVICE_MANAGER_RIGHTS.SC_MANAGER_CONNECT);
269
270                                 svcHandle = OpenService (scHandle, serviceName, SERVICE_RIGHTS.SERVICE_PAUSE_CONTINUE);
271                                 if (svcHandle == IntPtr.Zero)
272                                         throw CreateCannotOpenServiceException (serviceName,
273                                                 machineName);
274
275                                 SERVICE_STATUS status = new SERVICE_STATUS ();
276                                 if (!ControlService (svcHandle, SERVICE_CONTROL_TYPE.SERVICE_CONTROL_PAUSE, ref status))
277                                         throw new InvalidOperationException (string.Format (
278                                                 CultureInfo.CurrentCulture, "Cannot pause {0} service"
279                                                 + " on computer '{1}'.", serviceName, machineName),
280                                                 new Win32Exception ());
281                         } finally {
282                                 if (svcHandle != IntPtr.Zero)
283                                         CloseServiceHandle (svcHandle);
284                                 if (scHandle != IntPtr.Zero)
285                                         CloseServiceHandle (scHandle);
286                         }
287                 }
288
289                 public override void Refresh ()
290                 {
291                         // force refresh of status
292                         _status.dwServiceType = 0;
293                 }
294
295                 public override void Start (string [] args)
296                 {
297                         string serviceName = ServiceController.ServiceName;
298                         string machineName = ServiceController.MachineName;
299                         IntPtr scHandle = IntPtr.Zero;
300                         IntPtr svcHandle = IntPtr.Zero;
301                         IntPtr [] arguments = new IntPtr [args.Length];
302
303                         try {
304                                 scHandle = OpenServiceControlManager (machineName,
305                                         SERVICE_MANAGER_RIGHTS.SC_MANAGER_CONNECT);
306
307                                 svcHandle = OpenService (scHandle, serviceName, SERVICE_RIGHTS.SERVICE_START);
308                                 if (svcHandle == IntPtr.Zero)
309                                         throw CreateCannotOpenServiceException (serviceName,
310                                                 machineName);
311
312                                 for (int i = 0; i < args.Length; i++) {
313                                         string argument = args [i];
314                                         arguments [i] = Marshal.StringToHGlobalAnsi (argument);
315                                 }
316
317                                 if (!StartService (svcHandle, arguments.Length, arguments))
318                                         throw new InvalidOperationException (string.Format (
319                                                 CultureInfo.CurrentCulture, "Cannot start {0} service"
320                                                 + " on computer '{1}'.", serviceName, machineName),
321                                                 new Win32Exception ());
322                         } finally {
323                                 for (int i = 0; i < arguments.Length; i++)
324                                         Marshal.FreeHGlobal (arguments [i]);
325                                 if (svcHandle != IntPtr.Zero)
326                                         CloseServiceHandle (svcHandle);
327                                 if (scHandle != IntPtr.Zero)
328                                         CloseServiceHandle (scHandle);
329                         }
330
331                 }
332
333                 public override void Stop ()
334                 {
335                         string serviceName = ServiceController.ServiceName;
336                         string machineName = ServiceController.MachineName;
337                         IntPtr scHandle = IntPtr.Zero;
338                         IntPtr svcHandle = IntPtr.Zero;
339
340                         try {
341                                 scHandle = OpenServiceControlManager (machineName,
342                                         SERVICE_MANAGER_RIGHTS.SC_MANAGER_CONNECT);
343
344                                 svcHandle = OpenService (scHandle, serviceName, SERVICE_RIGHTS.SERVICE_STOP);
345                                 if (svcHandle == IntPtr.Zero)
346                                         throw CreateCannotOpenServiceException (serviceName,
347                                                 machineName);
348
349                                 SERVICE_STATUS status = new SERVICE_STATUS ();
350                                 if (!ControlService (svcHandle, SERVICE_CONTROL_TYPE.SERVICE_CONTROL_STOP, ref status))
351                                         throw new InvalidOperationException (string.Format (
352                                                 CultureInfo.CurrentCulture, "Cannot stop {0} service"
353                                                 + " on computer '{1}'.", serviceName, machineName),
354                                                 new Win32Exception ());
355                         } finally {
356                                 if (svcHandle != IntPtr.Zero)
357                                         CloseServiceHandle (svcHandle);
358                                 if (scHandle != IntPtr.Zero)
359                                         CloseServiceHandle (scHandle);
360                         }
361                 }
362
363                 private static ServiceController [] GetDependentServices (string serviceName, string machineName)
364                 {
365                         IntPtr scHandle = IntPtr.Zero;
366                         IntPtr svcHandle = IntPtr.Zero;
367                         IntPtr buffer = IntPtr.Zero;
368
369                         try {
370                                 scHandle = OpenServiceControlManager (machineName,
371                                         SERVICE_MANAGER_RIGHTS.SC_MANAGER_CONNECT);
372
373                                 svcHandle = OpenService (scHandle, serviceName, SERVICE_RIGHTS.SERVICE_ENUMERATE_DEPENDENTS);
374                                 if (svcHandle == IntPtr.Zero)
375                                         throw CreateCannotOpenServiceException (serviceName, machineName);
376
377                                 uint bufferSize = 0;
378                                 uint bytesNeeded = 0;
379                                 uint servicesReturned = 0;
380
381                                 ServiceController [] services;
382
383                                 while (true) {
384                                         if (!EnumDependentServices (svcHandle, SERVICE_STATE_REQUEST.SERVICE_STATE_ALL, buffer, bufferSize, out bytesNeeded, out servicesReturned)) {
385                                                 int err = Marshal.GetLastWin32Error ();
386                                                 if (err == ERROR_MORE_DATA) {
387                                                         buffer = Marshal.AllocHGlobal ((int) bytesNeeded);
388                                                         bufferSize = bytesNeeded;
389                                                 } else {
390                                                         throw new Win32Exception (err);
391                                                 }
392                                         } else {
393                                                 int iPtr = buffer.ToInt32 ();
394
395                                                 services = new ServiceController [servicesReturned];
396                                                 for (int i = 0; i < servicesReturned; i++) {
397                                                         ENUM_SERVICE_STATUS serviceStatus = (ENUM_SERVICE_STATUS) Marshal.PtrToStructure (
398                                                                 new IntPtr (iPtr), typeof (ENUM_SERVICE_STATUS));
399                                                         // TODO: use internal ctor that takes displayname too ?
400                                                         services [i] = new ServiceController (serviceStatus.pServiceName,
401                                                                 machineName);
402                                                         // move on to the next services
403                                                         iPtr += ENUM_SERVICE_STATUS.SizeOf;
404                                                 }
405
406                                                 // we're done, so exit the loop
407                                                 break;
408                                         }
409                                 }
410
411                                 return services;
412                         } finally {
413                                 if (scHandle != IntPtr.Zero)
414                                         CloseServiceHandle (scHandle);
415                                 if (svcHandle != IntPtr.Zero)
416                                         CloseServiceHandle (svcHandle);
417                                 if (buffer != IntPtr.Zero)
418                                         Marshal.FreeHGlobal (buffer);
419                         }
420                 }
421
422                 private static ServiceController [] GetServiceDependencies (string serviceName, string machineName)
423                 {
424                         IntPtr scHandle = IntPtr.Zero;
425                         IntPtr svcHandle = IntPtr.Zero;
426                         IntPtr buffer = IntPtr.Zero;
427
428                         try {
429                                 scHandle = OpenServiceControlManager (machineName,
430                                         SERVICE_MANAGER_RIGHTS.SC_MANAGER_CONNECT);
431
432                                 svcHandle = OpenService (scHandle, serviceName, SERVICE_RIGHTS.SERVICE_QUERY_CONFIG);
433                                 if (svcHandle == IntPtr.Zero)
434                                         throw CreateCannotOpenServiceException (serviceName, machineName);
435
436                                 uint bufferSize = 0;
437                                 uint bytesNeeded = 0;
438
439                                 ServiceController [] services;
440
441                                 while (true) {
442                                         if (!QueryServiceConfig (svcHandle, buffer, bufferSize, out bytesNeeded)) {
443                                                 int err = Marshal.GetLastWin32Error ();
444                                                 if (err == ERROR_INSUFFICIENT_BUFFER) {
445                                                         buffer = Marshal.AllocHGlobal ((int) bytesNeeded);
446                                                         bufferSize = bytesNeeded;
447                                                 } else {
448                                                         throw new Win32Exception (err);
449                                                 }
450                                         } else {
451                                                 QUERY_SERVICE_CONFIG config = (QUERY_SERVICE_CONFIG) Marshal.PtrToStructure (
452                                                         buffer, typeof (QUERY_SERVICE_CONFIG));
453
454                                                 Hashtable depServices = new Hashtable ();
455                                                 IntPtr iPtr = config.lpDependencies;
456                                                 StringBuilder sb = new StringBuilder ();
457                                                 string currentChar = Marshal.PtrToStringUni (iPtr, 1);
458                                                 while (currentChar != "\0") {
459                                                         sb.Append (currentChar);
460                                                         iPtr = new IntPtr (iPtr.ToInt64 () + Marshal.SystemDefaultCharSize);
461                                                         currentChar = Marshal.PtrToStringUni (iPtr, 1);
462                                                         if (currentChar != "\0") {
463                                                                 continue;
464                                                         }
465                                                         iPtr = new IntPtr (iPtr.ToInt64 () + Marshal.SystemDefaultCharSize);
466                                                         currentChar = Marshal.PtrToStringUni (iPtr, 1);
467                                                         string dependency = sb.ToString ();
468                                                         if (dependency [0] == SC_GROUP_IDENTIFIER) {
469                                                                 ServiceController [] groupServices = GetServices (
470                                                                         machineName, SERVICE_TYPE.SERVICE_WIN32,
471                                                                         dependency.Substring (1));
472                                                                 foreach (ServiceController sc in groupServices) {
473                                                                         if (!depServices.Contains (sc.ServiceName))
474                                                                                 depServices.Add (sc.ServiceName, sc);
475                                                                 }
476                                                         } else if (!depServices.Contains (dependency)) {
477                                                                 depServices.Add (dependency, new ServiceController (dependency, machineName));
478                                                         }
479                                                         sb.Length = 0;
480                                                 }
481
482                                                 services = new ServiceController [depServices.Count];
483                                                 depServices.Values.CopyTo (services, 0);
484                                                 break;
485                                         }
486                                 }
487
488                                 return services;
489                         } finally {
490                                 if (scHandle != IntPtr.Zero)
491                                         CloseServiceHandle (scHandle);
492                                 if (svcHandle != IntPtr.Zero)
493                                         CloseServiceHandle (svcHandle);
494                                 if (buffer != IntPtr.Zero)
495                                         Marshal.FreeHGlobal (buffer);
496                         }
497                 }
498
499                 private static string GetServiceDisplayName (IntPtr scHandle, string serviceName, string machineName)
500                 {
501                         StringBuilder buffer = new StringBuilder ();
502
503                         uint bufferSize = (uint) buffer.Capacity;
504
505                         while (true) {
506                                 if (!GetServiceDisplayName (scHandle, serviceName, buffer, ref bufferSize)) {
507                                         int err = Marshal.GetLastWin32Error ();
508                                         if (err == ERROR_INSUFFICIENT_BUFFER) {
509                                                 // allocate additional byte for terminating null char
510                                                 buffer = new StringBuilder ((int) bufferSize + 1);
511                                                 bufferSize = (uint) buffer.Capacity;
512                                         } else {
513                                                 throw new InvalidOperationException (string.Format (
514                                                         CultureInfo.CurrentCulture, "Service {0} was not"
515                                                         + " found on computer '{1}'.", serviceName,
516                                                         machineName), new Win32Exception ());
517                                         }
518                                 } else {
519                                         return buffer.ToString ();
520                                 }
521                         }
522                 }
523
524                 private static string GetServiceName (IntPtr scHandle, string displayName)
525                 {
526                         StringBuilder buffer = new StringBuilder ();
527
528                         uint bufferSize = (uint) buffer.Capacity;
529
530                         while (true) {
531                                 if (!GetServiceKeyName (scHandle, displayName, buffer, ref bufferSize)) {
532                                         int err = Marshal.GetLastWin32Error ();
533                                         if (err == ERROR_INSUFFICIENT_BUFFER) {
534                                                 // allocate additional byte for terminating null char
535                                                 buffer = new StringBuilder ((int) bufferSize + 1);
536                                                 bufferSize = (uint) buffer.Capacity;
537                                         } else {
538                                                 throw new Win32Exception ();
539                                         }
540                                 } else {
541                                         return buffer.ToString ();
542                                 }
543                         }
544                 }
545
546                 private static SERVICE_STATUS_PROCESS GetServiceStatus (string serviceName, string machineName)
547                 {
548                         IntPtr scHandle = IntPtr.Zero;
549                         IntPtr svcHandle = IntPtr.Zero;
550                         IntPtr buffer = IntPtr.Zero;
551
552                         try {
553                                 scHandle = OpenServiceControlManager (machineName,
554                                         SERVICE_MANAGER_RIGHTS.SC_MANAGER_CONNECT);
555
556                                 svcHandle = OpenService (scHandle, serviceName, SERVICE_RIGHTS.SERVICE_QUERY_STATUS);
557                                 if (svcHandle == IntPtr.Zero)
558                                         throw CreateCannotOpenServiceException (serviceName, machineName);
559
560                                 int bufferSize = 0;
561                                 int bytesNeeded = 0;
562
563                                 while (true) {
564                                         if (!QueryServiceStatusEx (svcHandle, SC_STATUS_PROCESS_INFO, buffer, bufferSize, out bytesNeeded)) {
565                                                 int err = Marshal.GetLastWin32Error ();
566                                                 if (err == ERROR_INSUFFICIENT_BUFFER) {
567                                                         buffer = Marshal.AllocHGlobal (bytesNeeded);
568                                                         bufferSize = bytesNeeded;
569                                                 } else {
570                                                         throw new Win32Exception (err);
571                                                 }
572                                         } else {
573                                                 SERVICE_STATUS_PROCESS serviceStatus = (SERVICE_STATUS_PROCESS) Marshal.PtrToStructure (
574                                                         buffer, typeof (SERVICE_STATUS_PROCESS));
575                                                 return serviceStatus;
576                                         }
577                                 }
578                         } finally {
579                                 if (scHandle != IntPtr.Zero)
580                                         CloseServiceHandle (scHandle);
581                                 if (svcHandle != IntPtr.Zero)
582                                         CloseServiceHandle (svcHandle);
583                                 if (buffer != IntPtr.Zero)
584                                         Marshal.FreeHGlobal (buffer);
585                         }
586                 }
587
588                 private static ServiceController [] GetServices (string machineName, SERVICE_TYPE serviceType, string group)
589                 {
590                         IntPtr scHandle = IntPtr.Zero;
591                         IntPtr buffer = IntPtr.Zero;
592
593                         try {
594                                 scHandle = OpenServiceControlManager (machineName, 
595                                         SERVICE_MANAGER_RIGHTS.SC_MANAGER_ENUMERATE_SERVICE);
596
597                                 uint bufferSize = 0;
598                                 uint bytesNeeded = 0;
599                                 uint servicesReturned = 0;
600                                 uint resumeHandle = 0;
601
602                                 ServiceController [] services;
603
604                                 while (true) {
605                                         if (!EnumServicesStatusEx (scHandle, SC_ENUM_PROCESS_INFO, serviceType, SERVICE_STATE_REQUEST.SERVICE_STATE_ALL, buffer, bufferSize, out bytesNeeded, out servicesReturned, ref resumeHandle, group)) {
606                                                 int err = Marshal.GetLastWin32Error ();
607                                                 if (err == ERROR_MORE_DATA) {
608                                                         buffer = Marshal.AllocHGlobal ((int) bytesNeeded);
609                                                         bufferSize = bytesNeeded;
610                                                 } else {
611                                                         throw new Win32Exception (err);
612                                                 }
613                                         } else {
614                                                 int iPtr = buffer.ToInt32 ();
615
616                                                 services = new ServiceController [servicesReturned];
617                                                 for (int i = 0; i < servicesReturned; i++) {
618                                                         ENUM_SERVICE_STATUS_PROCESS serviceStatus = (ENUM_SERVICE_STATUS_PROCESS) Marshal.PtrToStructure (
619                                                                 new IntPtr (iPtr), typeof (ENUM_SERVICE_STATUS_PROCESS));
620                                                         // TODO: use internal ctor that takes displayname too
621                                                         services [i] = new ServiceController (serviceStatus.pServiceName,
622                                                                 machineName);
623                                                         // move on to the next services
624                                                         iPtr += ENUM_SERVICE_STATUS_PROCESS.SizeOf;
625                                                 }
626
627                                                 // we're done, so exit the loop
628                                                 break;
629                                         }
630                                 }
631
632                                 return services;
633                         } finally {
634                                 if (scHandle != IntPtr.Zero)
635                                         CloseServiceHandle (scHandle);
636                                 if (buffer != IntPtr.Zero)
637                                         Marshal.FreeHGlobal (buffer);
638                         }
639                 }
640
641                 private static IntPtr OpenServiceControlManager (string machineName, SERVICE_MANAGER_RIGHTS rights)
642                 {
643                         return OpenServiceControlManager (machineName, rights, false);
644                 }
645
646                 private static IntPtr OpenServiceControlManager (string machineName, SERVICE_MANAGER_RIGHTS rights, bool ignoreWin32Error)
647                 {
648                                 IntPtr scHandle = OpenSCManager (machineName, SERVICES_ACTIVE_DATABASE,
649                                         rights);
650                                 if (scHandle == IntPtr.Zero) {
651                                         string msg = string.Format (CultureInfo.CurrentCulture,
652                                                 "Cannot open Service Control Manager on computer '{0}'."
653                                                 + " This operation might require other priviliges.",
654                                                 machineName);
655                                         if (ignoreWin32Error)
656                                                 throw new InvalidOperationException (msg);
657                                         throw new InvalidOperationException (msg, new Win32Exception ());
658                                 }
659                                 return scHandle;
660                 }
661
662                 private static InvalidOperationException CreateCannotOpenServiceException (string serviceName, string machineName)
663                 {
664                         return new InvalidOperationException (string.Format (CultureInfo.CurrentCulture,
665                                 "Cannot open {0} service on computer '{1}'.", serviceName, machineName),
666                                 new Win32Exception ());
667                 }
668
669                 #region PInvoke declaration
670
671                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
672                 private static extern void CloseServiceHandle (IntPtr SCHANDLE);
673
674                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
675                 private static extern bool ControlService (
676                         IntPtr hService,
677                         SERVICE_CONTROL_TYPE dwControl,
678                         ref SERVICE_STATUS lpServiceStatus);
679
680                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
681                 private static extern bool EnumDependentServices (
682                         IntPtr hService,
683                         SERVICE_STATE_REQUEST dwServiceState,
684                         IntPtr lpServices,
685                         uint cbBufSize,
686                         out uint pcbBytesNeeded,
687                         out uint lpServicesReturned);
688
689                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
690                 private static extern bool EnumServicesStatusEx (
691                         IntPtr hSCManager,
692                         int InfoLevel,
693                         SERVICE_TYPE dwServiceType,
694                         SERVICE_STATE_REQUEST dwServiceState,
695                         IntPtr lpServices,
696                         uint cbBufSize,
697                         out uint pcbBytesNeeded,
698                         out uint lpServicesReturned,
699                         ref uint lpResumeHandle,
700                         string pszGroupName);
701
702                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
703                 private static extern bool GetServiceDisplayName (
704                         IntPtr hSCManager,
705                         string lpServiceName,
706                         StringBuilder lpDisplayName,
707                         ref uint lpcchBuffer);
708
709                 [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
710                 private static extern bool GetServiceKeyName (
711                         IntPtr hSCManager,
712                         string lpDisplayName,
713                         StringBuilder lpServiceName,
714                         ref uint lpcchBuffer);
715
716                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
717                 private static extern IntPtr OpenSCManager (
718                         string lpMachineName,
719                         string lpSCDB,
720                         SERVICE_MANAGER_RIGHTS scParameter);
721
722                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
723                 private static extern IntPtr OpenService (
724                         IntPtr SCHANDLE,
725                         string lpSvcName,
726                         SERVICE_RIGHTS dwNumServiceArgs);
727
728                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
729                 private static extern bool QueryServiceConfig (
730                         IntPtr hService,
731                         IntPtr lpServiceConfig,
732                         uint cbBufSize,
733                         out uint pcbBytesNeeded);
734
735                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
736                 private static extern bool QueryServiceStatusEx (
737                         IntPtr serviceHandle,
738                         int InfoLevel,
739                         IntPtr lpBuffer,
740                         int cbBufSize,
741                         out int pcbBytesNeeded);
742
743                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
744                 private static extern bool StartService (
745                         IntPtr SVHANDLE,
746                         int dwNumServiceArgs,
747                         IntPtr [] lpServiceArgVectors);
748
749                 private const int SC_ENUM_PROCESS_INFO = 0;
750                 private const char SC_GROUP_IDENTIFIER = '+';
751                 private const int SC_STATUS_PROCESS_INFO = 0;
752                 private const int SERVICE_NO_CHANGE = -1;
753                 private const int ERROR_MORE_DATA = 234;
754                 private const int ERROR_INSUFFICIENT_BUFFER = 122;
755                 private const int STANDARD_RIGHTS_REQUIRED = 0xf0000;
756                 private const string SERVICES_ACTIVE_DATABASE = "ServicesActive";
757
758                 internal struct QUERY_SERVICE_CONFIG
759                 {
760                         public int dwServiceType;
761                         public int dwStartType;
762                         public int dwErrorControl;
763                         public IntPtr lpBinaryPathName;
764                         public IntPtr lpLoadOrderGroup;
765                         public int dwTagId;
766                         public IntPtr lpDependencies;
767                         public IntPtr lpServiceStartName;
768                         public IntPtr lpDisplayName;
769                 }
770
771                 [Flags]
772                 private enum SERVICE_RIGHTS
773                 {
774                         SERVICE_QUERY_CONFIG = 1,
775                         SERVICE_CHANGE_CONFIG = 2,
776                         SERVICE_QUERY_STATUS = 4,
777                         SERVICE_ENUMERATE_DEPENDENTS = 8,
778                         SERVICE_START = 16,
779                         SERVICE_STOP = 32,
780                         SERVICE_PAUSE_CONTINUE = 64,
781                         SERVICE_INTERROGATE = 128,
782                         SERVICE_USER_DEFINED_CONTROL = 256,
783                         SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SERVICE_QUERY_CONFIG |
784                                 SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS |
785                                 SERVICE_ENUMERATE_DEPENDENTS | SERVICE_START | SERVICE_STOP |
786                                 SERVICE_PAUSE_CONTINUE | SERVICE_INTERROGATE |
787                                 SERVICE_USER_DEFINED_CONTROL)
788                 }
789
790                 private enum SERVICE_MANAGER_RIGHTS : uint
791                 {
792                         STANDARD_RIGHTS_READ = 0x20000,
793                         STANDARD_RIGHTS_WRITE = 0x20000,
794                         STANDARD_RIGHTS_EXECUTE = 0x20000,
795                         STANDARD_RIGHTS_ALL = 0x1F0000,
796
797                         SC_MANAGER_ALL_ACCESS = 0xf003f,
798                         SC_MANAGER_CONNECT = 1,
799                         SC_MANAGER_CREATE_SERVICE = 2,
800                         SC_MANAGER_ENUMERATE_SERVICE = 4,
801                         SC_MANAGER_LOCK = 8,
802                         SC_MANAGER_QUERY_LOCK_STATUS = 16,
803                         SC_MANAGER_MODIFY_BOOT_CONFIG = 32,
804
805                         GENERIC_READ = 0x80000000,
806                         GENERIC_WRITE = 0x40000000,
807                         GENERIC_EXECUTE = 0x20000000,
808                         GENERIC_ALL = 0x10000000
809                 }
810
811                 [StructLayout (LayoutKind.Sequential)]
812                 private struct ENUM_SERVICE_STATUS_PROCESS
813                 {
814                         public static readonly int SizeOf = Marshal.SizeOf (typeof (ENUM_SERVICE_STATUS_PROCESS));
815
816                         [MarshalAs (UnmanagedType.LPWStr)]
817                         public string pServiceName;
818
819                         [MarshalAs (UnmanagedType.LPWStr)]
820                         public string pDisplayName;
821
822                         public SERVICE_STATUS_PROCESS ServiceStatus;
823                 }
824
825                 [StructLayout (LayoutKind.Sequential)]
826                 private struct ENUM_SERVICE_STATUS
827                 {
828                         public static readonly int SizeOf = Marshal.SizeOf (typeof (ENUM_SERVICE_STATUS));
829
830                         [MarshalAs (UnmanagedType.LPWStr)]
831                         public string pServiceName;
832                         [MarshalAs (UnmanagedType.LPWStr)]
833                         public string pDisplayName;
834                         public SERVICE_STATUS ServiceStatus;
835                 }
836
837                 [StructLayout (LayoutKind.Sequential)]
838                 private struct SERVICE_STATUS
839                 {
840                         public ServiceType dwServiceType;
841                         public ServiceControllerStatus dwCurrentState;
842                         public SERVICE_CONTROL_ACCEPTED dwControlsAccepted;
843                         public int dwWin32ExitCode;
844                         public int dwServiceSpecificExitCode;
845                         public uint dwCheckPoint;
846                         public uint dwWaitHint;
847                 }
848
849                 [StructLayout (LayoutKind.Sequential, Pack = 1)]
850                 private struct SERVICE_STATUS_PROCESS
851                 {
852                         public static readonly int SizeOf = Marshal.SizeOf (typeof (SERVICE_STATUS_PROCESS));
853
854                         public ServiceType dwServiceType;
855                         public ServiceControllerStatus dwCurrentState;
856                         public SERVICE_CONTROL_ACCEPTED dwControlsAccepted;
857                         public int dwWin32ExitCode;
858                         public int dwServiceSpecificExitCode;
859                         public int dwCheckPoint;
860                         public int dwWaitHint;
861                         public int dwProcessId;
862                         public int dwServiceFlags;
863                 }
864
865                 private enum SERVICE_TYPE
866                 {
867                         SERVICE_KERNEL_DRIVER = 0x1,
868                         SERVICE_FILE_SYSTEM_DRIVER = 0x2,
869                         SERVICE_ADAPTER = 0x4,
870                         SERVICE_RECOGNIZER_DRIVER = 0x8,
871                         SERVICE_DRIVER = (SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_RECOGNIZER_DRIVER),
872                         SERVICE_WIN32_OWN_PROCESS = 0x10,
873                         SERVICE_WIN32_SHARE_PROCESS = 0x20,
874                         SERVICE_INTERACTIVE_PROCESS = 0x100,
875                         SERVICETYPE_NO_CHANGE = SERVICE_NO_CHANGE,
876                         SERVICE_WIN32 = (SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS),
877                         SERVICE_TYPE_ALL = (SERVICE_WIN32 | SERVICE_ADAPTER | SERVICE_DRIVER | SERVICE_INTERACTIVE_PROCESS)
878                 }
879
880                 private enum SERVICE_START_TYPE
881                 {
882                         SERVICE_BOOT_START = 0x0,
883                         SERVICE_SYSTEM_START = 0x1,
884                         SERVICE_AUTO_START = 0x2,
885                         SERVICE_DEMAND_START = 0x3,
886                         SERVICE_DISABLED = 0x4,
887                         SERVICESTARTTYPE_NO_CHANGE = SERVICE_NO_CHANGE
888                 }
889
890                 private enum SERVICE_ERROR_CONTROL
891                 {
892                         SERVICE_ERROR_IGNORE = 0x0,
893                         SERVICE_ERROR_NORMAL = 0x1,
894                         SERVICE_ERROR_SEVERE = 0x2,
895                         SERVICE_ERROR_CRITICAL = 0x3,
896                         msidbServiceInstallErrorControlVital = 0x8000,
897                         SERVICEERRORCONTROL_NO_CHANGE = SERVICE_NO_CHANGE
898                 }
899
900                 private enum SERVICE_STATE_REQUEST
901                 {
902                         SERVICE_ACTIVE = 0x1,
903                         SERVICE_INACTIVE = 0x2,
904                         SERVICE_STATE_ALL = (SERVICE_ACTIVE | SERVICE_INACTIVE)
905                 }
906
907                 private enum SERVICE_CONTROL_TYPE
908                 {
909                         SERVICE_CONTROL_STOP = 0x1,
910                         SERVICE_CONTROL_PAUSE = 0x2,
911                         SERVICE_CONTROL_CONTINUE = 0x3,
912                         SERVICE_CONTROL_INTERROGATE = 0x4,
913                         SERVICE_CONTROL_SHUTDOWN = 0x5,
914                         SERVICE_CONTROL_PARAMCHANGE = 0x6,
915                         SERVICE_CONTROL_NETBINDADD = 0x7,
916                         SERVICE_CONTROL_NETBINDREMOVE = 0x8,
917                         SERVICE_CONTROL_NETBINDENABLE = 0x9,
918                         SERVICE_CONTROL_NETBINDDISABLE = 0xA,
919                         SERVICE_CONTROL_DEVICEEVENT = 0xB,
920                         SERVICE_CONTROL_HARDWAREPROFILECHANGE = 0xC,
921                         SERVICE_CONTROL_POWEREVENT = 0xD,
922                         SERVICE_CONTROL_SESSIONCHANGE = 0xE
923                 }
924
925                 [Flags]
926                 private enum SERVICE_CONTROL_ACCEPTED
927                 {
928                         SERVICE_ACCEPT_NONE = 0x0,
929                         SERVICE_ACCEPT_STOP = 0x1,
930                         SERVICE_ACCEPT_PAUSE_CONTINUE = 0x2,
931                         SERVICE_ACCEPT_SHUTDOWN = 0x4,
932                         SERVICE_ACCEPT_PARAMCHANGE = 0x8,
933                         SERVICE_ACCEPT_NETBINDCHANGE = 0x10,
934                         SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 0x20,
935                         SERVICE_ACCEPT_POWEREVENT = 0x40,
936                         SERVICE_ACCEPT_SESSIONCHANGE = 0x80
937                 }
938
939                 #endregion PInvoke declaration
940         }
941 }