c++ - 两次调用 EnumServicesStatusEx() 时,我仍然在 C++ 中得到 EROR_MORE_DATA

标签 c++ winapi msdn

我在我的代码中调用了 EnumServicesStatusEx() 两次,第一次应该失败并将正确的缓冲区大小放入 dwBuffNeeded 中,这样当我第二次调用它时缓冲区大小应该是正确的。但是,有时,在第二次通话后,我并不总是会收到 ERROR_MORE_DATA。任何想法为什么?谢谢

DWORD pId=GetCurrentProcessId();
    SC_HANDLE hSCM    = NULL;
    PUCHAR  pBuf    = NULL;
    ULONG  dwBufSize   = 0x00;
    ULONG  dwBufNeed   = 0x00;
    ULONG  dwNumberOfService = 0x00;
    LPENUM_SERVICE_STATUS_PROCESS pInfo = NULL;

    hSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT );

    if (hSCM == NULL)
    {
        GetCustomLog().Log( SV_ERROR, 10004807, "Could not open Service Control Manager: %s", GetLastOSErrorString().c_str() );
        return;
    }

    //Query services once to get correct buffer size, always fails
    if ( EnumServicesStatusEx(
        hSCM,
        SC_ENUM_PROCESS_INFO,
        SERVICE_WIN32, 
        SERVICE_ACTIVE,
        NULL,
        dwBufSize,
        &dwBufNeed,
        &dwNumberOfService,
        NULL,
        NULL) == 0 )
    {

        DWORD err = GetLastError();
        if ( ERROR_MORE_DATA == err )
        {
            dwBufSize = dwBufNeed + 0x10;
            pBuf  = (PUCHAR) malloc(dwBufSize);

            //Query services again with correct buffer size
            if ( EnumServicesStatusEx(
                hSCM,
                SC_ENUM_PROCESS_INFO,
                SERVICE_WIN32, 
                SERVICE_ACTIVE,
                pBuf,
                dwBufSize,
                &dwBufNeed,
                &dwNumberOfService,
                NULL,
                NULL ) == 0 )

            {
//FAILS HERE
                GetCustomLog().Log( SV_ERROR, 10004808, "Could not enumerate services, error: %s", GetLastOSErrorString().c_str() );
                free(pBuf);
                return;
            }
        }
        else
        {
            GetCustomLog().Log( SV_ERROR, 10004809, "Could not enumerate services, error: %s", GetLastOSErrorString().c_str() );
            return;
        }

最佳答案

我也有同样的问题,但在查询 SERVICE_STATE_ALL 时,我认为每次调用都需要相同数量的内存(除非已安装/卸载服务)。仅使用 pcbBytesNeeded 参数中返回大小的缓冲区重试是不够的:

BOOL WINAPI EnumServicesStatusEx(
  _In_         SC_HANDLE hSCManager,
  _In_         SC_ENUM_TYPE InfoLevel,
  _In_         DWORD dwServiceType,
  _In_         DWORD dwServiceState,
  _Out_opt_    LPBYTE lpServices,
  _In_         DWORD cbBufSize,
  _Out_        LPDWORD pcbBytesNeeded,
  _Out_        LPDWORD lpServicesReturned,
  _Inout_opt_  LPDWORD lpResumeHandle,
  _In_opt_     LPCTSTR pszGroupName
);

与其他 WIN32 API 调用不同,这不是返回所需字节的绝对数量,而是返回相对于 cbBufSize 参数所需的额外 字节。作为实验,我特意提供了一个较小的缓冲区,并且每次都将其加倍,以找出系统将在 pcbBytesNeeded 中返回什么作为响应。在这个系统上 sizeof(ENUM_SERVICE_STATUS_PROCESS) 是 56 字节。最后一行是导致调用成功的最小缓冲区。

+-----------+----------------+
| cbBufSize | pcbBytesNeeded | 
+-----------+----------------+
|      112  |         37158  |
|      224  |         37013  |
|      448  |         36766  |
|      896  |         36374  |
|     1792  |         35280  |
|     3584  |         33202  |
|     7168  |         28972  |
|    14336  |         20765  |
|    28672  |          4215  |
|    32032  |             0  |
+-----------+----------------+

您可以看到每一行粗略地加起来就是所需的缓冲区大小,而且就系统而言,所需的缓冲区大小并不是很容易预测。本例调用成功返回的服务条目数为233条,仅需13048字节。事实证明,ENUM_SERVICE_STATUS_PROCESS lpDisplayName 和 lpServiceName 指针指向的字符串只是存储在所提供缓冲区的尾端(我一直想知道它们的支持在哪里,以及为什么它稳定并且不需要单独存储释放)。无论如何,这解释了有些不确定的响应以及奇数:系统可能有不适合的当前条目,并且确切知道它需要多少大小,但猜测其余部分。

下面的代码是可靠的,通常只需要两次调用,但有时可以调用三次(即使使用 SERVICE_STATE_ALL)。我不知道为什么需要三个,系统的低估一次会持续几分钟,但最终会自行解决。我从未见过它需要四个调用。

int EnumerateAllServices(SC_HANDLE hSCM) {
  void* buf = NULL;
  DWORD bufSize = 0;
  DWORD moreBytesNeeded, serviceCount;
  for (;;) {
    printf("Calling EnumServiceStatusEx with bufferSize %d\n", bufSize);
    if (EnumServicesStatusEx(
        hSCM,
        SC_ENUM_PROCESS_INFO,
        SERVICE_WIN32,
        SERVICE_STATE_ALL,
        (LPBYTE)buf,
        bufSize,
        &moreBytesNeeded,
        &serviceCount,
        NULL,
        NULL)) {
      ENUM_SERVICE_STATUS_PROCESS* services = (ENUM_SERVICE_STATUS_PROCESS*)buf;
      for (DWORD i = 0; i < serviceCount; ++i) {
        printf("%s\n", services[i].lpServiceName);
      }
      free(buf);
      return 0;
    }
    int err = GetLastError();
    if (ERROR_MORE_DATA != err) {
      free(buf);
      return err;
    }
    bufSize += moreBytesNeeded;
    free(buf);
    buf = malloc(bufSize);
  }
}

“+=”是一个“技巧”,并没有非常清楚地记录(但事后看来,我现在理解了 MSDN 对 pcbBytesNeeded 参数的解释中的细微差别):

pcbBytesNeeded [out]

    A pointer to a variable that receives the number of bytes
    needed to return the remaining service entries, if the buffer
    is too small.

关于c++ - 两次调用 EnumServicesStatusEx() 时,我仍然在 C++ 中得到 EROR_MORE_DATA,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14756347/

相关文章:

c++ - 太阳系项目,C++。无法获得负加速度(行星不会绕回)

c++ - 具有结构键的 std::map 的高效比较器

c++ - AlphaBlend 生成不正确的颜色

.net - 以编程方式查询 MSDN?

c++ - 类初始化 - 属性没有默认构造函数

c - DestroyWindow() 是否从消息队列中删除窗口的消息?

c - 如何更改 Rich Edit 控件中的下划线颜色 (Win32/C)

c# - msdn网站上成员和方法的区别?

windows - 如何在 Python/Go/C/Batch 中获取 Windows 中的扩展文件属性?

c++ - 虚基类子对象之前是什么?