CollectPerformanceData 구현
시스템에서 OpenPerformanceData 함수를 성공적으로 호출한 후 CollectPerformanceData 함수를 호출하여 카운터 데이터를 수집합니다. 공급자가 쿼리된 개체를 지원하는 경우 연결된 서비스, 드라이버 또는 애플리케이션에 연결하고 카운터 데이터를 요청합니다.
매개 변수는 pQuery
다음 중 하나입니다.
- 하나 이상의 소수점 정수의 공백으로 구분된 목록: 목록에서 지원되는 개체 형식에 대한 성능 데이터를 수집합니다.
Global
: 범주에 포함된 개체를 제외하고 지원되는 모든 로컬 개체 형식에Costly
대한 성능 데이터를 수집합니다.Costly
: 프로세서 시간 또는 메모리 사용량 측면에서 데이터를 수집하는 데 비용이 많이 드는 지원되는 모든 로컬 개체 형식에 대한 성능 데이터를 수집합니다. (사용되지 않음: 일반적으로 이 범주에는 개체 형식이 없어야 합니다.)Foreigh
: 지원되는 모든 원격 개체 형식에 대한 성능 데이터를 수집합니다. (사용되지 않음: 일반적으로 이 범주에는 개체 형식이 없어야 합니다.)MetadataGlobal
(new): 범주에 포함된 개체를 제외하고 지원되는 모든 로컬 개체 형식에 대한 메타데이터를Costly
수집합니다. 이는 둘 중 하나PERF_METADATA_MULTIPLE_INSTANCES
또는 로 설정해야 하며 결과에 블록을 포함PERF_INSTANCE_DEFINITION
해서는 안 된다는NumInstances
점을 제외하고 동일합니다Global
PERF_METADATA_NO_INSTANCES
.MetadataCostly
(new): 범주에 포함된 지원되는 모든 로컬 개체 형식에 대한 메타데이터를Costly
수집합니다. 이는 둘 중 하나PERF_METADATA_MULTIPLE_INSTANCES
또는 로 설정해야 하며 결과에 블록을 포함PERF_INSTANCE_DEFINITION
해서는 안 된다는NumInstances
점을 제외하고 동일합니다Costly
PERF_METADATA_NO_INSTANCES
.
MetadataGlobal
Windows 10 20H1 이상에서는 쿼리 유형과 MetadataCostly
쿼리 유형이 새로 추가되었습니다. Windows는 공급자가 레지스트리 값을 추가한 경우에만 메타데이터 쿼리를 HKLM\CurrentControlSet\Services\<provider-name>\Performance\Collect Supports Metadata
만듭니다. 공급자가 지원하는 값을 1로 설정합니다. 메타데이터 쿼리를 사용하면 Windows에서 데이터 수집을 수행하지 않고도 지원되는 성능 개체에 대한 정보를 수집할 수 있습니다. 특히 데이터 수집 비용이 많이 드는 경우 공급자에 메타데이터 쿼리에 대한 지원을 추가하는 것이 좋습니다.
다음 예제에서는 CollectPerformanceData 함수의 구현을 보여줍니다. 이 함수에 사용된 카운터의 정의를 포함하는 헤더 파일은 OpenPerformanceData 구현에 표시됩니다. C++를 사용하여 이 함수를 구현하는 경우 함수를 선언할 때 extern "C"를 사용해야 합니다.
// Callback that the performance service calls when the consumer wants to sample
// your counter data. Get the counter data and return it to the consumer.
extern "C" DWORD APIENTRY CollectPerfData(LPWSTR pQuery,
LPVOID* ppData,
LPDWORD pcbData,
LPDWORD pObjectsReturned)
{
BOOL fQuerySupported = FALSE;
DWORD TotalQuerySize = 0;
PBYTE pObjects = (PBYTE)*ppData; // Used to add counter objects to the buffer.
PEER_INSTANCE inst;
*pObjectsReturned = 0;
if (0 == g_OpenCount) // Open did not successfully initialize
{
*pcbData = 0;
*pObjectsReturned = 0;
return ERROR_SUCCESS;
}
// Confirm that we support the requested objects. The query string is passed
// to this function as it was passed to RegQueryValueEx. For this example,
// it should never be the case that we are being asked for objects that
// we do not support because we included the [objects] section in the .ini file.
fQuerySupported = IsQuerySupported(pQuery, &g_QueriedObjects);
if (fQuerySupported == FALSE)
{
*pcbData = 0;
*pObjectsReturned = 0;
return ERROR_SUCCESS;
}
// If multiple instance objects are queried, you need to discover how many
// instances exist so you can determine the buffer size that the
// query requires. This value can potentially change from query to query.
// The Peer object is a multiple instance object. For this example,
// set the number of instances to 2 if the Peer object was queried.
if (QUERIED_PEER_OBJECT == (g_QueriedObjects & QUERIED_PEER_OBJECT))
{
g_Peer.Object.NumInstances = 2;
g_Peer.Object.TotalByteLength = sizeof(PEER) +
sizeof(PEER_INSTANCE) * g_Peer.Object.NumInstances;
}
// Check pcbData to see if ppData is large enough to hold our counters.
// If the buffer is not large enough, return ERROR_MORE_DATA. This tells
// the calling application to increase the buffer size and query again.
TotalQuerySize = GetQuerySize(g_QueriedObjects);
if (TotalQuerySize > *pcbData)
{
*pcbData = 0;
*pObjectsReturned = 0;
return ERROR_MORE_DATA;
}
else
{
*pcbData = TotalQuerySize;
}
// If the query includes the Transfer object, collect the counter data
// for the Transfer object and copy it to the ppData buffer.
if (QUERIED_TRANSFER_OBJECT == (g_QueriedObjects & QUERIED_TRANSFER_OBJECT))
{
// Add calls to retrieve counter data from the server/driver/application.
// This example hard codes the counter data.
g_Transfer.BytesSentData = 5;
g_Transfer.AvailableBandwidthData = 20;
g_Transfer.TotalBandwidthData = 50;
// Since this is a single instance object, just copy the object
// to the buffer.
memcpy((PTRANSFER)pObjects, &g_Transfer, sizeof(TRANSFER));
pObjects += g_Transfer.Object.TotalByteLength;
(*pObjectsReturned)++;
}
// If the query includes the Peer object, collect the counter data
// for the Peer object and its instances and copy it to the ppData buffer.
if (QUERIED_PEER_OBJECT == (g_QueriedObjects & QUERIED_PEER_OBJECT))
{
// Copy the object and counter definition pieces to the buffer,
// the instance data follows.
memcpy((PPEER)pObjects, &g_Peer, sizeof(PEER));
pObjects += sizeof(PEER);
// Initialize the instance information.
ZeroMemory(&inst, sizeof(PEER_INSTANCE));
inst.Instance.ByteLength = sizeof(PERF_INSTANCE_DEFINITION) + sizeof(inst.InstanceName);
inst.Instance.UniqueID = PERF_NO_UNIQUE_ID;
inst.Instance.NameOffset = sizeof(PERF_INSTANCE_DEFINITION);
inst.CounterBlock.ByteLength = EndOfPeerData;
// Instance-specific data for the first instance. This information is
// hard coded for this example.
inst.Instance.NameLength = sizeof(INSTANCE_NAME_1);
StringCchCopy(inst.InstanceName, MAX_INSTANCE_NAME_LEN+1, INSTANCE_NAME_1);
inst.BytesServedData = 15;
// Copy the instance.
memcpy((PPEER_INSTANCE)pObjects, &inst, sizeof(PEER_INSTANCE));
pObjects += sizeof(PEER_INSTANCE);
// Instance-specific data for the second instance.
inst.Instance.NameLength = sizeof(INSTANCE_NAME_2);
StringCchCopy(inst.InstanceName, MAX_INSTANCE_NAME_LEN+1, INSTANCE_NAME_2);
inst.BytesServedData = 30;
// Copy the instance.
memcpy((PPEER_INSTANCE)pObjects, &inst, sizeof(PEER_INSTANCE));
pObjects += sizeof(PEER_INSTANCE);
(*pObjectsReturned)++;
}
*ppData = (LPVOID)pObjects;
return ERROR_SUCCESS;
}
// Scan the query string to see if we support the objects.
BOOL IsQuerySupported(LPWSTR pQuery, DWORD* pQueriedObjects)
{
BOOL fSupported = FALSE;
WCHAR IndexString[33+1];
LPWSTR pCopy = NULL;
DWORD dwQueryLen = 0;
*pQueriedObjects = 0;
// Copy the query string and make it lowercase.
dwQueryLen = wcslen(pQuery) + 1;
pCopy = new WCHAR[dwQueryLen];
wcscpy_s(pCopy, dwQueryLen, pQuery);
_wcslwr_s(pCopy, dwQueryLen);
if (wcsstr(pCopy, L"global"))
{
fSupported = TRUE;
*pQueriedObjects |= QUERIED_ALL_OBJECTS;
}
else
{
// See if the query contains the index value for
// the Transfer object.
_ultow_s(g_TransferIndex, IndexString, 33, 10);
if (wcsstr(pCopy, IndexString))
{
fSupported = TRUE;
*pQueriedObjects |= QUERIED_TRANSFER_OBJECT;
}
// See if the query contains the index value for
// the Peer object.
_ultow_s(g_PeerIndex, IndexString, 33, 10);
if (wcsstr(pCopy, IndexString))
{
fSupported = TRUE;
*pQueriedObjects |= QUERIED_PEER_OBJECT;
}
}
if (pCopy)
delete pCopy;
return fSupported;
}
// Determine the required buffer size for the query.
DWORD GetQuerySize(DWORD QueriedObjects)
{
DWORD QuerySize = 0;
if (QUERIED_TRANSFER_OBJECT == (QueriedObjects & QUERIED_TRANSFER_OBJECT))
QuerySize = g_Transfer.Object.TotalByteLength;
if (QUERIED_PEER_OBJECT == (g_QueriedObjects & QUERIED_PEER_OBJECT))
QuerySize += g_Peer.Object.TotalByteLength;
return QuerySize;
}