共用方式為


實作 CollectPerformanceData

系統成功呼叫 OpenPerformanceData 函式之後,它會呼叫 CollectPerformanceData 函式來收集計數器資料。 如果提供者支援查詢的物件,它會連絡與其相關聯的服務、驅動程式或應用程式,並要求它提供計數器資料。

參數 pQuery 會是下列其中一項:

  • 以空格分隔的一個或多個十進位整數清單:收集清單中任何支援之物件類型的效能資料。
  • Global:收集所有支援的本機物件類型的效能資料,但類別中包含的 Costly 物件除外。
  • Costly:針對所有支援的本機物件類型收集效能資料,其資料在處理器時間或記憶體使用量方面會耗費大量成本。 (過時:這個類別中通常不應該有物件類型。
  • Foreigh:收集所有支援的遠端物件類型的效能資料。 (過時:這個類別中通常不應該有物件類型。
  • MetadataGlobal new ):收集所有支援的本機物件類型的中繼資料,但類別中包含的 Costly 物件除外。 這與 Global 應該設定為 PERF_METADATA_MULTIPLE_INSTANCESPERF_METADATA_NO_INSTANCES 相同, NumInstances 結果不應包含任何 PERF_INSTANCE_DEFINITION 區塊。
  • MetadataCostly new ):收集類別中包含的 Costly 所有支援本機物件類型的中繼資料。 這與 Costly 應該設定為 PERF_METADATA_MULTIPLE_INSTANCESPERF_METADATA_NO_INSTANCES 相同, NumInstances 結果不應包含任何 PERF_INSTANCE_DEFINITION 區塊。

MetadataGlobal MetadataCostly 查詢類型是 Windows 10 20H1 和更新版本的新功能。 只有在提供者已新增 HKLM\CurrentControlSet\Services\<provider-name>\Performance\Collect Supports Metadata 登錄值時,Windows 才會進行中繼資料查詢。 將值設定為 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;
}