Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

820 lines
23 KiB

/*===================================================================
Microsoft Denali
Microsoft Confidential.
Copyright 1996 Microsoft Corporation. All Rights Reserved.
Component: Main
File: axperf.cpp
Owner: LeiJin
Abstract:
This file implements the Extensible Objects for the ActiveX Server
object type
===================================================================*/
//--------------------------------------------------------------------
// Include Files
//
//--------------------------------------------------------------------
#include "denpre.h"
#pragma hdrstop
#include "windows.h"
#include "winperf.h"
#include "axpfdata.h"
#define _NO_TRACING_
#include "dbgutil.h"
//DECLARE_DEBUG_PRINTS_OBJECT();
//DECLARE_DEBUG_VARIABLE();
#include <asppdef.h> // from denali
#include <perfutil.h>
#define QUERY_GLOBAL 1
#define QUERY_ITEMS 2
#define QUERY_FOREIGN 3
#define QUERY_COSTLY 4
// 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)
extern AXPD g_AxDataDefinition;
DWORD g_dwOpenCount = 0;
BOOL bInitOK = FALSE; // true = DLL Initialized OK
BOOL bSharedMemInitd = FALSE; // state of Shared init
HANDLE g_hASPWASProcessWait = NULL; // handle to the W3SVC process
// so we know when to release
// the counters.
// WinSE 5901
CRITICAL_SECTION g_CS;
WCHAR GLOBAL_STRING[] = L"Global";
WCHAR FOREIGN_STRING[] = L"Foreign";
WCHAR COSTLY_STRING[] = L"Costly";
WCHAR NULL_STRING[] = L"\0"; // pointer to null string
/*
* Output Debug String should occur in Debug only
*/
#ifdef _DEBUG
BOOL gfOutputDebugString = TRUE;
#else
BOOL gfOutputDebugString = FALSE;
#endif
#define DebugOutputDebugString(x) \
{\
if (gfOutputDebugString) \
{ \
OutputDebugString(x); \
} \
}
//-------------------------------------------------------------------
// Function Prototypes
//
// these are used to insure that the data collection functions accessed
// by Perf lib will have the correct calling format
//-------------------------------------------------------------------
DWORD APIENTRY OpenASPPerformanceData(LPWSTR lpDeviceNames);
DWORD APIENTRY CollectASPPerformanceData(LPWSTR lpValueName,
LPVOID *lppData,
LPDWORD lpcbTotalBytes,
LPDWORD lpNumObjectTypes
);
DWORD APIENTRY CloseASPPerformanceData(void);
DWORD APIENTRY RegisterAXS(void);
DWORD APIENTRY UnRegisterAXS(void);
DWORD GetQueryType (IN LPWSTR lpValue);
BOOL IsNumberInUnicodeList ( IN DWORD dwNumber,
IN LPWSTR lpwszUnicodeList);
CPerfMainBlock g_Shared; // shared global memory block
/***************************************************************************++
Routine Description:
Routine drops the shared memory if the managing process of the memory
goes away.
Arguments:
LPVOID lpParameter - Unused
BOOL bUnused - Unused
Return Value:
None
--***************************************************************************/
VOID CALLBACK ShutdownMemory(
PVOID,
BOOLEAN
)
{
EnterCriticalSection ( &g_CS );
//
// Now clean up the shared memory object.
//
if ( bSharedMemInitd )
{
g_Shared.UnInit();
bSharedMemInitd = FALSE;
}
LeaveCriticalSection ( &g_CS );
}
//--------------------------------------------------------------------
//
// OpenASPPerformanceData
//
// This routine will open and map the memory used by the ActiveX Server
// to pass performance data in. This routine also initializes the data
// structure used to pass data back to the registry.
//
// Arguments:
//
// Pointer to object ID to be opened.
//
// Return Value:
//
// None.
//--------------------------------------------------------------------
//extern "C" DWORD APIENTRY OpenASPPerformanceData(LPWSTR lpDeviceNames)
DWORD APIENTRY OpenASPPerformanceData(LPWSTR lpDeviceNames)
{
int status;
DWORD RetCode = ERROR_SUCCESS;
PERF_COUNTER_DEFINITION *pCounterDef;
DWORD size = sizeof(DWORD);
DebugOutputDebugString("Open");
// WinSE 5901
EnterCriticalSection(&g_CS);
LONG nOpenCount = InterlockedIncrement((LONG *)&g_dwOpenCount);
if (nOpenCount > 1){
goto ExitPathSuccess;
};
// Hold the counter to 1, even if we are no sure to have this
// initialized correctly
// 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
HKEY hKeyServerPerf;
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"SYSTEM\\CurrentControlSet\\Services\\ASP\\Performance",
0L,
KEY_READ,
&hKeyServerPerf);
if (ERROR_SUCCESS != status) {
RetCode = status;
goto ExitPath;
}
DWORD type;
DWORD dwFirstCounter;
DWORD dwFirstHelp;
status = RegQueryValueEx(hKeyServerPerf,
"First Counter",
0L,
&type,
(LPBYTE)&dwFirstCounter,
&size);
if (ERROR_SUCCESS != status || size != sizeof(DWORD)) {
RegCloseKey(hKeyServerPerf);
RetCode = status;
goto ExitPath;
}
status = RegQueryValueEx(hKeyServerPerf,
"First Help",
0L,
&type,
(LPBYTE)&dwFirstHelp,
&size);
if (ERROR_SUCCESS != status || size != sizeof(DWORD)) {
RegCloseKey(hKeyServerPerf);
RetCode = status;
goto ExitPath;
}
//
// NOTE: the initialiation could also retrieve
// LastCounter and LastHelp if they wanted
// to do bounds checking on the new number
//
g_AxDataDefinition.AXSObjectType.ObjectNameTitleIndex += dwFirstCounter;
g_AxDataDefinition.AXSObjectType.ObjectHelpTitleIndex += dwFirstHelp;
pCounterDef = (PERF_COUNTER_DEFINITION *)&(g_AxDataDefinition.Counters[0]);
int i;
for (i = 0; i < AX_NUM_PERFCOUNT; i++, pCounterDef++) {
pCounterDef->CounterNameTitleIndex += dwFirstCounter;
pCounterDef->CounterHelpTitleIndex += dwFirstHelp;
}
RegCloseKey(hKeyServerPerf); // close key to registry
bInitOK = TRUE; // ok to use this function
// we have already incremented g_dwOpenCount
// before going through this path
ExitPathSuccess:
LeaveCriticalSection(&g_CS);
return ERROR_SUCCESS;
ExitPath:
InterlockedDecrement((LONG *)&g_dwOpenCount);
LeaveCriticalSection(&g_CS);
return RetCode;
}
//--------------------------------------------------------------------
// DWORD CollectASPPerformanceData
//
// Description:
//
// This routine will return the data for the AxctiveX Server counters.
//
// Arguments:
//
// IN LPWSTR lpValueName
// pointer to a wide chacter 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 the *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 that receives the number of the 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
//
// ERROR_SUCCESS if success or any other error.
//
//--------------------------------------------------------------------
DWORD APIENTRY CollectASPPerformanceData(IN LPWSTR lpValueName,
IN OUT LPVOID *lppData,
IN OUT LPDWORD lpcbTotalBytes,
IN OUT LPDWORD lpNumObjectTypes)
{
BOOL fHookingUp = FALSE;
// before doing anything else, see if Open went Ok.
DebugOutputDebugString("collect");
if(!bInitOK) {
//unable to continue because open failed
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_SUCCESS;
}
//
// variables used for error logging
DWORD dwQueryType;
// see if this is a foreign(i.e. non-NT) computer data request
dwQueryType = GetQueryType(lpValueName);
if (QUERY_FOREIGN == dwQueryType) {
// this routine does not service requests for data from
// Non-NT computers
*lpcbTotalBytes = (DWORD)0;
*lpNumObjectTypes = (DWORD)0;
return ERROR_SUCCESS;
}
if (QUERY_ITEMS == dwQueryType) {
if (!(IsNumberInUnicodeList(g_AxDataDefinition.AXSObjectType.ObjectNameTitleIndex,
lpValueName))) {
// request received for data object not provided by this routine
*lpcbTotalBytes = (DWORD)0;
*lpNumObjectTypes = (DWORD)0;
return ERROR_SUCCESS;
}
}
if (QUERY_GLOBAL == dwQueryType) {
/* Comment the following code out, looks like that it is for
debugging only.
int i;
i++;
*/
}
AXPD *pAxDataDefinition = (AXPD *)*lppData;
ULONG SpaceNeeded = QWORD_MULTIPLE((sizeof(AXPD) + SIZE_OF_AX_PERF_DATA));
if ( *lpcbTotalBytes < SpaceNeeded) {
*lpcbTotalBytes = (DWORD)0;
*lpNumObjectTypes = (DWORD)0;
return ERROR_MORE_DATA;
}
//
//Copy the (constant, initialized) Object Type and counter defintions to the caller's
//data buffer
//
memmove(pAxDataDefinition, &g_AxDataDefinition, sizeof(AXPD));
//
// Format and collect Active X server performance data from shared memory
//
PERF_COUNTER_BLOCK *pPerfCounterBlock = (PERF_COUNTER_BLOCK *)&pAxDataDefinition[1];
pPerfCounterBlock->ByteLength = SIZE_OF_AX_PERF_DATA;
PDWORD pdwCounter = (PDWORD)(&pPerfCounterBlock[1]);
//
// Must be in a Critical Section when dealing
// with the shared memory, so we don't attempt
// to release it while we are using it.
//
EnterCriticalSection(&g_CS);
// if the shared memory isn't init, try again here
// Whether we are trying to hook up or not will depend
// completely on whether we are all ready hooked up.
fHookingUp = !bSharedMemInitd;
if (bSharedMemInitd == FALSE) {
bSharedMemInitd = !!SUCCEEDED(g_Shared.Init());
}
// If we initialized the shared memory then we need
// to start monitoring the W3SVC to make sure we let
// go of the memory if W3SVC goes down.
if ( bSharedMemInitd && fHookingUp )
{
//
// if we re-initialized then we need to setup the
// wait on the process again. it is possible that
// the previous wait has not been cleaned up (since
// we can't clean it up in the callback function) so
// if this is the case we need to clean it up first.
//
if ( g_hASPWASProcessWait != NULL )
{
if ( !UnregisterWait( g_hASPWASProcessWait ) )
{
// Nothing we could do here. We will
// end up just leaking it.
}
g_hASPWASProcessWait = NULL;
}
HANDLE hWASHandle = g_Shared.GetWASProcessHandle();
if ( hWASHandle == NULL )
{
g_Shared.UnInit();
bSharedMemInitd = FALSE;
}
else
{
//
// Register to wait on the managing process,
// so we release any shared memory if the managing
// process shutsdown or crashes.
//
if ( !RegisterWaitForSingleObject( &g_hASPWASProcessWait,
hWASHandle,
&ShutdownMemory,
NULL,
INFINITE,
WT_EXECUTEONLYONCE |
WT_EXECUTEINIOTHREAD ) )
{
// If we could not wait on the handle then
// we have to release the shared memory now,
// it's not safe to keep it around.
g_Shared.UnInit();
bSharedMemInitd = FALSE;
}
}
}
// Get statistics from shared memory if the shared memory is
// inited. Else, return just the definitions.
// If we didn't get the counters we want to just memset the structure
// to show all the counters as zero.
if ((bSharedMemInitd == FALSE) || FAILED(g_Shared.GetStats(pdwCounter)))
{
memset( pdwCounter, 0, SIZE_OF_AX_PERF_DATA );
}
// Done dealing with the shared memory,
// so now we can go ahead and release it
// if need be.
LeaveCriticalSection(&g_CS);
pdwCounter += AX_NUM_PERFCOUNT;
// update arguments for return
*lpNumObjectTypes = 1;
*lpcbTotalBytes = QWORD_MULTIPLE((DIFF((PBYTE)pdwCounter - (PBYTE)pAxDataDefinition)));
*lppData = (PBYTE)(*lppData) + *lpcbTotalBytes;
return ERROR_SUCCESS;
}
//-------------------------------------------------------------------
// DWORD CloseASPPerformanceData
//
// Description:
//
// This routine closes the open handles to ActiveX Server performance
// counters.
//
// Arguments:
//
// None.
//
// Return Value:
//
// ERROR_SUCCESS
//
//--------------------------------------------------------------------
DWORD APIENTRY CloseASPPerformanceData(void)
{
DebugOutputDebugString("Close");
EnterCriticalSection(&g_CS);
LONG nLeft = InterlockedDecrement((LONG *)&g_dwOpenCount);
if (nLeft == 0) {
g_Shared.UnInit();
bInitOK = FALSE;
};
if ( g_hASPWASProcessWait != NULL )
{
if ( !UnregisterWait( g_hASPWASProcessWait ) )
{
// Nothing we could do here. We will
// end up just leaking it.
}
g_hASPWASProcessWait = NULL;
}
LeaveCriticalSection(&g_CS);
return ERROR_SUCCESS;
}
static const TCHAR szPerformance[] = TEXT("SYSTEM\\CurrentControlSet\\Services\\ASP\\Performance");
static const TCHAR szAXS[] = TEXT("SYSTEM\\CurrentControlSet\\Services\\ASP");
static const TCHAR szLibrary[] = TEXT("Library");
static const TCHAR szOpen[] = TEXT("Open");
static const TCHAR szClose[] = TEXT("Close");
static const TCHAR szCollect[] = TEXT("Collect");
static const TCHAR szLibraryValue[] = TEXT("aspperf.dll");
static const TCHAR szOpenValue[] = TEXT("OpenASPPerformanceData");
static const TCHAR szCloseValue[] = TEXT("CloseASPPerformanceData");
static const TCHAR szCollectValue[] = TEXT("CollectASPPerformanceData");
//--------------------------------------------------------------------
//
//
//--------------------------------------------------------------------
DWORD APIENTRY RegisterAXS(void)
{
HKEY hkey;
if ((RegCreateKey(HKEY_LOCAL_MACHINE, szPerformance, &hkey)) != ERROR_SUCCESS)
return E_FAIL;
if ((RegSetValueEx(hkey, szLibrary, 0, REG_SZ, (const unsigned char *)&szLibraryValue, lstrlen(szLibraryValue))) != ERROR_SUCCESS)
goto LRegErr;
if ((RegSetValueEx(hkey, szOpen, 0, REG_SZ, (const unsigned char *)&szOpenValue, lstrlen(szOpenValue))) != ERROR_SUCCESS)
goto LRegErr;
if ((RegSetValueEx(hkey, szClose, 0, REG_SZ, (const unsigned char *)&szCloseValue, lstrlen(szCloseValue))) != ERROR_SUCCESS)
goto LRegErr;
if ((RegSetValueEx(hkey, szCollect, 0, REG_SZ, (const unsigned char *)&szCollectValue, lstrlen(szCollectValue))) != ERROR_SUCCESS)
goto LRegErr;
RegCloseKey(hkey);
return NOERROR;
LRegErr:
RegCloseKey(hkey);
return E_FAIL;
}
//--------------------------------------------------------------------
//
//
//--------------------------------------------------------------------
DWORD APIENTRY UnRegisterAXS(void)
{
if ((RegDeleteKey(HKEY_LOCAL_MACHINE, szPerformance)) != ERROR_SUCCESS)
return (E_FAIL);
if ((RegDeleteKey(HKEY_LOCAL_MACHINE, szAXS)) != ERROR_SUCCESS)
return (E_FAIL);
else
return NOERROR;
}
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 "Foriegn" 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 founde
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 delimter 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
STDAPI DLLRegisterServer(void)
{
return RegisterAXS();
}
BOOL WINAPI DllMain(HINSTANCE hInstDLL, // handle to the DLL module
DWORD dwReason, // reason for calling function
LPVOID lpvReserved // reserved
)
{
switch(dwReason){
case DLL_PROCESS_ATTACH:
// CREATE_DEBUG_PRINT_OBJECT( "aspperf" );
DisableThreadLibraryCalls(hInstDLL);
InitializeCriticalSection(&g_CS);
return TRUE;
case DLL_PROCESS_DETACH:
DeleteCriticalSection(&g_CS);
return TRUE;
}
return TRUE;
}