New tests.
[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 #if NET_2_0
595                                 scHandle = OpenServiceControlManager (machineName, 
596                                         SERVICE_MANAGER_RIGHTS.SC_MANAGER_ENUMERATE_SERVICE);
597 #else
598                                 scHandle = OpenServiceControlManager (machineName,
599                                         SERVICE_MANAGER_RIGHTS.SC_MANAGER_ENUMERATE_SERVICE, true);
600 #endif
601
602                                 uint bufferSize = 0;
603                                 uint bytesNeeded = 0;
604                                 uint servicesReturned = 0;
605                                 uint resumeHandle = 0;
606
607                                 ServiceController [] services;
608
609                                 while (true) {
610                                         if (!EnumServicesStatusEx (scHandle, SC_ENUM_PROCESS_INFO, serviceType, SERVICE_STATE_REQUEST.SERVICE_STATE_ALL, buffer, bufferSize, out bytesNeeded, out servicesReturned, ref resumeHandle, group)) {
611                                                 int err = Marshal.GetLastWin32Error ();
612                                                 if (err == ERROR_MORE_DATA) {
613                                                         buffer = Marshal.AllocHGlobal ((int) bytesNeeded);
614                                                         bufferSize = bytesNeeded;
615                                                 } else {
616                                                         throw new Win32Exception (err);
617                                                 }
618                                         } else {
619                                                 int iPtr = buffer.ToInt32 ();
620
621                                                 services = new ServiceController [servicesReturned];
622                                                 for (int i = 0; i < servicesReturned; i++) {
623                                                         ENUM_SERVICE_STATUS_PROCESS serviceStatus = (ENUM_SERVICE_STATUS_PROCESS) Marshal.PtrToStructure (
624                                                                 new IntPtr (iPtr), typeof (ENUM_SERVICE_STATUS_PROCESS));
625                                                         // TODO: use internal ctor that takes displayname too
626                                                         services [i] = new ServiceController (serviceStatus.pServiceName,
627                                                                 machineName);
628                                                         // move on to the next services
629                                                         iPtr += ENUM_SERVICE_STATUS_PROCESS.SizeOf;
630                                                 }
631
632                                                 // we're done, so exit the loop
633                                                 break;
634                                         }
635                                 }
636
637                                 return services;
638                         } finally {
639                                 if (scHandle != IntPtr.Zero)
640                                         CloseServiceHandle (scHandle);
641                                 if (buffer != IntPtr.Zero)
642                                         Marshal.FreeHGlobal (buffer);
643                         }
644                 }
645
646                 private static IntPtr OpenServiceControlManager (string machineName, SERVICE_MANAGER_RIGHTS rights)
647                 {
648                         return OpenServiceControlManager (machineName, rights, false);
649                 }
650
651                 private static IntPtr OpenServiceControlManager (string machineName, SERVICE_MANAGER_RIGHTS rights, bool ignoreWin32Error)
652                 {
653                                 IntPtr scHandle = OpenSCManager (machineName, SERVICES_ACTIVE_DATABASE,
654                                         rights);
655                                 if (scHandle == IntPtr.Zero) {
656                                         string msg = string.Format (CultureInfo.CurrentCulture,
657                                                 "Cannot open Service Control Manager on computer '{0}'."
658                                                 + " This operation might require other priviliges.",
659                                                 machineName);
660                                         if (ignoreWin32Error)
661                                                 throw new InvalidOperationException (msg);
662                                         throw new InvalidOperationException (msg, new Win32Exception ());
663                                 }
664                                 return scHandle;
665                 }
666
667                 private static InvalidOperationException CreateCannotOpenServiceException (string serviceName, string machineName)
668                 {
669                         return new InvalidOperationException (string.Format (CultureInfo.CurrentCulture,
670                                 "Cannot open {0} service on computer '{1}'.", serviceName, machineName),
671                                 new Win32Exception ());
672                 }
673
674                 #region PInvoke declaration
675
676                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
677                 private static extern void CloseServiceHandle (IntPtr SCHANDLE);
678
679                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
680                 private static extern bool ControlService (
681                         IntPtr hService,
682                         SERVICE_CONTROL_TYPE dwControl,
683                         ref SERVICE_STATUS lpServiceStatus);
684
685                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
686                 private static extern bool EnumDependentServices (
687                         IntPtr hService,
688                         SERVICE_STATE_REQUEST dwServiceState,
689                         IntPtr lpServices,
690                         uint cbBufSize,
691                         out uint pcbBytesNeeded,
692                         out uint lpServicesReturned);
693
694                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
695                 private static extern bool EnumServicesStatusEx (
696                         IntPtr hSCManager,
697                         int InfoLevel,
698                         SERVICE_TYPE dwServiceType,
699                         SERVICE_STATE_REQUEST dwServiceState,
700                         IntPtr lpServices,
701                         uint cbBufSize,
702                         out uint pcbBytesNeeded,
703                         out uint lpServicesReturned,
704                         ref uint lpResumeHandle,
705                         string pszGroupName);
706
707                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
708                 private static extern bool GetServiceDisplayName (
709                         IntPtr hSCManager,
710                         string lpServiceName,
711                         StringBuilder lpDisplayName,
712                         ref uint lpcchBuffer);
713
714                 [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
715                 private static extern bool GetServiceKeyName (
716                         IntPtr hSCManager,
717                         string lpDisplayName,
718                         StringBuilder lpServiceName,
719                         ref uint lpcchBuffer);
720
721                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
722                 private static extern IntPtr OpenSCManager (
723                         string lpMachineName,
724                         string lpSCDB,
725                         SERVICE_MANAGER_RIGHTS scParameter);
726
727                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
728                 private static extern IntPtr OpenService (
729                         IntPtr SCHANDLE,
730                         string lpSvcName,
731                         SERVICE_RIGHTS dwNumServiceArgs);
732
733                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
734                 private static extern bool QueryServiceConfig (
735                         IntPtr hService,
736                         IntPtr lpServiceConfig,
737                         uint cbBufSize,
738                         out uint pcbBytesNeeded);
739
740                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
741                 private static extern bool QueryServiceStatusEx (
742                         IntPtr serviceHandle,
743                         int InfoLevel,
744                         IntPtr lpBuffer,
745                         int cbBufSize,
746                         out int pcbBytesNeeded);
747
748                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
749                 private static extern bool StartService (
750                         IntPtr SVHANDLE,
751                         int dwNumServiceArgs,
752                         IntPtr [] lpServiceArgVectors);
753
754                 private const int SC_ENUM_PROCESS_INFO = 0;
755                 private const char SC_GROUP_IDENTIFIER = '+';
756                 private const int SC_STATUS_PROCESS_INFO = 0;
757                 private const int SERVICE_NO_CHANGE = -1;
758                 private const int ERROR_MORE_DATA = 234;
759                 private const int ERROR_INSUFFICIENT_BUFFER = 122;
760                 private const int STANDARD_RIGHTS_REQUIRED = 0xf0000;
761                 private const string SERVICES_ACTIVE_DATABASE = "ServicesActive";
762
763                 internal struct QUERY_SERVICE_CONFIG
764                 {
765                         public int dwServiceType;
766                         public int dwStartType;
767                         public int dwErrorControl;
768                         public IntPtr lpBinaryPathName;
769                         public IntPtr lpLoadOrderGroup;
770                         public int dwTagId;
771                         public IntPtr lpDependencies;
772                         public IntPtr lpServiceStartName;
773                         public IntPtr lpDisplayName;
774                 }
775
776                 [Flags]
777                 private enum SERVICE_RIGHTS
778                 {
779                         SERVICE_QUERY_CONFIG = 1,
780                         SERVICE_CHANGE_CONFIG = 2,
781                         SERVICE_QUERY_STATUS = 4,
782                         SERVICE_ENUMERATE_DEPENDENTS = 8,
783                         SERVICE_START = 16,
784                         SERVICE_STOP = 32,
785                         SERVICE_PAUSE_CONTINUE = 64,
786                         SERVICE_INTERROGATE = 128,
787                         SERVICE_USER_DEFINED_CONTROL = 256,
788                         SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SERVICE_QUERY_CONFIG |
789                                 SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS |
790                                 SERVICE_ENUMERATE_DEPENDENTS | SERVICE_START | SERVICE_STOP |
791                                 SERVICE_PAUSE_CONTINUE | SERVICE_INTERROGATE |
792                                 SERVICE_USER_DEFINED_CONTROL)
793                 }
794
795                 private enum SERVICE_MANAGER_RIGHTS : uint
796                 {
797                         STANDARD_RIGHTS_READ = 0x20000,
798                         STANDARD_RIGHTS_WRITE = 0x20000,
799                         STANDARD_RIGHTS_EXECUTE = 0x20000,
800                         STANDARD_RIGHTS_ALL = 0x1F0000,
801
802                         SC_MANAGER_ALL_ACCESS = 0xf003f,
803                         SC_MANAGER_CONNECT = 1,
804                         SC_MANAGER_CREATE_SERVICE = 2,
805                         SC_MANAGER_ENUMERATE_SERVICE = 4,
806                         SC_MANAGER_LOCK = 8,
807                         SC_MANAGER_QUERY_LOCK_STATUS = 16,
808                         SC_MANAGER_MODIFY_BOOT_CONFIG = 32,
809
810                         GENERIC_READ = 0x80000000,
811                         GENERIC_WRITE = 0x40000000,
812                         GENERIC_EXECUTE = 0x20000000,
813                         GENERIC_ALL = 0x10000000
814                 }
815
816                 [StructLayout (LayoutKind.Sequential)]
817                 private struct ENUM_SERVICE_STATUS_PROCESS
818                 {
819                         public static readonly int SizeOf = Marshal.SizeOf (typeof (ENUM_SERVICE_STATUS_PROCESS));
820
821                         [MarshalAs (UnmanagedType.LPWStr)]
822                         public string pServiceName;
823
824                         [MarshalAs (UnmanagedType.LPWStr)]
825                         public string pDisplayName;
826
827                         public SERVICE_STATUS_PROCESS ServiceStatus;
828                 }
829
830                 [StructLayout (LayoutKind.Sequential)]
831                 private struct ENUM_SERVICE_STATUS
832                 {
833                         public static readonly int SizeOf = Marshal.SizeOf (typeof (ENUM_SERVICE_STATUS));
834
835                         [MarshalAs (UnmanagedType.LPWStr)]
836                         public string pServiceName;
837                         [MarshalAs (UnmanagedType.LPWStr)]
838                         public string pDisplayName;
839                         public SERVICE_STATUS ServiceStatus;
840                 }
841
842                 [StructLayout (LayoutKind.Sequential)]
843                 private struct SERVICE_STATUS
844                 {
845                         public ServiceType dwServiceType;
846                         public ServiceControllerStatus dwCurrentState;
847                         public SERVICE_CONTROL_ACCEPTED dwControlsAccepted;
848                         public int dwWin32ExitCode;
849                         public int dwServiceSpecificExitCode;
850                         public uint dwCheckPoint;
851                         public uint dwWaitHint;
852                 }
853
854                 [StructLayout (LayoutKind.Sequential, Pack = 1)]
855                 private struct SERVICE_STATUS_PROCESS
856                 {
857                         public static readonly int SizeOf = Marshal.SizeOf (typeof (SERVICE_STATUS_PROCESS));
858
859                         public ServiceType dwServiceType;
860                         public ServiceControllerStatus dwCurrentState;
861                         public SERVICE_CONTROL_ACCEPTED dwControlsAccepted;
862                         public int dwWin32ExitCode;
863                         public int dwServiceSpecificExitCode;
864                         public int dwCheckPoint;
865                         public int dwWaitHint;
866                         public int dwProcessId;
867                         public int dwServiceFlags;
868                 }
869
870                 private enum SERVICE_TYPE
871                 {
872                         SERVICE_KERNEL_DRIVER = 0x1,
873                         SERVICE_FILE_SYSTEM_DRIVER = 0x2,
874                         SERVICE_ADAPTER = 0x4,
875                         SERVICE_RECOGNIZER_DRIVER = 0x8,
876                         SERVICE_DRIVER = (SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_RECOGNIZER_DRIVER),
877                         SERVICE_WIN32_OWN_PROCESS = 0x10,
878                         SERVICE_WIN32_SHARE_PROCESS = 0x20,
879                         SERVICE_INTERACTIVE_PROCESS = 0x100,
880                         SERVICETYPE_NO_CHANGE = SERVICE_NO_CHANGE,
881                         SERVICE_WIN32 = (SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS),
882                         SERVICE_TYPE_ALL = (SERVICE_WIN32 | SERVICE_ADAPTER | SERVICE_DRIVER | SERVICE_INTERACTIVE_PROCESS)
883                 }
884
885                 private enum SERVICE_START_TYPE
886                 {
887                         SERVICE_BOOT_START = 0x0,
888                         SERVICE_SYSTEM_START = 0x1,
889                         SERVICE_AUTO_START = 0x2,
890                         SERVICE_DEMAND_START = 0x3,
891                         SERVICE_DISABLED = 0x4,
892                         SERVICESTARTTYPE_NO_CHANGE = SERVICE_NO_CHANGE
893                 }
894
895                 private enum SERVICE_ERROR_CONTROL
896                 {
897                         SERVICE_ERROR_IGNORE = 0x0,
898                         SERVICE_ERROR_NORMAL = 0x1,
899                         SERVICE_ERROR_SEVERE = 0x2,
900                         SERVICE_ERROR_CRITICAL = 0x3,
901                         msidbServiceInstallErrorControlVital = 0x8000,
902                         SERVICEERRORCONTROL_NO_CHANGE = SERVICE_NO_CHANGE
903                 }
904
905                 private enum SERVICE_STATE_REQUEST
906                 {
907                         SERVICE_ACTIVE = 0x1,
908                         SERVICE_INACTIVE = 0x2,
909                         SERVICE_STATE_ALL = (SERVICE_ACTIVE | SERVICE_INACTIVE)
910                 }
911
912                 private enum SERVICE_CONTROL_TYPE
913                 {
914                         SERVICE_CONTROL_STOP = 0x1,
915                         SERVICE_CONTROL_PAUSE = 0x2,
916                         SERVICE_CONTROL_CONTINUE = 0x3,
917                         SERVICE_CONTROL_INTERROGATE = 0x4,
918                         SERVICE_CONTROL_SHUTDOWN = 0x5,
919                         SERVICE_CONTROL_PARAMCHANGE = 0x6,
920                         SERVICE_CONTROL_NETBINDADD = 0x7,
921                         SERVICE_CONTROL_NETBINDREMOVE = 0x8,
922                         SERVICE_CONTROL_NETBINDENABLE = 0x9,
923                         SERVICE_CONTROL_NETBINDDISABLE = 0xA,
924                         SERVICE_CONTROL_DEVICEEVENT = 0xB,
925                         SERVICE_CONTROL_HARDWAREPROFILECHANGE = 0xC,
926                         SERVICE_CONTROL_POWEREVENT = 0xD,
927                         SERVICE_CONTROL_SESSIONCHANGE = 0xE
928                 }
929
930                 [Flags]
931                 private enum SERVICE_CONTROL_ACCEPTED
932                 {
933                         SERVICE_ACCEPT_NONE = 0x0,
934                         SERVICE_ACCEPT_STOP = 0x1,
935                         SERVICE_ACCEPT_PAUSE_CONTINUE = 0x2,
936                         SERVICE_ACCEPT_SHUTDOWN = 0x4,
937                         SERVICE_ACCEPT_PARAMCHANGE = 0x8,
938                         SERVICE_ACCEPT_NETBINDCHANGE = 0x10,
939                         SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 0x20,
940                         SERVICE_ACCEPT_POWEREVENT = 0x40,
941                         SERVICE_ACCEPT_SESSIONCHANGE = 0x80
942                 }
943
944                 #endregion PInvoke declaration
945         }
946 }