Delphi 11和WinAPI.WinSvc(枚举服务状态函数)

hec6srdp  于 2022-11-04  发布在  其他
关注(0)|答案(1)|浏览(133)

我用下面的代码得到了一个Windows服务列表。它在 Delphi 10.2.3和更早的版本中运行得很好:

uses WinSvc;

//-------------------------------------
// Get a list of services
//
// return TRUE if successful
//
// sMachine:
//   machine name, ie: \SERVER
//   empty = local machine
//
// dwServiceType
//   SERVICE_WIN32,
//   SERVICE_DRIVER or
//   SERVICE_TYPE_ALL
//
// dwServiceState
//   SERVICE_ACTIVE,
//   SERVICE_INACTIVE or
//   SERVICE_STATE_ALL
//
// slServicesList
//   TStrings variable to storage
//
function ServiceGetList(
  sMachine : string;
  dwServiceType,
  dwServiceState : DWord;
  slServicesList : TStrings )
  : boolean;
const
  //
  // assume that the total number of
  // services is less than 4096.
  // increase if necessary
  cnMaxServices = 4096;

type
  TSvcA = array[0..cnMaxServices]
          of TEnumServiceStatus;
  PSvcA = ^TSvcA;

var
  //
  // temp. use
  j : integer;

  //
  // service control
  // manager handle
  schm          : SC_Handle;

  //
  // bytes needed for the
  // next buffer, if any
  nBytesNeeded,

  //
  // number of services
  nServices,

  //
  // pointer to the
  // next unread service entry
  nResumeHandle : DWord;

  //
  // service status array
  ssa : PSvcA;
begin
  Result := false;

  // connect to the service
  // control manager
  schm := OpenSCManager(
    PChar(sMachine),
    Nil,
    SC_MANAGER_ALL_ACCESS);

  // if successful...
  if(schm > 0)then
  begin
    nResumeHandle := 0;

    New(ssa);

    EnumServicesStatus(
      schm,
      dwServiceType,
      dwServiceState,
      ssa^[0],
      SizeOf(ssa^),
      nBytesNeeded,
      nServices,
      nResumeHandle );

    //
    // assume that our initial array
    // was large enough to hold all
    // entries. add code to enumerate
    // if necessary.
    //

    for j := 0 to nServices-1 do
    begin
      slServicesList.
        Add( StrPas(
          ssa^[j].lpDisplayName ) );
    end;

    Result := true;

    Dispose(ssa);

    // close service control
    // manager handle
    CloseServiceHandle(schm);
  end;
end;

要将所有Windows服务的列表放入名为ListBox1的ListBox中,请执行以下操作:

ServiceGetList( '',
  SERVICE_WIN32,
  SERVICE_STATE_ALL,
  ListBox1.Items );

我尝试在 Delphi 10.4和Delphi 11中使用相同的代码,但是EnumServicesStatus函数有一个问题:
[dcc32错误]单元1.pas(145):E2010不兼容的类型:'LPENUM服务状态'和'枚举服务状态'
当我尝试使用LPENUM_SERVICE_STATUSW而不是TEnumServiceStatus时:

type
  TSvcA = array[0..cnMaxServices]
          of LPENUM_SERVICE_STATUSW;// instead TEnumServiceStatus;

出现“访问冲突”错误。
也许关键点在Winapi.WinSvc.pas中的外部函数中:

function EnumServicesStatus;    external advapi32 name 'EnumServicesStatusW';
ffdz8vbo

ffdz8vbo1#

预先分配这么大的数组并不是一个好主意。你不能假设PC实际上安装了多少服务。让EnumServicesStatus()告诉你为数组分配多少服务。
此外,您还必须考虑到可能需要多次调用EnumServicesStatus()才能获得所有状态。
现在,关于编译器问题-实际的EnumServiceStatus() API需要指向ENUM_SERVICE_STATUS记录数组的指针(在WinSvc单位中别名为TEnumServiceStatus),而不是LPENUM_SERVICE_STATUS指针数组。在早期版本的 Delphi 中,Winsvc单元声明了EnumServiceStatusW(),以获取对数组中第一个ENUM_SERVICE_STATUSvar引用。但显然,它后来被重新声明为获取指向第一个ENUM_SERVICE_STATUS的指针,以匹配实际API。
所以,说了这么多,试着像这样:

uses
  WinSvc;

{$IFDEF CONDITIONALEXPRESSIONS}
  {$IF CompilerVersion >= 34}
    {$DEFINE lpServices_Param_Is_Pointer}
  {$IFEND}
{$ENDIF}

function ServiceGetList(
  const sMachine : string;
  dwServiceType,
  dwServiceState : DWord;
  slServicesList : TStrings )
  : Boolean;
var
  j : integer;
  schm : SC_Handle;
  nBytesNeeded,
  nServices,
  nResumeHandle : DWord;
  buffer : array of Byte;
  ssa, ss : PEnumServiceStatus;
begin
  Result := False;

  schm := OpenSCManager(
    PChar(sMachine),
    nil,
    SC_MANAGER_CONNECT or SC_MANAGER_ENUMERATE_SERVICE);

  if (schm <> 0) then
  try
    nResumeHandle := 0;
    if not EnumServicesStatus(
      schm,
      dwServiceType,
      dwServiceState,
      {$IFDEF lpServices_Param_Is_Pointer}nil{$ELSE}PEnumServiceStatus(nil)^{$ENDIF},
      0,
      nBytesNeeded,
      nServices,
      nResumeHandle) then
    begin
      if (GetLastError() <> ERROR_INSUFFICIENT_BUFFER) and (GetLastError() <> ERROR_MORE_DATA) then
      begin
        Exit;
      end;
      SetLength(buffer, nBytesNeeded);
      ssa := PEnumServiceStatus(buffer);
      if not EnumServicesStatus(
        schm,
        dwServiceType,
        dwServiceState,
        ssa{$IFNDEF lpServices_Param_Is_Pointer}^{$ENDIF},
        Length(buffer),
        nBytesNeeded,
        nServices,
        nResumeHandle) then
      begin
        Exit;
      end;
    end;
    if (nServices > 0) then
    begin
      ss := ssa;
      for j := 0 to nServices-1 do
      begin
        slServicesList.Add(ss.lpDisplayName);
        Inc(ss);
      end;
    end;
  finally
    CloseServiceHandle(schm);
  end;
  Result := True;
end;

相关问题