// --gwperf.c-------------------------------------------------------------------
//
// This file contains the DLL side of the Exchange SDK performance
// monitoring component. When this DLL is called by the NT Performance
// Monitor it reads the performance data from a shared memory section
// created by the gateway side, and returns it to the Performance Monitor.
// This DLL is also capable of reading the data from several different
// gateway processes and combining them into one single Performance
// Monitor object with multiple instances.
//
// Copyright (C) Microsoft Corp. 1986-1996. All Rights Reserved.
// -----------------------------------------------------------------------------
#include "edk.h"
#include "monitor.h"
#include "monshare.h"
#include "gwperfm.h"
#include <winperf.h>
//
// Debugging Information
//
// Define VERBOSE_DEBUG here to write to the debug log with every call to
// CollectGatewayPerformanceData and the routines it calls. Do this only
// if you need it, since this routine is called once every second and can
// quickly fill up your disk!!!
// #define VERBOSE_DEBUG
//
// Local Structure Declarations
//
//$--GINSTANCE------------------------------------------------------------------
// Holds information about a single gateway instance.
// -----------------------------------------------------------------------------
typedef struct _GInstance // (hungarian notation = gi)
{
BOOL fOpen; // TRUE if this instance is open
HANDLE hFileMapping; // file mapping handle
LONG cLockReference; // mutex lock reference count
HANDLE hMutex; // mutex handle
LPMONSHAREDMEMORY lpmsmMemory; // shared mem for gw communication
LPWSTR lpwszName; // gateway instance name
} GINSTANCE, *LPGINSTANCE;
//$--GTYPE----------------------------------------------------------------------
// Holds information about the gateway type that this DLL handles.
// -----------------------------------------------------------------------------
typedef struct _GType // (hungarian notation = gt)
{
ULONG cCollectFailures; // number of times error on collect
ULONG cInstances; // number of instances in array
LPGINSTANCE rggiInstances; // alloc'd array of instance structs
LONG iObjectName; // title index of obj we return
} GTYPE, *LPGTYPE;
//
// Public Function Declarations
//
PM_OPEN_PROC OpenGatewayPerformanceData;
PM_COLLECT_PROC CollectGatewayPerformanceData;
PM_CLOSE_PROC CloseGatewayPerformanceData;
//
// Local Function Declarations
//
static DWORD CreateGType(
IN LPWSTR grwszInstanceNames,
OUT LPGTYPE * lppgt);
static DWORD DestroyGType(
IN OUT LPGTYPE lpgt);
static DWORD OpenGInstance(
IN LPWSTR lpwszInstanceName,
IN OUT LPGINSTANCE lpgi,
OUT LPLONG lpiObjectName);
static DWORD CloseGInstance(
IN OUT LPGINSTANCE lpgi);
static DWORD CountMultiStrings(
IN LPWSTR grwsz,
OUT LPULONG pcwsz);
static DWORD LockSharedMemory(
IN LPGINSTANCE lpgi);
static DWORD UnlockSharedMemory(
IN LPGINSTANCE lpgi);
static BOOL fOurDataRequested(
IN LPGTYPE lpgt,
IN LPWSTR lpwszRequest);
static VOID ReadSharedMemoryFromRegistry(
IN OUT LPGINSTANCE lpgi);
//
// Local Constants
//
static const ULONG MAX_COLLECT_FAILURES = 10;
//
// Local Variables
//
static LPGTYPE lpgtContext = NULL;
static SECURITY_ATTRIBUTES sa;
static char rgbForSecurityDescriptor[SECURITY_DESCRIPTOR_MIN_LENGTH];
static SECURITY_ATTRIBUTES * psa;
static SECURITY_DESCRIPTOR * psd;
//
// DLL Hook Routines
//
//$--DllEntryPoint------------------------------------------------------------
// Perform DLL initialization and shutdown. Returns TRUE if successful,
// and FALSE if unsuccessful.
// ---------------------------------------------------------------------------
BOOL WINAPI DllEntryPoint( // RETURNS: BOOL
IN HINSTANCE hInstDll, // DLL instance handle
IN DWORD fdwReason, // reason we're being called
IN LPVOID lpvReserved) // reserved
{
BOOL fResult = TRUE;
BOOL fEventLogOpen = FALSE;
HRESULT hr = NOERROR;
CHAR szDllPath[MAX_PATH+1] = {0};
DWORD cchDllPath = 0;
DEBUGPUBLIC("DllEntryPoint()\n");
// Check to see why we were called.
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
DEBUGACTION("DLL_PROCESS_ATTACH");
// Get the path and file name of this DLL.
cchDllPath = GetModuleFileName(
GetInstanceModule(hInstDll),
szDllPath,
sizeof(szDllPath) - 1);
if (cchDllPath == 0)
{
hr = HR_LOG(E_FAIL);
MODULE_WARNING(
"GWPERF_DLL_ENTRY_MODULE_NAME: GetModuleFileName() failed.");
}
// If getting the name succeeded then initialize event logging.
else
{
hr = HrEventOpenLog(
"EDK GWPerf",
szDllPath,
NULL,
NULL,
NULL,
NULL);
if (FAILED(hr))
{
MODULE_WARNING1(
"GWPERF_DLL_ENTRY_OPEN_LOG: HrEventOpenLog() failed for DLL %s.",
szDllPath);
}
else
{
fEventLogOpen = TRUE;
}
}
// Set up a security descriptor with a NULL DACL, to allow all
// access to an object. This is used if we need to create the
// mutex and shared memory section. Failure to do this is a
// fatal error.
psa = &sa;
psd = (SECURITY_DESCRIPTOR *)rgbForSecurityDescriptor;
if (!InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION))
{
EventLogMsg(
GWPERF_ERROR,
0,
GetLastError());
fResult = FALSE;
}
else if (!SetSecurityDescriptorDacl(psd, TRUE, (PACL) NULL, FALSE))
{
EventLogMsg(
GWPERF_ERROR,
0,
GetLastError());
fResult = FALSE;
}
else
{
psa->nLength = sizeof(*psa);
psa->lpSecurityDescriptor = psd;
psa->bInheritHandle = TRUE;
}
// If there was a fatal error on open, then close the event log.
if (fResult == FALSE && fEventLogOpen == TRUE)
{
(void) HrEventCloseLog();
fEventLogOpen = FALSE;
}
break;
case DLL_THREAD_ATTACH:
DEBUGACTION("DLL_THREAD_ATTACH");
break;
case DLL_THREAD_DETACH:
DEBUGACTION("DLL_THREAD_DETACH");
break;
case DLL_PROCESS_DETACH:
DEBUGACTION("DLL_PROCESS_DETACH");
// Shut down event logging.
hr = HrEventCloseLog();
if (FAILED(hr))
{
MODULE_WARNING(
"GWPERF_DLL_ENTRY_CLOSE_LOG: HrEventCloseLog() failed for DLL %s.");
}
break;
default:
// Invalid reason.
MODULE_ERROR("GWPERF_DLL_ENTRY_REASON: Invalid reason code.");
fResult = FALSE;
break;
}
ASSERTERROR(fResult, "GWPERF_DLL_ENTRY_FAILED: GWPerf.dll failed to load.");
return fResult;
}
//$--OpenGatewayPerformanceData-------------------------------------------------
// Function called by the NT Performance Monitor (via the registry) to begin
// performance monitoring on a gateway.
//
// NOTE: In this function, all strings are explicitly WCHAR UNICODE strings
// because that is what is passed in as device names.
// -----------------------------------------------------------------------------
DWORD APIENTRY OpenGatewayPerformanceData(// RETURNS: Win32 error code
IN LPWSTR grwszInstanceNames)// strings from Linkage\Export
{
DWORDdwStatus = ERROR_SUCCESS;
DEBUGPUBLIC("OpenGatewayPerformanceData()\n");
// If we're already open then the path to this DLL must exist in multiple
// Performance keys, which is an error.
if (lpgtContext)
{
dwStatus = ERROR_DLL_INIT_FAILED;
EventLogMsg(
GWPERF_OPEN_ALREADY_OPEN,
0,
0);
goto cleanup;
}
// If they didn't have a Linkage\Export value then we can't continue.
if (grwszInstanceNames == NULL)
{
dwStatus = ERROR_DLL_INIT_FAILED;
EventLogMsg(
GWPERF_OPEN_NO_LINKAGE,
0,
0);
goto cleanup;
}
// Create the GTYPE structure and begin all the monitoring.
dwStatus = CreateGType(grwszInstanceNames, &lpgtContext);
if (dwStatus)
goto cleanup;
cleanup:
return dwStatus;
}
//$--CollectGatewayPerformanceData----------------------------------------------
// Function called by the NT Performance Monitor to collect performance data
// on a gateway. Always returns either ERROR_SUCCESS or ERROR_MORE_DATA.
// -----------------------------------------------------------------------------
DWORD APIENTRY CollectGatewayPerformanceData( // RETURNS: Win32 error code
IN LPWSTR lpwszRequest,// type of data requested
IN OUT LPVOID * lppvData,// buffer where data should be put
IN OUT LPDWORD lpcbBytes,// size of data buffer
OUT LPDWORD lpcObjectTypes)// number of object types returned
{
DWORDdwStatus = ERROR_SUCCESS;
LPBYTE*lppData= (LPBYTE *)lppvData;
DWORDigi= 0;
LPGINSTANCE lpgi= NULL;
LPMONSHAREDMEMORYlpmsm= NULL;
BOOLfPOTCopied= FALSE;
LPBYTElpOutBuf= *lppData;
LPBYTElpOutBufPtr= *lppData;
DWORDcbOutBufSize= *lpcbBytes;
BOOLfFoundRunning = FALSE;
PPERF_OBJECT_TYPElpOutPOT= NULL;
#ifdef VERBOSE_DEBUG
DEBUGPUBLIC("CollectGatewayPerformanceData()\n");
#endif
// Set the return variables to return no data.
*lpcbBytes = 0;
*lpcObjectTypes = 0;
dwStatus = ERROR_SUCCESS;
// If they didn't call open before calling here, then log an
// error (but only once).
if (lpgtContext == NULL)
{
static BOOL fAlreadyLogged = FALSE;
if (!fAlreadyLogged)
{
EventLogMsg(
GWPERF_COLLECT_NOT_OPEN,
0,
0);
fAlreadyLogged = TRUE;
}
goto cleanup;
}
// If we've failed too many times then return with no data.
if (lpgtContext->cCollectFailures > MAX_COLLECT_FAILURES)
{
goto cleanup;
}
// If we know what our object number is, and the request string is not
// asking for our object number, then return with no data.
if (lpgtContext->iObjectName != -1 &&
!fOurDataRequested(lpgtContext, lpwszRequest))
goto cleanup;
// Loop to build the data structure for all instances by appending
// together the structures for each instance. The new data structure
// is created in the buffer provided by the caller.
for (igi = 0; igi < lpgtContext->cInstances; igi++)
{
lpgi = &lpgtContext->rggiInstances[igi];
lpmsm = lpgi->lpmsmMemory;
// Initial check for valid data. If not valid here then don't bother
// locking the mutex. This keeps us from degrading system performance
// by locking and unlocking a mutex every time we are called when
// our gateway isn't running.
if (!lpmsm->fDataIsValid)
continue;
// Lock shared memory for gateway instance. It will be unlocked
// later. Any exceptional conditions between now and "later" must
// call UnlockSharedMemory.
dwStatus = LockSharedMemory(lpgi);
if (dwStatus)
goto cleanup;
// If this shared memory block doesn't contain valid data then skip it.
lpmsm = lpgi->lpmsmMemory;
if (!lpmsm->fDataIsValid)
{
dwStatus = UnlockSharedMemory(lpgi);
if (dwStatus)
goto cleanup;
continue;
}
// If we don't already have our object number then get it from the
// shared memory block.
if (lpgtContext->iObjectName == -1)
{
PPERF_OBJECT_TYPE lpPOT = NULL;
lpPOT = (PPERF_OBJECT_TYPE)
(((LPBYTE)(lpgi->lpmsmMemory))
+ lpgi->lpmsmMemory->ibHeaderOffset);
lpgtContext->iObjectName = lpPOT->ObjectNameTitleIndex;
// Now that we finally know our number, check to make sure it is one
// of the ones requested.
if (!fOurDataRequested(lpgtContext, lpwszRequest))
{
dwStatus = UnlockSharedMemory(lpgi);
goto cleanup;
}
}
// Flag that we found a running gateway instance (i.e. one that has
// the fDataIsValid flag set).
fFoundRunning = TRUE;
// Copy PERF_OBJECT_TYPE header structure from the first running
// gateway instance we find.
if (!fPOTCopied)
{
// If the buffer isn't big enough then request a larger buffer
// from the caller.
if (lpmsm->cbHeaderSize > cbOutBufSize)
{
dwStatus = UnlockSharedMemory(lpgi);
if (dwStatus)
goto cleanup;
dwStatus = ERROR_MORE_DATA;
goto cleanup;
}
// Copy the header.
MoveMemory(
lpOutBufPtr,
((LPBYTE)(lpmsm)) + lpmsm->ibHeaderOffset,
lpmsm->cbHeaderSize);
lpOutPOT = (PPERF_OBJECT_TYPE)lpOutBufPtr;
lpOutBufPtr += lpmsm->cbHeaderSize;
cbOutBufSize -= lpmsm->cbHeaderSize;
// Start the header's instance count at 0 instances (it will
// be incremented each time we add an instance structure).
lpOutPOT->NumInstances = 0;
// Flag that we copied over the PERF_OBJECT_TYPE header structure.
fPOTCopied = TRUE;
}
// If the buffer isn't big enough then request a larger buffer
// from the caller.
if (lpmsm->cbInstanceSize > cbOutBufSize)
{
dwStatus = UnlockSharedMemory(lpgi);
if (dwStatus)
goto cleanup;
dwStatus = ERROR_MORE_DATA;
goto cleanup;
}
// Copy the instance data from this gateway instance.
MoveMemory(
lpOutBufPtr,
((LPBYTE)(lpmsm)) + lpmsm->ibInstanceOffset,
lpmsm->cbInstanceSize);
lpOutBufPtr += lpmsm->cbInstanceSize;
cbOutBufSize -= lpmsm->cbInstanceSize;
// Bump up variables in the PERF_OBJECT_TYPE header to reflect
// the addition of this instance.
lpOutPOT->TotalByteLength = lpOutBufPtr - lpOutBuf;
lpOutPOT->NumInstances++;
// Unlock the shared memory.
dwStatus = UnlockSharedMemory(lpgi);
if (dwStatus)
goto cleanup;
}
// If we found a running gateway instance then update the return variables.
if (fFoundRunning)
{
*lpcObjectTypes = 1;
*lppData = lpOutBufPtr;
*lpcbBytes = lpOutBufPtr - lpOutBuf;
}
cleanup:
// If there was an error other than ERROR_MORE_DATA, then increment
// the collect failure count. When there gets to be too many errors,
// we will start returning "no data" on every call to collect.
if (dwStatus && dwStatus != ERROR_MORE_DATA)
{
++lpgtContext->cCollectFailures;
if (lpgtContext->cCollectFailures > MAX_COLLECT_FAILURES)
{
EventLogMsg(
GWPERF_COLLECT_TOO_MANY_FAILURES,
0,
0);
}
dwStatus = ERROR_SUCCESS;
}
return dwStatus;
}
//$--CloseGatewayPerformanceData------------------------------------------------
// Function called by the NT Performance Monitor to end collection of
// performance data.
// -----------------------------------------------------------------------------
DWORD APIENTRY CloseGatewayPerformanceData(// RETURNS: Win32 error code
void)
{
DWORDdwStatus = ERROR_SUCCESS;
DEBUGPUBLIC("CloseGatewayPerformanceData()\n");
// If they didn't call "open" then log an error (but only once).
if (lpgtContext == NULL)
{
static BOOL fAlreadyLogged = FALSE;
if (!fAlreadyLogged)
{
EventLogMsg(
GWPERF_CLOSE_NOT_OPEN,
0,
0);
fAlreadyLogged = TRUE;
}
goto cleanup;
}
// Free the gateway type context.
dwStatus = DestroyGType(lpgtContext);
if (dwStatus)
goto cleanup;
lpgtContext = NULL;
cleanup:
return dwStatus;
}
//$--CreateGType----------------------------------------------------------------
// Create a GTYPE structure.
// -----------------------------------------------------------------------------
static DWORD CreateGType( // RETURNS: Win32 error code
IN LPWSTR grwszInstanceNames, // group of instance name strings
OUT LPGTYPE * lppgt) // allocated GTYPE structure
{
DWORDdwStatus = ERROR_SUCCESS;
ULONG cInstances = 0;
LPGINSTANCE lpgi= NULL;
ULONGigi= 0;
LPWSTRlpwszInstanceName= NULL;
DWORD iObjectName = 0;
LPGTYPE lpgt = NULL;
DEBUGPRIVATE("CreateGType()\n");
// Count the instance name strings passed to us.
dwStatus = CountMultiStrings(grwszInstanceNames, &cInstances);
if (dwStatus)
goto cleanup;
// Allocate the GTYPE structure.
lpgt = malloc(sizeof(*lpgt));
if (lpgt == NULL)
{
dwStatus = ERROR_NOT_ENOUGH_MEMORY;
EventLogMsg(
GWPERF_OPEN_ERROR,
0,
1, dwStatus);
goto cleanup;
}
ZeroMemory(lpgt, sizeof(*lpgt));
// Fill in various fields in the GTYPE.
lpgt->cInstances = cInstances;
lpgt->iObjectName = -1;
// Allocate an array of GINSTANCE's, one for each instance name string.
lpgt->rggiInstances = calloc(cInstances, sizeof(GINSTANCE));
if (lpgt->rggiInstances == NULL)
{
dwStatus = ERROR_NOT_ENOUGH_MEMORY;
EventLogMsg(
GWPERF_OPEN_ERROR,
0,
1, dwStatus);
goto cleanup;
}
ZeroMemory(lpgt->rggiInstances, cInstances * sizeof(GINSTANCE));
// Loop to open the GINSTANCE's.
for (
igi = 0, lpwszInstanceName = grwszInstanceNames;
igi < lpgt->cInstances;
igi++, lpwszInstanceName += (wcslen(lpwszInstanceName) + 1))
{
lpgi = &lpgt->rggiInstances[igi];
// Open this gateway instance.
dwStatus = OpenGInstance(lpwszInstanceName, lpgi, &iObjectName);
if (dwStatus)
goto cleanup;
// If we haven't found the object name title index yet, and
// one was returned from OpenGInstance, use it.
if (lpgt->iObjectName == -1)
{
lpgt->iObjectName = iObjectName;
}
}
// Return the pointer to the GTYPE.
*lppgt = lpgt;
cleanup:
if (dwStatus && lpgt)
{
(void) DestroyGType(lpgt);
}
return dwStatus;
}
//$--DestroyGType---------------------------------------------------------------
// Destroy a GTYPE structure (even a partially constructed one).
// -----------------------------------------------------------------------------
static DWORD DestroyGType( // RETURNS: Win32 error code
IN OUT LPGTYPE lpgt) // allocated GTYPE structure
{
DWORDdwStatus = ERROR_SUCCESS;
DWORDdwStatusTemp = ERROR_SUCCESS;
ULONGigi= 0;
DEBUGPRIVATE("DestroyGType()\n");
if (lpgt)
{
// Loop to close each GINSTANCE.
if (lpgt->rggiInstances)
{
for (igi = 0; igi < lpgt->cInstances; igi++)
{
dwStatusTemp = CloseGInstance(&lpgt->rggiInstances[igi]);
if (dwStatusTemp)
{
dwStatus = dwStatusTemp;
}
}
FREE(lpgt->rggiInstances);
}
// Free the GTYPE.
FREE(lpgt);
}
return dwStatus;
}
//$--OpenGInstance--------------------------------------------------------------
// Sets up a GINSTANCE structure and maps its shared memory.
// -----------------------------------------------------------------------------
static DWORD OpenGInstance( // RETURNS: Win32 error code
IN LPWSTR lpwszInstanceName, // name of gateway instance
IN OUT LPGINSTANCE lpgi, // instance struct to fill in
OUT LPLONG lpiObjectName) // obj title name index for instance
// (-1 = it doesn't have valid data)
{
DWORDdwStatus = ERROR_SUCCESS;
LONG cchOutSize = 0;
PPERF_OBJECT_TYPE lpPOT = NULL;
WCHARwszMappingName[MAX_PATH] = {0};
WCHARwszMutexName[MAX_PATH] = {0};
LONG iObjectName = -1;
DEBUGPRIVATE("OpenGInstance()\n");
// Initially assume the instance is not locked.
lpgi->cLockReference = 0;
// Add an allocated copy of the instance name to the GINSTANCE structure.
lpgi->lpwszName = _wcsdup(lpwszInstanceName);
if (lpgi->lpwszName == NULL)
{
dwStatus = ERROR_NOT_ENOUGH_MEMORY;
EventLogMsg(
GWPERF_OPEN_ERROR,
0,
1, dwStatus);
goto cleanup;
}
// Create the name of the mutex.
cchOutSize = wsprintfW(
wszMutexName,
MON_MUTEX_NAME_TEMPLATE_W,
lpwszInstanceName);
if ((cchOutSize + 1) * sizeof(CHAR) > sizeof(wszMutexName))
{
dwStatus = ERROR_BUFFER_OVERFLOW;
EventLogMsg(
GWPERF_OPEN_ERROR,
0,
1, dwStatus);
goto cleanup;
}
(void) CharUpperW(wszMutexName);
// Create the name of the shared memory mapping.
cchOutSize = wsprintfW(
wszMappingName,
MON_MAPPING_NAME_TEMPLATE_W,
lpwszInstanceName);
if ((cchOutSize + 1) * sizeof(CHAR) > sizeof(wszMappingName))
{
dwStatus = ERROR_BUFFER_OVERFLOW;
EventLogMsg(
GWPERF_OPEN_ERROR,
0,
1, dwStatus);
goto cleanup;
}
(void) CharUpperW(wszMappingName);
// First try to open the mutex and get ownership.
lpgi->hMutex = OpenMutexW(
MUTEX_ALL_ACCESS,
FALSE,
wszMutexName);
if (lpgi->hMutex)
{
dwStatus = LockSharedMemory(lpgi);
if (dwStatus)
goto cleanup;
}
// If the mutex didn't exist then create it and get ownership.
else
{
dwStatus = GetLastError();
if (dwStatus != ERROR_FILE_NOT_FOUND)
{
EventLogMsg(
GWPERF_OPEN_ERROR,
0,
1, dwStatus);
goto cleanup;
}
lpgi->hMutex = CreateMutexW(
psa,
TRUE,
wszMutexName);
if (lpgi->hMutex == NULL)
{
dwStatus = GetLastError();
EventLogMsg(
GWPERF_OPEN_ERROR,
0,
1, dwStatus);
goto cleanup;
}
lpgi->cLockReference++;
}
// First try to open the shared memory mapping.
lpgi->hFileMapping = OpenFileMappingW(
FILE_MAP_WRITE,
FALSE,
wszMappingName);
// If the shared memory mapping didn't exist then create it.
if (lpgi->hFileMapping == NULL)
{
dwStatus = GetLastError();
if (dwStatus != ERROR_FILE_NOT_FOUND)
{
EventLogMsg(
GWPERF_OPEN_ERROR,
0,
1, dwStatus);
goto cleanup;
}
lpgi->hFileMapping = CreateFileMappingW(
(HANDLE)0xFFFFFFFF, // create in paging file
psa, // no security
PAGE_READWRITE, // read/write access
0, // size (high)
sizeof(MONSHAREDMEMORY), // size(low)
wszMappingName);// mapping object name
if (lpgi->hFileMapping == NULL)
{
dwStatus = GetLastError();
EventLogMsg(
GWPERF_OPEN_ERROR,
0,
1, dwStatus);
goto cleanup;
}
}
// Map a view of the shared memory file.
lpgi->lpmsmMemory = MapViewOfFile(
lpgi->hFileMapping,
FILE_MAP_WRITE,
0, 0, 0);
if (lpgi->lpmsmMemory == NULL)
{
dwStatus = GetLastError();
EventLogMsg(
GWPERF_OPEN_ERROR,
0,
1, dwStatus);
goto cleanup;
}
// If the signature isn't correct then we have a version mismatch.
// (0 probably means it hasn't been fully created yet).
if (lpgi->lpmsmMemory->dwSignature != MON_SHARED_MEMORY_SIGNATURE &&
lpgi->lpmsmMemory->dwSignature != 0)
{
EventLogMsgW(
GWPERF_VERSION_MISMATCH,
1, lpwszInstanceName,
0);
dwStatus = ERROR_DLL_INIT_FAILED;
goto cleanup;
}
// If there is no valid data in the shared memory section then try
// to read it from the registry where the gateway saved it the last
// time it was started. If there was an error then the shared memory
// fDataIsValid flag is returned as FALSE.
if (!lpgi->lpmsmMemory->fDataIsValid)
{
ReadSharedMemoryFromRegistry(lpgi);
// Check the signature again because we just read it in.
if (lpgi->lpmsmMemory->dwSignature != MON_SHARED_MEMORY_SIGNATURE &&
lpgi->lpmsmMemory->dwSignature != 0)
{
EventLogMsgW(
GWPERF_VERSION_MISMATCH,
1, lpwszInstanceName,
0);
dwStatus = ERROR_DLL_INIT_FAILED;
goto cleanup;
}
}
// If there is valid data in the shared memory section then get the
// object name title index from it.
if (lpgi->lpmsmMemory->fDataIsValid)
{
lpPOT = (PPERF_OBJECT_TYPE) (((LPBYTE)(lpgi->lpmsmMemory))
+ lpgi->lpmsmMemory->ibHeaderOffset);
iObjectName = lpPOT->ObjectNameTitleIndex;
}
// Unlock the mutex.
dwStatus = UnlockSharedMemory(lpgi);
if (dwStatus)
goto cleanup;
// Flag that the instance is open.
lpgi->fOpen = TRUE;
// Return the object name title index.
*lpiObjectName = iObjectName;
cleanup:
// If we had an error then close the GINSTANCE and return the error code.
if (dwStatus)
{
(void) CloseGInstance(lpgi);
}
return dwStatus;
}
//$--CloseGInstance-------------------------------------------------------------
// Closes a GINSTANCE structure, closes handles, unmaps shared memory, etc.
// (even a partially opened one).
// -----------------------------------------------------------------------------
static DWORD CloseGInstance( // RETURNS: Win32 error code
IN OUT LPGINSTANCE lpgi) // instance struct to fill in
{
DWORDdwStatus = ERROR_SUCCESS;
DWORDdwStatusTemp = ERROR_SUCCESS;
BOOL fItWorked = TRUE;
DEBUGPRIVATE("CloseGInstance()\n");
// Free the allocated instance name.
FREE(lpgi->lpwszName);
// Release and close the shared memory mutex.
if (lpgi->hMutex)
{
// Release the mutex lock.
while (lpgi->cLockReference > 0)
{
dwStatusTemp = UnlockSharedMemory(lpgi);
if (dwStatusTemp)
{
dwStatus = dwStatusTemp;
break;
}
}
// Close the handle to the mutex.
fItWorked = CloseHandle(lpgi->hMutex);
if (!fItWorked)
{
dwStatus = GetLastError();
EventLogMsg(
GWPERF_ERROR,
0,
1, dwStatus);
}
lpgi->hMutex = NULL;
}
// Unmap the view of the shared memory.
if (lpgi->lpmsmMemory)
{
fItWorked = UnmapViewOfFile(lpgi->lpmsmMemory);
if (!fItWorked)
{
dwStatus = GetLastError();
EventLogMsg(
GWPERF_ERROR,
0,
1, dwStatus);
}
lpgi->lpmsmMemory = NULL;
}
// Close the file mapping handle.
if (lpgi->hFileMapping)
{
fItWorked = CloseHandle(lpgi->hFileMapping);
if (!fItWorked)
{
dwStatus = GetLastError();
EventLogMsg(
GWPERF_ERROR,
0,
1, dwStatus);
}
lpgi->hFileMapping = NULL;
}
// Zero out the GINSTANCE structure (which will also clear the fOpen flag).
ZeroMemory(lpgi, sizeof(*lpgi));
return dwStatus;
}
//$--CountMultiStrings----------------------------------------------------------
// Returns the number of null terminated strings in a group of word strings.
// A group of word strings consists of zero or more null-terminated word
// strings followed by a null word.
// -----------------------------------------------------------------------------
static DWORD CountMultiStrings( // RETURNS: Win32 error code
IN LPWSTR grwsz, // group of strings to count
OUT LPULONG pcwsz) // number of strings found
{
DWORD dwStatus = ERROR_SUCCESS;
LPWSTR pwsz = NULL;
DEBUGPRIVATE("CountMultiStrings()\n");
*pcwsz = 0;
for (pwsz = grwsz; *pwsz; pwsz += (wcslen(pwsz) + 1))
{
(*pcwsz)++;
}
return dwStatus;
}
//$--LockSharedMemory-----------------------------------------------------------
// Locks the mutex associated with a shared memory block.
// -----------------------------------------------------------------------------
static DWORD LockSharedMemory( // RETURNS: Win32 error code
IN LPGINSTANCE lpgi) // gateway instance to lock
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwWaitStatus = 0;
#ifdef VERBOSE_DEBUG
DEBUGPRIVATE("LockSharedMemory()\n");
#endif
// Lock the mutex.
dwWaitStatus = WaitForSingleObject(lpgi->hMutex, MON_MUTEX_TIMEOUT);
switch (dwWaitStatus)
{
// Failed--log and return error.
case WAIT_FAILED:
dwStatus = GetLastError();
EventLogMsg(
GWPERF_ERROR,
0,
1, dwStatus);
goto cleanup;
// Timed out--log and return error.
case WAIT_TIMEOUT:
dwStatus = ERROR_DLL_INIT_FAILED;
EventLogMsgW(
GWPERF_MUTEX_TIMEOUT,
1, lpgi->lpwszName,
0);
goto cleanup;
// Success--increment the reference count.
default:
++lpgi->cLockReference;
break;
}
cleanup:
return dwStatus;
}
//$--UnlockSharedMemory---------------------------------------------------------
// Unlocks the mutex associated with a shared memory block.
// -----------------------------------------------------------------------------
static DWORD UnlockSharedMemory( // RETURNS: Win32 error code
IN LPGINSTANCE lpgi) // gateway instance to unlock
{
DWORD dwStatus = ERROR_SUCCESS;
BOOL fItWorked = FALSE;
#ifdef VERBOSE_DEBUG
DEBUGPRIVATE("UnlockSharedMemory()\n");
#endif
// Don't unlock more than we lock.
if (lpgi->cLockReference > 0)
{
// Unlock the mutex.
fItWorked = ReleaseMutex(lpgi->hMutex);
if (!fItWorked)
{
dwStatus = GetLastError();
EventLogMsg(
GWPERF_ERROR,
0,
1, dwStatus);
goto cleanup;
}
// Decrement the reference count.
--lpgi->cLockReference;
}
cleanup:
return dwStatus;
}
//$--fOurDataRequested----------------------------------------------------------
// Returns TRUE if the value request string is requesting our data.
// -----------------------------------------------------------------------------
static BOOL fOurDataRequested( // RETURNS: TRUE=yes, FALSE=no
IN LPGTYPE lpgt, // gateway type structure
IN LPWSTR lpwszRequest)// perfomance data request string
{
BOOL fReturn = FALSE;
LPWSTR lpwszOldPtr = NULL;
LONG iObjectName = 0;
DEBUGPRIVATE("fOurDataRequested()\n");
// If they requested "Global", "Costly" or an empty string then
// they want our data.
if (*lpwszRequest == 0 ||
!wcscmp(lpwszRequest, L"Global") ||
!wcscmp(lpwszRequest, L"Costly"))
{
fReturn = TRUE;
goto cleanup;
}
// If they requested "Foreign" then they don't want our data.
if (!wcscmp(lpwszRequest, L"Foreign"))
{
fReturn = FALSE;
goto cleanup;
}
// Search the list for our number.
fReturn = FALSE;
while (lpwszRequest != lpwszOldPtr)
{
lpwszOldPtr = lpwszRequest;
iObjectName = wcstol(lpwszRequest, &lpwszRequest, 10);
if (iObjectName == lpgt->iObjectName &&
lpwszRequest != lpwszOldPtr)
{
fReturn = TRUE;
goto cleanup;
}
}
cleanup:
return fReturn;
}
//$--ReadSharedMemoryFromRegistry-----------------------------------------------
// Read the default shared memory data from the registry, where it was stored
// the last time the gateway started. If there was an error then the shared
// memory fDataIsValid flag is returned as FALSE.
// -----------------------------------------------------------------------------
static VOID ReadSharedMemoryFromRegistry(// RETURNS: nothing
IN OUT LPGINSTANCE lpgi) // gateway instance to unlock
{
DWORD dwStatus = ERROR_SUCCESS;
BOOL fItWorked = FALSE;
CHAR szKeyName[MAX_PATH] = {0};
HKEY hKey = NULL;
DWORD cch = 0;
DWORD dwType = 0;
DWORD cbData = 0;
DEBUGPRIVATE("ReadSharedMemoryFromRegistry()\n");
// Zero out the shared memory section first.
ZeroMemory(lpgi->lpmsmMemory, sizeof(*(lpgi->lpmsmMemory)));
// Create the registry key name from the gateway instance name.
cch = sprintf(
szKeyName,
"SYSTEM\\CurrentControlSet\\Services\\%S\\Parameters",
lpgi->lpwszName);
ASSERTERROR(
(cch * sizeof(*szKeyName)) < sizeof(szKeyName),
"wsprintf overflow: szKeyName");
// Open the registry key.
dwStatus = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
szKeyName,
0L,
KEY_READ,
&hKey);
if (dwStatus != ERROR_SUCCESS)
{
EventLogMsg(
GWPERF_OPEN_DEFAULT_COUNTERS,
1, szKeyName,
1, dwStatus);
goto cleanup;
}
// Read the value into the shared memory section.
cbData = sizeof(*(lpgi->lpmsmMemory));
dwStatus = RegQueryValueEx(
hKey,
"ObjectDefaultPerformanceData",
NULL,
&dwType,
(LPBYTE) lpgi->lpmsmMemory,
&cbData);
// If it is not found then log a message telling the user to run the
// gateway once first.
if (dwStatus == ERROR_FILE_NOT_FOUND)
{
EventLogMsgW(
GWPERF_NO_DEFAULT_COUNTERS,
1, lpgi->lpwszName,
0);
goto cleanup;
}
if (dwStatus != ERROR_SUCCESS)
{
EventLogMsg(
GWPERF_READ_DEFAULT_COUNTERS,
1, szKeyName,
1, dwStatus);
goto cleanup;
}
if (dwType != REG_BINARY)
{
EventLogMsg(
GWPERF_DEFAULT_COUNTERS_WRONG_TYPE,
1, szKeyName,
0);
dwStatus = ERROR_FILE_NOT_FOUND;
goto cleanup;
}
cleanup:
// Close the registry key (if it's open).
if (hKey)
{
dwStatus = RegCloseKey(hKey);
if (dwStatus != ERROR_SUCCESS)
{
EventLogMsg(
GWPERF_CLOSE_DEFAULT_COUNTERS,
1, szKeyName,
1, dwStatus);
// We can ignore an error here.
dwStatus = ERROR_SUCCESS;
}
}
// If it didn't work, then clear out the shared memory, mark it
// as having no valid data and continue on.
if (dwStatus)
{
ZeroMemory(lpgi->lpmsmMemory, sizeof(*(lpgi->lpmsmMemory)));
lpgi->lpmsmMemory->fDataIsValid = FALSE;
}
}