시스템에서 OpenPerformanceData 함수를 성공적으로 호출한 후 CollectPerformanceData 함수를 호출하여 카운터 데이터를 수집합니다. 공급자가 쿼리된 개체를 지원하는 경우 연결된 서비스, 드라이버 또는 애플리케이션에 연결하고 카운터 데이터를 요청합니다.
pQuery 매개 변수는 다음 중 하나입니다.
- 하나 이상의 소수점 정수의 공백으로 구분된 목록: 목록에서 지원되는 개체 형식에 대한 성능 데이터를 수집합니다.
-
Global:Costly범주에 포함된 개체를 제외하고 지원되는 모든 로컬 개체 형식에 대한 성능 데이터를 수집합니다. -
Costly: 프로세서 시간 또는 메모리 사용량 측면에서 데이터를 수집하는 데 비용이 많이 드는 지원되는 모든 로컬 개체 형식에 대한 성능 데이터를 수집합니다. (사용되지 않음: 일반적으로 이 범주에는 개체 형식이 없어야 합니다.) -
Foreign: 지원되는 모든 원격 개체 형식에 대한 성능 데이터를 수집합니다. (사용되지 않음: 일반적으로 이 범주에는 개체 형식이 없어야 합니다.) -
MetadataGlobal(새):Costly범주에 포함된 개체를 제외하고 지원되는 모든 로컬 개체 형식에 대한 메타데이터를 수집합니다.GlobalNumInstances또는PERF_METADATA_MULTIPLE_INSTANCES설정해야 하며 결과에PERF_METADATA_NO_INSTANCES블록이 포함되지 않아야 한다는 점을 제외하면PERF_INSTANCE_DEFINITION동일합니다. -
MetadataCostly(새):Costly범주에 포함된 지원되는 모든 로컬 개체 형식에 대한 메타데이터를 수집합니다.CostlyNumInstances또는PERF_METADATA_MULTIPLE_INSTANCES설정해야 하며 결과에PERF_METADATA_NO_INSTANCES블록이 포함되지 않아야 한다는 점을 제외하면PERF_INSTANCE_DEFINITION동일합니다.
MetadataGlobal 및 MetadataCostly 쿼리 형식은 Windows 10 20H1 이상에서 새로 추가되었습니다. 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;
}