首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Delphi11和WinAPI.WinSvc (函数EnumServiceState)

Delphi11和WinAPI.WinSvc (函数EnumServiceState)
EN

Stack Overflow用户
提问于 2022-06-20 01:17:48
回答 1查看 289关注 0票数 1

我使用下面的代码获取Windows服务的列表。它适用于Delphi 10.2.3和更早版本:

代码语言:javascript
运行
复制
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服务的列表输入名为ListBox ListBox1

代码语言:javascript
运行
复制
ServiceGetList( '',
  SERVICE_WIN32,
  SERVICE_STATE_ALL,
  ListBox1.Items );

我尝试在Delphi10.4和Delphi11中使用相同的代码,但是EnumServicesStatus函数有一个问题:

dcc32错误单位1.PAS(145):E2010不兼容类型:“LPENUM_SERVICE_STATUSW”和“_ENUM_SERVICE_STATUSW”

当我尝试LPENUM_SERVICE_STATUSW而不是TEnumServiceStatus

代码语言:javascript
运行
复制
type
  TSvcA = array[0..cnMaxServices]
          of LPENUM_SERVICE_STATUSW;// instead TEnumServiceStatus;

我发现了一个“访问违规”错误。

也许关键在于Winapi.WinSvc.pas中的外部函数

代码语言:javascript
运行
复制
function EnumServicesStatus;    external advapi32 name 'EnumServicesStatusW';
EN

Stack Overflow用户

回答已采纳

发布于 2022-06-20 01:33:32

预先分配这样大的数组不是个好主意。您不能假设PC实际安装了多少服务。让EnumServicesStatus()告诉您要为数组分配多少。

此外,您还必须考虑可能需要多次调用EnumServicesStatus()才能获得所有状态。

现在,关于编译器问题--实际的EnumServiceStatus() API需要一个指向ENUM_SERVICE_STATUS记录数组(在WinSvc单元中别名为TEnumServiceStatus )的指针,而不是一个LPENUM_SERVICE_STATUS指针数组。在Delphi的早期版本中,Winsvc单元声明EnumServiceStatusW()对数组中的第一个ENUM_SERVICE_STATUS进行var引用。但是很明显,它已经被重新声明,取而代之的是一个指向第一个ENUM_SERVICE_STATUS的指针,以匹配实际的API。

所以,用这句话来说,尝试更像这样的东西:

代码语言:javascript
运行
复制
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;
票数 3
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72681375

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档