Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/162.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 当两次调用EnumServicesStatusEx()时,我仍然会在C++;_C++_Winapi_Msdn - Fatal编程技术网

C++ 当两次调用EnumServicesStatusEx()时,我仍然会在C++;

C++ 当两次调用EnumServicesStatusEx()时,我仍然会在C++;,c++,winapi,msdn,C++,Winapi,Msdn,我在代码中调用EnumServicesStatusEx()两次,第一次应该失败,并将正确的缓冲区大小放入dwBuffNeeded中,以便在第二次调用时,缓冲区大小应该正确。但是,有时,并非总是在第二次呼叫后我仍然会收到更多的错误数据。你知道为什么吗?谢谢 DWORD pId=GetCurrentProcessId(); SC_HANDLE hSCM = NULL; PUCHAR pBuf = NULL; ULONG dwBufSize = 0x00;

我在代码中调用EnumServicesStatusEx()两次,第一次应该失败,并将正确的缓冲区大小放入dwBuffNeeded中,以便在第二次调用时,缓冲区大小应该正确。但是,有时,并非总是在第二次呼叫后我仍然会收到更多的错误数据。你知道为什么吗?谢谢

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;
        }

我也有同样的问题,但在查询服务\u STATE\u 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参数所需的额外字节数。作为一个实验,我特意提供了一个很小的缓冲区,每次只需将缓冲区增加一倍,就可以知道系统将返回多少pcbbytes作为响应。在这个系统上,sizeof(枚举服务状态进程)是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_进程lpDisplayName和lpServiceName指针指向的字符串只存储在提供的缓冲区的末尾(我一直想知道这些指针的备份在哪里,为什么它是稳定的,不需要单独释放)。无论如何,这解释了一些不确定的响应以及奇数:系统可能有不适合的当前条目,并且确切地知道它需要多少大小,但是猜测剩余的

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

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);
  }
}

由于服务信息不是静态的,理论上,对
EnumServicesStatusEx()
的两次调用之间可能发生任何事情,因此最好以迭代方式使用此API

正如Kat Marsen所建议的,您当然可以反复尝试获取所有服务(只要API返回ERROR\u MORE\u数据,就循环并增长缓冲区)。
但这有两个缺点:

  • 您告诉SCM不断地填写它实际上已经发送给您的信息(即使是非常罕见的信息,我实际上希望避免)
  • 根据MSDN文档,缓冲区的硬上限为256K字节。正如我在上面的评论中提到的,后者几乎不太可能;作为一名程序员,你仍然想稳操胜券
  • 迭代法 相反,我们只能假设负责EnumServicesStatusEx()的作者有理由这样设计它:

    • 一种缓冲区,如果有效且至少足以满足一种服务,则立即填充
    • SCM为尚未迭代的服务假定的剩余字节的输出参数
    • 最重要的是,有一个输出参数告诉您恢复点
    恢复点和余数都允许使用更简单的迭代方法

    让我展示完整的真实世界函数,工作得相当好,并有一些C++的好东西。

    chunk\u fetch\u all\u win32\u services
    预先分配一些内存,为每个块调用回调,并让回调决定是消耗内存还是保留内存以便重用。
    enum_all_win32_服务
    使用每个区块,然后为每个服务调用回调

    #include <type_traits>
    #include <memory>
    #include <stddef.h>
    #include <Windows.h>
    
    using std::unique_ptr;
    using std::error_code;
    using std::system_error;
    using std::system_category;
    using std::function;
    using std::make_unique;
    
    
    /** @short Fetch all win32 services in chunks.
    
        This function fetches all services of type `SERVICE_WIN32=(SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS)`,
        in chunks as decided by the SCM (refer to EnumServicesStatusEx()).
    
        @param scmHandle Handle to the SCM with access right SC_MANAGER_ENUMERATE_SERVICE
        @param stateMask One of SERVICE_ACTIVE, SERVICE_INACTIVE, SERVICE_STATE_ALL
        In the callback you can decide whether you want to consume the passed memory or leave
        it in order to be reused.
    
        @note This is most probably rare but expect the callback being invoked multiple times
        in case the SCM didn't return information about all services at once.
     */
    bool chunk_fetch_all_win32_services(SC_HANDLE scmHandle, DWORD stateMask, const function<bool(unique_ptr<ENUM_SERVICE_STATUS_PROCESS[]>&, DWORD /*nServices*/)>& cb, error_code* ec)
    {
        // (optionally) preallocate
        // (the amount stems from Win XP's upper size of 64k;
        //  on a typical Win7 system there are about 220 services (about 34k))
        unique_ptr<BYTE[]> mem = make_unique<BYTE[]>(64 * 1024);
        DWORD nAllocated = 64 * 1024, nRemaining;
        DWORD resumePoint = 0;
        do
        {
            DWORD nServices;
            if (!EnumServicesStatusEx(scmHandle, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, stateMask, mem.get(), nAllocated, &nRemaining, &nServices, &resumePoint, nullptr))
            {
                const int errorCode = GetLastError();
                if (errorCode != ERROR_MORE_DATA)
                {
                    if (!ec)
                        throw system_error{ errorCode, system_category(), "Can't enumerate services" };
                    ec->assign(errorCode, system_category());
                    return false;
                }
            }
    
            if (nServices)
            {
                // memory initialized, transfer ownership to typed pointer
                unique_ptr<ENUM_SERVICE_STATUS_PROCESS[]> cache{ LPENUM_SERVICE_STATUS_PROCESS(mem.release()) };
                if (!cb(cache, nServices))
                    // early bail-out requested
                    return true;
    
                // document that the typed pointer can be 'downgraded' again without the need to destroy objects
                static_assert(std::is_trivially_destructible_v<ENUM_SERVICE_STATUS_PROCESS>);
                // possibly reuse existing buffer
                mem.reset(PBYTE(cache.release()));
            }
    
            if (nRemaining)
            {
                // re-allocate if buffer too small or consumed by callback
                if (!mem || nAllocated < nRemaining)
                    mem = make_unique<BYTE[]>(nRemaining),
                    nAllocated = nRemaining;
            }
    
            // loop as long as there are more services to be enumerated
        } while (nRemaining);
    
        return true;
    }
    
    /** @short Enumerate all win32 services.
    
        This function enumerates all services of type `SERVICE_WIN32=(SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS)`.
    
        @param scmHandle Handle to the SCM with access right SC_MANAGER_ENUMERATE_SERVICE
        @param stateMask One of SERVICE_ACTIVE, SERVICE_INACTIVE, SERVICE_STATE_ALL
        @param cb Callback receiving process information of a single service process information and the number of services.
    
        @note This is most probably rare but expect the number of services passed to the callback to change
        in case the SCM didn't return information about all services at once; if, then this of course happens
        after the provided number of information items have been iterated.
     */
    bool enum_all_win32_services(SC_HANDLE scmHandle, DWORD stateMask, const function<bool(ENUM_SERVICE_STATUS_PROCESS&, DWORD /*nServices*/)>& cb, error_code* ec)
    {
        return chunk_fetch_all_win32_services(scmHandle, stateMask, [&cb](unique_ptr<ENUM_SERVICE_STATUS_PROCESS[]>& cache, DWORD nServices)
        {
            for (size_t idx = 0; idx < nServices; ++idx)
            {
                if (!cb(cache[idx], nServices))
                    // early bail-out requested
                    return true;
            }
    
            return true;
        }, ec);
    }
    
    #包括
    #包括
    #包括
    #包括
    使用std::unique\u ptr;
    使用std::error\u代码;
    使用std::系统错误;
    使用std::system_类别;
    使用std::函数;
    使用std::使_唯一;
    /**@short以块的形式获取所有win32服务。
    此函数获取类型为“服务\u WIN32=(服务\u WIN32\u自有\u进程|服务\u WIN32\u共享\u进程)”的所有服务,
    以SCM决定的块为单位(请参阅EnumServicesStatusEx()。
    @参数SCM使用访问权限SCU管理器枚举服务处理SCM的句柄
    @参数状态掩码服务\活动、服务\非活动、服务\状态\全部中的一个
    在回调中,您可以决定是使用传递的内存还是离开
    这是为了重复使用。
    @注意,这很可能很少见,但预期回调会被多次调用
    如果SCM没有立即返回所有服务的信息。
    */
    bool chunk_fetch_all_win32_服务(SC_HANDLE scmHandle、DWORD状态掩码、常量函数和cb、错误代码*ec)
    {
    //(可选)预先分配
    //(金额来源于Win XP的上限64k;
    //在典型的Win7系统上,大约有220个服务(约34k))
    unique_ptr mem=使_唯一(64*1024);
    DWORD nAllocated=64*1024,n保留;
    DWORD恢复点=0;
    做
    {
    德沃德N服务;
    如果(!EnumServicesStatusEx(scmHandle、SC_ENUM_PROCESS_INFO、SERVICE_WIN32、stateMask、mem.get()、nAllocated、&nRemaining、&nServices、&resumePoint、nullptr))
    {
    const int errorCode=GetLastError();
    if(errorCode!=错误\u更多\u数据)
    {
    如果(!ec)
    抛出system_error{errorCode,system_category(),“无法枚举服务”};
    ec->assign(错误代码,系统类别());
    返回false;
    }
    }
    if(N服务)
    {
    //内存已初始化,将所有权转移到类型指针
    唯一的\u ptr缓存{LPENUM\u服务\u STAT
    
    #include <type_traits>
    #include <memory>
    #include <stddef.h>
    #include <Windows.h>
    
    using std::unique_ptr;
    using std::error_code;
    using std::system_error;
    using std::system_category;
    using std::function;
    using std::make_unique;
    
    
    /** @short Fetch all win32 services in chunks.
    
        This function fetches all services of type `SERVICE_WIN32=(SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS)`,
        in chunks as decided by the SCM (refer to EnumServicesStatusEx()).
    
        @param scmHandle Handle to the SCM with access right SC_MANAGER_ENUMERATE_SERVICE
        @param stateMask One of SERVICE_ACTIVE, SERVICE_INACTIVE, SERVICE_STATE_ALL
        In the callback you can decide whether you want to consume the passed memory or leave
        it in order to be reused.
    
        @note This is most probably rare but expect the callback being invoked multiple times
        in case the SCM didn't return information about all services at once.
     */
    bool chunk_fetch_all_win32_services(SC_HANDLE scmHandle, DWORD stateMask, const function<bool(unique_ptr<ENUM_SERVICE_STATUS_PROCESS[]>&, DWORD /*nServices*/)>& cb, error_code* ec)
    {
        // (optionally) preallocate
        // (the amount stems from Win XP's upper size of 64k;
        //  on a typical Win7 system there are about 220 services (about 34k))
        unique_ptr<BYTE[]> mem = make_unique<BYTE[]>(64 * 1024);
        DWORD nAllocated = 64 * 1024, nRemaining;
        DWORD resumePoint = 0;
        do
        {
            DWORD nServices;
            if (!EnumServicesStatusEx(scmHandle, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, stateMask, mem.get(), nAllocated, &nRemaining, &nServices, &resumePoint, nullptr))
            {
                const int errorCode = GetLastError();
                if (errorCode != ERROR_MORE_DATA)
                {
                    if (!ec)
                        throw system_error{ errorCode, system_category(), "Can't enumerate services" };
                    ec->assign(errorCode, system_category());
                    return false;
                }
            }
    
            if (nServices)
            {
                // memory initialized, transfer ownership to typed pointer
                unique_ptr<ENUM_SERVICE_STATUS_PROCESS[]> cache{ LPENUM_SERVICE_STATUS_PROCESS(mem.release()) };
                if (!cb(cache, nServices))
                    // early bail-out requested
                    return true;
    
                // document that the typed pointer can be 'downgraded' again without the need to destroy objects
                static_assert(std::is_trivially_destructible_v<ENUM_SERVICE_STATUS_PROCESS>);
                // possibly reuse existing buffer
                mem.reset(PBYTE(cache.release()));
            }
    
            if (nRemaining)
            {
                // re-allocate if buffer too small or consumed by callback
                if (!mem || nAllocated < nRemaining)
                    mem = make_unique<BYTE[]>(nRemaining),
                    nAllocated = nRemaining;
            }
    
            // loop as long as there are more services to be enumerated
        } while (nRemaining);
    
        return true;
    }
    
    /** @short Enumerate all win32 services.
    
        This function enumerates all services of type `SERVICE_WIN32=(SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS)`.
    
        @param scmHandle Handle to the SCM with access right SC_MANAGER_ENUMERATE_SERVICE
        @param stateMask One of SERVICE_ACTIVE, SERVICE_INACTIVE, SERVICE_STATE_ALL
        @param cb Callback receiving process information of a single service process information and the number of services.
    
        @note This is most probably rare but expect the number of services passed to the callback to change
        in case the SCM didn't return information about all services at once; if, then this of course happens
        after the provided number of information items have been iterated.
     */
    bool enum_all_win32_services(SC_HANDLE scmHandle, DWORD stateMask, const function<bool(ENUM_SERVICE_STATUS_PROCESS&, DWORD /*nServices*/)>& cb, error_code* ec)
    {
        return chunk_fetch_all_win32_services(scmHandle, stateMask, [&cb](unique_ptr<ENUM_SERVICE_STATUS_PROCESS[]>& cache, DWORD nServices)
        {
            for (size_t idx = 0; idx < nServices; ++idx)
            {
                if (!cb(cache[idx], nServices))
                    // early bail-out requested
                    return true;
            }
    
            return true;
        }, ec);
    }