The Collect Function

The system calls each application's Collect function whenever a performance monitor program calls the RegQueryValueEx function to collect performance data. This function returns the application's performance data.

Use the following function prototype for your Collect function.

DWORD WINAPI CollectPerformanceData(
    LPWSTR lpwszValue, 
    LPVOID *lppData, 
    LPDWORD lpcbBytes, 
    LPDWORD lpcObjectTypes);
 

The name CollectPerformanceData is a place-holder for an application-defined name.

Argument Description
lpwszValue Pointer to a string specified by the performance monitor program in a call to the RegQueryValueEx function. The string uses one of the formats described in Retrieving Selected Data.
LppData On input, points to a pointer to the location where the data is to be placed. On successful exit, set *lppData to the next byte in the buffer available for data, such as one byte past the last byte of your data. The data returned must be a multiple of a DWORD in length. It must conform to the PERF_OBJECT_TYPE structure, unless this is a collection from a foreign computer. If foreign, any PERF_OBJECT_TYPE structures returned must be preceded by a PERF_DATA_BLOCK structure for the foreign computer. If the Collect function fails for any reason, leave *lppData unchanged.
LpcbBytes On input, points to a 32-bit value that specifies the size, in bytes, of the lppData buffer. On successful exit, set *lpcbBytes to the size, in bytes, of the data written to the lppData buffer. This must be a multiple of sizeof(DWORD) (a multiple of 4). If the Collect function fails for any reason, set *lpcbBytes to zero.
lpcObjectTypes On successful exit, set *lpcObjectTypes to the number of object type definitions being returned. If the Collect function fails for any reason, it should set *lpcObjectTypes to zero.

If the requested data specified by lpwszValue does not correspond to any of the object indexes or foreign computers supported by your program, leave *lppData unchanged, and set *lpcbBytes and *lpcObjectTypes both to zero. This indicates that no data is returned. If your data collection is time-consuming, you should only respond to specific requests and Costly requests. You should also lower the priority of the thread collecting the data, so that it does not adversely affect system performance.

The Collect function must return one of the values shown in the following table.

Return value Description
ERROR_MORE_DATA Indicates that the size of the lppData buffer as specified by *lpcbBytes is not large enough to store the data to be returned. In this case, leave *lppData unchanged, and set *lpcbBytes and *lpcObjectTypes to zero. No attempt is made to indicate the required buffer size, because this may change before the next call.
ERROR_SUCCESS Return this value in all other cases, even if no data is returned or an error occurs. To report errors other than insufficient buffer size, use the system event log, but do not flood the event log with errors on every data collection operation.

To provide more information to the user, the Collect function should write any error that prevents the function from completing successfully in the system event log. For more information, see Event Logging.

If the application collecting the data is running on another machine (remotely), then the extensible counter functions are called in the context of the Winlogon process, which handles the server side of the remote connection. This distinction is important when troubleshooting problems that only occur remotely. The Winlogon process may access the functions using multiple threads, usually one per remote connection. This means that the Open procedure can be called more than once. You should handle this in your code accordingly, so that initialization tasks are not performed more than necessary. The Collect function may also be called concurrently by multiple threads, so you should be careful how you initialize and use temporary data. Static variables should only be used if the data is intended to be shared across threads.

The connection to the machine providing the data should be tested each time data is requested. If the connection cannot be made, you need to return some sort of indication. One suggestion is to provide a Status counter for the object that indicates whether a valid connection exists. When the connection does not exist, set the Status counter and return the last set of valid data.

For foreign computer interfaces, the opening of a channel to the foreign computer must be done in the Collect function because the computer name is not provided to the Open function. The performance DLL should save a handle to the foreign computer to avoid reconnecting on each data collection call, which would significantly slow down system performance.

Once you get the data for a foreign computer, construct a PERF_DATA_BLOCK as the first thing in *lppData, so that your application can return the data. If the system you are measuring does not provide the requested information, provide a reasonable value. You should use values from the local system for time and counter frequency if they are not provided remotely. You may need to use the PerfTime and .PerfFreq members of the PERF_OBJECT_TYPE structure for some counters as well.

Other things that you might do in the Collect function include:

After the collect procedure returns successfully, the system performs the following tests to try and catch logic errors in the collect procedure. The first test to fail will generate an event log message and, in most cases, the data is discarded to prevent any further problems due to invalid pointers. These tests are normally enabled though they can be disabled by changing a registry variable value as described below:

HKEY_LOCAL_MACHINE

\Software

\Microsoft

\Windows NT

\CurrentVersion

\Perflib

ExtCounterTestLevel = Test_Level

The Test_Level is a REG_DWORD that specifies the test level. Test level 1 requests all tests. Test level 2 requests basic tests. Test level 3 requests no tests. The default is test level 1.

The basic tests performed on the data buffer are as follows:

The following tests are performed only if test level 1 is used.