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.