Data Collection DLL

The data collection DLL, called VGACTRS.DLL, is included here in the next few sections. First, lets start with the input to the lodctr utility.

The first file we need is the one giving offsets to the counters, called here VGACTRNM.H:


__________________________________________________________________ // // vgactrnm.h // // Offset definition file for extensible counter objects and counters // // These "relative" offsets must start at 0 and be multiples of 2 (i.e. // even numbers). In the Open Procedure, they will be added to the // "First Counter" and "First Help" values for the device they belong to, // in order to determine the absolute location of the counter and // object names and corresponding Explain text in the registry. // // This file is used by the extensible counter DLL code as well as the // counter name and Explain text definition file (.INI) file that is used // by LODCTR to load the names into the registry. // #define VGAOBJ 0 #define BITBLTS 2 #define TEXTOUTS 4 __________________________________________________________________

Next we have the file defining the object name and the counter names, and the Explain text. Each Explain text line must actually be one (possibly long) line of text.


__________________________________________________________________
[info] drivername=VGA symbolfile=vgactrnm.h [languages] 009=English [text] VGAOBJ_009_NAME=VGA VGAOBJ_009_HELP=The VGA Object Type handles the VGA device on your system. BITBLTS_009_NAME=BitBlts/sec BITBLTS_009_HELP=BitBlts/sec is the rate your system is sending blocks of pixels to the display. TEXTOUTS_009_NAME=TextOuts/sec TEXTOUTS_009_HELP=TextOuts/sec is the rate your system is sending lines of text to the display. __________________________________________________________________

The next file defines the data structures that will be returned to the performance monitor.


__________________________________________________________________
/*++ BUILD Version: 0001 // Increment this if a change has global effects Copyright (c) 1992 Microsoft Corporation Module Name: datavga.h Abstract: Header file for the VGA Extensible Object data definitions This file contains definitions to construct the dynamic data which is returned by the Configuration Registry. Data from various driver API calls is placed into the structures shown here. --*/ #ifndef _DATAVGA_H_ #define _DATAVGA_H_ // // The routines that load these structures assume that all fields // are packed and aligned on DWORD boundaries. Alpha support may // change this assumption so the pack pragma is used here to insure // the DWORD packing assumption remains valid. // #pragma pack (4) // // Extensible Object definitions // // Update the following sort of define when adding an object type. #define VGA_NUM_PERF_OBJECT_TYPES 1 //---------------------------------------------------------------------------- // // VGA Resource object type counter definitions. // // These are used in the counter definitions to describe the relative // position of each counter in the returned data. // #define NUM_BITBLTS_OFFSET sizeof(DWORD) #define NUM_TEXTOUTS_OFFSET NUM_BITBLTS_OFFSET + sizeof(DWORD) #define SIZE_OF_VGA_PERFORMANCE_DATA \ NUM_TEXTOUTS_OFFSET + sizeof(DWORD) // // This is the counter structure presently returned by VGA. // typedef struct _VGA_DATA_DEFINITION { PERF_OBJECT_TYPE VgaObjectType; PERF_COUNTER_DEFINITION NumBitBlts; PERF_COUNTER_DEFINITION NumTextOuts; } VGA_DATA_DEFINITION; #pragma pack () #endif //_DATAVGA_H_ __________________________________________________________________

In the next file we have the initialization of the object and counter definition structures with constant data. To understand this file, you will have to dust off your copy of the WINPERF.H header file where the structures are defined.


__________________________________________________________________
/*++ BUILD Version: 0001 // Increment this if a change has global effects Copyright (c) 1992 Microsoft Corporation Module Name: datavga.c Abstract: A file containing the constant data structures used by the Performance Monitor data for the VGA Extensible Objects. This file contains a set of constant data structures which are currently defined for the VGA Extensible Objects. This is an example of how other such objects could be defined. Revision History: None. --*/ // // Include Files // #include <windows.h> #include <winperf.h> #include "vgactrnm.h" #include "datavga.h" // // Constant structure initializations // defined in datavga.h // VGA_DATA_DEFINITION VgaDataDefinition = { { sizeof(VGA_DATA_DEFINITION) + SIZE_OF_VGA_PERFORMANCE_DATA, sizeof(VGA_DATA_DEFINITION), sizeof(PERF_OBJECT_TYPE), VGAOBJ, 0, VGAOBJ, 0, PERF_DETAIL_NOVICE, (sizeof(VGA_DATA_DEFINITION)-sizeof(PERF_OBJECT_TYPE))/ sizeof(PERF_COUNTER_DEFINITION), 0, 0, 0 }, { sizeof(PERF_COUNTER_DEFINITION), BITBLTS, 0, BITBLTS, 0, 0, PERF_DETAIL_NOVICE, PERF_COUNTER_COUNTER, sizeof(DWORD), NUM_BITBLTS_OFFSET }, { sizeof(PERF_COUNTER_DEFINITION), TEXTOUTS, 0, TEXTOUTS, 0, 0, PERF_DETAIL_NOVICE, PERF_COUNTER_COUNTER, sizeof(DWORD), NUM_TEXTOUTS_OFFSET } }; __________________________________________________________________

In the next file, PERFUTIL.H, are some useful declarations we have found handy for performance data collection DLLs:


__________________________________________________________________
/*++ BUILD Version: 0001 // Increment this if a change has global effects Copyright (c) 1992 Microsoft Corporation Module Name: perfutil.h Abstract: This file supports routines used to parse and create Performance Monitor Data
Structures. It actually supports Performance Object types with multiple instances Revision History: --*/ #ifndef _PERFUTIL_H_ #define _PERFUTIL_H_ // enable this define to log process heap data to the event log #ifdef PROBE_HEAP_USAGE #undef PROBE_HEAP_USAGE #endif // // Utility macro. This is used to reserve a DWORD multiple of bytes for Unicode strings
// embedded in the definitional data, viz., object instance names. // #define DWORD_MULTIPLE(x) (((x+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD)) // (assumes dword is 4 bytes long and pointer is a dword in size) #define ALIGN_ON_DWORD(x) ((VOID *)( ((DWORD) x & 0x00000003) ? ( ((DWORD) x & 0xFFFFFFFC) + 4 ) : ( (DWORD) x ) )) extern WCHAR GLOBAL_STRING[]; // Global command (get all local ctrs) extern WCHAR FOREIGN_STRING[]; // get data from foreign computers extern WCHAR COSTLY_STRING[]; extern WCHAR NULL_STRING[]; #define QUERY_GLOBAL 1 #define QUERY_ITEMS 2 #define QUERY_FOREIGN 3 #define QUERY_COSTLY 4 // // The definition of the only routine of perfutil.c, It builds part of a performance data
// instance (PERF_INSTANCE_DEFINITION) as described in winperf.h // HANDLE MonOpenEventLog (); VOID MonCloseEventLog (); DWORD GetQueryType (IN LPWSTR); BOOL IsNumberInUnicodeList (DWORD, LPWSTR); typedef struct _LOCAL_HEAP_INFO_BLOCK { DWORD AllocatedEntries; DWORD AllocatedBytes; DWORD FreeEntries; DWORD FreeBytes; } LOCAL_HEAP_INFO, *PLOCAL_HEAP_INFO; // // Memory Probe macro // #ifdef PROBE_HEAP_USAGE #define HEAP_PROBE() { \ DWORD dwHeapStatus[5]; \ NTSTATUS CallStatus; \ dwHeapStatus[4] = __LINE__; \ if (!(CallStatus = memprobe (dwHeapStatus, 16L, NULL))) { \ REPORT_INFORMATION_DATA (VGA_HEAP_STATUS, LOG_DEBUG, \ &dwHeapStatus, sizeof(dwHeapStatus)); \ } else { \ REPORT_ERROR_DATA (VGA_HEAP_STATUS_ERROR, LOG_DEBUG, \ &CallStatus, sizeof (DWORD)); \ } \ } #else #define HEAP_PROBE() ; #endif #endif //_PERFUTIL_H_ __________________________________________________________________

Similarly, the next file holds functions generally useful to performance data collection DLLs. These handle two routine chores: handling of the Event Log, and parsing of the Unicode Value string which tells your DLL what objects are being collected by the performance monitor. We wanted to be sure to include it, just for you.


__________________________________________________________________
/*++ BUILD Version: 0001 // Increment this if a change has global effects Copyright (c) 1992 Microsoft Corporation Module Name: perfutil.c Abstract: This file implements the utility routines used to construct the common parts of a PERF_INSTANCE_DEFINITION (see winperf.h) and perform event logging functions. Revision History: --*/ // // include files // #include <windows.h> #include <string.h> #include <winperf.h> #include "vgactrs.h" // error message definition #include "perfmsg.h" #include "perfutil.h" #define INITIAL_SIZE 1024L #define EXTEND_SIZE 1024L // // Global data definitions. // ULONG ulInfoBufferSize = 0; HANDLE hEventLog = NULL; // event log handle for reporting events // initialized in Open... routines DWORD dwLogUsers = 0; // count of functions using event log DWORD MESSAGE_LEVEL = 0; WCHAR GLOBAL_STRING[] = L"Global"; WCHAR FOREIGN_STRING[] = L"Foreign"; WCHAR COSTLY_STRING[] = L"Costly"; WCHAR NULL_STRING[] = L"\0"; // pointer to null string // test for delimiter, end of line and non-digit characters // used by IsNumberInUnicodeList routine // #define DIGIT 1 #define DELIMITER 2 #define INVALID 3 #define EvalThisChar(c,d) ( \ (c == d) ? DELIMITER : \ (c == 0) ? DELIMITER : \ (c < (WCHAR)'0') ? INVALID : \ (c > (WCHAR)'9') ? INVALID : \ DIGIT) HANDLE MonOpenEventLog ( ) /*++ Routine Description: Reads the level of event logging from the registry and opens the channel to the event logger for subsequent event log entries. Arguments: None Return Value: Handle to the event log for reporting events. NULL if open not successful. --*/ { HKEY hAppKey; TCHAR LogLevelKeyName[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib"; TCHAR LogLevelValueName[] = "EventLogLevel"; LONG lStatus; DWORD dwLogLevel; DWORD dwValueType; DWORD dwValueSize; // if global value of the logging level not initialized or is disabled, // check the registry to see if it should be updated. if (!MESSAGE_LEVEL) { lStatus = RegOpenKeyEx (HKEY_LOCAL_MACHINE, LogLevelKeyName, 0, KEY_READ, &hAppKey); dwValueSize = sizeof (dwLogLevel); if (lStatus == ERROR_SUCCESS) { lStatus = RegQueryValueEx (hAppKey, LogLevelValueName, (LPDWORD)NULL, &dwValueType, (LPBYTE)&dwLogLevel, &dwValueSize); if (lStatus == ERROR_SUCCESS) { MESSAGE_LEVEL = dwLogLevel; } else { MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT; } RegCloseKey (hAppKey); } else { MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT; } } if (hEventLog == NULL){ hEventLog = RegisterEventSource ( (LPTSTR)NULL, // Use Local Machine APP_NAME); // event log app name to find in registry if (hEventLog != NULL) { REPORT_INFORMATION (UTIL_LOG_OPEN, LOG_DEBUG); } } if (hEventLog != NULL) { dwLogUsers++; // increment count of perfctr log users } return (hEventLog); } VOID MonCloseEventLog ( ) /*++ Routine Description: Closes the handle to the event logger if this is the last caller Arguments: None Return Value: None --*/ { if (hEventLog != NULL) { dwLogUsers--; // decrement usage if (dwLogUsers <= 0) { // and if we're the last, then close up log REPORT_INFORMATION (UTIL_CLOSING_LOG, LOG_DEBUG); DeregisterEventSource (hEventLog); } } } DWORD GetQueryType ( IN LPWSTR lpValue ) /*++ GetQueryType returns the type of query described in the lpValue string so that the appropriate processing method may be used Arguments IN lpValue string passed to PerfRegQuery Value for processing Return Value QUERY_GLOBAL if lpValue == 0 (null pointer) lpValue == pointer to Null string lpValue == pointer to "Global" string QUERY_FOREIGN if lpValue == pointer to "Foreign" string QUERY_COSTLY if lpValue == pointer to "Costly" string otherwise: QUERY_ITEMS --*/ { WCHAR *pwcArgChar, *pwcTypeChar; BOOL bFound; if (lpValue == 0) { return QUERY_GLOBAL; } else if (*lpValue == 0) { return QUERY_GLOBAL; } // check for "Global" request pwcArgChar = lpValue; pwcTypeChar = GLOBAL_STRING; bFound = TRUE; // assume found until contradicted // check to the length of the shortest string while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) { if (*pwcArgChar++ != *pwcTypeChar++) { bFound = FALSE; // no match break; // bail out now } } if (bFound) return QUERY_GLOBAL; // check for "Foreign" request pwcArgChar = lpValue; pwcTypeChar = FOREIGN_STRING; bFound = TRUE; // assume found until contradicted // check to the length of the shortest string while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) { if (*pwcArgChar++ != *pwcTypeChar++) { bFound = FALSE; // no match break; // bail out now } } if (bFound) return QUERY_FOREIGN; // check for "Costly" request pwcArgChar = lpValue; pwcTypeChar = COSTLY_STRING; bFound = TRUE; // assume found until contradicted // check to the length of the shortest string while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) { if (*pwcArgChar++ != *pwcTypeChar++) { bFound = FALSE; // no match break; // bail out now } } if (bFound) return QUERY_COSTLY; // if not Global and not Foreign and not Costly, // then it must be an item list return QUERY_ITEMS; } BOOL IsNumberInUnicodeList ( IN DWORD dwNumber, IN LPWSTR lpwszUnicodeList ) /*++ IsNumberInUnicodeList Arguments: IN dwNumber DWORD number to find in list IN lpwszUnicodeList Null terminated, Space delimited list of decimal numbers Return Value: TRUE: dwNumber was found in the list of unicode number strings FALSE: dwNumber was not found in the list. --*/ { DWORD dwThisNumber; WCHAR *pwcThisChar; BOOL bValidNumber; BOOL bNewItem; BOOL bReturnValue; WCHAR wcDelimiter; // could be an argument to be more flexible if (lpwszUnicodeList == 0) return FALSE; // null pointer, # not found pwcThisChar = lpwszUnicodeList; dwThisNumber = 0; wcDelimiter = (WCHAR)' '; bValidNumber = FALSE; bNewItem = TRUE; while (TRUE) { switch (EvalThisChar (*pwcThisChar, wcDelimiter)) { case DIGIT: // if this is the first digit after a delimiter, then // set flags to start computing the new number if (bNewItem) { bNewItem = FALSE; bValidNumber = TRUE; } if (bValidNumber) { dwThisNumber *= 10; dwThisNumber += (*pwcThisChar - (WCHAR)'0'); } break; case DELIMITER: // a delimiter is either the delimiter character or the // end of the string ('\0') if when the delimiter has been // reached a valid number was found, then compare it to the // number from the argument list. if this is the end of the // string and no match was found, then return. // if (bValidNumber) { if (dwThisNumber == dwNumber) return TRUE; bValidNumber = FALSE; } if (*pwcThisChar == 0) { return FALSE; } else { bNewItem = TRUE; dwThisNumber = 0; } break; case INVALID: // if an invalid character was encountered, ignore all // characters up to the next delimiter and then start fresh. // the invalid number is not compared. bValidNumber = FALSE; break; default: break; } pwcThisChar++; } } // IsNumberInUnicodeList __________________________________________________________________

The next file is the one that has all the active code for opening data collection, collecting data, and closing the DLL. We had to get here eventually. We call the heart of the matter PERFVGA.C. Creative, huh?


__________________________________________________________________
/*++ BUILD Version: 0001 // Increment this if a change has global effects Copyright (c) 1992 Microsoft Corporation Module Name: perfvga.c Abstract: This file implements the Extensible Objects for the Vga object type Revision History --*/ // // Include Files // #include <windows.h> #include <string.h> #include <wcstr.h> #include <winperf.h> #include "vgactrs.h" // error message definition #include "perfmsg.h" #include "perfutil.h" #include "datavga.h" // // References to constants which initialize the Object type definitions // extern VGA_DATA_DEFINITION VgaDataDefinition; DWORD dwOpenCount = 0; // count of "Open" threads BOOL bInitOK = FALSE; // true = DLL initialized OK // // Vga data structures // HANDLE hVgaSharedMemory; // Handle of Vga Shared Memory PPERF_COUNTER_BLOCK pCounterBlock; // // Function Prototypes // // these are used to insure that the data collection functions // accessed by Perflib will have the correct calling format. // PM_OPEN_PROC OpenVgaPerformanceData; PM_COLLECT_PROC CollectVgaPerformanceData; PM_CLOSE_PROC CloseVgaPerformanceData; DWORD APIENTRY OpenVgaPerformanceData( LPWSTR lpDeviceNames ) /*++ Routine Description: This routine will open and map the memory used by the VGA driver to pass performance data in. This routine also initializes the data structures used to pass data back to the registry Arguments: Pointer to object ID of each device to be opened (VGA) Return Value: None. --*/ { LONG status; TCHAR szMappedObject[] = TEXT("VGA_COUNTER_BLOCK"); HKEY hKeyDriverPerf; DWORD size; DWORD type; DWORD dwFirstCounter; DWORD dwFirstHelp; // // Since SCREG is multi-threaded and will call this routine in // order to service remote performance queries, this library // must keep track of how many times it has been opened (i.e. // how many threads have accessed it). the registry routines will // limit access to the initialization routine to only one thread // at a time so synchronization (i.e. reentrancy) should not be // a problem // if (!dwOpenCount) { // open Eventlog interface hEventLog = MonOpenEventLog(); // open shared memory used by device driver to pass performance values hVgaSharedMemory = OpenFileMapping(FILE_MAP_READ, FALSE, szMappedObject); pCounterBlock = NULL; // initialize pointer to memory // log error if unsuccessful if (hVgaSharedMemory == NULL) { REPORT_ERROR (VGAPERF_OPEN_FILE_MAPPING_ERROR, LOG_USER); // this is fatal, if we can't get data then there's no // point in continuing. status = GetLastError(); // return error goto OpenExitPoint; } else { // if opened ok, then map pointer to memory pCounterBlock = (PPERF_COUNTER_BLOCK) MapViewOfFile(hVgaSharedMemory, FILE_MAP_READ, 0, 0, 0); if (pCounterBlock == NULL) { REPORT_ERROR (VGAPERF_UNABLE_MAP_VIEW_OF_FILE, LOG_USER); // this is fatal, if we can't get data then there's no // point in continuing. status = GetLastError(); // return error } } // get counter and help index base values from registry // Open key to registry entry // read First Counter and First Help values // update static data structures by adding base to // offset value in structure. status = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Vga\\Performance", 0L, KEY_ALL_ACCESS, &hKeyDriverPerf); if (status != ERROR_SUCCESS) { REPORT_ERROR_DATA (VGAPERF_UNABLE_OPEN_DRIVER_KEY, LOG_USER, &status, sizeof(status)); // this is fatal, if we can't get the base values of the // counter or help names, then the names won't be available // to the requesting application so there's not much // point in continuing. goto OpenExitPoint; } size = sizeof (DWORD); status = RegQueryValueEx( hKeyDriverPerf, "First Counter", 0L, &type, (LPBYTE)&dwFirstCounter, &size); if (status != ERROR_SUCCESS) { REPORT_ERROR_DATA (VGAPERF_UNABLE_READ_FIRST_COUNTER, LOG_USER, &status, sizeof(status)); // this is fatal, if we can't get the base values of the // counter or help names, then the names won't be available // to the requesting application so there's not much // point in continuing. goto OpenExitPoint; } size = sizeof (DWORD); status = RegQueryValueEx( hKeyDriverPerf, "First Help", 0L, &type, (LPBYTE)&dwFirstHelp, &size); if (status != ERROR_SUCCESS) { REPORT_ERROR_DATA (VGAPERF_UNABLE_READ_FIRST_HELP, LOG_USER, &status, sizeof(status)); // this is fatal, if we can't get the base values of the // counter or help names, then the names won't be available // to the requesting application so there's not much // point in continuing. goto OpenExitPoint; } // // NOTE: the initialization program could also retrieve // LastCounter and LastHelp if they wanted to do // bounds checking on the new number. e.g. // // counter->CounterNameTitleIndex += dwFirstCounter; // if (counter->CounterNameTitleIndex > dwLastCounter) { // LogErrorToEventLog (INDEX_OUT_OF_BOUNDS); // } VgaDataDefinition.VgaObjectType.ObjectNameTitleIndex += dwFirstCounter; VgaDataDefinition.VgaObjectType.ObjectHelpTitleIndex += dwFirstHelp; VgaDataDefinition.NumBitBlts.CounterNameTitleIndex += dwFirstCounter; VgaDataDefinition.NumBitBlts.CounterHelpTitleIndex += dwFirstHelp; VgaDataDefinition.NumTextOuts.CounterNameTitleIndex += dwFirstCounter; VgaDataDefinition.NumTextOuts.CounterHelpTitleIndex += dwFirstHelp; RegCloseKey (hKeyDriverPerf); // close key to registry bInitOK = TRUE; // ok to use this function } dwOpenCount++; // increment OPEN counter status = ERROR_SUCCESS; // for successful exit OpenExitPoint: return status; } DWORD APIENTRY CollectVgaPerformanceData( IN LPWSTR lpValueName, IN OUT LPVOID *lppData, IN OUT LPDWORD lpcbTotalBytes, IN OUT LPDWORD lpNumObjectTypes ) /*++ Routine Description: This routine will return the data for the VGA counters. Arguments: IN LPWSTR lpValueName pointer to a wide character string passed by registry. IN OUT LPVOID *lppData IN: pointer to the address of the buffer to receive the completed PerfDataBlock and subordinate structures. This routine will append its data to the buffer starting at the point referenced by *lppData. OUT: points to the first byte after the data structure added by this routine. This routine updated the value at lppdata after appending its data. IN OUT LPDWORD lpcbTotalBytes IN: the address of the DWORD that tells the size in bytes of the buffer referenced by the lppData argument OUT: the number of bytes added by this routine is written to the DWORD pointed to by this argument IN OUT LPDWORD NumObjectTypes IN: the address of the DWORD to receive the number of objects added by this routine OUT: the number of objects added by this routine is written to the DWORD pointed to by this argument Return Value: ERROR_MORE_DATA if buffer passed is too small to hold data any error conditions encountered are reported to the event log if event logging is enabled. ERROR_SUCCESS if success or any other error. Errors, however are also reported to the event log. --*/ { // Variables for reformatting the data ULONG SpaceNeeded; PDWORD pdwCounter; PERF_COUNTER_BLOCK *pPerfCounterBlock; VGA_DATA_DEFINITION *pVgaDataDefinition; // Variables for collecting data about Vga Resouces LPWSTR lpFromString; LPWSTR lpToString; INT iStringLength; // variables used for error logging DWORD dwDataReturn[2]; DWORD dwQueryType; // // before doing anything else, see if Open went OK // if (!bInitOK) { // unable to continue because open failed. *lpcbTotalBytes = (DWORD) 0; *lpNumObjectTypes = (DWORD) 0; return ERROR_SUCCESS; // yes, this is a successful exit } // see if this is a foreign (i.e. non-NT) computer data request // dwQueryType = GetQueryType (lpValueName); if (dwQueryType == QUERY_FOREIGN) { // this routine does not service requests for data from // Non-NT computers *lpcbTotalBytes = (DWORD) 0; *lpNumObjectTypes = (DWORD) 0; return ERROR_SUCCESS; } if (dwQueryType == QUERY_ITEMS){ if ( !(IsNumberInUnicodeList (VgaDataDefinition.VgaObjectType.ObjectNameTitleIndex, lpValueName))) { // request received for data object not provided by this routine *lpcbTotalBytes = (DWORD) 0; *lpNumObjectTypes = (DWORD) 0; return ERROR_SUCCESS; } } pVgaDataDefinition = (VGA_DATA_DEFINITION *) *lppData; SpaceNeeded = sizeof(VGA_DATA_DEFINITION) + SIZE_OF_VGA_PERFORMANCE_DATA; if ( *lpcbTotalBytes < SpaceNeeded ) { *lpcbTotalBytes = (DWORD) 0; *lpNumObjectTypes = (DWORD) 0; return ERROR_MORE_DATA; } // // Copy the (constant, initialized) Object Type and counter definitions // to the caller's data buffer // memmove(pVgaDataDefinition, &VgaDataDefinition, sizeof(VGA_DATA_DEFINITION)); // // Format and collect VGA data from shared memory // pPerfCounterBlock = (PERF_COUNTER_BLOCK *) &pVgaDataDefinition[1]; pPerfCounterBlock->ByteLength = SIZE_OF_VGA_PERFORMANCE_DATA; pdwCounter = (PDWORD) (&pPerfCounterBlock[1]); *pdwCounter = *((PDWORD) pCounterBlock); *++pdwCounter = ((PDWORD) pCounterBlock)[1]; *lppData = (PVOID) ++pdwCounter; // update arguments fore return *lpNumObjectTypes = 1; *lpcbTotalBytes = (PBYTE) pdwCounter - (PBYTE) pVgaDataDefinition; return ERROR_SUCCESS; } DWORD APIENTRY CloseVgaPerformanceData( ) /*++ Routine Description: This routine closes the open handles to VGA device performance counters Arguments: None. Return Value: ERROR_SUCCESS --*/ { if (!(--dwOpenCount)) { // when this is the last thread... CloseHandle(hVgaSharedMemory); pCounterBlock = NULL; MonCloseEventLog(); } return ERROR_SUCCESS; } __________________________________________________________________

The next two counters handle declarations for the messages used in the Event Log.


__________________________________________________________________
/*++ BUILD Version: 0001 // Increment this if a change has global effects Copyright (c) 1992 Microsoft Corporation Module Name: perfmsg.h Abstract: This file provides the macros and definitions used by the extensible counters for reporting events to the event logging facility Revision History: --*/ #ifndef _PERFMSG_H_ #define _PERFMSG_H_ // // Report error message ID's for Counters // #define APP_NAME "vgactrs" // // The constant below defines how many (if any) messages will be reported // to the event logger. As the number goes up in value more and more events // will be reported. The purpose of this is to allow lots of messages during // development and debugging (e.g. a message level of 3) to a minimum of // messages (e.g. operational messages with a level of 1) or no messages if // message logging inflicts too much of a performance penalty. Right now // this is a compile time constant, but could later become a registry entry. // // Levels: LOG_NONE = No event log messages ever // LOG_USER = User event log messages (e.g. errors) // LOG_DEBUG = Minimum Debugging // LOG_VERBOSE = Maximum Debugging // #define LOG_NONE 0 #define LOG_USER 1 #define LOG_DEBUG 2 #define LOG_VERBOSE 3 #define MESSAGE_LEVEL_DEFAULT LOG_USER // define macros // // Format for event log calls without corresponding insertion strings is: // REPORT_xxx (message_value, message_level) // where: // xxx is the severity to be displayed in the event log // message_value is the numeric ID from above // message_level is the "filtering" level of error reporting // using the error levels above. // // if the message has a corresponding insertion string whose symbol conforms // to the format CONSTANT = numeric value and CONSTANT_S = string constant for // that message, then the // // REPORT_xxx_STRING (message_value, message_level) // // macro may be used. // // // REPORT_SUCCESS was intended to show Success in the error log, rather it // shows "N/A" so for now it's the same as information, though it could // (should) be changed in the future // #define REPORT_SUCCESS(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_INFORMATION_TYPE, \ 0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE) #define REPORT_INFORMATION(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_INFORMATION_TYPE, \ 0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE) #define REPORT_WARNING(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_WARNING_TYPE, \ 0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE) #define REPORT_ERROR(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, \ 0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE) #define REPORT_INFORMATION_DATA(i,l,d,s) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_INFORMATION_TYPE, \ 0, i, (PSID)NULL, 0, s, NULL, (PVOID)(d)) : FALSE) #define REPORT_WARNING_DATA(i,l,d,s) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_WARNING_TYPE, \ 0, i, (PSID)NULL, 0, s, NULL, (PVOID)(d)) : FALSE) #define REPORT_ERROR_DATA(i,l,d,s) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, \ 0, i, (PSID)NULL, 0, s, NULL, (PVOID)(d)) : FALSE) // External Variables extern HANDLE hEventLog; // handle to event log extern DWORD dwLogUsers; // counter of event log using routines extern DWORD MESSAGE_LEVEL; // event logging detail level #endif //_PERFMSG_H_ __________________________________________________________________

Here's some more message stuff:


__________________________________________________________________ ; ;Copyright (c) 1992 Microsoft Corporation ; ;Module Name: ; ; vgactrs.h ; (derived from vgactrs.mc by the message compiler ) ; ;Abstract: ; ; Event message definitions used by routines in VGACTRS.DLL ; ;Revision History: ; ;--*/ ;// ;#ifndef _VGACTRS_H_ ;#define _VGACTRS_H_ ;// MessageIdTypedef=DWORD ;// ;// Perfutil messages ;// MessageId=1900 Severity=Informational Facility=Application SymbolicName=UTIL_LOG_OPEN Language=English An extensible counter has opened the Event Log for VGACTRS.DLL . ;// MessageId=1999 Severity=Informational Facility=Application SymbolicName=UTIL_CLOSING_LOG Language=English An extensible counter has closed the Event Log for VGACTRS.DLL . ;// MessageId=2000 Severity=Error Facility=Application SymbolicName=VGAPERF_OPEN_FILE_MAPPING_ERROR Language=English Unable to open mapped file containing VGA driver performance data. . ;// MessageId=+1 Severity=Error Facility=Application SymbolicName=VGAPERF_UNABLE_MAP_VIEW_OF_FILE Language=English Unable to map to shared memory file containing VGA driver performance data. . ;// MessageId=+1 Severity=Error Facility=Application SymbolicName=VGAPERF_UNABLE_OPEN_DRIVER_KEY Language=English Unable open "Performance" key of VGA driver in registry. Status code is returned in data. . ;// MessageId=+1 Severity=Error Facility=Application SymbolicName=VGAPERF_UNABLE_READ_FIRST_COUNTER Language=English Unable to read the "First Counter" value under the Vga\Performance Key. Status codes returned in data. . ;// MessageId=+1 Severity=Error Facility=Application SymbolicName=VGAPERF_UNABLE_READ_FIRST_HELP Language=English Unable to read the "First Help" value under the Vga\Performance Key. Status codes returned in data. . ;// ;#endif // _VGACTRS_H_ __________________________________________________________________

The remaining files are used in the process of building the measurement DLL. First we have VGACTRS.EF.


__________________________________________________________________
LIBRARY vgactrs DESCRIPTION 'Performance Monitor Counter' EXPORTS OpenVgaPerformanceData @1 CollectVgaPerformanceData @2 CloseVgaPerformanceData @3 __________________________________________________________________

The MAKFILE is crucial to the build process, as you might already suspect:


__________________________________________________________________
# # DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source # file to this component. This file merely indirects to the real make file # that is shared by all the driver components of the Windows NT DDK # !INCLUDE $(NTMAKEENV)\makefile.def __________________________________________________________________

Next is MAKEFILE.INC:


__________________________________________________________________
.\msg00001.bin : vgactrs.mc erase .\vgactrs.h erase .\msg00001.bin erase .\vgactrs.rc mc -v -s vgactrs.mc .\vgactrs.rc : vgactrs.mc erase .\vgactrs.h erase .\msg00001.bin erase .\vgactrs.rc mc -v -s vgactrs.mc .\vgactrs.h: vgactrs.mc erase .\vgactrs.h erase .\msg00001.bin erase .\vgactrs.rc mc -v -s vgactrs.mc __________________________________________________________________

Finally, the glue that binds it all together, the SOURCES file that describes the build procedure to the build utility:


__________________________________________________________________
TARGETNAME=vgactrs TARGETPATH=$(BASEDIR)\lib TARGETTYPE=DYNLINK TARGETLIBS=$(BASEDIR)\lib\*\$(DDKBUILDENV)\kernel32.lib \ $(BASEDIR)\lib\*\$(DDKBUILDENV)\advapi32.lib DLLBASE=0x7500000 USE_CRTDLL=1 SOURCES=perfutil.c \ perfvga.c \ datavga.c \ vgactrs.rc C_DEFINES= -DWIN32 -DSTRICT NTTARGETFILE0=vgactrs.h vgactrs.rc msg00001.bin __________________________________________________________________

Okay, now it's your turn.