How the DLL Interfaces with a Performance Monitor Application

As discussed in Chapter 12 of this book, a performance monitor program retrieves performance data by specifying the HKEY_PERFORMANCE_DATA special handle in a call to the RegQueryValueEx function. If successful, RegQueryValueEx fills a buffer of the application with the requested performance data.

The first time an application calls RegQueryValueEx, or if the application uses the RegOpenKey function to open HKEY_PERFORMANCE_DATA, the Registry controller calls the Open function for all applications with the necessary Performance key entries. This gives each performance DLL an opportunity to initialize its performance data structures. Then, for performance DLLs whose Open function returned successfully, or for those with a Collect function but no Open function, the Registry controller calls the performance DLL's Collect function.

After the initial Open function calls, subsequent application calls to RegQueryValueEx only cause the Registry controller to call the Collect functions.

When the application has finished collecting performance data, it specifies HKEY_PERFORMANCE_DATA in a call to the RegCloseKey function. This causes the Registry controller to call the Close function for all applications. The performance DLLs will then be unloaded.

Note that it is possible for multiple programs to collect performance data at the same time. The Registry controller calls a performance DLL's Open and Close functions only once for each performance monitoring process. For remote measurement, the Registry controller limits access to these routines to only one thread at a time, so synchronization (for example, re-entrancy) is not a problem.

Important For local measurement, because multiple processes may be making simultaneous calls, the program must prevent any conflicts from multiple concurrent requests for data.

The Open Function

The Registry controller calls the Open function, if one is provided, whenever a performance monitor application first connects to the Registry to collect performance data. This function performs whatever initialization is required for the application to provide performance data. Use the PM_OPEN_PROC function prototype defined in WINPERF.H:


PM_OPEN_PROC OpenPerformanceData; DWORD APIENTRY OpenPerformanceData(LPWSTR lpDeviceNames);

The lpDeviceNames argument points to a buffer containing the REG_MULTI_SZ strings stored in the Export value at the ...\Services\ApplicationName\Linkage key in the Registry. If this entry does not exist, lpDeviceNames is NULL. The strings are Unicode, separated by a UNICODE_NULL, and terminated by two UNICODE_NULL characters. The strings are the names of the devices managed by this application, and the Open function should call CreateFile to open a handle to each device named. If a CreateFile call fails, the Open function should return the error code returned by the GetLastError function; otherwise, it should return ERROR_SUCCESS.

The Open function initializes the data structures it returns to the performance monitor application. In particular, it examines the Registry to get the Counters and Help indexes of the objects and counters supported by the application. These indexes are then stored in the appropriate members of the PERF_OBJECT_TYPE and PERF_COUNTER_DEFINITION structures, which define the application's counter objects and counters. The example code at the end of this chapter shows the technique for using the First Counter and First Help values that the lodctr utility creates in the program's Performance key to determine the Counters and Help indexes of the program's counter objects and counters.

Other initialization tasks that might be performed by the Open function include the following:

The Collect Function

The Registry controller 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 in the format described in Chapter 12, "Writing a Custom Windows NT Performance Monitor." Use the PM_COLLECT_PROC function prototype defined in WINPERF.H:


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

Table 13.5 Collect Function Arguments and Descriptions

Argument

Description

lpwszValue

Points to a string specified by the performance monitor program in the RegQueryValueEx call. For an example of code that parses this string, see the example at the end of this chapter. The string uses one of the following case-sensitive formats to identify the type of data being requested:

Global

Requests data for all counters on the local machine except those included in the Costly category.

index1 index2 ...

Requests data for the specified objects, where index 1, index2, and so forth, are whitespace-separated Unicode strings representing the decimal value of an object's Counters index. The Collect function needs to convert the strings to integers and then compare them to the Counters indexes of the application's counter objects. The Collect function returns data for all counters associated with the specified counter objects.

Foreign ComputerName

Requests data for all counters on a computer that does not support the Windows NT Registry calls for returning data remotely. ComputerName identifies the computer. If this application is a provider of foreign remote statistics, it should keep a handle that enables access to the foreign system. This avoids reconnection for each data collection. The Collect function should use the handle to get the data.

Foreign ComputerName index1 index2 ...

Requests data for the specified objects on a foreign computer.

Costly

Requests data for all counters whose data is expensive to collect. It is up to the application writer to determine whether any of the application's counter objects are in this category. Windows NT Performance Monitor does not use this category.

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 data definition and its descendants as specified in WINPERF.H, 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.

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.

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

Table 13.6 Collect Function Return Values and Descriptions

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.


The Close Function

The Registry controller calls each application's Close function when a performance monitor application calls the RegCloseKey function to close the HKEY_PERFORMANCE_DATA handle. This function performs any cleanup operations required by the application's performance data collection mechanism. For example, the function could close device handles opened by CreateFile, or close a handle to a file mapping object. Use the PM_CLOSE_PROC function prototype defined in WINPERF.H:


PM_CLOSE_PROC ClosePerformanceData; DWORD APIENTRY ClosePerformanceData();

The function should return ERROR_SUCCESS.