C++ 当两次调用EnumServicesStatusEx()时,我仍然会在C++;
我在代码中调用EnumServicesStatusEx()两次,第一次应该失败,并将正确的缓冲区大小放入dwBuffNeeded中,以便在第二次调用时,缓冲区大小应该正确。但是,有时,并非总是在第二次呼叫后我仍然会收到更多的错误数据。你知道为什么吗?谢谢C++ 当两次调用EnumServicesStatusEx()时,我仍然会在C++;,c++,winapi,msdn,C++,Winapi,Msdn,我在代码中调用EnumServicesStatusEx()两次,第一次应该失败,并将正确的缓冲区大小放入dwBuffNeeded中,以便在第二次调用时,缓冲区大小应该正确。但是,有时,并非总是在第二次呼叫后我仍然会收到更多的错误数据。你知道为什么吗?谢谢 DWORD pId=GetCurrentProcessId(); SC_HANDLE hSCM = NULL; PUCHAR pBuf = NULL; ULONG dwBufSize = 0x00;
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为尚未迭代的服务假定的剩余字节的输出参数
- 最重要的是,有一个输出参数告诉您恢复点
让我展示完整的真实世界函数,工作得相当好,并有一些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);
}