// --monitor.c------------------------------------------------------------------
//
// This file implements the monitored object side of the EDK performance
// monitoring component.
//
// This component uses the stats.lib to generate a suite of statistics. The
// statistics are periodically copied to a memory area shared with the other
// side of the performance monitoring component: a performance DLL which is
// called by the NT Performance Monitor.
//
// Copyright (C) Microsoft Corp. 1986-1996. All Rights Reserved.
// -----------------------------------------------------------------------------
#include "edk.h"
#include "monshare.h"
#include "monctx.h"
#include "monitor.chk"
//
// Internal Function Declarations
//
static HRESULT HrAllocateContext(
OUT LPMONCONTEXT *lppmcx);
static HRESULT HrGetCurrentTime(
OUT __int64 * lpdwlTime);
static HRESULT HrReadObjectClass(
IN LPMONCONTEXT lpmcx);
static HRESULT HrReadRegistryInfo(
IN LPMONCONTEXT lpmcx);
static HRESULT HrReadRegistryDWORD(
IN HKEY hKey,
IN LPSTR lpszValueName,
OUT LPDWORD lpdwValue);
static HRESULT HrCreateSharedMemory(
IN LPMONCONTEXT lpmcx);
static HRESULT HrFillInSharedMemory(
IN LPMONCONTEXT lpmcx,
IN DWORD ccdCounters,
IN LPCOUNTERDEF lpcdCounters);
static HRESULT HrBuildStructures(
IN LPMONCONTEXT lpmcx,
IN DWORD ccdCounters,
IN LPCOUNTERDEF lpcdCounters);
static HRESULT HrMonFindFolder(
IN LPMONFOLDER lpmfFolderList,
IN LPMAPIFOLDER lpFolder,
OUT LPMONFOLDER * lppmfFolder);
static HRESULT HrStartUpdateThread(
IN LPMONCONTEXT lpmcx);
static DWORD WINAPI UpdateThread(
IN LPVOID lpThreadParameter);
static HRESULT HrFreeEverything(
IN LPMONCONTEXT *lppmcx);
static HRESULT HrFreeCounterList(
IN LPMONCOUNTER *lppmcCounterList);
static HRESULT HrFreeFolderList(
IN LPMONFOLDER *lppmfFolderList);
static HRESULT HrAddToCounters(
IN LPMONCONTEXT lpmcx,
IN LPMONCOUNTER lpmcList,
IN LONG dwAmount);
static HRESULT HrSetCounters(
IN LPMONCOUNTER lpmcList,
IN DWORD dwAmount);
static HRESULT HrComputeCounterType(
IN COUNTERTYPE ctStatistic,
IN PERIODTYPE perPeriod,
IN DWORD dwUserCounterType,
OUT DWORD *lpdwCounterType);
static HRESULT HrAlignPointer(
IN DWORD cbAlignment,
IN OUT LPVOID * lppvPointer);
static ULONG STDAPICALLTYPE FolderNotification(
IN LPVOID lpvContext,
IN DWORD cNotification,
IN LPNOTIFICATION lpNotifications);
static HRESULT HrWakeUpdateThread(
IN LPMONCONTEXT lpmcx);
static HRESULT HrRegisterNotification(
IN LPMONCONTEXT lpmcx);
static HRESULT HrLinkCounterIntoList(
IN LPMONCONTEXT lpmcx,
IN LPMONFOLDER lpmf,
IN LPMONCOUNTER lpmc,
IN COUNTERTYPE ctCounterType);
static HRESULT HrVerifyLinkage(
IN LPMONCONTEXT lpmcx);
static HRESULT HrSaveSharedMemoryInRegistry(
IN LPMONCONTEXT lpmcx);
//
// Local Variable Declarations
//
static LPMONCONTEXT lpmcxContext = NULL; // place to save allocated context
// Security descriptor to allow all access -- only need to initialize once!!!
static BOOL fSecurityDescriptorInitialized = FALSE;
static SECURITY_ATTRIBUTES sa;
static char rgbForSecurityDescriptor[SECURITY_DESCRIPTOR_MIN_LENGTH];
static SECURITY_ATTRIBUTES * psa = &sa;
static SECURITY_DESCRIPTOR * psd
= (SECURITY_DESCRIPTOR *)rgbForSecurityDescriptor;
//
// Monitor API Routines
//
//$--HrMonInit--------------------------------------------------------------
// Begins performance monitoring of the current monitored object.
//
// If lpszObjectClass != NULL, then use lpszObjectClass as the class of the
// monitored object.
// If lpszObjectClass == NULL, then read the object class from the
// Parameters\ObjectClass value under the object's registry key, or if
// it's not present, assume that the object class is the same as the object
// name.
// -----------------------------------------------------------------------------
HRESULT HrMonInit( // RETURNS: HRESULT
IN DWORD dwFlags,// for future use--must be zero
IN LPSTR lpszObjectClass,// class of monitored object, or NULL
IN LPSTR lpszObjectName, // gateway instance name
IN DWORD dwObjectTitleOffset,// index number of object name in
// the registry database (offset from
// First Counter)
IN DWORD dwObjectDetailLevel,// complexity of object (see winperf.h)
IN LONG dwDefaultCounter,// zero-based number of default counter
// for this object
IN DWORD ccdNumberOfCounters,// number of counter structures
// being passed in
IN LPCOUNTERDEF lpcdCounters)// pointer to array of counter
// structures
{
// This routine creates a "context" structure in allocated memory that
// holds all the information needed for performance monitoring. When fully
// allocated, this context contains open handles to various objects, and
// pointers to other allocated memory structures. HrMonInit() calls
// several other routines, each of which allocates some of the handles
// and structures attached to the context.
//
// If one of these routines fails for any reason, it does NOT clean up what
// it has created in the context. Instead, it simply returns an error, and
// then HrMonInit calls a "catch-all" cleanup routine called
// HrFreeEverything(). HrFreeEverything frees/closes anything attached
// to the context and then frees the context itself. HrFreeEverything()
// is also called by MonitorShutdown() to end performance monitoring.
HRESULThr = NOERROR;
DEBUGPUBLIC("HrMonInit()\n");
// Check the parameters.
hr = CHK_HrMonInit(
dwFlags,
lpszObjectClass,
lpszObjectName,
dwObjectTitleOffset,
dwObjectDetailLevel,
dwDefaultCounter,
ccdNumberOfCounters,
lpcdCounters);
if (FAILED(hr))
RETURN(hr);
// 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.
if (!fSecurityDescriptorInitialized)
{
if (!InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION))
{
hr = HR_LOG(E_FAIL);
goto cleanup_no_free;
}
if (!SetSecurityDescriptorDacl(psd, TRUE, (PACL) NULL, FALSE))
{
hr = HR_LOG(E_FAIL);
goto cleanup_no_free;
}
psa->nLength = sizeof(*psa);
psa->lpSecurityDescriptor = psd;
psa->bInheritHandle = TRUE;
fSecurityDescriptorInitialized = TRUE;
}
// Make sure this hasn't already been called before.
if (lpmcxContext != NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup_no_free;
}
// Allocate and zero out performance monitoring context.
hr = HrAllocateContext(&lpmcxContext);
if (FAILED(hr))
{
goto cleanup;
}
// Store various parameters in the context.
lpmcxContext->iObjectTitleOffset = dwObjectTitleOffset;
lpmcxContext->dwObjectDetailLevel = dwObjectDetailLevel;
lpmcxContext->dwDefaultCounter = dwDefaultCounter;
// Get the current system time and put it in the context.
hr = HrGetCurrentTime(&lpmcxContext->dwlCurrentTime);
if (FAILED(hr))
{
goto cleanup;
}
// Add allocated copies of the gateway type name and instance name to
// the performance monitoring context. If they passed NULL for the
// gateway type then read it from the Parameters\ObjectClass value
// under the gateway's service entry in the registry.
lpmcxContext->lpszObjectName = _strdup(lpszObjectName);
if (lpmcxContext->lpszObjectName == NULL)
{
hr = HR_LOG(E_OUTOFMEMORY);
goto cleanup;
}
if (lpszObjectClass)
{
lpmcxContext->lpszObjectClass = _strdup(lpszObjectClass);
if (lpmcxContext->lpszObjectClass == NULL)
{
hr = HR_LOG(E_OUTOFMEMORY);
goto cleanup;
}
}
else
{
hr = HrReadObjectClass(lpmcxContext);
if (FAILED(hr))
{
goto cleanup;
}
}
// Verify that there is a back link from the gateway type to the
// gateway name so the DLL can link to our shared memory section.
hr = HrVerifyLinkage(lpmcxContext);
if (FAILED(hr))
{
goto cleanup;
}
// Read the Counter and Help values from the registry.
hr = HrReadRegistryInfo(lpmcxContext);
if (FAILED(hr))
{
goto cleanup;
}
// Create the shared memory section and mutex. This leaves us as the
// owner of the mutex and we must release it before we return.
hr = HrCreateSharedMemory(lpmcxContext);
if (FAILED(hr))
{
goto cleanup;
}
// Fill in the shared memory section with the structure that will be
// passed to the DLL, and from there to the Performance Monitor.
hr = HrFillInSharedMemory(
lpmcxContext, ccdNumberOfCounters, lpcdCounters);
if (FAILED(hr))
{
goto cleanup;
}
// Save a copy of the shared memory block to the registry so that
// GWPERF.DLL will be able to generate a blank set of counters when
// the gateway isn't running.
hr = HrSaveSharedMemoryInRegistry(lpmcxContext);
if (FAILED(hr))
{
goto cleanup;
}
// Set up structures representing folders, and linked lists of counters
// hanging off the performance monitoring context.
hr = HrBuildStructures(
lpmcxContext, ccdNumberOfCounters, lpcdCounters);
if (FAILED(hr))
{
goto cleanup;
}
// Register the notification routines on all folders that need them.
hr = HrRegisterNotification(lpmcxContext);
if (FAILED(hr))
{
goto cleanup;
}
// If there are any jumping sliding windows or folders, then start
// the thread that updates them.
if (lpmcxContext->lpmcJSWCounterList || lpmcxContext->lpmfFolderList)
{
hr = HrStartUpdateThread(lpmcxContext);
if (FAILED(hr))
{
goto cleanup;
}
}
// Set flag that the shared memory data structure is now valid.
lpmcxContext->lpmsmSharedMemory->fDataIsValid = TRUE;
// Release the mutex (it was "locked" when the mutex was created).
hr = HrMonUnlockCounters();
if (FAILED(hr))
{
goto cleanup;
}
cleanup:
if (FAILED(hr))
{
HRESULT hrT = NOERROR;
hrT = HrFreeEverything(&lpmcxContext);
if (FAILED(hrT))
HR_LOG(hrT);
}
cleanup_no_free:
RETURN(hr);
}
//$--HrMonUninit----------------------------------------------------------
// Ends performance monitoring of the current gateway.
// -----------------------------------------------------------------------------
HRESULT HrMonUninit(void) // RETURNS: HRESULT
{
HRESULThr = NOERROR;
DEBUGPUBLIC("HrMonUninit()\n");
// Make sure there's a context.
if (lpmcxContext == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
ASSERT_WRITE_PTR(lpmcxContext, sizeof(*lpmcxContext),
"lpmcxContext: bad write pointer");
// Shut down, and free the context and all memory attached to it.
hr = HrFreeEverything(&lpmcxContext);
if (FAILED(hr))
goto cleanup;
cleanup:
RETURN(hr);
}
//$--HrMonCollectNDRStats----------------------------------------------------
// Call this after processing an NDR.
// -----------------------------------------------------------------------------
HRESULT HrMonCollectNDRStats(// RETURNS: HRESULT
IN DWORD cNDRs,// number of NDR's processed
IN DIRECTIONTYPE dir)// direction of NDR's
{
HRESULThr = NOERROR;
HRESULThrT= NOERROR;
DEBUGPUBLIC("HrMonCollectNDRStats()\n");
// Check the parameters.
hr = CHK_HrMonCollectNDRStats(cNDRs, dir);
if (FAILED(hr))
RETURN(hr);
// Make sure there's a context.
if (lpmcxContext == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
ASSERT_WRITE_PTR(lpmcxContext, sizeof(*lpmcxContext),
"lpmcxContext: bad write pointer");
// Lock the shared memory against modification.
hr = HrMonLockCounters();
if (FAILED(hr))
goto cleanup;
// Get current time for JSW's.
hr = HrGetCurrentTime(&lpmcxContext->dwlCurrentTime);
if (FAILED(hr))
goto cleanup;
// Update the counters.
switch (dir)
{
case DIRECTIONTYPE_IN:
hr = HrAddToCounters(lpmcxContext, lpmcxContext->lpmcNDRsIn, cNDRs);
if (FAILED(hr))
goto cleanup;
break;
case DIRECTIONTYPE_OUT:
hr = HrAddToCounters(lpmcxContext, lpmcxContext->lpmcNDRsOut, cNDRs);
if (FAILED(hr))
goto cleanup;
break;
}
cleanup:
// Unlock the shared memory.
if (lpmcxContext)
{
hrT = HrMonUnlockCounters();
if (FAILED(hrT))
{
hr = hrT;
}
}
RETURN(hr);
}
//$--HrMonCollectMessageXferStats-------------------------------------
// Call this after transferring a message.
// -----------------------------------------------------------------------------
HRESULT HrMonCollectMessageXferStats( // RETURNS: HRESULT
IN DWORD cMessages, // number of messages transferred
IN DWORD cBytes, // number of bytes transferred
IN DIRECTIONTYPE dir)// direction of message transfer
{
HRESULThr= NOERROR;
HRESULThrT= NOERROR;
DEBUGPUBLIC("HrMonCollectMessageXferStats()\n");
// Check the parameters.
hr = CHK_HrMonCollectMessageXferStats(cMessages, cBytes, dir);
if (FAILED(hr))
RETURN(hr);
// Make sure there's a context.
if (lpmcxContext == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
ASSERT_WRITE_PTR(lpmcxContext, sizeof(*lpmcxContext),
"lpmcxContext: bad write pointer");
hr = HrMonLockCounters();
if (FAILED(hr))
goto cleanup;
hr = HrGetCurrentTime(&lpmcxContext->dwlCurrentTime);
if (FAILED(hr))
goto cleanup;
switch (dir)
{
case DIRECTIONTYPE_IN:
hr = HrAddToCounters(
lpmcxContext,
lpmcxContext->lpmcMessagesTransferredIn,
cMessages);
if (FAILED(hr))
goto cleanup;
hr = HrAddToCounters(
lpmcxContext,
lpmcxContext->lpmcBytesTransferredIn,
cBytes);
if (FAILED(hr))
goto cleanup;
break;
case DIRECTIONTYPE_OUT:
hr = HrAddToCounters(
lpmcxContext,
lpmcxContext->lpmcMessagesTransferredOut,
cMessages);
if (FAILED(hr))
goto cleanup;
hr = HrAddToCounters(
lpmcxContext,
lpmcxContext->lpmcBytesTransferredOut,
cBytes);
if (FAILED(hr))
goto cleanup;
break;
}
cleanup:
if (lpmcxContext)
{
hrT = HrMonUnlockCounters();
if (FAILED(hrT))
{
hr = hrT;
}
}
RETURN(hr);
}
//$--HrMonCollectAssociationStats--------------------------------------------
// Call this after making or breaking an association, or to set a new total
// number of associations.
// -----------------------------------------------------------------------------
HRESULT HrMonCollectAssociationStats( // RETURNS: HRESULT
IN BOOL fSetNewTotal,// if TRUE, iAssociations becomes
// the new total of associations.
// if FALSE, iAssociations is added
// to the number of associations.
IN LONG cAssociations)// number of associations to add to
// total (can be negative), or new
// total if fSetNewTotal == TRUE
{
HRESULThr= NOERROR;
HRESULThrT= NOERROR;
DEBUGPUBLIC("HrMonCollectAssociationStats()\n");
// Check the parameters.
hr = CHK_HrMonCollectAssociationStats(fSetNewTotal, cAssociations);
if (FAILED(hr))
RETURN(hr);
// Make sure there's a context.
if (lpmcxContext == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
ASSERT_WRITE_PTR(lpmcxContext, sizeof(*lpmcxContext),
"lpmcxContext: bad write pointer");
hr = HrMonLockCounters();
if (FAILED(hr))
goto cleanup;
hr = HrGetCurrentTime(&lpmcxContext->dwlCurrentTime);
if (FAILED(hr))
goto cleanup;
if (fSetNewTotal)
{
// Set all counters to the given value.
ASSERTERROR(cAssociations >= 0,
"cAssociations < 0 on set associations");
hr = HrSetCounters(
lpmcxContext->lpmcAssociations,
cAssociations);
if (FAILED(hr))
goto cleanup;
}
else
{
// Add given value to counters (value can be negative).
hr = HrAddToCounters(
lpmcxContext,
lpmcxContext->lpmcAssociations,
cAssociations);
if (FAILED(hr))
goto cleanup;
}
cleanup:
if (lpmcxContext)
{
hrT = HrMonUnlockCounters();
if (FAILED(hrT))
{
hr = hrT;
}
}
RETURN(hr);
}
//$--HrMonLockCounters------------------------------------------------------
// Locks the block of counters against access by other threads/processes.
// This should be called before accessing a user defined counter.
// -----------------------------------------------------------------------------
HRESULT HrMonLockCounters(void)// RETURNS: HRESULT
{
HRESULT hr = NOERROR;
DWORDdwReturnValue= 0;
DEBUGPUBLIC("HrMonLockCounters()\n");
// Make sure there's a context.
if (lpmcxContext == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
ASSERT_WRITE_PTR(lpmcxContext, sizeof(*lpmcxContext),
"lpmcxContext: bad write pointer");
ASSERTERROR(lpmcxContext->hSharedMemoryMutex != NULL,
"lpmcxContext->hSharedMemoryMutex == NULL");
dwReturnValue = WaitForSingleObject(
lpmcxContext->hSharedMemoryMutex,
MON_MUTEX_TIMEOUT);
if (dwReturnValue == WAIT_FAILED || dwReturnValue == WAIT_TIMEOUT)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
ASSERTERROR(
dwReturnValue == WAIT_FAILED ||
dwReturnValue == WAIT_ABANDONED ||
dwReturnValue == WAIT_OBJECT_0 ||
dwReturnValue == WAIT_TIMEOUT,
"dwReturnValue: illegal value");
ASSERTERROR(dwReturnValue != WAIT_TIMEOUT,
"lpmcxContext->hSharedMemoryMutex: dwReturnValue == WAIT_TIMEOUT");
cleanup:
RETURN(hr);
}
//$--HrMonUnlockCounters----------------------------------------------------
// Unlocks the block of counters to allow access by other threads/processes.
// This should be called after accessing a user defined counter.
// -----------------------------------------------------------------------------
HRESULT HrMonUnlockCounters(void)// RETURNS: HRESULT
{
HRESULT hr = NOERROR;
BOOL fItWorked = FALSE;
DEBUGPUBLIC("HrMonUnlockCounters()\n");
// Make sure there's a context.
if (lpmcxContext == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
ASSERT_WRITE_PTR(lpmcxContext, sizeof(*lpmcxContext),
"lpmcxContext: bad write pointer");
ASSERTERROR(lpmcxContext->hSharedMemoryMutex != NULL,
"lpmcxContext->hSharedMemoryMutex == NULL");
fItWorked = ReleaseMutex(lpmcxContext->hSharedMemoryMutex);
if (!fItWorked)
{
DWORD dwStatus = GetLastError();
hr = HR_LOG(E_FAIL);
goto cleanup;
}
cleanup:
RETURN(hr);
}
//
// Internal Helper Functions
//
//$--HrAllocateContext----------------------------------------------------------
// Helper function that allocates a new performance monitoring context and
// zeros out its components.
// -----------------------------------------------------------------------------
static HRESULT HrAllocateContext( // RETURNS: HRESULT
OUT LPMONCONTEXT *lppmcx)// pointer to address variable for
// performance monitor context ptr
{
LPMONCONTEXTlpmcxNew = NULL;
HRESULThr = NOERROR;
DEBUGPRIVATE("HrAllocateContext()\n");
// Check the parameters.
hr = CHK_HrAllocateContext(lppmcx);
if (FAILED(hr))
RETURN(hr);
lpmcxNew = malloc(sizeof(*lpmcxNew));
if (lpmcxNew == NULL)
{
hr = HR_LOG(E_OUTOFMEMORY);
goto cleanup;
}
ZeroMemory(lpmcxNew, sizeof(*lpmcxNew));
*lppmcx = lpmcxNew;
cleanup:
RETURN(hr);
}
//$--HrGetCurrentTime-----------------------------------------------------------
// Helper function that returns the current system time in 8-byte format.
// -----------------------------------------------------------------------------
static HRESULT HrGetCurrentTime(// RETURNS: HRESULT
OUT __int64 * lpdwlTime)// current time
{
HRESULThr = NOERROR;
SYSTEMTIMEst= {0};
__int64dwlTime= 0;
BOOLfItWorked= TRUE;
DEBUGPRIVATE("HrGetCurrentTime()\n");
// Check the parameters.
hr = CHK_HrGetCurrentTime(lpdwlTime);
if (FAILED(hr))
RETURN(hr);
GetSystemTime(&st);
fItWorked = SystemTimeToFileTime(&st, &TO_FILETIME(dwlTime));
if (!fItWorked)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
*lpdwlTime = dwlTime;
cleanup:
RETURN(hr);
}
//$--HrReadRegistryInfo---------------------------------------------------------
// Helper function that reads the First Counter and First Help values from the
// SYSTEM\CurrentControlSet\Services\<lpszObjectClass>\Performance key.
// Note that the context must already have lpszObjectClass defined.
// -----------------------------------------------------------------------------
static HRESULT HrReadRegistryInfo( // RETURNS: HRESULT
IN LPMONCONTEXT lpmcx)// pointer to perf mon context
{
HRESULThr = NOERROR;
DWORDdwStatus = 0;
TCHARszKeyName[MAX_PATH] = {0};
HKEYhKey = NULL;
DWORDcch = 0;
DEBUGPRIVATE("HrReadRegistryInfo()\n");
// Check the parameters.
hr = CHK_HrReadRegistryInfo(lpmcx);
if (FAILED(hr))
RETURN(hr);
ASSERT_STRING_PTR(lpmcx->lpszObjectClass,
"lpmcx->lpszObjectClass: bad string pointer");
// Create the registry key name from the gateway type.
cch = wsprintf(
szKeyName,
TEXT("SYSTEM\\CurrentControlSet\\Services\\%s\\Performance"),
lpmcx->lpszObjectClass);
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)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Read the values: First Counter, First help, Last Counter, Last Help.
hr = HrReadRegistryDWORD(hKey, "First Counter",
&lpmcx->dwFirstCounter);
if (FAILED(hr))
{
goto cleanup;
}
hr = HrReadRegistryDWORD(hKey, "First Help",
&lpmcx->dwFirstHelp);
if (FAILED(hr))
{
goto cleanup;
}
hr = HrReadRegistryDWORD(hKey, "Last Counter",
&lpmcx->dwLastCounter);
if (FAILED(hr))
{
goto cleanup;
}
hr = HrReadRegistryDWORD(hKey, "Last Help",
&lpmcx->dwLastHelp);
if (FAILED(hr))
{
goto cleanup;
}
cleanup:
// Close the registry key (if it's open).
if (hKey)
{
dwStatus = RegCloseKey(hKey);
if (dwStatus != ERROR_SUCCESS)
{
hr = HR_LOG(E_FAIL);
}
}
RETURN (hr);
}
//$--HrReadObjectClass----------------------------------------------------------
// Helper function that reads the gateway type out of the registry.
// -----------------------------------------------------------------------------
static HRESULT HrReadObjectClass( // RETURNS: HRESULT
IN LPMONCONTEXT lpmcx) // pointer to perf mon context
{
HRESULThr = NOERROR;
DWORD dwStatus = ERROR_SUCCESS;
BOOL fValueNotFound = FALSE;
TCHARszKeyName[MAX_PATH] = {0};
HKEYhKey = NULL;
DWORDcch = 0;
DWORD dwType = 0;
TCHAR szObjectClass[MAX_PATH] = {0};
DWORD cbObjectClass = sizeof(szObjectClass);
DEBUGPRIVATE("HrReadObjectClass()\n");
// Check the parameters.
hr = CHK_HrReadObjectClass(lpmcx);
if (FAILED(hr))
RETURN(hr);
// Create the registry key name from the gateway name.
cch = wsprintf(
szKeyName,
TEXT("SYSTEM\\CurrentControlSet\\Services\\%s\\Parameters"),
lpmcx->lpszObjectName);
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)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Read the value.
dwStatus = RegQueryValueEx(
hKey,
TEXT("ObjectClass"),
NULL,
&dwType,
szObjectClass,
&cbObjectClass);
if (dwStatus != ERROR_SUCCESS || dwType != REG_SZ)
{
if (dwStatus == ERROR_FILE_NOT_FOUND)
fValueNotFound = TRUE;
else
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Add it to the context.
lpmcx->lpszObjectClass = _strdup(szObjectClass);
if (lpmcx->lpszObjectClass == NULL)
{
hr = HR_LOG(E_OUTOFMEMORY);
goto cleanup;
}
cleanup:
// If we couldn't read it then use the gateway name as the type.
if (fValueNotFound)
{
lpmcx->lpszObjectClass = _strdup(lpmcx->lpszObjectClass);
if (lpmcx->lpszObjectClass == NULL)
hr = HR_LOG(E_FAIL);
}
// Close the registry key (if it's open).
if (hKey)
{
dwStatus = RegCloseKey(hKey);
if (dwStatus != ERROR_SUCCESS)
hr = HR_LOG(E_FAIL);
}
RETURN(hr);
}
//$--HrReadRegistryDWORD--------------------------------------------------------
// Helper function that reads a DWORD value from the registry and makes sure
// everything is as it should be.
// -----------------------------------------------------------------------------
static HRESULT HrReadRegistryDWORD( // RETURNS: HRESULT
IN HKEY hKey,// open key handle
IN LPSTR lpszValueName,// name of value to get
OUT LPDWORD lpdwValue)// address of DWORD to return value
{
HRESULThr= NOERROR;
DWORDdwStatus= ERROR_SUCCESS;
DWORDdwValueType= 0;
DWORDdwValueSize= sizeof(*lpdwValue);
DEBUGPRIVATE("HrReadRegistryDWORD()\n");
// Check the parameters.
hr = CHK_HrReadRegistryDWORD(hKey, lpszValueName, lpdwValue);
if (FAILED(hr))
RETURN(hr);
dwStatus = RegQueryValueEx(
hKey,
lpszValueName,
0L,
&dwValueType,
(LPBYTE)lpdwValue,
&dwValueSize);
if (dwStatus != ERROR_SUCCESS)
{
hr = HR_LOG(E_FAIL);
}
else if (dwValueType != REG_DWORD)
{
hr = HR_LOG(E_FAIL);
}
RETURN(hr);
}
//$--HrCreateSharedMemory-------------------------------------------------------
// Helper function that creates a shared memory section and a mutex.
// NOTE: On successful return, the mutex will be owned by the current thread
// and must be released.
// -----------------------------------------------------------------------------
static HRESULT HrCreateSharedMemory(// RETURNS: HRESULT
IN LPMONCONTEXT lpmcx)// pointer to performance monitoring
// context
{
HRESULThr = NOERROR;
CHARszMappingName[MAX_PATH] = {0};
CHARszMutexName[MAX_PATH] = {0};
BOOLfItWorked = FALSE;
DWORDcch = 0;
DEBUGPRIVATE("HrCreateSharedMemory()\n");
// Check the parameters.
hr = CHK_HrCreateSharedMemory(lpmcx);
if (FAILED(hr))
RETURN(hr);
ASSERTERROR(lpmcx->cbSharedMemorySize == 0,
"lpmcx->cbSharedMemorySize != 0");
ASSERTERROR(lpmcx->hSharedMemoryMapping == NULL,
"lpmcx->hSharedMemoryMapping != NULL");
ASSERTERROR(lpmcx->lpmsmSharedMemory == NULL,
"lpmcx->lpmsmSharedMemory != NULL");
ASSERTERROR(lpmcx->hSharedMemoryMutex == NULL,
"lpmcx->hSharedMemoryMutex != NULL");
// Create the name of the mutex that controls access to the shared memory.
cch = wsprintf(
szMutexName,
MON_MUTEX_NAME_TEMPLATE,
lpmcx->lpszObjectName);
ASSERTERROR(
(cch * sizeof(*szMutexName)) < sizeof(szMutexName),
"wsprintf overflow: szMutexName");
(void) CharUpper(szMutexName);
MODULE_STATUS1("mutex name = [%s]", szMutexName);
// First try to open the mutex and get ownership.
lpmcx->hSharedMemoryMutex = OpenMutex(
MUTEX_ALL_ACCESS,
FALSE,
szMutexName);
if (lpmcx->hSharedMemoryMutex)
{
hr = HrMonLockCounters();
if (FAILED(hr))
goto cleanup;
}
// If the mutex didn't exist then create it and get ownership.
else
{
if (GetLastError() != ERROR_FILE_NOT_FOUND)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
lpmcx->hSharedMemoryMutex = CreateMutex(
psa,
TRUE,
szMutexName);
if (lpmcx->hSharedMemoryMutex == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}
// Set the shared memory size.
lpmcx->cbSharedMemorySize = sizeof(MONSHAREDMEMORY);
// Create name of shared memory file mapping object.
cch = wsprintf(
szMappingName,
MON_MAPPING_NAME_TEMPLATE,
lpmcx->lpszObjectName);
ASSERTERROR(
(cch * sizeof(*szMappingName)) < sizeof(szMappingName),
"wsprintf overflow: szMappingName");
(void) CharUpper(szMappingName);
MODULE_STATUS1("mapping name = [%s]", szMappingName);
// First try to open the shared memory file mapping object.
lpmcx->hSharedMemoryMapping = OpenFileMapping(
FILE_MAP_ALL_ACCESS,
FALSE,
szMappingName);
// If the file mapping object didn't exist then create it.
if (lpmcx->hSharedMemoryMapping == NULL)
{
if (GetLastError() != ERROR_FILE_NOT_FOUND)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
lpmcx->hSharedMemoryMapping = CreateFileMapping(
(HANDLE)0xFFFFFFFF, // create in paging file
psa, // no security
PAGE_READWRITE, // read/write access
0, // size (high)
lpmcx->cbSharedMemorySize, // size(low)
szMappingName);// mapping object name
if (lpmcx->hSharedMemoryMapping == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}
// Map a view of the file mapping object.
lpmcx->lpmsmSharedMemory = MapViewOfFile(
lpmcx->hSharedMemoryMapping,
FILE_MAP_ALL_ACCESS,
0,
0,
lpmcx->cbSharedMemorySize);
if (lpmcx->lpmsmSharedMemory == NULL)
{
hr = HR_LOG(E_OUTOFMEMORY);
goto cleanup;
}
ZeroMemory(lpmcx->lpmsmSharedMemory, lpmcx->cbSharedMemorySize);
// Set the flag that the shared memory is not valid yet.
lpmcx->lpmsmSharedMemory->fDataIsValid = FALSE;
// Set the signature value.
lpmcx->lpmsmSharedMemory->dwSignature = MON_SHARED_MEMORY_SIGNATURE;
cleanup:
RETURN(hr);
}
//$--HrFillInSharedMemory-------------------------------------------------------
// Helper function that fills in the shared memory section with the structure
// that is sent to the Windows NT Performance Monitor via the DLL.
//
// This process is done in two passes. The first pass sets up the various
// pointers into the structure where data will be written, but does not write
// any actual data. After the first pass, the size of the resulting structure
// is known, and a check is made to see that the resulting structure will fit
// into the shared memory section. The second pass then takes the pointers
// that were set up by the first pass and fills in the actual data.
//
// The structure that is created in the shared memory section has the
// following format, which conforms to the specification in WINPERF.H for
// the structure used to communicate with the Windows NT Performance Monitor:
//
// /+===============================+
// / | _PERF_OBJECT_TYPE |
// | +===============================+
// | | _PERF_COUNTER_DEFINITION #1 |
// | +-------------------------------+
//"Header ---->| | _PERF_COUNTER_DEFINITION #2 |
// Data" | +-------------------------------+
// | : ........................... :
// | +-------------------------------+
// \ | _PERF_COUNTER_DEFINITION #N |
// >+===============================+
// / | _PERF_INSTANCE_DEFINITION |
// | +-------------------------------+
// | | Instance Name (UNICODE) |
// | +===============================+
// | | _PERF_COUNTER_BLOCK |
// | +-------------------------------+
//"Instance -->| | Counter #1 (COUNTER) |
// Data" | +-------------------------------+
// | | Counter #2 (COUNTER) |
// | +-------------------------------+
// | | .................... :
// | +-------------------------------+
// \ | Counter #N (COUNTER) |
// \+===============================+
//
// In addition to the above structure, the shared memory section contains
// some housekeeping information that is used to communicate with the
// DLL, but is not sent to the Windows NT Performance Monitor. This
// information is described in the shared memory structure defined in
// MONSHARE.H. In this housekeeping information is information that tells
// the DLL how to break the structure diagrammed above into two parts:
// "Header Data" and "Instance Data". This is done so that if there are two
// instances of a gateway communicating with the same DLL, the DLL can easily
// take the "Header Data" from one gateway and combine it with the
// "Instance Data" sections from both gateways to form a single
// structure to pass to the Windows NT Performance Monitor. This allows
// the user of Performance Monitor to see the gateways as a single object
// with multiple instances, rather than two separate objects.
// -----------------------------------------------------------------------------
static HRESULT HrFillInSharedMemory(// RETURNS: HRESULT
IN LPMONCONTEXT lpmcx, // pointer to perf mon context
IN DWORD ccdCounters,// number of COUNTERDEF structures
IN LPCOUNTERDEF lpcdCounters)// array of COUNTERDEF structures
{
HRESULThr= NOERROR;
// Variables used to create _PERF_COUNTER_DEFINITION structures, and to
// create MONFOLDER and MONCOUNTER structures.
DWORDiCounter= 0;
LPCOUNTERDEFlpcdSrc= NULL;
PPERF_COUNTER_DEFINITIONlpDest= NULL;
LPCOUNTERDEFlpcdCounter= NULL;
// Pointers used to allocate chunks off of the
// MONSHAREDMEMORY.rgbDataBlock[] structure.
LPBYTElpbNextFreeByte= NULL;
LPBYTElpbNextFreeByteLim= NULL;
// Pointers to structures defined in winperf.h.
PPERF_OBJECT_TYPElpObjectType= NULL;
PPERF_COUNTER_DEFINITIONlpCounterDefinition= NULL;
PPERF_INSTANCE_DEFINITIONlpInstanceDefinition= NULL;
LPWSTRlpwszInstanceName= NULL;
DWORDcwInstanceNameSize= 0;
PPERF_COUNTER_BLOCKlpCounterBlock= NULL;
LPCOUNTERlpcntCounter= NULL;
DWORD cLargeCounters = 0;
DWORD iCounterAllocated = 0;
LPBYTElpbStructureEnd= NULL;
DEBUGPRIVATE("HrFillInSharedMemory()\n");
// Check the parameters.
hr = CHK_HrFillInSharedMemory(lpmcx, ccdCounters, lpcdCounters);
if (FAILED(hr))
RETURN(hr);
ASSERTERROR(lpmcx->lpcntCounter == NULL, "lpmcx->lpcntCounter != NULL");
// Count the number of user-defined counters that are of size
// PERF_SIZE_LARGE because we will have to allow double space for them.
// While we're counting, if we find any counters of size PERF_SIZE_ZERO
// or PERF_SIZE_VARIABLE_LEN then return an error, because we don't
// support those types.
for (iCounter = 0; iCounter < ccdCounters; iCounter++)
{
if (lpcdCounters[iCounter].ctStatistic == COUNTERTYPE_USER_DEFINED)
{
switch (lpcdCounters[iCounter].dwUserCounterType & 0x00000300)
{
case PERF_SIZE_LARGE:
cLargeCounters++;
break;
case PERF_SIZE_DWORD:
break;
default:
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}
}
// Initially zero out the block in shared memory.
ZeroMemory(lpmcx->lpmsmSharedMemory->rgbDataBlock, MON_DATA_BLOCK_SIZE);
// Set pointers into the build buffer area of shared memory.
// These pointers are used to get chunks of memory from the
// buffer and check for overflow.
lpbNextFreeByte = lpmcx->lpmsmSharedMemory->rgbDataBlock;
lpbNextFreeByteLim = lpbNextFreeByte + MON_DATA_BLOCK_SIZE;
// Set pointers into the build buffer for where the various structures
// will go (don't fill anything in just yet--we have to check for overflow
// first!).
hr = HrAlignPointer(4, &lpbNextFreeByte);
if (FAILED(hr))
goto cleanup;
lpObjectType = (PERF_OBJECT_TYPE *)lpbNextFreeByte;
lpbNextFreeByte += sizeof(*lpObjectType);
lpCounterDefinition = (PERF_COUNTER_DEFINITION *)lpbNextFreeByte;
lpbNextFreeByte += ccdCounters * sizeof(*lpCounterDefinition);
lpInstanceDefinition = (PERF_INSTANCE_DEFINITION *)lpbNextFreeByte;
lpbNextFreeByte += sizeof(*lpInstanceDefinition);
// NOTE: Instance name is always UNICODE!!!
lpwszInstanceName = (LPWSTR)lpbNextFreeByte;
// Calculate how many characters are needed for the conversion.
if (lstrlen(lpmcx->lpszObjectName) == 0)
{
cwInstanceNameSize = 0;
}
else
{
cwInstanceNameSize = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
lpmcx->lpszObjectName, -1, 0, 0);
if (cwInstanceNameSize == 0)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
cwInstanceNameSize--;
}
lpbNextFreeByte += (cwInstanceNameSize + 1) * sizeof(WCHAR);
hr = HrAlignPointer(4, &lpbNextFreeByte);
if (FAILED(hr))
goto cleanup;
lpCounterBlock = (PERF_COUNTER_BLOCK *)lpbNextFreeByte;
lpbNextFreeByte += sizeof(lpCounterBlock->ByteLength);
lpcntCounter = (LPCOUNTER)lpbNextFreeByte;
lpbNextFreeByte += (ccdCounters + cLargeCounters) * sizeof(COUNTER);
hr = HrAlignPointer(4, &lpbNextFreeByte);
if (FAILED(hr))
goto cleanup;
lpbStructureEnd = lpbNextFreeByte;
// Check for overflow.
if (lpbNextFreeByte > lpbNextFreeByteLim)
{
hr = HR_LOG(E_OUTOFMEMORY);
goto cleanup;
}
// Fill in _PERF_OBJECT_TYPE structure.
lpObjectType->TotalByteLength = lpbStructureEnd - (LPBYTE)lpObjectType;
lpObjectType->DefinitionLength =
(LPBYTE)lpInstanceDefinition - (LPBYTE)lpObjectType;
lpObjectType->HeaderLength =
(LPBYTE)lpCounterDefinition - (LPBYTE)lpObjectType;
lpObjectType->ObjectNameTitleIndex =
lpmcx->dwFirstCounter + lpmcx->iObjectTitleOffset;
if (lpObjectType->ObjectNameTitleIndex > lpmcx->dwLastCounter)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}
lpObjectType->ObjectNameTitle = NULL;
lpObjectType->ObjectHelpTitleIndex =
lpmcx->dwFirstHelp + lpmcx->iObjectTitleOffset;
if (lpObjectType->ObjectHelpTitleIndex > lpmcx->dwLastHelp)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}
lpObjectType->ObjectHelpTitle = NULL;
lpObjectType->DetailLevel = lpmcx->dwObjectDetailLevel;
lpObjectType->NumCounters = ccdCounters;
lpObjectType->DefaultCounter = lpmcx->dwDefaultCounter;
lpObjectType->NumInstances = 1;
lpObjectType->CodePage = 0;
lpObjectType->PerfTime.LowPart = 0;
lpObjectType->PerfTime.HighPart = 0;
lpObjectType->PerfFreq.LowPart = 0;
lpObjectType->PerfFreq.HighPart = 0;
// Fill in _PERF_COUNTER_DEFINITION structures.
// Set iCounterAllocated = 1 to skip a DWORD at the front for the
// PERF_COUNTER_BLOCK header.
iCounterAllocated = 1;
for (iCounter = 0; iCounter < ccdCounters; ++iCounter)
{
lpcdSrc = &lpcdCounters[iCounter];
lpDest = &lpCounterDefinition[iCounter];
ASSERT_DETAIL_LEVEL(lpcdSrc->dwDetailLevel,
"lpcdSrc->dwDetailLevel: invalid value");
lpDest->ByteLength = sizeof(*lpDest);
lpDest->CounterNameTitleIndex =
lpmcx->dwFirstCounter + lpcdSrc->iCounterTitleOffset;
if (lpDest->CounterNameTitleIndex > lpmcx->dwLastCounter)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}
lpDest->CounterNameTitle = NULL;
lpDest->CounterHelpTitleIndex =
lpmcx->dwFirstHelp + lpcdSrc->iCounterTitleOffset;
if (lpDest->CounterHelpTitleIndex > lpmcx->dwLastHelp)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}
lpDest->CounterNameTitle = NULL;
lpDest->DefaultScale = lpcdSrc->dwDefaultScale;
lpDest->DetailLevel = lpcdSrc->dwDetailLevel;
hr = HrComputeCounterType(
lpcdSrc->ctStatistic,
lpcdSrc->perPeriod,
lpcdSrc->dwUserCounterType,
&lpDest->CounterType);
if (FAILED(hr))
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}
if ((lpcdSrc->dwUserCounterType & 0x00000300) == PERF_SIZE_LARGE)
{
lpDest->CounterSize = 2 * sizeof(COUNTER);
lpDest->CounterOffset = iCounterAllocated * sizeof(COUNTER);
iCounterAllocated += 2;
}
else
{
lpDest->CounterSize = sizeof(COUNTER);
lpDest->CounterOffset = iCounterAllocated * sizeof(COUNTER);
iCounterAllocated++;
}
}
// Fill in _PERF_INSTANCE_DEFINITION structure.
lpInstanceDefinition->ByteLength =
(LPBYTE)lpCounterBlock - (LPBYTE)lpInstanceDefinition;
lpInstanceDefinition->ParentObjectTitleIndex = 0;
lpInstanceDefinition->ParentObjectInstance = 0;
lpInstanceDefinition->UniqueID = PERF_NO_UNIQUE_ID;
lpInstanceDefinition->NameOffset = sizeof(*lpInstanceDefinition);
// Fill in instance name. NOTE: Instance name is always UNICODE!!!
lpInstanceDefinition->NameLength = (cwInstanceNameSize + 1) * sizeof(WCHAR);
if (lstrlen(lpmcx->lpszObjectName) == 0)
{
cwInstanceNameSize = 0;
}
else
{
if (
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpmcx->lpszObjectName,
-1, lpwszInstanceName, cwInstanceNameSize + 1) == 0)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}
lpwszInstanceName[cwInstanceNameSize] = 0;
// Fill in _PERF_COUNTER_BLOCK structure.
lpCounterBlock->ByteLength = lpbStructureEnd - (LPBYTE)lpCounterBlock;
// Fill in the shared memory header block (this is some information in the
// shared memory, before the _PERF blocks, which helps the DLL get easy
// access to the two halves of the _PERF structure group that it needs).
lpmcx->lpmsmSharedMemory->ibHeaderOffset =
(LPBYTE)lpObjectType - (LPBYTE)lpmcx->lpmsmSharedMemory;
lpmcx->lpmsmSharedMemory->cbHeaderSize =
(LPBYTE)lpInstanceDefinition - (LPBYTE)lpObjectType;
lpmcx->lpmsmSharedMemory->ibInstanceOffset =
(LPBYTE)lpInstanceDefinition - (LPBYTE)lpmcx->lpmsmSharedMemory;
lpmcx->lpmsmSharedMemory->cbInstanceSize =
lpbStructureEnd - (LPBYTE)lpInstanceDefinition;
// Save a pointer to the counters in the context (for use by
// HrMonBuildStructures).
lpmcx->lpcntCounter = lpcntCounter;
cleanup:
RETURN(hr);
}
//$--HrBuildStructures----------------------------------------------------------
// Helper function that sets up structures representing folders and counters
// in the performance monitoring context.
// -----------------------------------------------------------------------------
static HRESULT HrBuildStructures( // RETURNS: HRESULT
IN LPMONCONTEXT lpmcx,// pointer to perf mon context
IN DWORD ccdCounters,// number of COUNTERDEF structures
IN LPCOUNTERDEF lpcdCounters)// array of COUNTERDEF structures
{
HRESULThr= NOERROR;
HRESULThrT= NOERROR;
// Variables used to create MONFOLDER and MONCOUNTER structures.
LPMONCOUNTERlpmcCounter= NULL;
LPMONFOLDERlpmfFolder= NULL;
// Variables used to access COUNTERDEF structures.
DWORDiCounter= 0;
LPCOUNTERDEFlpcdCounter= NULL;
// Variables used to create jumping sliding windows (JSW's).
__int64dwlJSWStartTime= 0;
DEBUGPRIVATE("HrBuildStructures()\n");
// Check the parameters.
hr = CHK_HrBuildStructures(lpmcx, ccdCounters, lpcdCounters);
if (FAILED(hr))
RETURN(hr);
ASSERTERROR(lpmcx->lpmcMessagesTransferredIn == NULL,
"lpmcx->lpmcMessagesTransferredIn != NULL");
ASSERTERROR(lpmcx->lpmcMessagesTransferredOut == NULL,
"lpmcx->lpmcMessagesTransferredOut != NULL");
ASSERTERROR(lpmcx->lpmcBytesTransferredIn == NULL,
"lpmcx->lpmcBytesTransferredIn != NULL");
ASSERTERROR(lpmcx->lpmcBytesTransferredOut == NULL,
"lpmcx->lpmcBytesTransferredOut != NULL");
ASSERTERROR(lpmcx->lpmcNDRsIn == NULL,
"lpmcx->lpmcNDRsIn != NULL");
ASSERTERROR(lpmcx->lpmcNDRsOut == NULL,
"lpmcx->lpmcNDRsOut != NULL");
ASSERTERROR(lpmcx->lpmcAssociations == NULL,
"lpmcx->lpmcAssociations != NULL");
ASSERTERROR(lpmcx->lpmcFreeList == NULL,
"lpmcx->lpmcFreeList != NULL");
ASSERTERROR(lpmcx->lpmcJSWCounterList == NULL,
"lpmcx->lpmcJSWCounterList != NULL");
ASSERTERROR(lpmcx->lpmfFolderList == NULL,
"lpmcx->lpmfFolderList != NULL");
// Compute time to use as base for JSW's, which should be on an even
// minute boundry.
dwlJSWStartTime = (lpmcx->dwlCurrentTime / ONE_MINUTE) * ONE_MINUTE;
// Process each COUNTERDEF structure. If it is for a user defined counter
// then return a pointer to it to the caller of HrMonInit(). If it is
// for any other type of counter, then add entries to the MONCOUNTER lists
// and the MONFOLDER list as appropriate and create a JSWindow structure
// if needed.
for (iCounter = 0; iCounter < ccdCounters; ++iCounter)
{
lpcdCounter = &lpcdCounters[iCounter];
// If this is a user defined counter then return a pointer to
// the actual counter in the place requested in the call to
// HrMonInit().
if (lpcdCounter->ctStatistic == COUNTERTYPE_USER_DEFINED)
{
ASSERTERROR(lpcdCounter->lppcntUserCounter != NULL,
"lpcdCounter->lppcntUserCounter == NULL");
ASSERTERROR(lpcdCounter->perPeriod == PERIODTYPE_NONE,
"lpcdCounter->perPeriod != PERIODTYPE_NONE");
*(lpcdCounter->lppcntUserCounter) =
&(lpmcx->lpcntCounter[iCounter]);
// Skip to the next COUNTERDEF.
continue;
}
// If we reach here then it's not a user defined counter.
ASSERTERROR(lpcdCounter->ctStatistic < COUNTERTYPE_LAST,
"lpcdCounter->ctStatistic: illegal value");
ASSERTERROR(lpcdCounter->perPeriod < PERIODTYPE_LAST,
"lpcdCounter->perPeriod: illegal value");
ASSERT_DETAIL_LEVEL(lpcdCounter->dwDetailLevel,
"lpcd->dwDetailLevel: illegal value");
// Allocate and fill in a new MONCOUNTER structure. Put new
// MONCOUNTER structure on the free list so MonFreeEverything()
// will be able to find it and free it in case we have to abort.
// The free list is really just a temporary holding area that
// will never have more than one entry at a time.
lpmcCounter = malloc(sizeof(*lpmcCounter));
if (lpmcCounter == NULL)
{
hr = HR_LOG(E_OUTOFMEMORY);
goto cleanup;
}
ZeroMemory(lpmcCounter, sizeof(*lpmcCounter));
lpmcCounter->lpmcNext = lpmcx->lpmcFreeList;
lpmcx->lpmcFreeList = lpmcCounter;
// If the period type is anything other than PERIODTYPE_CONTINUOUS
// then we need to zero out the counter when HrMonUninit is called.
// This makes sure all the counters go to zero for any PerfMon still
// watching. We shouldn't zero the "continuous" ones, because their
// displayed value is based on their increase, and zeroing them may
// result in a huge value being briefly displayed. Of course, we
// will see that huge value anyway if PerfMon is still watching when
// the gateway restarts, but that seems like a better place to take
// the hit.
if (lpcdCounter->perPeriod != PERIODTYPE_CONTINUOUS)
lpmcCounter->fZeroOnFree = TRUE;
// If there is a folder associated with this COUNTERDEF, then we need
// to find a MONFOLDER structure that matches it or create a new one if
// one does not already exist. If there is no folder associated with
// this counter then we can skip this part.
lpmfFolder = NULL;
if (lpcdCounter->lpFolder != NULL)
{
// See if a MONFOLDER structure has already been defined for this
// folder.
hrT = HrMonFindFolder(lpmcx->lpmfFolderList, lpcdCounter->lpFolder,
&lpmfFolder);
if (FAILED(hrT))
{
ULONGcValues= 0;
LPSPropValuelpProps= NULL;
SizedSPropTagArray(1, rgPropTags) =
{1, {PR_MESSAGE_SIZE}};
// A MONFOLDER structure has not already been defined for
// this folder, so allocate one, fill it in and add it to
// the folder list.
lpmfFolder = malloc(sizeof(*lpmfFolder));
if (lpmfFolder == NULL)
{
hr = HR_LOG(E_OUTOFMEMORY);
goto cleanup;
}
ZeroMemory(lpmfFolder, sizeof(*lpmfFolder));
lpmfFolder->lpmcx = lpmcx;
lpmfFolder->lpFolder = lpcdCounter->lpFolder;
// We need to do AddRef/Release because we are keeping
// folder pointers around between HrMonInit and HrMonUninit.
MAPICALL(lpmfFolder->lpFolder)->AddRef(lpmfFolder->lpFolder);
lpmfFolder->lpmfNext = lpmcx->lpmfFolderList;
lpmcx->lpmfFolderList = lpmfFolder;
// Get the number of bytes in the folder (PR_MESSAGE_SIZE)
// and store it in lpmfFolder->cTotalBytesEnteringFolder.
// This will allow proper handling of the "bytes leaving
// folder" counter in the case where messages are waiting
// in the folder when monitoring starts.
hr = lpmfFolder->lpFolder->lpVtbl->GetProps(
lpmfFolder->lpFolder,
(LPSPropTagArray) &rgPropTags,
fMapiUnicode,
&cValues,
&lpProps);
if (FAILED(hr) || hr == MAPI_W_ERRORS_RETURNED)
{
if (hr == MAPI_W_ERRORS_RETURNED)
{
MAPIFREEBUFFER(lpProps);
hr = HR_LOG(E_FAIL);
}
goto cleanup;
}
lpmfFolder->cTotalBytesEnteringFolder = lpProps[0].Value.ul;
MAPIFREEBUFFER(lpProps);
// Create a critical section for this folder.
InitializeCriticalSection(&lpmfFolder->csCriticalSection);
lpmfFolder->fCriticalSectionInitialized = TRUE;
// Set the folder to get it's initial size.
lpmfFolder->fUpdateFolderSize = TRUE;
}
}
// Take the MONCOUNTER structure off the free list in preparation
// for linking it into another list.
ASSERTERROR(lpmcx->lpmcFreeList != NULL, "free list empty");
lpmcCounter = lpmcx->lpmcFreeList;
lpmcx->lpmcFreeList = lpmcx->lpmcFreeList->lpmcNext;
// Link the MONCOUNTER structure into the proper list. This could
// be a general list attached to the context structure, or a folder-
// specific list attached to a MONFOLDER structure.
hr = HrLinkCounterIntoList(
lpmcx,
lpmfFolder,
lpmcCounter,
lpcdCounter->ctStatistic);
if (FAILED(hr))
{
goto cleanup;
}
// Create the JSW attached to the counter structure if one is requested
// by the PERIODTYPE.
if (lpcdCounter->perPeriod == PERIODTYPE_LAST_N_MINUTES)
{
__int64 dwlJumpTime = ONE_MINUTE;
hr = HrJSOpen(
JSWINDOW_TOTAL_RATE,
&TO_FILETIME(dwlJSWStartTime),
&TO_FILETIME(dwlJumpTime),
lpcdCounter->cMinutes,
&lpmcCounter->hjswJumpWindow);
if (FAILED(hr))
{
lpmcCounter->hjswJumpWindow = NULL;// Just to make sure.
goto cleanup;
}
// Also add the counter structure to the list of counter structures
// that have JSWs.
lpmcCounter->lpmcNextJSW = lpmcx->lpmcJSWCounterList;
lpmcx->lpmcJSWCounterList = lpmcCounter;
}
// Add a pointer to the counter to the MONCOUNTER structure.
if (lpmcCounter)
{
lpmcCounter->lpcntCounter = &(lpmcx->lpcntCounter[iCounter]);
}
} // end for
cleanup:
RETURN(hr);
}
//$--HrMonFindFolder------------------------------------------------------------
// Helper function that takes a MAPIFOLDER as input and searches a list of
// folders to find a MONFOLDER structure for that folder. If the folder is
// found a pointer to it is placed in the memory pointed to by lppmfFolder and
// NOERROR is returned. Otherwise, and error is returned.
// -----------------------------------------------------------------------------
static HRESULT HrMonFindFolder(// RETURNS: HRESULT
IN LPMONFOLDER lpmfFolderList,// list of folder structs to search
IN LPMAPIFOLDER lpFolder,// folder to search for
OUT LPMONFOLDER * lppmfFolder)// folder found
{
HRESULThr= NOERROR;
DEBUGPRIVATE("HrMonFindFolder()\n");
// Check the parameters.
hr = CHK_HrMonFindFolder(lpmfFolderList, lpFolder, lppmfFolder);
if (FAILED(hr))
RETURN(hr);
while (lpmfFolderList && lpmfFolderList->lpFolder != lpFolder)
{
lpmfFolderList = lpmfFolderList->lpmfNext;
ASSERT_READ_PTR_OR_NULL(lpmfFolderList, sizeof(*lpmfFolderList),
"lpmfFolderList: bad read pointer");
}
if (lpmfFolderList == NULL)
{
hr = HR_LOG(EDK_E_NOT_FOUND);
}
else
{
*lppmfFolder = lpmfFolderList;
}
RETURN(hr);
}
//$--HrStartUpdateThread--------------------------------------------------------
// Helper function that starts the thread that wakes up periodically to update
// the jumping sliding windows and folders.
// -----------------------------------------------------------------------------
static HRESULT HrStartUpdateThread( // RETURNS: HRESULT
IN LPMONCONTEXT lpmcx) // pointer to perf mon context
{
HRESULThr= NOERROR;
SECURITY_ATTRIBUTESsaEvent= {0, NULL, TRUE};
DEBUGPRIVATE("HrStartUpdateThread()\n");
// Check the parameters.
hr = CHK_HrStartUpdateThread(lpmcx);
if (FAILED(hr))
RETURN(hr);
ASSERTERROR(!lpmcx->fUpdateThreadRunning, "thread already running");
ASSERTERROR(lpmcx->hUpdateThread == NULL, "lpmcx->hUpdateThread != NULL");
ASSERTERROR(lpmcx->hUpdateThreadWakeEvent == NULL,
"lpmcx->hUpdateThreadWakeEvent != NULL");
ASSERTERROR(lpmcx->dwUpdateThreadID == 0, "lpmcx->dwUpdateThreadID != 0");
ASSERTERROR(lpmcx->fUpdateThreadTerminate == FALSE,
"lpmcx->fUpdateThreadTerminate != FALSE");
// Create an unnamed, inheritable, auto-reset event. This will be
// set by the gateway process to wake the thread, either to update
// or to terminate. Start it signaled so the update thread wakes
// up to set the initial values of the folder counters.
lpmcx->hUpdateThreadWakeEvent = CreateEvent(&saEvent, FALSE, TRUE, NULL);
if (lpmcx->hUpdateThreadWakeEvent == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Create the thread.
lpmcx->hUpdateThread = CreateThread(NULL, 0, UpdateThread, lpmcx, 0,
&lpmcx->dwUpdateThreadID);
if (lpmcx->hUpdateThread == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
cleanup:
if (FAILED(hr))
{
CLOSEHANDLE(lpmcx->hUpdateThreadWakeEvent);
}
RETURN (hr);
}
//$--UpdateThread---------------------------------------------------------------
// Function that is called as a seperate thread. It wakes up periodically and
// updates the jumping sliding windows and folders.
// -----------------------------------------------------------------------------
static DWORD WINAPI UpdateThread( // RETURNS: error code
IN LPVOID lpThreadParameter)// pointer to perf mon context
{
static BOOLfGetPropsErrorLoggedOnce= FALSE;
static BOOLfJSWFailureLoggedOnce= FALSE;
LPMONCONTEXTlpmcx= lpThreadParameter;
HRESULThr= NOERROR;
DWORDdwStatus= ERROR_SUCCESS;
LPMONCOUNTERlpmc= NULL;
LPMONFOLDERlpmf= NULL;
DWORDdwMillisecondsToWait= 0;
DWORDdwWindowValue= 0;
DWORDdwFractionCompleteNum= 0;
DWORDdwFractionCompleteDen= 0;
DWORDcMessagesInFolder= 0;
DWORDcBytesInFolder= 0;
DWORDcMessagesEnteringFolder= 0;
DWORDcBytesEnteringFolder= 0;
DWORDcMessagesLeavingFolder= 0;
DWORDcBytesLeavingFolder= 0;
ULONGcValues= 0;
LPSPropValuelpProps= NULL;
SizedSPropTagArray(2, rgPropTags) =
{2, {PR_CONTENT_COUNT, PR_MESSAGE_SIZE}};
BOOL fCountersLocked = FALSE;
DEBUGPRIVATE("UpdateThread()\n");
// Check the parameters.
hr = CHK_UpdateThread(lpThreadParameter);
if (FAILED(hr))
RETURN(hr);
// Loop until flag is set to stop the thread.
while (!lpmcx->fUpdateThreadTerminate)
{
// If we have JSW's, then calculate how many milliseconds to wait
// until the next even minute mark. Wait an extra second just to
// be sure we don't wake up too early.
if (lpmcx->lpmcJSWCounterList)
{
__int64 dwlCurrentTime = 0;
hr = HrGetCurrentTime(&dwlCurrentTime);
if (FAILED(hr))
goto cleanup;
dwMillisecondsToWait = (DWORD)
(((ONE_MINUTE - (dwlCurrentTime % ONE_MINUTE))
+ 10000000) / 10000);
}
// Otherwise, wait until the other thread wakes us up.
else
{
dwMillisecondsToWait = INFINITE;
}
// Wait for specified time or wake event.
dwStatus = WaitForSingleObject(
lpmcx->hUpdateThreadWakeEvent,
dwMillisecondsToWait);
if (dwStatus != WAIT_TIMEOUT && dwStatus != WAIT_OBJECT_0)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Check for terminate request.
if (lpmcx->fUpdateThreadTerminate)
goto cleanup;
// Update the folder counters from the "update request" variables.
if (lpmcx->lpmfFolderList)
{
for (
lpmf = lpmcx->lpmfFolderList;
lpmf;
lpmf = lpmf->lpmfNext)
{
cMessagesEnteringFolder = 0;
cBytesEnteringFolder = 0;
cMessagesLeavingFolder = 0;
cBytesLeavingFolder = 0;
cMessagesInFolder = 0;
cBytesInFolder = 0;
if (lpmf->fUpdateFolderSize)
{
// Check for terminate request.
if (lpmcx->fUpdateThreadTerminate)
goto cleanup;
// Get and reset the "update request" variables.
EnterCriticalSection(&lpmf->csCriticalSection);
cMessagesEnteringFolder = lpmf->cMessagesEnteringFolder;
lpmf->cMessagesEnteringFolder = 0;
cBytesEnteringFolder = lpmf->cBytesEnteringFolder;
lpmf->cBytesEnteringFolder = 0;
cMessagesLeavingFolder = lpmf->cMessagesLeavingFolder;
lpmf->cMessagesLeavingFolder = 0;
cBytesLeavingFolder = lpmf->cBytesLeavingFolder;
lpmf->cBytesLeavingFolder = 0;
lpmf->fUpdateFolderSize = FALSE;
LeaveCriticalSection(&lpmf->csCriticalSection);
// Check for terminate request.
if (lpmcx->fUpdateThreadTerminate)
goto cleanup;
// Get number of messages and bytes in folder.
if (lpmf->lpmcMessagesInFolder ||
lpmf->lpmcBytesInFolder ||
lpmf->lpmcBytesLeavingFolder)
{
// Get the PR_CONTENT_COUNT and PR_MESSAGE_SIZE props.
hr = lpmf->lpFolder->lpVtbl->GetProps(
lpmf->lpFolder,
(LPSPropTagArray) &rgPropTags,
fMapiUnicode,
&cValues,
&lpProps);
if (FAILED(hr) || hr == MAPI_W_ERRORS_RETURNED)
{
if (hr == MAPI_W_ERRORS_RETURNED)
{
MAPIFREEBUFFER(lpProps);
}
goto cleanup;
}
cMessagesInFolder = lpProps[0].Value.ul;
cBytesInFolder = lpProps[1].Value.ul;
MAPIFREEBUFFER(lpProps);
}
// Check for terminate request.
if (lpmcx->fUpdateThreadTerminate)
goto cleanup;
// If they want the number of bytes leaving the folder
// then calculate it and keep track of it here.
if (lpmf->lpmcBytesLeavingFolder)
{
// Compute the total number of bytes entering folder
// so far.
lpmf->cTotalBytesEnteringFolder += cBytesEnteringFolder;
// Figure out how many bytes should be in the folder
// according to the running totals of bytes entering
// and bytes leaving that we have reported. If the
// "should be" is higher than the actual, then report
// the difference as the number of bytes leaving the
// folder, and update the reported number leaving.
if (lpmf->cTotalBytesEnteringFolder -
lpmf->cTotalBytesLeavingFolder >
cBytesInFolder)
{
cBytesLeavingFolder =
lpmf->cTotalBytesEnteringFolder -
lpmf->cTotalBytesLeavingFolder -
cBytesInFolder;
lpmf->cTotalBytesLeavingFolder +=
cBytesLeavingFolder;
}
}
// Lock the counters.
hr = HrMonLockCounters();
if (FAILED(hr))
goto cleanup;
fCountersLocked = TRUE;
// Get the system time.
hr = HrGetCurrentTime(&lpmcx->dwlCurrentTime);
if (FAILED(hr))
goto cleanup;
// Update the counters.
hr = HrAddToCounters(
lpmcx,
lpmf->lpmcMessagesEnteringFolder,
cMessagesEnteringFolder);
if (FAILED(hr))
goto cleanup;
hr = HrAddToCounters(
lpmcx,
lpmf->lpmcBytesEnteringFolder,
cBytesEnteringFolder);
if (FAILED(hr))
goto cleanup;
hr = HrAddToCounters(
lpmcx,
lpmf->lpmcMessagesLeavingFolder,
cMessagesLeavingFolder);
if (FAILED(hr))
goto cleanup;
hr = HrAddToCounters(
lpmcx,
lpmf->lpmcBytesLeavingFolder,
cBytesLeavingFolder);
if (FAILED(hr))
goto cleanup;
hr = HrSetCounters(
lpmf->lpmcMessagesInFolder,
cMessagesInFolder);
if (FAILED(hr))
goto cleanup;
hr = HrSetCounters(
lpmf->lpmcBytesInFolder,
cBytesInFolder);
if (FAILED(hr))
goto cleanup;
// Unlock the counters.
fCountersLocked = FALSE;
hr = HrMonUnlockCounters();
if (FAILED(hr))
goto cleanup;
}
}
}
// Update the JSW counters.
if (lpmcx->lpmcJSWCounterList)
{
for (
lpmc = lpmcx->lpmcJSWCounterList;
lpmc;
lpmc = lpmc->lpmcNextJSW)
{
ASSERT_READ_PTR(lpmc, sizeof(*lpmc),
"lpmc: bad write pointer");
ASSERT_WRITE_PTR(lpmc->lpcntCounter,
sizeof(*lpmc->lpcntCounter),
"lpmc->lpcntCounter: bad write pointer");
ASSERTERROR(lpmc->hjswJumpWindow != NULL,
"lpmc->hjswJumpWindow == NULL");
// Check for terminate request.
if (lpmcx->fUpdateThreadTerminate)
goto cleanup;
// Lock the counters.
hr = HrMonLockCounters();
if (FAILED(hr))
goto cleanup;
fCountersLocked = TRUE;
// Get the value of the JSWindow (update system time).
hr = HrGetCurrentTime(&lpmcx->dwlCurrentTime);
if (FAILED(hr))
goto cleanup;
hr = HrJSGetValue(
lpmc->hjswJumpWindow,
&TO_FILETIME(lpmcx->dwlCurrentTime),
&dwWindowValue,
&dwFractionCompleteNum,
&dwFractionCompleteDen);
if (FAILED(hr))
goto cleanup;
*lpmc->lpcntCounter = dwWindowValue;
// Unlock the counters.
fCountersLocked = FALSE;
hr = HrMonUnlockCounters();
if (FAILED(hr))
goto cleanup;
} // end for
} // end if
} // end while (!lpmcx->fUpdateThreadTerminate)
cleanup:
// Unlock the counters if they're locked.
if (fCountersLocked)
{
hr = HrMonUnlockCounters();
if (FAILED(hr))
HR_LOG(hr);
}
// Always return success.
return(ERROR_SUCCESS);
}
//$--HrFreeEverything-----------------------------------------------------------
// Helper function that shuts down the thread, monitoring, and frees memory.
// -----------------------------------------------------------------------------
static HRESULT HrFreeEverything(// RETURNS: HRESULT
IN LPMONCONTEXT *lppmcx)// the address of the pointer to the
// performance monitoring context (the
// pointer will be set to NULL)
{
BOOLfItWorked= FALSE;
HRESULThr= NOERROR;
HRESULThrT= NOERROR;
LPMONCONTEXTlpmcx= NULL;
LPMONFOLDERlpmf= NULL;
LPMONCOUNTERlpmc= NULL;
DEBUGPRIVATE("HrFreeEverything()\n");
// Check the parameters.
hr = CHK_HrFreeEverything(lppmcx);
if (FAILED(hr))
RETURN(hr);
// This pointer is easier to use.
lpmcx = *lppmcx;
// Free the context only if there is one allocated.
if (lpmcx)
{
// Tell the update thread to shut down and wait for it to do so.
if (lpmcx->hUpdateThread)
{
lpmcx->fUpdateThreadTerminate = TRUE;
hrT = HrWakeUpdateThread(lpmcx);
if (FAILED(hrT))
hr = hrT;
else
{
DWORD dw = WaitForSingleObject(lpmcx->hUpdateThread, INFINITE);
if (dw != WAIT_OBJECT_0)
{
hr = HR_LOG(E_FAIL);
}
}
CLOSEHANDLE(lpmcx->hUpdateThread);
lpmcx->dwUpdateThreadID = 0;
}
// Close the handle to the thread termination event.
CLOSEHANDLE(lpmcx->hUpdateThreadWakeEvent);
// Unhook monitoring routines and delete critical sections for folders.
for (
lpmf = lpmcx->lpmfFolderList;
lpmf;
lpmf = lpmf->lpmfNext)
{
if (lpmf->fCriticalSectionInitialized)
{
DeleteCriticalSection(&lpmf->csCriticalSection);
lpmf->fCriticalSectionInitialized = FALSE;
}
if (lpmf->lpTable)
{
hrT = MAPICALL(lpmf->lpTable)->
Unadvise(lpmf->lpTable, lpmf->ulConnection);
if (FAILED(hrT))
hr = hrT;
lpmf->lpTable = NULL;
ULRELEASE(lpmf->lpTable);
lpmf->lpTable = NULL;
}
ULRELEASE(lpmf->lpAdvise);
lpmf->lpAdvise = NULL;
lpmf->ulConnection = 0;
}
// Free strings attached to the context.
ASSERT_STRING_PTR_OR_NULL(lpmcx->lpszObjectName,
"lpmcx->lpszObjectName: bad string pointer");
FREE(lpmcx->lpszObjectName);
ASSERT_STRING_PTR_OR_NULL(lpmcx->lpszObjectClass,
"lpmcx->lpszObjectClass: bad string pointer");
FREE(lpmcx->lpszObjectClass);
// Close all the JSW's.
for (lpmc = lpmcx->lpmcJSWCounterList; lpmc; lpmc = lpmc->lpmcNextJSW)
{
ASSERTERROR(lpmc->hjswJumpWindow != NULL,
"lpmc->hjswJumpWindow == NULL");
JSClose(lpmc->hjswJumpWindow);
lpmc->hjswJumpWindow = NULL;
}
lpmcx->lpmcJSWCounterList = NULL;
// Free all the MONCOUNTER structures attached directly to the context.
hrT = HrFreeCounterList(&lpmcx->lpmcMessagesTransferredIn);
if (FAILED(hrT))
hr = hrT;
hrT = HrFreeCounterList(&lpmcx->lpmcMessagesTransferredOut);
if (FAILED(hrT))
hr = hrT;
hrT = HrFreeCounterList(&lpmcx->lpmcBytesTransferredIn);
if (FAILED(hrT))
hr = hrT;
hrT = HrFreeCounterList(&lpmcx->lpmcBytesTransferredOut);
if (FAILED(hrT))
hr = hrT;
hrT = HrFreeCounterList(&lpmcx->lpmcNDRsIn);
if (FAILED(hrT))
hr = hrT;
hrT = HrFreeCounterList(&lpmcx->lpmcNDRsOut);
if (FAILED(hrT))
hr = hrT;
hrT = HrFreeCounterList(&lpmcx->lpmcAssociations);
if (FAILED(hrT))
hr = hrT;
hrT = HrFreeCounterList(&lpmcx->lpmcFreeList);
if (FAILED(hrT))
hr = hrT;
// Free all the MONFOLDER structures, including the MONCOUNTER
// structures attached to the folders.
hrT = HrFreeFolderList(&lpmcx->lpmfFolderList);
if (FAILED(hrT))
hr = hrT;
// If we mapped the shared memory, unmap the view of the file.
// NOTE: LEAVE THE fDateIsValid FLAG UNCHANGED SO ANYBODY STILL
// WATCHING US WILL SEE VALID DATA!!!
if (lpmcx->lpmsmSharedMemory)
{
fItWorked = UnmapViewOfFile(lpmcx->lpmsmSharedMemory);
if (!fItWorked)
{
hr = HR_LOG(E_FAIL);
}
lpmcx->lpmsmSharedMemory = NULL;
}
// Close file mapping object if it is open.
CLOSEHANDLE(lpmcx->hSharedMemoryMapping);
// Clear other shared memory variables.
lpmcx->cbSharedMemorySize = 0;
// Close mutex handle if it is open.
CLOSEHANDLE(lpmcx->hSharedMemoryMutex);
// Free the context itself.
FREE(*lppmcx);
}
RETURN(hr);
}
//$--HrFreeCounterList----------------------------------------------------------
// Helper function that frees a list of MONCOUNTER structures.
// -----------------------------------------------------------------------------
static HRESULT HrFreeCounterList(// RETURNS: HRESULT
IN LPMONCOUNTER *lppmcCounterList)// address of pointer to first element
// of counter list to be freed
{
HRESULThr= NOERROR;
LPMONCOUNTER lpmc= NULL;
LPMONCOUNTERlpmcNext= NULL;
DEBUGPRIVATE("HrFreeCounterList()\n");
// Check the parameters.
hr = CHK_HrFreeCounterList(lppmcCounterList);
if (FAILED(hr))
RETURN(hr);
lpmc = *lppmcCounterList;
while (lpmc)
{
ASSERT_WRITE_PTR(lpmc, sizeof(*lpmc), "lpmc: bad write pointer");
ASSERTWARNING(lpmc->hjswJumpWindow == NULL,
"lpmc->hjswJumpWindow != NULL");
// Zero out the actual counter if requested.
if (lpmc->fZeroOnFree && lpmc->lpcntCounter)
*(lpmc->lpcntCounter) = 0;
// Now free this node and move to the next one.
lpmcNext = lpmc->lpmcNext;
FREE(lpmc);
lpmc = lpmcNext;
}
*lppmcCounterList = NULL;
RETURN(hr);
}
//$--HrFreeFolderList-----------------------------------------------------------
// Helper function that frees a list of MONFOLDER structures and the
// MONCOUNTER structures attached to them.
// -----------------------------------------------------------------------------
static HRESULT HrFreeFolderList(// RETURNS: HRESULT
IN LPMONFOLDER *lppmfFolderList)// address of pointer to first element
// of folder list to be freed
{
HRESULThr= NOERROR;
HRESULThrT= NOERROR;
LPMONFOLDER lpmf= NULL;
LPMONFOLDERlpmfNext= NULL;
DEBUGPRIVATE("HrFreeFolderList()\n");
// Check the parameters.
hr = CHK_HrFreeFolderList(lppmfFolderList);
if (FAILED(hr))
RETURN(hr);
lpmf = *lppmfFolderList;
while (lpmf)
{
ULONG ulRefCount = 0;
ASSERT_WRITE_PTR(lpmf, sizeof(*lpmf), "lpmf: bad write pointer");
// We need to do AddRef/Release because we are keeping
// folder pointers around between HrMonInit and HrMonUninit.
ulRefCount = MAPICALL(lpmf->lpFolder)->Release(lpmf->lpFolder);
ASSERTWARNING(ulRefCount <= 2, "ulRefCount > 2");
hrT = HrFreeCounterList(&lpmf->lpmcMessagesInFolder);
if (FAILED(hrT))
hr = hrT;
hrT = HrFreeCounterList(&lpmf->lpmcBytesInFolder);
if (FAILED(hrT))
hr = hrT;
hrT = HrFreeCounterList(&lpmf->lpmcMessagesEnteringFolder);
if (FAILED(hrT))
hr = hrT;
hrT = HrFreeCounterList(&lpmf->lpmcBytesEnteringFolder);
if (FAILED(hrT))
hr = hrT;
hrT = HrFreeCounterList(&lpmf->lpmcMessagesLeavingFolder);
if (FAILED(hrT))
hr = hrT;
hrT = HrFreeCounterList(&lpmf->lpmcBytesLeavingFolder);
if (FAILED(hrT))
hr = hrT;
lpmfNext = lpmf->lpmfNext;
FREE(lpmf);
lpmf = lpmfNext;
}
*lppmfFolderList = NULL;
RETURN(hr);
}
//$--HrAddToCounters------------------------------------------------------------
// Helper function that adds a value to each of the counters on the list.
// -----------------------------------------------------------------------------
static HRESULT HrAddToCounters(// RETURNS: HRESULT
IN LPMONCONTEXT lpmcx,// pointer to perf mon context
IN LPMONCOUNTER lpmcList,// pointer to list of counters
IN LONG dwAmount)// amount to add to each one
{
static BOOLfLoggedOnce= FALSE;
HRESULThr= NOERROR;
LPMONCOUNTERlpmc= NULL;
DEBUGPRIVATE("HrAddToCounters()\n");
// Check the parameters.
hr = CHK_HrAddToCounters(lpmcx, lpmcList, dwAmount);
if (FAILED(hr))
RETURN(hr);
for (lpmc = lpmcList; lpmc; lpmc = lpmc->lpmcNext)
{
ASSERT_READ_PTR(lpmc, sizeof(*lpmc), "lpmc: bad read pointer");
if (lpmc->hjswJumpWindow)
{
// Update jump window.
hr = HrJSCollectDataPoint(
lpmc->hjswJumpWindow,
&TO_FILETIME(lpmcx->dwlCurrentTime),
dwAmount);
if (FAILED(hr))
goto cleanup;
}
else
{
// Update counter directly.
ASSERT_WRITE_PTR(
lpmc->lpcntCounter,
sizeof(*lpmc->lpcntCounter),
"lpmc->lpcntCounter: bad write pointer");
*lpmc->lpcntCounter += dwAmount;
}
}
cleanup:
RETURN(hr);
}
//$--HrSetCounters--------------------------------------------------------------
// Helper function that assigns a value to each of the counters on the list.
// -----------------------------------------------------------------------------
static HRESULT HrSetCounters(// RETURNS: HRESULT
IN LPMONCOUNTER lpmcList,// pointer to list of counters
IN DWORD dwAmount)// amount to set each one to
{
HRESULThr= NOERROR;
LPMONCOUNTERlpmc= NULL;
DEBUGPRIVATE("HrSetCounters()\n");
// Check the parameters.
hr = CHK_HrSetCounters(lpmcList, dwAmount);
if (FAILED(hr))
RETURN(hr);
for (lpmc = lpmcList; lpmc; lpmc = lpmc->lpmcNext)
{
ASSERT_READ_PTR(lpmc, sizeof(*lpmc), "lpmc: bad read pointer");
ASSERTERROR(lpmc->hjswJumpWindow == NULL,
"lpmc->hjswJumpWindow != NULL");
ASSERT_WRITE_PTR(lpmc->lpcntCounter, sizeof(*lpmc->lpcntCounter),
"lpmc->lpcntCounter: bad write pointer");
*lpmc->lpcntCounter = dwAmount;
}
RETURN(hr);
}
//$--HrComputeCounterType-------------------------------------------------------
// Helper function that computes the counter type value to use in the
// _PERF_COUNTER_DEFINITION structure defined in winperf.h.
// -----------------------------------------------------------------------------
static HRESULT HrComputeCounterType(// RETURNS: HRESULT
IN COUNTERTYPE ctStatistic,// the statistic this counter monitors
IN PERIODTYPE perPeriod,// the period of this counter
IN DWORD dwUserCounterType,// the CounterType (if ctStatistic is
// COUNTERTYPE_USER_DEFINED)
OUT DWORD * lpdwCounterType)// returned counter type
{
HRESULThr= NOERROR;
DWORDdwCounterType= PERF_COUNTER_RAWCOUNT;
DEBUGPRIVATE("HrComputeCounterType()\n");
// Check the parameters.
hr = CHK_HrComputeCounterType(
ctStatistic,
perPeriod,
dwUserCounterType,
lpdwCounterType);
if (FAILED(hr))
RETURN(hr);
switch (ctStatistic)
{
case COUNTERTYPE_MESSAGES_IN_FOLDER:
case COUNTERTYPE_BYTES_IN_FOLDER:
case COUNTERTYPE_ASSOCIATIONS:
ASSERTERROR(perPeriod == PERIODTYPE_NONE,
"perPeriod != PERIODTYPE_NONE");
dwCounterType = PERF_COUNTER_RAWCOUNT;
break;
case COUNTERTYPE_MESSAGES_ENTERING_FOLDER:
case COUNTERTYPE_BYTES_ENTERING_FOLDER:
case COUNTERTYPE_MESSAGES_LEAVING_FOLDER:
case COUNTERTYPE_BYTES_LEAVING_FOLDER:
case COUNTERTYPE_MESSAGES_TRANSFERRED_IN:
case COUNTERTYPE_BYTES_TRANSFERRED_IN:
case COUNTERTYPE_MESSAGES_TRANSFERRED_OUT:
case COUNTERTYPE_BYTES_TRANSFERRED_OUT:
case COUNTERTYPE_NDRS_IN:
case COUNTERTYPE_NDRS_OUT:
ASSERTERROR(perPeriod != PERIODTYPE_NONE,
"perPeriod == PERIODTYPE_NONE");
if (perPeriod == PERIODTYPE_CONTINUOUS)
{
dwCounterType = PERF_COUNTER_COUNTER;
}
else
{
dwCounterType = PERF_COUNTER_RAWCOUNT;
}
break;
case COUNTERTYPE_USER_DEFINED:
dwCounterType = dwUserCounterType;
break;
default:
hr = HR_LOG(E_FAIL);
goto cleanup;
}
*lpdwCounterType = dwCounterType;
cleanup:
RETURN(hr);
}
//$--HrAlignPointer-------------------------------------------------------------
// Helper function that moves a pointer forward, if nescessary, so that it has
// a given alignment. NOTE: the alignment must be a power of 2.
// -----------------------------------------------------------------------------
static HRESULT HrAlignPointer( // RETURNS: HRESULT
IN DWORD cbAlignment,// the alignment desired
IN OUT LPVOID * lppvPointer)// the pointer to be aligned
{
HRESULThr= NOERROR;
DEBUGPRIVATE("HrAlignPointer()\n");
// Make sure the alignment is a power of 2.
// Check the parameters.
hr = CHK_HrAlignPointer(cbAlignment, lppvPointer);
if (FAILED(hr))
RETURN(hr);
*lppvPointer = ((LPVOID)
(((DWORD)(((LPBYTE)(*lppvPointer)) + cbAlignment - 1)) &
(~(cbAlignment - 1)))
);
RETURN(hr);
}
//$--FolderNotification---------------------------------------------------------
// Function called by the notification engine when a folder is changed.
//It sets the "update request" variables to tell the folder update thread
//to update the counters associated with this folder. In order to make this
//routine run faster it does not update the counters directly. Since the
//notification engine only allows one notification routine per process to
//run at any one time, this improves efficiency.
// -----------------------------------------------------------------------------
static ULONG STDAPICALLTYPE FolderNotification( // RETURNS: S_OK (always)
IN LPVOID lpvContext,// pointer to MONFOLDER structure
IN DWORD cNotification,// number of elements in following array
IN LPNOTIFICATION lpNotifications)// array of notification structures
{
HRESULThr= NOERROR;
LPMONFOLDERlpmf= lpvContext;
ULONGcValues= 0;
LPSPropValuelpProps= NULL;
DWORDcBytesEnteringFolder= 0;
DWORDcMessagesEnteringFolder= 0;
DWORDcBytesLeavingFolder= 0;
DWORDcMessagesLeavingFolder= 0;
BOOLfUpdateFolderSize= FALSE;
DEBUGPRIVATE("FolderNotification()\n");
// Check the parameters.
hr = CHK_FolderNotification(lpvContext, cNotification, lpNotifications);
if (FAILED(hr))
return (S_OK);
// If cNotification == 0, just take a snapshot of the folder.
if (cNotification == 0)
{
fUpdateFolderSize = TRUE;
}
// Go through the list of notifications, counting up the number of bytes
// and messages that entered or left the folder. Also figure out if the
// folder size changed so we know if we need to get the new size.
for (; cNotification; cNotification--, lpNotifications++)
{
// If it's not a table notification then ignore it.
if (lpNotifications->ulEventType == fnevTableModified)
{
// Handle different types of table notifications.
switch (lpNotifications->info.tab.ulTableEvent)
{
case TABLE_ROW_ADDED:
// Update counters for bytes and messages entering folder.
if (lpmf->lpmcBytesEnteringFolder)
{
for (
lpProps = lpNotifications->info.tab.row.lpProps,
cValues = lpNotifications->info.tab.row.cValues;
cValues;
cValues--, lpProps++)
{
if (lpProps->ulPropTag == PR_MESSAGE_SIZE)
{
cBytesEnteringFolder += lpProps->Value.ul;
break;
}
}
ASSERTWARNING(cValues,
"PR_MESSAGE_SIZE not found: 0 assumed");
}
cMessagesEnteringFolder++;
fUpdateFolderSize = TRUE;
break;
case TABLE_ROW_DELETED:
// Update counter for messages leaving folder. The
// number of bytes leaving the folder will be calculated
// in the thread, since the notification engine doesn't
// give us that information.
cMessagesLeavingFolder++;
fUpdateFolderSize = TRUE;
break;
case TABLE_SORT_DONE:
case TABLE_RESTRICT_DONE:
case TABLE_SETCOL_DONE:
// Nothing really changed in the folder.
break;
default:
// (fall through)
case TABLE_ROW_MODIFIED:
case TABLE_CHANGED:
case TABLE_ERROR:
fUpdateFolderSize = TRUE;
break;
} // end switch
} // end if
} // end for
// Now set the "update request" variables for any counters that need
// updating.
if (fUpdateFolderSize)
{
EnterCriticalSection(&lpmf->csCriticalSection);
lpmf->fUpdateFolderSize = TRUE;
if (cBytesEnteringFolder && lpmf->lpmcBytesEnteringFolder)
lpmf->cBytesEnteringFolder += cBytesEnteringFolder;
if (cMessagesEnteringFolder && lpmf->lpmcMessagesEnteringFolder)
lpmf->cMessagesEnteringFolder += cMessagesEnteringFolder;
if (cBytesLeavingFolder && lpmf->lpmcBytesLeavingFolder)
lpmf->cBytesLeavingFolder += cBytesLeavingFolder;
if (cMessagesLeavingFolder && lpmf->lpmcMessagesLeavingFolder)
lpmf->cMessagesLeavingFolder += cMessagesLeavingFolder;
LeaveCriticalSection(&lpmf->csCriticalSection);
(void) HrWakeUpdateThread(lpmf->lpmcx);
}
return (S_OK);
}
//$--HrWakeUpdateThread---------------------------------------------------------
// Helper function that signals the update thread to wake up.
// -----------------------------------------------------------------------------
static HRESULT HrWakeUpdateThread(
IN LPMONCONTEXT lpmcx)
{
HRESULThr= NOERROR;
BOOLfItWorked= TRUE;
DEBUGPRIVATE("HrWakeUpdateThread()\n");
// Check the parameters.
hr = CHK_HrWakeUpdateThread(lpmcx);
if (FAILED(hr))
RETURN(hr);
fItWorked = SetEvent(lpmcx->hUpdateThreadWakeEvent);
if (!fItWorked)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
cleanup:
RETURN(hr);
}
//$--HrRegisterNotification-----------------------------------------------------
// Helper function that registers notification on folders.
// -----------------------------------------------------------------------------
static HRESULT HrRegisterNotification( // RETURNS: HRESULT
IN LPMONCONTEXT lpmcx) // pointer to perf mon context
{
HRESULT hr = NOERROR;
HRESULT hrT= SUCCESS_SUCCESS;
LPMONFOLDERlpmf= NULL;
SPropTagArrayrgPropTagBytesInMessage= {1, {PR_MESSAGE_SIZE}};
ULONG ulRowCount = 0;
DEBUGPRIVATE("HrRegisterNotification()\n");
// Check the parameters.
hr = CHK_HrRegisterNotification(lpmcx);
if (FAILED(hr))
RETURN(hr);
// Start monitoring for each folder in the list.
for (lpmf = lpmcx->lpmfFolderList; lpmf; lpmf = lpmf->lpmfNext)
{
ASSERT_WRITE_PTR(lpmf, sizeof(*lpmf), "lpmf: bad write pointer");
ASSERTERROR(lpmf->lpmcx == lpmcx, "lpmf->lpmcx != lpmcx");
ASSERTERROR(lpmf->lpFolder != NULL, "lpmf->lpFolder == NULL");
ASSERTERROR(lpmf->ulConnection == 0,
"lpmf: folder already has notification set");
ASSERTWARNING(
lpmf->lpmcMessagesInFolder ||
lpmf->lpmcBytesInFolder ||
lpmf->lpmcMessagesEnteringFolder ||
lpmf->lpmcBytesEnteringFolder ||
lpmf->lpmcMessagesLeavingFolder ||
lpmf->lpmcBytesLeavingFolder,
"lpmf: folder without counters");
ASSERTWARNING(lpmf->lpTable == NULL, "lpmf->lpTable != NULL");
ASSERTWARNING(lpmf->lpAdvise == NULL, "lpmf->lpAdvise != NULL");
// Set notification on this folder.
hrT = MAPICALL(lpmf->lpFolder)->GetContentsTable(lpmf->lpFolder, MAPI_DEFERRED_ERRORS,
&lpmf->lpTable);
if (FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
hrT = MAPICALL(lpmf->lpTable)->SetColumns(lpmf->lpTable,
&rgPropTagBytesInMessage, 0);
if (FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
hrT = HrAllocAdviseSink(FolderNotification, lpmf, &lpmf->lpAdvise);
if (FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
hrT = MAPICALL(lpmf->lpTable)->Advise(
lpmf->lpTable,
fnevTableModified,
lpmf->lpAdvise,
&lpmf->ulConnection);
if (FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Call GetRowCount to make Advise work correctly.
hrT = MAPICALL(lpmf->lpTable)->GetRowCount(
lpmf->lpTable,
0,
&ulRowCount);
if (FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
} // end for
cleanup:
RETURN(hr);
}
//$--HrLinkCounterIntoList------------------------------------------------------
// Helper function that takes a MONCOUNTER structure and links it into the
// proper list in the MONCONTEXT or the specified MONFOLDER, depending on the
// counter type.
// -----------------------------------------------------------------------------
static HRESULT HrLinkCounterIntoList(// RETURNS: return code
IN LPMONCONTEXT lpmcx,// pointer to perf mon context
IN LPMONFOLDER lpmf,// for folder-specific counters
IN LPMONCOUNTER lpmc,// counter structure to add to list
IN COUNTERTYPE ctCounterType)// type of counter this is
{
MONCOUNTER **lppmcPlaceToAdd= NULL;
HRESULThr= NOERROR;
DEBUGPRIVATE("HrLinkCounterIntoList()\n");
// Check the parameters.
hr = CHK_HrLinkCounterIntoList(lpmcx, lpmf, lpmc, ctCounterType);
if (FAILED(hr))
RETURN(hr);
switch (ctCounterType)
{
case COUNTERTYPE_MESSAGES_IN_FOLDER:
ASSERT_WRITE_PTR(lpmf, sizeof(*lpmf), "lpmf: bad write pointer");
lppmcPlaceToAdd = &lpmf->lpmcMessagesInFolder;
break;
case COUNTERTYPE_BYTES_IN_FOLDER:
ASSERT_WRITE_PTR(lpmf, sizeof(*lpmf), "lpmf: bad write pointer");
lppmcPlaceToAdd = &lpmf->lpmcBytesInFolder;
break;
case COUNTERTYPE_MESSAGES_ENTERING_FOLDER:
ASSERT_WRITE_PTR(lpmf, sizeof(*lpmf), "lpmf: bad write pointer");
lppmcPlaceToAdd = &lpmf->lpmcMessagesEnteringFolder;
break;
case COUNTERTYPE_BYTES_ENTERING_FOLDER:
ASSERT_WRITE_PTR(lpmf, sizeof(*lpmf), "lpmf: bad write pointer");
lppmcPlaceToAdd = &lpmf->lpmcBytesEnteringFolder;
break;
case COUNTERTYPE_MESSAGES_LEAVING_FOLDER:
ASSERT_WRITE_PTR(lpmf, sizeof(*lpmf), "lpmf: bad write pointer");
lppmcPlaceToAdd = &lpmf->lpmcMessagesLeavingFolder;
break;
case COUNTERTYPE_BYTES_LEAVING_FOLDER:
ASSERT_WRITE_PTR(lpmf, sizeof(*lpmf), "lpmf: bad write pointer");
lppmcPlaceToAdd = &lpmf->lpmcBytesLeavingFolder;
break;
case COUNTERTYPE_MESSAGES_TRANSFERRED_IN:
lppmcPlaceToAdd = &lpmcx->lpmcMessagesTransferredIn;
break;
case COUNTERTYPE_BYTES_TRANSFERRED_IN:
lppmcPlaceToAdd = &lpmcx->lpmcBytesTransferredIn;
break;
case COUNTERTYPE_MESSAGES_TRANSFERRED_OUT:
lppmcPlaceToAdd = &lpmcx->lpmcMessagesTransferredOut;
break;
case COUNTERTYPE_BYTES_TRANSFERRED_OUT:
lppmcPlaceToAdd = &lpmcx->lpmcBytesTransferredOut;
break;
case COUNTERTYPE_NDRS_IN:
lppmcPlaceToAdd = &lpmcx->lpmcNDRsIn;
break;
case COUNTERTYPE_NDRS_OUT:
lppmcPlaceToAdd = &lpmcx->lpmcNDRsOut;
break;
case COUNTERTYPE_ASSOCIATIONS:
lppmcPlaceToAdd = &lpmcx->lpmcAssociations;
break;
default:
hr = HR_LOG(E_FAIL);
goto cleanup;
}
lpmc->lpmcNext = *lppmcPlaceToAdd;
*lppmcPlaceToAdd = lpmc;
cleanup:
RETURN(hr);
}
//$--HrVerifyLinkage------------------------------------------------------------
// Verify that the Export value in the Linkage key for the gateway type
// exists and contains the gateway name.
// -----------------------------------------------------------------------------
static HRESULT HrVerifyLinkage( // RETURNS: HRESULT
IN LPMONCONTEXT lpmcx) // pointer to perf mon context
{
HRESULThr = NOERROR;
DWORD dwStatus = ERROR_SUCCESS;
TCHARszKeyName[MAX_PATH] = {0};
HKEYhKey = NULL;
DWORDcch = 0;
DWORD dwType = 0;
TCHAR szExport[1000] = {0};
DWORD cbExport = sizeof(szExport);
BOOL fFound = FALSE;
LPTSTR psz = NULL;
DEBUGPRIVATE("HrVerifyLinkage()\n");
// Check the parameters.
hr = CHK_HrVerifyLinkage(lpmcx);
if (FAILED(hr))
RETURN(hr);
// Create the registry key name from the gateway name.
cch = wsprintf(
szKeyName,
TEXT("SYSTEM\\CurrentControlSet\\Services\\%s\\Linkage"),
lpmcx->lpszObjectClass);
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)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Read the value.
dwStatus = RegQueryValueEx(
hKey,
TEXT("Export"),
NULL,
&dwType,
szExport,
&cbExport);
if (dwStatus != ERROR_SUCCESS || dwType != REG_MULTI_SZ)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Look in the multi-valued value for the gateway name.
fFound = FALSE;
for (psz = szExport; *psz; psz += (strlen(psz) + 1))
{
if (!lstrcmpi(psz, lpmcx->lpszObjectName))
{
fFound = TRUE;
break;
}
}
// If we didn't find it then return an error.
if (!fFound)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
cleanup:
// Close the registry key (if it's open).
if (hKey)
{
dwStatus = RegCloseKey(hKey);
if (dwStatus != ERROR_SUCCESS)
hr = HR_LOG(E_FAIL);
}
RETURN(hr);
}
//$--HrSaveSharedMemoryInRegistry-----------------------------------------------
// Save a copy of the shared memory block to the registry so that
// GWPERF.DLL will be able to generate a blank set of counters when
// the gateway isn't running.
// -----------------------------------------------------------------------------
static HRESULT HrSaveSharedMemoryInRegistry( // RETURNS: HRESULT
IN LPMONCONTEXT lpmcx) // pointer to perf mon context
{
HRESULThr = NOERROR;
DWORD dwStatus = ERROR_SUCCESS;
TCHARszKeyName[MAX_PATH] = {0};
HKEYhKey = NULL;
DWORDcch = 0;
BOOL fSave_DataIsValid = FALSE;
DEBUGPRIVATE("HrSaveSharedMemoryInRegistry()\n");
// Check the parameters.
hr = CHK_HrSaveSharedMemoryInRegistry(lpmcx);
if (FAILED(hr))
RETURN(hr);
// Save off the previous value of fDataIsValid.
fSave_DataIsValid = lpmcx->lpmsmSharedMemory->fDataIsValid;
// Create the registry key name from the gateway instance name.
cch = wsprintf(
szKeyName,
TEXT("SYSTEM\\CurrentControlSet\\Services\\%s\\Parameters"),
lpmcx->lpszObjectName);
ASSERTERROR(
(cch * sizeof(*szKeyName)) < sizeof(szKeyName),
"wsprintf overflow: szKeyName");
// Open the registry key.
dwStatus = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
szKeyName,
0L,
KEY_SET_VALUE,
&hKey);
if (dwStatus != ERROR_SUCCESS)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Temporarily set the flag that the data in the shared memory
// is valid, so that it will be stored as valid in the registry.
lpmcx->lpmsmSharedMemory->fDataIsValid = TRUE;
// Write the value (calculate the size of the block that contains
// real data by adding the offset of the instance data and its size).
dwStatus = RegSetValueEx(
hKey,
TEXT("ObjectDefaultPerformanceData"),
0,
REG_BINARY,
(LPBYTE) lpmcx->lpmsmSharedMemory,
lpmcx->lpmsmSharedMemory->ibInstanceOffset +
lpmcx->lpmsmSharedMemory->cbInstanceSize);
if (dwStatus != ERROR_SUCCESS)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
cleanup:
// Restore the previous value of fDataIsValid.
lpmcx->lpmsmSharedMemory->fDataIsValid = fSave_DataIsValid;
// Close the registry key (if it's open).
if (hKey)
{
dwStatus = RegCloseKey(hKey);
if (dwStatus != ERROR_SUCCESS)
hr = HR_LOG(E_FAIL);
}
RETURN(hr);
}