Guest

uAkServices

By: vcldeveloper on Sep 12th, 2011  |  syntax: Delphi  |  size: 11.42 KB  |  hits: 74  |  expires: Never
download  |  raw  |  embed  |  report abuse
Copied
  1. {*******************************************************************************
  2. /// <author> Ali Keshavarz (vcldeveloper@gmail.com) </author>
  3. /// <date> 09/13/2011 </date>
  4. /// <license>
  5. ///  This work is licensed under the Creative Commons Attribution 3.0 Unported
  6. ///  License. To view a copy of this license, visit
  7. ///  http://creativecommons.org/licenses/by/3.0/
  8. ///  or send a letter to Creative Commons, 171 Second Street, Suite 300,
  9. ///  San Francisco, California, 94105, USA.
  10. /// </license>
  11. *******************************************************************************}
  12.  
  13. unit uAkServices;
  14.  
  15. interface
  16.  
  17. uses
  18.   Generics.Collections;
  19.  
  20. type
  21.   /// Service status values; these items are defined in Windows SDK. For more
  22.   /// info refer to MSDN.
  23.   TAkServiceState = (SERVICE_STOPPED          = $00000001,
  24.                      SERVICE_START_PENDING    = $00000002,
  25.                      SERVICE_STOP_PENDING     = $00000003,
  26.                      SERVICE_RUNNING          = $00000004,
  27.                      SERVICE_CONTINUE_PENDING = $00000005,
  28.                      SERVICE_PAUSE_PENDING    = $00000006,
  29.                      SERVICE_PAUSED           = $00000007 );
  30.  
  31.   /// Service startup mode values; these items are defined in Windows SDK. For
  32.   /// more info refer to MSDN.
  33.   TAkServiceStartMode = (SERVICE_BOOT_START,
  34.                          SERVICE_SYSTEM_START,
  35.                          SERVICE_AUTO_START,
  36.                          SERVICE_DEMAND_START,
  37.                          SERVICE_DISABLED );
  38.  
  39.   /// <summary>
  40.   /// Service information structure. This structure is used by TAkServiceList
  41.   /// class to save info for each individual service.
  42.   /// </summary>
  43.   TAkServiceInfo = record
  44.   public
  45.     /// A path to service executable file.
  46.     BinaryPath : string;
  47.     /// Current service state.
  48.     CurrentState : TAkServiceState;
  49.  
  50.     Description : string;
  51.     /// User-friendly name of service
  52.     DisplayName : string;
  53.     /// Service flags. It can be either zero or SERVICE_RUNS_IN_SYSTEM_PROCESS.
  54.     Flags : Integer;
  55.     /// Actual name of service which is also used as service key name in Registry.
  56.     Name : string;
  57.     /// Process identifier for a running service. If the service is not running
  58.     /// or it is running in System process, then ProcessID will be zero.
  59.     ProcessID : Cardinal;
  60.     /// Startup mode of service.
  61.     StartMode : TAkServiceStartMode;
  62.     /// Username by which service logged into system.
  63.     StartName : string;
  64.   end;
  65.  
  66.   /// Holds a list of TAkServiceInfo reccords as the list of Windows services.
  67.   TAkServiceList = TList<TAkServiceInfo>;
  68.  
  69.   TAkServiceInfoEnumerator = TEnumerator<TAkServiceInfo>;
  70.  
  71.   /// <summary>
  72.   /// Provides a list of installed Windows services on a given machine.
  73.   /// </summary>
  74.   TAkServices = class
  75.   private
  76.     FLastUpdateTime : TDateTime;
  77.     FList : TAkServiceList;
  78.     FMachineName : string;
  79.     function GetCount: Integer;
  80.     function GetItem(Index: Integer): TAkServiceInfo;
  81.     procedure GetServiceConfig(hSCManager: Cardinal; var Service: TAkServiceInfo);
  82.     procedure SetMachineName(const Value: string);
  83.   protected
  84.     procedure InitializeList; virtual;
  85.     procedure InternalRefresh; virtual;
  86.   public
  87.     /// <summary>
  88.     /// Constructor for TAkServices.
  89.     /// </summary>
  90.     /// <param name="AMachineName">
  91.     ///  (in) Name of the machine which its services should be listed. Default = ''
  92.     /// </param>
  93.     /// <param name="."></param>
  94.     constructor Create(const AMachineName: string = '');
  95.     destructor Destroy; override;
  96.     function GetEnumerator: TAkServiceInfoEnumerator;
  97.     /// <summary> Updates list of services. </summary>
  98.     procedure Refresh;
  99.     /// <summary>
  100.     /// Indicates number of services. If the list is not updated yet, it will be
  101.     /// updated first.
  102.     /// </summary>
  103.     property Count: Integer read GetCount;
  104.     /// <summary>
  105.     /// Returns item at the Index position of the list of services.
  106.     /// If the list is not updated yet, it will be updated first.
  107.     /// </summary>
  108.     property Item[Index: Integer]: TAkServiceInfo read GetItem; default;
  109.     /// <summary>
  110.     /// Indicates last update time for the list. It will be updated each time
  111.     /// Refresh method is called.
  112.     /// </summary>
  113.     property LastUpdateTime: TDateTime read FLastUpdateTime;
  114.     /// <summary>Name of the machine which its services should be listed. </summary>
  115.     property MachineName: string read FMachineName write SetMachineName;
  116.   end;
  117.  
  118.  
  119. implementation
  120.  
  121. uses
  122.   SysUtils,
  123.   Windows,
  124.   JwaWinNT,
  125.   JwaWinType,
  126.   JwaWinSvc,
  127.   JwaWinError;
  128.  
  129. { TAkServices }
  130.  
  131. constructor TAkServices.Create(const AMachineName: string);
  132. begin
  133.   inherited Create;
  134.   FMachineName := AMachineName;
  135. end;
  136.  
  137. destructor TAkServices.Destroy;
  138. begin
  139.   System.TMonitor.Enter(Self);
  140.   try
  141.     FList.Free;
  142.     FList := nil;
  143.   finally
  144.     System.TMonitor.Exit(Self);
  145.   end;
  146.  
  147.   inherited;
  148. end;
  149.  
  150. function TAkServices.GetCount: Integer;
  151. begin
  152.   InitializeList;
  153.   Result := FList.Count;
  154. end;
  155.  
  156. function TAkServices.GetEnumerator: TAkServiceInfoEnumerator;
  157. begin
  158.   InitializeList;
  159.   Result := FList.GetEnumerator;
  160. end;
  161.  
  162. function TAkServices.GetItem(Index: Integer): TAkServiceInfo;
  163. begin
  164.   InitializeList;
  165.   Result := FList[Index];
  166. end;
  167.  
  168. procedure TAkServices.InitializeList;
  169. begin
  170.   if not Assigned(FList) then
  171.   begin
  172.     System.TMonitor.Enter(Self);
  173.     try
  174.       FList := TAkServiceList.Create;
  175.       InternalRefresh;
  176.     finally
  177.       System.TMonitor.Exit(Self);
  178.     end;
  179.   end;
  180. end;
  181.  
  182. procedure TAkServices.GetServiceConfig(hSCManager: Cardinal; var Service: TAkServiceInfo);
  183. var
  184.   dwBufNeeded: Cardinal;
  185.   dwBufSize: Cardinal;
  186.   hService : Cardinal;
  187.  
  188.   /// <summary>
  189.   /// Retrieves service binary path, start mode, and user name.
  190.   /// </summary>
  191.   procedure GetRequiredConfig(var Service: TAkServiceInfo);
  192.   var
  193.     lpServiceConfig : LPQUERY_SERVICE_CONFIG;
  194.   begin
  195.     dwBufSize := 0;
  196.     dwBufNeeded := 0;
  197.     /// Retrieve required buffer size.
  198.     QueryServiceConfig(hService, nil, dwBufSize, dwBufNeeded);
  199.     if GetLastError <> ERROR_INSUFFICIENT_BUFFER then
  200.       RaiseLastOSError;
  201.  
  202.     /// Alocate enough buffer size.
  203.     dwBufSize := dwBufNeeded;
  204.     lpServiceConfig := AllocMem(dwBufSize);
  205.     try
  206.       /// Get service configuration
  207.       if not QueryServiceConfig(hService, lpServiceConfig, dwBufSize, dwBufNeeded) then
  208.         RaiseLastOSError;
  209.  
  210.       /// Save configuration data in the record.
  211.       Service.BinaryPath := lpServiceConfig.lpBinaryPathName;
  212.       Service.StartMode := TAkServiceStartMode(lpServiceConfig.dwStartType);
  213.       Service.StartName := lpServiceConfig.lpServiceStartName;
  214.     finally
  215.       FreeMem(lpServiceConfig);
  216.     end;
  217.   end;
  218.  
  219.   /// <summary>
  220.   /// Retrieves service description.
  221.   /// </summary>
  222.   procedure GetOptionalConfig(var Service: TAkServiceInfo);
  223.   var
  224.     lpServiceDescBuff : LPSERVICE_DESCRIPTION;
  225.   begin
  226.     dwBufSize := 0;
  227.     dwBufNeeded := 0;
  228.     /// Retrieve required buffer size.
  229.     QueryServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, nil, dwBufSize, dwBufNeeded);
  230.     if GetLastError <> ERROR_INSUFFICIENT_BUFFER then
  231.       RaiseLastOSError;
  232.  
  233.     /// Alocate enough buffer size.
  234.     dwBufSize := dwBufNeeded;
  235.     lpServiceDescBuff := AllocMem(dwBufSize);
  236.     try
  237.       /// Retrieve service description
  238.       if not QueryServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION,
  239.                                  PByte(lpServiceDescBuff), dwBufSize, dwBufNeeded) then
  240.         RaiseLastOSError;
  241.       /// Save service description in the record.
  242.       if Assigned(lpServiceDescBuff.lpDescription) then
  243.         SetString(Service.Description,
  244.                   lpServiceDescBuff.lpDescription,
  245.                   StrLen(lpServiceDescBuff.lpDescription));
  246.     finally
  247.       FreeMem(lpServiceDescBuff);
  248.     end;
  249.   end;
  250.  
  251. begin
  252.   Assert(hSCManager > 0, 'Service manager is not initialized.');
  253.  
  254.   /// Open a service handle to be used by QueryServiceConfig() and QueryServiceConfig2() functions.
  255.   hService := OpenService(hSCManager,PChar(Service.Name),SERVICE_QUERY_CONFIG);
  256.   if hService = 0 then
  257.     RaiseLastOSError;
  258.   try
  259.     GetRequiredConfig(Service);
  260.     GetOptionalConfig(Service);
  261.   finally
  262.     CloseServiceHandle(hService);
  263.   end;
  264. end;
  265.  
  266. procedure TAkServices.InternalRefresh;
  267. var
  268.   dwBufNeeded: Cardinal;
  269.   dwBufSize: Cardinal;
  270.   dwNumOfServices: ULONG;
  271.   hSCM: SC_HANDLE;
  272.   lpResumeHandle: Cardinal;
  273.   NewItem : TAkServiceInfo;
  274.   pBuf: PBYTE;
  275.   pInfo: LPENUM_SERVICE_STATUS_PROCESS;
  276.   i: Integer;
  277. begin
  278.   Assert(Assigned(FList),'Internal list is not assigned yet!');
  279.  
  280.   System.TMonitor.Enter(FList);
  281.   try
  282.     dwBufSize := 0;
  283.     dwBufNeeded := 0;
  284.     dwNumOfServices := 0;
  285.     lpResumeHandle := 0;
  286.     FList.Clear;
  287.  
  288.     /// We need an open service manager handle to be able to enumerate services.
  289.     hSCM := OpenSCManager(PChar(FMachineName), nil,
  290.                           SC_MANAGER_ENUMERATE_SERVICE or SC_MANAGER_CONNECT);
  291.     if (hSCM = 0) then
  292.       RaiseLastOSError;
  293.  
  294.     try
  295.       /// First check how much buffer is needed by passing dwBufSize as zero,
  296.       /// and pBuf as nil.
  297.       EnumServicesStatusEx(hSCM, SC_ENUM_PROCESS_INFO, SERVICE_WIN32,
  298.                            SERVICE_STATE_ALL, nil, dwBufSize, dwBufNeeded,
  299.                            dwNumOfServices, lpResumeHandle, nil);
  300.       if (dwBufNeeded < 1) then
  301.         RaiseLastOSError;
  302.  
  303.       /// Alocate enough space for the buffer.
  304.       dwBufSize := dwBufNeeded;
  305.       pBuf := AllocMem(dwBufSize);
  306.       try
  307.         /// Retrieve services list
  308.         if not EnumServicesStatusEx(hSCM, SC_ENUM_PROCESS_INFO, SERVICE_WIN32,
  309.                                     SERVICE_STATE_ALL, pBuf, dwBufSize, dwBufNeeded,
  310.                                     dwNumOfServices, lpResumeHandle, nil) then
  311.           RaiseLastOSError;
  312.  
  313.         /// Type casting pBuf to LPENUM_SERVICE_STATUS_PROCESS record is necessary
  314.         ///  so that we can access each service info in the buffer as a record.
  315.         pInfo := LPENUM_SERVICE_STATUS_PROCESS(pBuf);
  316.         /// Write services list
  317.         for i := 0 to dwNumOfServices-1 do
  318.         begin
  319.           NewItem.Name := pInfo.lpServiceName;
  320.           NewItem.DisplayName := pInfo.lpDisplayName;
  321.           NewItem.Flags := pInfo.ServiceStatusProcess.dwServiceFlags;
  322.           NewItem.ProcessID := pInfo.ServiceStatusProcess.dwProcessId;
  323.           NewItem.CurrentState := TAkServiceState(pInfo.ServiceStatusProcess.dwCurrentState);
  324.           GetServiceConfig(hSCM, NewItem);
  325.           FList.Add(NewItem);
  326.  
  327.           /// Go to next record in the buffer. Compiler will increment pInfo pointer
  328.           ///  according to size LPENUM_SERVICE_STATUS_PROCESS record.
  329.           Inc(pInfo);
  330.         end;
  331.         FLastUpdateTime := Now;
  332.       finally
  333.         FreeMem(pBuf);
  334.       end;
  335.     finally
  336.       CloseServiceHandle(hSCM);
  337.     end;
  338.   finally
  339.     System.TMonitor.Exit(FList);
  340.   end;
  341. end;
  342.  
  343. procedure TAkServices.Refresh;
  344. begin
  345.   /// If FList is not created yet, InitializeList will create it and calls InternalRefresh
  346.   /// automatically, but if the list is already created, just calling InternalRefresh
  347.   /// is enough.
  348.   if not Assigned(FList) then
  349.     InitializeList
  350.   else
  351.     InternalRefresh;
  352. end;
  353.  
  354. procedure TAkServices.SetMachineName(const Value: string);
  355. begin
  356.   if not SameText(FMachineName, Value) then
  357.   begin
  358.     FMachineName := Value;
  359.     Refresh;
  360.   end;
  361. end;
  362.  
  363.  
  364. end.