You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1716 lines
40 KiB
1716 lines
40 KiB
/*
|
|
- perfdll.cpp
|
|
-
|
|
* Purpose:
|
|
* Provide mechanism for user configuring of Perfmon Counters
|
|
* Implement Open, Collect and Close for Perfmon DLL Extension
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#include <windows.h>
|
|
#include <winperf.h>
|
|
#include <winerror.h>
|
|
|
|
#include <Pop3RegKeys.h>
|
|
#include <perfUtil.h>
|
|
#include <perfdll.h>
|
|
#include <loadperf.h> // for unlodctr
|
|
#include <shlwapi.h> // for SHDeleteKey
|
|
#include <string>
|
|
#include <cstring>
|
|
// ----------------------------------------------------------------------
|
|
// Declarations & Typedefs
|
|
// ----------------------------------------------------------------------
|
|
|
|
//
|
|
// Performance Counter Data Structures
|
|
|
|
typedef struct _perfdata
|
|
{
|
|
PERF_OBJECT_TYPE potGlobal;
|
|
PERF_COUNTER_DEFINITION * rgCntrDef;
|
|
PERF_COUNTER_BLOCK CntrBlk;
|
|
|
|
} PERFDATA;
|
|
|
|
|
|
typedef struct _instdata
|
|
{
|
|
PERF_INSTANCE_DEFINITION InstDef;
|
|
WCHAR szInstName[MAX_PATH];
|
|
PERF_COUNTER_BLOCK CntrBlk;
|
|
|
|
} INSTDATA;
|
|
|
|
|
|
typedef struct _perfinst
|
|
{
|
|
PERF_OBJECT_TYPE potInst;
|
|
PERF_COUNTER_DEFINITION * rgCntrDef;
|
|
|
|
} PERFINST;
|
|
|
|
|
|
//
|
|
// Constants and other stuff required by PerfMon
|
|
|
|
static WCHAR szGlobal[] = L"Global";
|
|
static WCHAR szForeign[] = L"Foreign";
|
|
static WCHAR szCostly[] = L"Costly";
|
|
static WCHAR szNull[] = L"\0";
|
|
|
|
#define DIGIT 1
|
|
#define DELIMITER 2
|
|
#define INVALID 3
|
|
|
|
#define QUERY_GLOBAL 1
|
|
#define QUERY_ITEMS 2
|
|
#define QUERY_FOREIGN 3
|
|
#define QUERY_COSTLY 4
|
|
|
|
static DWORD GetQueryType(LPWSTR lpValue);
|
|
static BOOL IsNumberInUnicodeList(DWORD dwNumber, LPWSTR pszUnicodeList);
|
|
|
|
#define EvalThisChar(c,d) ( \
|
|
(c == d) ? DELIMITER : \
|
|
(c == 0) ? DELIMITER : \
|
|
(c < (WCHAR)'0') ? INVALID : \
|
|
(c > (WCHAR)'9') ? INVALID : \
|
|
DIGIT)
|
|
|
|
// Perfmon likes things to be aligned on 8 byte boundaries
|
|
#define ROUND_TO_8_BYTE(x) (((x)+7) & (~7))
|
|
|
|
|
|
// PerfMon counter layout required for CollectData calls
|
|
|
|
static PERFDATA g_perfdata;
|
|
|
|
|
|
static PERFINST g_perfinst;
|
|
static INSTDATA g_instdata;
|
|
|
|
// perf data as layed out in shared memory
|
|
|
|
// Global Counters
|
|
static HANDLE g_hsmGlobalCntr = NULL;
|
|
static DWORD * g_rgdwGlobalCntr = NULL;
|
|
|
|
// Instance Counters
|
|
static HANDLE g_hsmInstAdm = NULL;
|
|
static HANDLE g_hsmInstCntr = NULL;
|
|
static INSTCNTR_DATA * g_pic = NULL;
|
|
static INSTREC * g_rgInstRec = NULL;
|
|
static DWORD * g_rgdwInstCntr = NULL;
|
|
|
|
|
|
// Accounting and protection stuff
|
|
|
|
static DWORD g_cRef = 0;
|
|
static HANDLE g_hmtxInst = NULL;
|
|
static BOOL g_fInit = FALSE;
|
|
|
|
// Parameter Info
|
|
static PERF_DATA_INFO g_PDI;
|
|
static BOOL g_fInitCalled = FALSE;
|
|
|
|
|
|
//Max instance to be 128
|
|
static const DWORD g_cMaxInst = 128;
|
|
|
|
|
|
// Function prototypes from winperf.h
|
|
|
|
PM_OPEN_PROC OpenPerformanceData;
|
|
PM_COLLECT_PROC CollectPerformanceData;
|
|
PM_CLOSE_PROC ClosePerformanceData;
|
|
|
|
// Helper functions
|
|
|
|
static HRESULT HrLogEvent(HANDLE hEventLog, WORD wType, DWORD msgid);
|
|
static HRESULT HrOpenSharedMemoryBlocks(HANDLE hEventLog, SECURITY_ATTRIBUTES * psa);
|
|
static HRESULT HrGetCounterIDsFromReg(HANDLE hEventLog, DWORD * pdwFirstCntr, DWORD * pdwFirstHelp);
|
|
static HRESULT HrAllocPerfCounterMem(HANDLE hEventLog);
|
|
static HRESULT HrFreePerfCounterMem(void);
|
|
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Implementation
|
|
// ----------------------------------------------------------------------
|
|
|
|
|
|
// Register constants
|
|
static wchar_t szServiceRegKeyPrefix[] = L"SYSTEM\\CurrentControlSet\\Services\\" ;
|
|
static wchar_t szServiceRegKeySuffix[] = L"\\Performance" ;
|
|
|
|
static wchar_t szEventLogRegKeyPrefix[] = L"System\\CurrentControlSet\\Services\\EventLog\\Application\\" ;
|
|
|
|
|
|
// ----------------------------------------------------------------------
|
|
// RegisterPerfDll -
|
|
// Create the registry keys we need.
|
|
// ----------------------------------------------------------------------
|
|
HRESULT RegisterPerfDll(LPCWSTR szService,
|
|
LPCWSTR szOpenFnName,
|
|
LPCWSTR szCollectFnName,
|
|
LPCWSTR szCloseFnName)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
wchar_t szFileName[_MAX_PATH+1] ;
|
|
DWORD dwRet;
|
|
|
|
// Use WIN32 API's to get the path to the module name
|
|
// DEVNOTE - JMW - Since we need to make sure we have the instance handle of the DLL
|
|
// and not the executable, we will use VirtualQuery.
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
VirtualQuery(RegisterPerfDll, &mbi, sizeof(mbi));
|
|
dwRet = GetModuleFileName( reinterpret_cast<HINSTANCE>(mbi.AllocationBase), szFileName, sizeof(szFileName)/sizeof(wchar_t) -1) ;
|
|
if (dwRet == 0)
|
|
{
|
|
goto Failed;
|
|
}
|
|
szFileName[_MAX_PATH]=0;
|
|
wchar_t szDrive[_MAX_DRIVE] ;
|
|
wchar_t szDir[_MAX_DIR ] ;
|
|
wchar_t szPerfFilename[ _MAX_FNAME ] ;
|
|
wchar_t szExt[_MAX_EXT ] ;
|
|
_wsplitpath( szFileName, szDrive, szDir, szPerfFilename, szExt ) ;
|
|
|
|
// Now that I have split it, put it back together with the Pop3Perf.dll in
|
|
// place of my module name
|
|
_wmakepath( szFileName, szDrive, szDir, L"Pop3Perf", L".dll" ) ;
|
|
|
|
hr = RegisterPerfDllEx(szService,
|
|
szPerfFilename,
|
|
szFileName,
|
|
szOpenFnName,
|
|
szCollectFnName,
|
|
szCloseFnName );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
|
|
Failed:
|
|
if (!FAILED(hr))
|
|
{
|
|
hr = GetLastError();
|
|
}
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// RegisterPerfDllEx -
|
|
// Create the registry keys we need, checking to see if they're already
|
|
// there.
|
|
//
|
|
// Parameters:
|
|
// szService Service Name
|
|
// szOpenFnName Name of the "Open" function
|
|
// szCollectFnName " " " "Collect" "
|
|
// szCloseFnName " " " "Close" "
|
|
//
|
|
// Returns:
|
|
// S_OK
|
|
// E_INVALIDARG
|
|
// ERROR_ALREADY_EXISTS
|
|
// <downstream error>
|
|
// ----------------------------------------------------------------------
|
|
HRESULT RegisterPerfDllEx(
|
|
IN LPCWSTR szService,
|
|
IN LPCWSTR szPerfSvc,
|
|
IN LPCWSTR szPerfMsgFile,
|
|
IN LPCWSTR szOpenFnName,
|
|
IN LPCWSTR szCollectFnName,
|
|
IN LPCWSTR szCloseFnName )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
wchar_t szFileName[_MAX_PATH+1] ;
|
|
DWORD cbExistingPath = (_MAX_PATH+1);
|
|
wchar_t szExistingPath[_MAX_PATH+1];
|
|
std::wstring wszRegKey ;
|
|
DWORD dwRet;
|
|
|
|
// Verify all the args and do the correct thing if they are NULL or zero length
|
|
if ( !szService ||
|
|
!szPerfSvc ||
|
|
!szOpenFnName ||
|
|
!szCollectFnName ||
|
|
!szCloseFnName )
|
|
{
|
|
hr = E_INVALIDARG ;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !szService[0] ||
|
|
!szPerfSvc[0] ||
|
|
!szOpenFnName[0] ||
|
|
!szCollectFnName[0] ||
|
|
!szCloseFnName[0] )
|
|
{
|
|
hr = E_INVALIDARG ;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Use WIN32 API's to get the path to the module name
|
|
// NOTE: We will assume that this DLL is also the EventMessageFile.
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
VirtualQuery(RegisterPerfDllEx, &mbi, sizeof(mbi));
|
|
dwRet = GetModuleFileName( reinterpret_cast<HINSTANCE>(mbi.AllocationBase), szFileName, sizeof(szFileName)/sizeof(wchar_t)-1) ;
|
|
if (dwRet == 0)
|
|
{
|
|
// Wow, don't know what happened,
|
|
goto Failed;
|
|
}
|
|
szFileName[_MAX_PATH]=0;
|
|
// If the user passed in NULL for the Event Log Message File for this Perfmon DLL,
|
|
// use this DLL as the Event Log Message File.
|
|
if (!szPerfMsgFile)
|
|
{
|
|
szPerfMsgFile = szFileName;
|
|
}
|
|
|
|
// Register the perfmon counter DLL under the
|
|
// provided service.
|
|
wszRegKey = szServiceRegKeyPrefix ;
|
|
wszRegKey += szService ;
|
|
wszRegKey += szServiceRegKeySuffix ;
|
|
|
|
// Check to see if the library has already been registered.
|
|
if (ERROR_SUCCESS==RegQueryString(
|
|
wszRegKey.c_str(),
|
|
L"Library",
|
|
szExistingPath,
|
|
&cbExistingPath ))
|
|
{
|
|
if (_wcsicmp(szExistingPath, szFileName))
|
|
{
|
|
// EventLog
|
|
// "RegisterPerfDllEx: Error: Attempt to replace Perfmon Library %s with %s. Failing. \n",
|
|
// szExistingPath,
|
|
// szFileName);
|
|
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
//Otherwise, the dll is already registered!
|
|
}
|
|
|
|
// Continue registering
|
|
if (ERROR_SUCCESS!=RegSetString(
|
|
wszRegKey.c_str(),
|
|
L"Library",
|
|
(LPWSTR)szFileName ))
|
|
{
|
|
goto Failed;
|
|
}
|
|
|
|
if (ERROR_SUCCESS!=RegSetString(
|
|
wszRegKey.c_str(),
|
|
L"Open",
|
|
(LPWSTR)szOpenFnName) )
|
|
{
|
|
goto Failed;
|
|
}
|
|
|
|
if (ERROR_SUCCESS!=RegSetString(
|
|
wszRegKey.c_str(),
|
|
L"Collect",
|
|
(LPWSTR)szCollectFnName) )
|
|
{
|
|
goto Failed;
|
|
}
|
|
|
|
if (ERROR_SUCCESS!=RegSetString(
|
|
wszRegKey.c_str(),
|
|
L"Close",
|
|
(LPWSTR)szCloseFnName) )
|
|
{
|
|
goto Failed;
|
|
}
|
|
|
|
// Set up the Event Message File
|
|
wszRegKey = szEventLogRegKeyPrefix ;
|
|
wszRegKey += szPerfSvc ;
|
|
|
|
// See if the EventMessageFile value is already set for this service
|
|
if (ERROR_SUCCESS==RegQueryString(
|
|
wszRegKey.c_str(),
|
|
L"EventMessageFile",
|
|
szExistingPath,
|
|
&cbExistingPath ))
|
|
{
|
|
if (_wcsicmp(szExistingPath, szPerfMsgFile))
|
|
{
|
|
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
// Set the EventMessageFile value
|
|
if ( ERROR_SUCCESS!=RegSetString(
|
|
wszRegKey.c_str(),
|
|
L"EventMessageFile",
|
|
(LPWSTR)szPerfMsgFile ) )
|
|
{
|
|
goto Failed;
|
|
}
|
|
|
|
if (ERROR_SUCCESS!=RegSetDWORD(
|
|
wszRegKey.c_str(),
|
|
L"TypesSupported",
|
|
0x07) ) // Error + Waring + Informational == 0x07
|
|
{
|
|
goto Failed;
|
|
}
|
|
|
|
// Assume that the CategoryMessageFile is the same as
|
|
// the EventMessageFile.
|
|
if (ERROR_SUCCESS!=RegSetString(
|
|
wszRegKey.c_str(),
|
|
L"CategoryMessageFile",
|
|
(LPWSTR)szPerfMsgFile ) )
|
|
{
|
|
goto Failed;
|
|
}
|
|
|
|
// NOTE: since we don't know what the count of categories is for the
|
|
// NOTE: CategoryMessageFile, do not set the CategoryCount value.
|
|
|
|
|
|
Cleanup:
|
|
if (FAILED(hr))
|
|
{
|
|
//EventLog ?? (L"RegisterPerfDllEx: Failed : (0x%08X)\n", hr);
|
|
}
|
|
|
|
return hr;
|
|
|
|
Failed:
|
|
if (!FAILED(hr))
|
|
{
|
|
hr = GetLastError();
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
/*
|
|
- HrInitPerf
|
|
-
|
|
* Purpose:
|
|
* Init data structure used to parameterize perfmon dll. Must be called
|
|
* in DllMain for reason DLL_PROCESS_ATTACH.
|
|
*/
|
|
|
|
HRESULT
|
|
HrInitPerf(PERF_DATA_INFO * pPDI)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!pPDI)
|
|
return E_INVALIDARG;
|
|
|
|
if (g_fInitCalled)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
CopyMemory(&g_PDI, pPDI, sizeof(PERF_DATA_INFO));
|
|
|
|
// Find the Service Name for using Event Logging on this Perfmon DLL
|
|
if (!(pPDI->wszPerfSvcName[0]))
|
|
{
|
|
wchar_t szFileName[_MAX_PATH+1] ;
|
|
DWORD dwRet;
|
|
|
|
// Use WIN32 API's to get the path to the module name
|
|
// DEVNOTE - JMW - Since we need to make sure we have the instance handle of the DLL
|
|
// and not the executable, we will use VirtualQuery.
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
VirtualQuery(HrInitPerf, &mbi, sizeof(mbi));
|
|
dwRet = GetModuleFileName( reinterpret_cast<HINSTANCE>(mbi.AllocationBase), szFileName, sizeof(szFileName)/sizeof(wchar_t)) ;
|
|
if (dwRet == 0)
|
|
{
|
|
// Wow, don't know what happened
|
|
goto err;
|
|
}
|
|
|
|
wchar_t szDrive[_MAX_DRIVE] ;
|
|
wchar_t szDir[_MAX_DIR ] ;
|
|
wchar_t szPerfFilename[ _MAX_FNAME ] ;
|
|
wchar_t szExt[_MAX_EXT ] ;
|
|
_wsplitpath( szFileName, szDrive, szDir, szPerfFilename, szExt ) ;
|
|
|
|
wcscpy(g_PDI.wszPerfSvcName, szPerfFilename);
|
|
|
|
}
|
|
|
|
// Safety: Need to alloc our own CounterTypes arrays
|
|
g_PDI.rgdwGlobalCounterTypes = NULL;
|
|
g_PDI.rgdwInstCounterTypes = NULL;
|
|
|
|
DWORD cb;
|
|
// Alloc & Copy Global Counter Types
|
|
if (g_PDI.cGlobalCounters)
|
|
{
|
|
cb = sizeof(DWORD) * g_PDI.cGlobalCounters;
|
|
|
|
g_PDI.rgdwGlobalCounterTypes = (DWORD *) malloc(cb);
|
|
|
|
if(NULL == g_PDI.rgdwGlobalCounterTypes)
|
|
{
|
|
hr=E_OUTOFMEMORY;
|
|
goto err;
|
|
}
|
|
|
|
CopyMemory(g_PDI.rgdwGlobalCounterTypes,
|
|
pPDI->rgdwGlobalCounterTypes,
|
|
cb);
|
|
g_PDI.rgdwGlobalCntrScale = (DWORD *) malloc(cb);
|
|
|
|
if(NULL == g_PDI.rgdwGlobalCntrScale)
|
|
{
|
|
hr=E_OUTOFMEMORY;
|
|
goto err;
|
|
}
|
|
CopyMemory(g_PDI.rgdwGlobalCntrScale,
|
|
pPDI->rgdwGlobalCntrScale,
|
|
cb);
|
|
}
|
|
|
|
// Alloc & Copy Inst Counter Types
|
|
if (g_PDI.cInstCounters)
|
|
{
|
|
cb = sizeof(DWORD) * g_PDI.cInstCounters;
|
|
|
|
g_PDI.rgdwInstCounterTypes = (DWORD *) malloc(cb);
|
|
if(NULL == g_PDI.rgdwInstCounterTypes)
|
|
{
|
|
hr=E_OUTOFMEMORY;
|
|
goto err;
|
|
}
|
|
|
|
CopyMemory(g_PDI.rgdwInstCounterTypes,
|
|
pPDI->rgdwInstCounterTypes,
|
|
cb);
|
|
}
|
|
|
|
// Done!
|
|
g_fInitCalled = TRUE;
|
|
|
|
return S_OK;
|
|
|
|
err:
|
|
if (g_PDI.rgdwGlobalCounterTypes)
|
|
{
|
|
free(g_PDI.rgdwGlobalCounterTypes);
|
|
g_PDI.rgdwGlobalCounterTypes=NULL;
|
|
}
|
|
if (g_PDI.rgdwGlobalCntrScale)
|
|
{
|
|
free(g_PDI.rgdwGlobalCntrScale);
|
|
g_PDI.rgdwGlobalCntrScale=NULL;
|
|
}
|
|
|
|
if (g_PDI.rgdwInstCounterTypes)
|
|
{
|
|
free(g_PDI.rgdwInstCounterTypes);
|
|
g_PDI.rgdwInstCounterTypes=NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*
|
|
- HrShutdownPerf
|
|
-
|
|
* Purpose:
|
|
* Release memory blocks allocated in HrInitPerf
|
|
*
|
|
*/
|
|
|
|
HRESULT HrShutdownPerf(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (g_cRef)
|
|
{
|
|
//EventLog ??(L"Warning: PERFDLL is being shut down with non-zero refcount!");
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
|
|
if (g_fInitCalled)
|
|
{
|
|
// We must invalidate the DLL if we release the memory in the g_PDI
|
|
g_fInitCalled = FALSE;
|
|
|
|
if (g_PDI.rgdwGlobalCounterTypes)
|
|
{
|
|
free(g_PDI.rgdwGlobalCounterTypes);
|
|
g_PDI.rgdwGlobalCounterTypes=NULL;
|
|
}
|
|
if (g_PDI.rgdwGlobalCntrScale)
|
|
{
|
|
free(g_PDI.rgdwGlobalCntrScale);
|
|
g_PDI.rgdwGlobalCntrScale=NULL;
|
|
}
|
|
|
|
if (g_PDI.rgdwInstCounterTypes)
|
|
{
|
|
free(g_PDI.rgdwInstCounterTypes);
|
|
g_PDI.rgdwInstCounterTypes=NULL;
|
|
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
- OpenPerformanceData
|
|
-
|
|
* Purpose:
|
|
* Called by PerfMon to init the counters and this DLL.
|
|
*
|
|
* Parameters:
|
|
* pszDeviceNames Ignored.
|
|
*
|
|
* Errors:
|
|
* dwStat Indicates various errors that can occur during init
|
|
*/
|
|
|
|
DWORD
|
|
APIENTRY
|
|
OpenPerformanceData(LPWSTR pszDeviceNames)
|
|
{
|
|
DWORD dwStat = ERROR_SUCCESS;
|
|
HANDLE hEventLog = NULL;
|
|
BOOL fInMutex = FALSE;
|
|
DWORD * rgdw;
|
|
SECURITY_ATTRIBUTES sa;
|
|
sa.lpSecurityDescriptor=NULL;
|
|
|
|
if (!g_fInitCalled)
|
|
return E_FAIL;
|
|
|
|
// REVIEW: Assumes Open will be single-threaded. Verify?
|
|
if (g_cRef == 0)
|
|
{
|
|
DWORD idx;
|
|
DWORD dwFirstCntr;
|
|
DWORD dwFirstHelp;
|
|
HRESULT hr = NOERROR;
|
|
|
|
// PERF_DATA_INFO wszSvcName is mandatory.
|
|
hEventLog = RegisterEventSource(NULL, g_PDI.wszPerfSvcName);
|
|
|
|
hr = HrInitializeSecurityAttribute(&sa);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
//HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrInitSA);
|
|
dwStat = (DWORD)hr;
|
|
goto err;
|
|
}
|
|
|
|
if (g_PDI.cInstCounters)
|
|
{
|
|
// Create Controling Mutex
|
|
hr = HrCreatePerfMutex(&sa,
|
|
g_PDI.wszInstMutexName,
|
|
&g_hmtxInst);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
//HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrInitSA);
|
|
dwStat = (DWORD)hr;
|
|
goto err;
|
|
}
|
|
|
|
fInMutex = TRUE;
|
|
}
|
|
|
|
// Open shared memory blocks
|
|
|
|
dwStat = (DWORD) HrOpenSharedMemoryBlocks(hEventLog, &sa);
|
|
if (FAILED(dwStat))
|
|
goto err;
|
|
|
|
// Allocate PERF_COUNTER_DEFINITION arrays for both g_perfdata
|
|
|
|
dwStat = (DWORD) HrAllocPerfCounterMem(hEventLog);
|
|
if (FAILED(dwStat))
|
|
goto err;
|
|
|
|
// Get First Counter and First Help string offsets from Registry
|
|
|
|
dwStat = (DWORD) HrGetCounterIDsFromReg(hEventLog,
|
|
&dwFirstCntr,
|
|
&dwFirstHelp);
|
|
if (FAILED(dwStat))
|
|
goto err;
|
|
|
|
//
|
|
// Fill in PerfMon structures to make Collect() faster
|
|
|
|
// Global Counters
|
|
if (g_PDI.cGlobalCounters)
|
|
{
|
|
PERF_COUNTER_DEFINITION * ppcd;
|
|
DWORD cb;
|
|
|
|
PERF_OBJECT_TYPE * ppot = &g_perfdata.potGlobal;
|
|
|
|
cb = sizeof(PERF_OBJECT_TYPE) +
|
|
(sizeof(PERF_COUNTER_DEFINITION) * g_PDI.cGlobalCounters) +
|
|
sizeof(PERF_COUNTER_BLOCK) +
|
|
(sizeof(DWORD) * g_PDI.cGlobalCounters);
|
|
|
|
ppot->TotalByteLength = ROUND_TO_8_BYTE(cb);
|
|
ppot->DefinitionLength = sizeof(PERF_OBJECT_TYPE) +
|
|
(g_PDI.cGlobalCounters * sizeof(PERF_COUNTER_DEFINITION));
|
|
|
|
ppot->HeaderLength = sizeof(PERF_OBJECT_TYPE);
|
|
ppot->ObjectNameTitleIndex = dwFirstCntr;
|
|
ppot->ObjectNameTitle = NULL;
|
|
ppot->ObjectHelpTitleIndex = dwFirstHelp;
|
|
ppot->ObjectHelpTitle = NULL;
|
|
ppot->DetailLevel = PERF_DETAIL_NOVICE;
|
|
ppot->NumCounters = g_PDI.cGlobalCounters;
|
|
ppot->DefaultCounter = -1;
|
|
ppot->NumInstances = PERF_NO_INSTANCES;
|
|
ppot->CodePage = 0;
|
|
|
|
dwFirstCntr += 2;
|
|
dwFirstHelp += 2;
|
|
|
|
rgdw = g_PDI.rgdwGlobalCounterTypes;
|
|
|
|
for (ppcd = g_perfdata.rgCntrDef, idx = 0;
|
|
idx < g_PDI.cGlobalCounters; ppcd++, idx++)
|
|
{
|
|
ppcd->ByteLength = sizeof(PERF_COUNTER_DEFINITION);
|
|
ppcd->CounterNameTitleIndex = dwFirstCntr;
|
|
ppcd->CounterNameTitle = NULL;
|
|
ppcd->CounterHelpTitleIndex = dwFirstHelp;
|
|
ppcd->CounterHelpTitle = NULL;
|
|
ppcd->DefaultScale = g_PDI.rgdwGlobalCntrScale[idx];
|
|
ppcd->DetailLevel = PERF_DETAIL_NOVICE;
|
|
ppcd->CounterType = g_PDI.rgdwGlobalCounterTypes[idx];
|
|
ppcd->CounterSize = sizeof(DWORD);
|
|
ppcd->CounterOffset = sizeof(PERF_COUNTER_BLOCK) +
|
|
(idx * sizeof(DWORD));
|
|
dwFirstCntr += 2;
|
|
dwFirstHelp += 2;
|
|
}
|
|
|
|
// Last step: set the size of the counter block and data for this object
|
|
|
|
g_perfdata.CntrBlk.ByteLength = sizeof(PERF_COUNTER_BLOCK) +
|
|
(g_PDI.cGlobalCounters * sizeof(DWORD));
|
|
|
|
} // Global Counters
|
|
|
|
// Instance Counters
|
|
if (g_PDI.cInstCounters)
|
|
{
|
|
PERF_COUNTER_DEFINITION * ppcd;
|
|
PERF_OBJECT_TYPE * ppot = &g_perfinst.potInst;
|
|
|
|
// TotalByteLength will be overridden on each call to CollectPerfData().
|
|
|
|
ppot->DefinitionLength = sizeof(PERF_OBJECT_TYPE) +
|
|
(g_PDI.cInstCounters * sizeof(PERF_COUNTER_DEFINITION));
|
|
ppot->HeaderLength = sizeof(PERF_OBJECT_TYPE);
|
|
ppot->ObjectNameTitleIndex = dwFirstCntr;
|
|
ppot->ObjectNameTitle = NULL;
|
|
ppot->ObjectHelpTitleIndex = dwFirstHelp;
|
|
ppot->ObjectHelpTitle = NULL;
|
|
ppot->DetailLevel = PERF_DETAIL_NOVICE;
|
|
ppot->NumCounters = g_PDI.cInstCounters;
|
|
ppot->DefaultCounter = -1;
|
|
ppot->NumInstances = 0;
|
|
ppot->CodePage = 0;
|
|
|
|
dwFirstCntr += 2;
|
|
dwFirstHelp += 2;
|
|
|
|
for (ppcd = g_perfinst.rgCntrDef, idx = 0;
|
|
idx < g_PDI.cInstCounters; ppcd++, idx++)
|
|
{
|
|
ppcd->ByteLength = sizeof(PERF_COUNTER_DEFINITION);
|
|
ppcd->CounterNameTitleIndex = dwFirstCntr;
|
|
ppcd->CounterNameTitle = NULL;
|
|
ppcd->CounterHelpTitleIndex = dwFirstHelp;
|
|
ppcd->CounterHelpTitle = NULL;
|
|
ppcd->DefaultScale = 0;
|
|
ppcd->DetailLevel = PERF_DETAIL_NOVICE;
|
|
ppcd->CounterType = g_PDI.rgdwInstCounterTypes[idx];
|
|
ppcd->CounterSize = sizeof(DWORD);
|
|
ppcd->CounterOffset = sizeof(PERF_COUNTER_BLOCK) +
|
|
(idx * sizeof(DWORD));
|
|
dwFirstCntr += 2;
|
|
dwFirstHelp += 2;
|
|
}
|
|
|
|
// Initialize generic INSTDATA for future use
|
|
g_instdata.InstDef.ByteLength = sizeof(PERF_INSTANCE_DEFINITION) +
|
|
(MAX_PATH * sizeof(WCHAR)) ;
|
|
g_instdata.InstDef.ParentObjectTitleIndex = 0; // No parent object
|
|
g_instdata.InstDef.ParentObjectInstance = 0;
|
|
g_instdata.InstDef.UniqueID = PERF_NO_UNIQUE_ID;
|
|
g_instdata.InstDef.NameOffset = sizeof(PERF_INSTANCE_DEFINITION);
|
|
g_instdata.InstDef.NameLength = 0; // To be overriden in Collect
|
|
g_instdata.CntrBlk.ByteLength = (sizeof(PERF_COUNTER_BLOCK) +
|
|
(g_PDI.cInstCounters * sizeof(DWORD)));
|
|
|
|
} // Instance Counters
|
|
|
|
// Done! Ready for business....
|
|
g_fInit = TRUE;
|
|
}
|
|
|
|
// If we got here, we're okay!
|
|
dwStat = ERROR_SUCCESS;
|
|
g_cRef++;
|
|
|
|
ret:
|
|
if (fInMutex)
|
|
ReleaseMutex(g_hmtxInst);
|
|
|
|
if (hEventLog)
|
|
DeregisterEventSource(hEventLog);
|
|
|
|
if (sa.lpSecurityDescriptor)
|
|
LocalFree(sa.lpSecurityDescriptor);
|
|
|
|
return dwStat;
|
|
|
|
err:
|
|
|
|
if (g_hsmGlobalCntr)
|
|
{
|
|
UnmapViewOfFile(g_rgdwGlobalCntr);
|
|
CloseHandle(g_hsmGlobalCntr);
|
|
}
|
|
|
|
if (g_hsmInstAdm)
|
|
{
|
|
UnmapViewOfFile(g_pic);
|
|
CloseHandle(g_hsmInstAdm);
|
|
}
|
|
|
|
if (g_hsmInstCntr)
|
|
{
|
|
UnmapViewOfFile(g_rgdwInstCntr);
|
|
CloseHandle(g_hsmInstCntr);
|
|
}
|
|
|
|
if (g_hmtxInst)
|
|
CloseHandle(g_hmtxInst);
|
|
|
|
// TODO: release all that memory we Alloc'd
|
|
HrFreePerfCounterMem();
|
|
|
|
goto ret;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
- CollectPerformanceData
|
|
-
|
|
* Purpose:
|
|
* Called by PerfMon to collect performance counter data
|
|
*
|
|
* Parameters:
|
|
* pszValueName
|
|
* ppvData
|
|
* pcbTotal
|
|
* pcObjTypes
|
|
*
|
|
* Errors:
|
|
* ERROR_SUCCESS
|
|
* ERROR_MORE_DATA
|
|
*/
|
|
|
|
DWORD
|
|
APIENTRY
|
|
CollectPerformanceData(
|
|
LPWSTR pszValueName,
|
|
LPVOID *ppvData,
|
|
LPDWORD pcbTotal,
|
|
LPDWORD pcObjTypes)
|
|
{
|
|
BOOL fCollectGlobalData;
|
|
BOOL fCollectInstData;
|
|
DWORD dwQueryType;
|
|
ULONG cbBuff = *pcbTotal;
|
|
char * pcT;
|
|
|
|
// In case we have to bail out
|
|
*pcbTotal = 0;
|
|
*pcObjTypes = 0;
|
|
|
|
if (!g_fInit)
|
|
return ERROR_SUCCESS;
|
|
|
|
// Determine Query Type; we only support QUERY_ITEMS
|
|
|
|
dwQueryType = GetQueryType(pszValueName);
|
|
|
|
if (dwQueryType == QUERY_FOREIGN)
|
|
return ERROR_SUCCESS;
|
|
|
|
// Assume PerfMon is collecting both until we prove otherwise
|
|
|
|
fCollectGlobalData = TRUE;
|
|
fCollectInstData = TRUE;
|
|
|
|
if (dwQueryType == QUERY_ITEMS)
|
|
{
|
|
if (!IsNumberInUnicodeList(g_perfdata.potGlobal.ObjectNameTitleIndex, pszValueName))
|
|
fCollectGlobalData = FALSE;
|
|
if (!IsNumberInUnicodeList(g_perfinst.potInst.ObjectNameTitleIndex, pszValueName))
|
|
fCollectInstData = FALSE;
|
|
|
|
if (!fCollectGlobalData && !fCollectInstData)
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
// Get a temporary pointer to the returned data buffer. If all goes well
|
|
// then we update ppvData before leaving, else we leave it unchanged and
|
|
// set *pcbTotal and pcObjTypes to zero and return ERROR_MORE_DATA.
|
|
|
|
pcT = (char *) *ppvData;
|
|
|
|
// Copy the data from the shared memory block for the system-wide
|
|
// counters into the buffer provided and update our OUT params.
|
|
|
|
if (g_rgdwGlobalCntr && fCollectGlobalData && g_PDI.cGlobalCounters)
|
|
{
|
|
DWORD cb;
|
|
DWORD cbTotal;
|
|
|
|
// Estimate total size, and see if we can fit.
|
|
|
|
if (g_perfdata.potGlobal.TotalByteLength > cbBuff)
|
|
return ERROR_MORE_DATA;
|
|
|
|
// Copy data in chunks.
|
|
cbTotal = 0;
|
|
|
|
// PERF_OBJECT_TYPE
|
|
cb = sizeof(PERF_OBJECT_TYPE);
|
|
cbTotal += cb;
|
|
CopyMemory((LPVOID) pcT, &g_perfdata.potGlobal, cb);
|
|
pcT += cb;
|
|
|
|
// PERF_COUNTER_DEFINITION []
|
|
cb = g_PDI.cGlobalCounters * sizeof(PERF_COUNTER_DEFINITION);
|
|
cbTotal += cb;
|
|
CopyMemory((LPVOID) pcT, g_perfdata.rgCntrDef, cb);
|
|
pcT += cb;
|
|
|
|
// PERF_COUNTER_BLOCK
|
|
cb = sizeof(PERF_COUNTER_BLOCK);
|
|
cbTotal += cb;
|
|
CopyMemory((LPVOID) pcT, &g_perfdata.CntrBlk , cb);
|
|
pcT += cb;
|
|
|
|
// (counters) DWORD []
|
|
cb = g_PDI.cGlobalCounters * sizeof(DWORD);
|
|
cbTotal += cb;
|
|
CopyMemory((LPVOID) pcT, g_rgdwGlobalCntr, cb);
|
|
pcT += cb;
|
|
|
|
// If we've done our math right, This assert should be valid.
|
|
//assert((DWORD)(pcT - (char *)*ppvData) == cbTotal);
|
|
|
|
if (g_perfdata.potGlobal.TotalByteLength > cbTotal)
|
|
{
|
|
cb = g_perfdata.potGlobal.TotalByteLength - cbTotal;
|
|
pcT += cb;
|
|
}
|
|
|
|
*pcbTotal += g_perfdata.potGlobal.TotalByteLength;
|
|
(*pcObjTypes)++;
|
|
}
|
|
|
|
// Copy the data from the shared memory block for the per-instance
|
|
// counters into the buffer provided and update our OUT params. We
|
|
// must enter the mutex here to prevent connection instances from
|
|
// being added to or removed from the list while we are copying data.
|
|
|
|
if (g_pic && fCollectInstData && g_PDI.cInstCounters)
|
|
{
|
|
DWORD cb;
|
|
DWORD cbTotal;
|
|
DWORD ism;
|
|
DWORD ipd;
|
|
DWORD cInst;
|
|
PERF_OBJECT_TYPE * pPOT;
|
|
INSTDATA * pInstData;
|
|
|
|
if (WaitForSingleObject(g_hmtxInst, INFINITE) != WAIT_OBJECT_0)
|
|
{
|
|
*pcbTotal = 0;
|
|
*pcObjTypes = 0;
|
|
|
|
// BUGBUG: wrong return code for the problem. We timed out waiting for
|
|
// BUGBUG: the mutex; However, we may not return anything other than
|
|
// BUGBUG: ERROR_SUCCESS or ERROR_MORE_DATA without disabling the DLL.
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
// Find out how many instances exist.
|
|
// NOTE: Zero instances is valid. We must still copy the "core" perf data.
|
|
cInst = g_pic->cInstRecInUse;
|
|
|
|
// Estimate total size, and see if we can fit.
|
|
|
|
cbTotal = sizeof(PERF_OBJECT_TYPE) +
|
|
(g_PDI.cInstCounters * sizeof(PERF_COUNTER_DEFINITION)) +
|
|
(cInst * sizeof(INSTDATA)) +
|
|
(cInst * (g_PDI.cInstCounters * sizeof(DWORD)));
|
|
// Must return data aligned on 8-byte boundaries.
|
|
cbTotal = ROUND_TO_8_BYTE(cbTotal);
|
|
|
|
if (cbTotal > (cbBuff - *pcbTotal))
|
|
{
|
|
ReleaseMutex(g_hmtxInst);
|
|
|
|
*pcbTotal = 0;
|
|
*pcObjTypes = 0;
|
|
|
|
return ERROR_MORE_DATA;
|
|
}
|
|
|
|
// Keep a pointer to beginig so we can update the "total bytes" value at the end.
|
|
pPOT = (PERF_OBJECT_TYPE *) pcT;
|
|
|
|
// PERF_OBJECT_TYPE
|
|
CopyMemory(pPOT,
|
|
&(g_perfinst.potInst),
|
|
sizeof(PERF_OBJECT_TYPE));
|
|
|
|
pcT += sizeof(PERF_OBJECT_TYPE);
|
|
|
|
// PERF_COUNTER_DEFINITION []
|
|
cb = g_PDI.cInstCounters * sizeof(PERF_COUNTER_DEFINITION);
|
|
CopyMemory(pcT, g_perfinst.rgCntrDef, cb);
|
|
pcT += cb;
|
|
|
|
// Find instances and copy their counter data blocks
|
|
for (ism = 0, ipd = 0; (ism < g_pic->cMaxInstRec) && (ipd < cInst); ism++)
|
|
{
|
|
if (g_rgInstRec[ism].fInUse)
|
|
{
|
|
pInstData = (INSTDATA *) pcT;
|
|
|
|
// PERF_INSTANCE_DATA
|
|
cb = sizeof(INSTDATA);
|
|
CopyMemory(pcT,
|
|
&g_instdata,
|
|
cb);
|
|
pcT += cb;
|
|
|
|
// (inst name) WCHAR [] (inside INSTDATA block)
|
|
cb = (wcslen(g_rgInstRec[ism].szInstName) + 1) * sizeof(WCHAR);
|
|
|
|
//Assert( cb > 0 );
|
|
//Assert( cb < (MAX_PATH * sizeof(WCHAR)) );
|
|
|
|
CopyMemory(pInstData->szInstName,
|
|
g_rgInstRec[ism].szInstName,
|
|
cb);
|
|
|
|
// Update PERF_INSTANCE_DEFINITION with correct name length.
|
|
pInstData->InstDef.NameLength = cb;
|
|
|
|
// (counters) DWORD []
|
|
cb = g_PDI.cInstCounters * sizeof(DWORD);
|
|
CopyMemory(pcT,
|
|
&g_rgdwInstCntr[(ism * g_PDI.cInstCounters)],
|
|
cb);
|
|
pcT += cb;
|
|
|
|
ipd++;
|
|
}
|
|
}
|
|
|
|
// NOTE: we are deliberately ignoring the condition of
|
|
// (ipd < cInst), even though that may indicate corruption
|
|
// of the Shared Memory Block. Further note that this code
|
|
// will never trap the condition of (ipd > cInst).
|
|
|
|
// Done looking at Shared Memory Block.
|
|
ReleaseMutex(g_hmtxInst);
|
|
|
|
// Align data on 8-byte boundary
|
|
cb = (DWORD)((char *) pcT - (char *) pPOT);
|
|
cbTotal = ROUND_TO_8_BYTE(cb);
|
|
if (cbTotal > cb)
|
|
pcT += (cbTotal - cb);
|
|
|
|
// Update PERF_OBJECT_TYPE with correct numbers
|
|
pPOT->TotalByteLength = cbTotal;
|
|
pPOT->NumInstances = ipd; // Use the count of instances we actually *found*
|
|
|
|
*pcbTotal += cbTotal;
|
|
(*pcObjTypes)++;
|
|
}
|
|
|
|
// We only get here if nothing failed. It is now safe
|
|
// to update *ppvData and return a success indication.
|
|
|
|
*ppvData = (LPVOID) pcT;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
- ClosePerformanceData
|
|
-
|
|
* Purpose:
|
|
* Called by PerfMon to uninit the counter DLL.
|
|
*
|
|
* Parameters:
|
|
* void
|
|
*
|
|
* Errors:
|
|
* ERROR_SUCCESS Always!
|
|
*/
|
|
|
|
DWORD
|
|
APIENTRY
|
|
ClosePerformanceData(void)
|
|
{
|
|
|
|
if (g_cRef > 0)
|
|
{
|
|
if (--g_cRef == 0)
|
|
{
|
|
// We're going to free stuff; make sure later calls
|
|
// don't de-ref bad pointers.
|
|
g_fInit = FALSE;
|
|
|
|
// Close down Global Counters
|
|
if (g_rgdwGlobalCntr)
|
|
UnmapViewOfFile(g_rgdwGlobalCntr);
|
|
|
|
if (g_hsmGlobalCntr)
|
|
CloseHandle(g_hsmGlobalCntr);
|
|
|
|
// Close down Instance Counters
|
|
// NOTE: g_pic is the starting offset of the SM block.
|
|
// NOTE: DO NOT Unmap on g_rgInstRec!
|
|
if (g_pic)
|
|
UnmapViewOfFile(g_pic);
|
|
|
|
if (g_hsmInstAdm)
|
|
CloseHandle(g_hsmInstAdm);
|
|
|
|
if (g_rgdwInstCntr)
|
|
UnmapViewOfFile(g_rgdwInstCntr);
|
|
|
|
if (g_hsmInstCntr)
|
|
CloseHandle(g_hsmInstCntr);
|
|
|
|
if (g_hmtxInst)
|
|
CloseHandle(g_hmtxInst);
|
|
|
|
// Free all that memory we've alloc'd
|
|
HrFreePerfCounterMem();
|
|
|
|
|
|
g_rgdwGlobalCntr = NULL;
|
|
g_hsmGlobalCntr = NULL;
|
|
g_pic = NULL;
|
|
g_rgInstRec = NULL;
|
|
g_rgdwInstCntr = NULL;
|
|
g_hsmInstAdm = NULL;
|
|
g_hsmInstCntr = NULL;
|
|
g_hmtxInst = NULL;
|
|
|
|
}
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
- GetQueryType
|
|
-
|
|
* Purpose:
|
|
* Returns the type of query described in the lpValue string so that
|
|
* the appropriate processing method may be used.
|
|
*
|
|
* Parameters:
|
|
* lpValue String passed to PerfRegQuery Value for processing
|
|
*
|
|
* Returns:
|
|
* QUERY_GLOBAL | QUERY_FOREIGN | QUERY_COSTLY | QUERY_ITEMS
|
|
*
|
|
*/
|
|
|
|
static
|
|
DWORD
|
|
GetQueryType(LPWSTR lpValue)
|
|
{
|
|
WCHAR * pwcArgChar, *pwcTypeChar;
|
|
BOOL bFound;
|
|
|
|
if (lpValue == 0)
|
|
return QUERY_GLOBAL;
|
|
|
|
if (*lpValue == 0)
|
|
return QUERY_GLOBAL;
|
|
|
|
// check for "Global" request
|
|
|
|
pwcArgChar = lpValue;
|
|
pwcTypeChar = szGlobal;
|
|
bFound = TRUE; // assume found until contradicted
|
|
|
|
// check to the length of the shortest string
|
|
|
|
while ((*pwcArgChar != 0) && (*pwcTypeChar != 0))
|
|
{
|
|
if (*pwcArgChar++ != *pwcTypeChar++)
|
|
{
|
|
bFound = FALSE; // no match
|
|
break; // bail out now
|
|
}
|
|
}
|
|
|
|
if (bFound)
|
|
return QUERY_GLOBAL;
|
|
|
|
// check for "Foreign" request
|
|
|
|
pwcArgChar = lpValue;
|
|
pwcTypeChar = szForeign;
|
|
bFound = TRUE; // assume found until contradicted
|
|
|
|
// check to the length of the shortest string
|
|
|
|
while ((*pwcArgChar != 0) && (*pwcTypeChar != 0))
|
|
{
|
|
if (*pwcArgChar++ != *pwcTypeChar++)
|
|
{
|
|
bFound = FALSE; // no match
|
|
break; // bail out now
|
|
}
|
|
}
|
|
|
|
if (bFound)
|
|
return QUERY_FOREIGN;
|
|
|
|
// check for "Costly" request
|
|
|
|
pwcArgChar = lpValue;
|
|
pwcTypeChar = szCostly;
|
|
bFound = TRUE; // assume found until contradicted
|
|
|
|
// check to the length of the shortest string
|
|
|
|
while ((*pwcArgChar != 0) && (*pwcTypeChar != 0))
|
|
{
|
|
if (*pwcArgChar++ != *pwcTypeChar++)
|
|
{
|
|
bFound = FALSE; // no match
|
|
break; // bail out now
|
|
}
|
|
}
|
|
|
|
if (bFound)
|
|
return QUERY_COSTLY;
|
|
|
|
// if not Global and not Foreign and not Costly,
|
|
// then it must be an item list
|
|
|
|
return QUERY_ITEMS;
|
|
}
|
|
|
|
|
|
/*
|
|
- IsNumberInUnicodeList
|
|
-
|
|
* Purpose:
|
|
* Determines if dwNumber is in the pszUnicodeList.
|
|
*
|
|
* Parameters:
|
|
* dwNumber Number to find in list
|
|
* pszUnicodeList Space delimited list of decimal numbers
|
|
*
|
|
* Errors:
|
|
* TRUE/FALSE If found/not found respectively
|
|
*
|
|
*/
|
|
|
|
static
|
|
BOOL
|
|
IsNumberInUnicodeList(DWORD dwNumber, LPWSTR pszUnicodeList)
|
|
{
|
|
DWORD dwThisNumber = 0;
|
|
BOOL bValidNumber = FALSE;
|
|
BOOL bNewItem = TRUE;
|
|
WCHAR wcDelimiter = (WCHAR)' ';
|
|
WCHAR *pwcThisChar;
|
|
|
|
if (pszUnicodeList == NULL)
|
|
return FALSE;
|
|
|
|
pwcThisChar = pszUnicodeList;
|
|
|
|
while (TRUE) /*lint !e774*/
|
|
{
|
|
switch (EvalThisChar(*pwcThisChar, wcDelimiter))
|
|
{
|
|
case DIGIT:
|
|
// if this is the first digit after a delimiter, then
|
|
// set flags to start computing the new number
|
|
|
|
if (bNewItem)
|
|
{
|
|
bNewItem = FALSE;
|
|
bValidNumber = TRUE;
|
|
}
|
|
|
|
if (bValidNumber)
|
|
{
|
|
dwThisNumber *= 10;
|
|
dwThisNumber += (*pwcThisChar - (WCHAR)'0');
|
|
}
|
|
break;
|
|
|
|
case DELIMITER:
|
|
// a delimter is either the delimiter character or the
|
|
// end of the string ('\0') if when the delimiter has been
|
|
// reached a valid number was found, then compare it to the
|
|
// number from the argument list. if this is the end of the
|
|
// string and no match was found, then return.
|
|
|
|
if (bValidNumber)
|
|
{
|
|
if (dwThisNumber == dwNumber)
|
|
return TRUE;
|
|
|
|
bValidNumber = FALSE;
|
|
}
|
|
|
|
if (*pwcThisChar == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
bNewItem = TRUE;
|
|
dwThisNumber = 0;
|
|
}
|
|
break;
|
|
|
|
case INVALID:
|
|
// if an invalid character was encountered, ignore all
|
|
// characters up to the next delimiter and then start fresh.
|
|
// the invalid number is not compared.
|
|
|
|
bValidNumber = FALSE;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
pwcThisChar++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
- HrLogEvent
|
|
-
|
|
* Purpose:
|
|
* Wrap up the call to ReportEvent to make things look nicer.
|
|
*/
|
|
HRESULT
|
|
HrLogEvent(HANDLE hEventLog, WORD wType, DWORD msgid)
|
|
{
|
|
if (g_fInitCalled)
|
|
{
|
|
DWORD cb = sizeof(WCHAR) * wcslen(g_PDI.wszSvcName);
|
|
if (hEventLog)
|
|
return ReportEvent(hEventLog,
|
|
wType, // Event Type
|
|
(WORD)0, // Category
|
|
msgid, // Event ID
|
|
NULL, // User SID
|
|
0, // # strings to merge
|
|
cb, // size of binary data (bytes)
|
|
NULL, // array of strings to merge
|
|
g_PDI.wszSvcName); // binary data
|
|
else
|
|
return E_FAIL;
|
|
}
|
|
else
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
/*
|
|
- HrOpenSharedMemoryBlocks
|
|
-
|
|
* Purpose:
|
|
* Encapsulate the grossness of opening the shared memory blocks.
|
|
*/
|
|
|
|
HRESULT
|
|
HrOpenSharedMemoryBlocks(HANDLE hEventLog, SECURITY_ATTRIBUTES * psa)
|
|
{
|
|
HRESULT hr=S_OK;
|
|
BOOL fExist;
|
|
|
|
if (!g_fInitCalled)
|
|
return E_FAIL;
|
|
|
|
if (!psa)
|
|
return E_INVALIDARG;
|
|
|
|
// Shared Memory for Global Counters
|
|
if (g_PDI.cGlobalCounters)
|
|
{
|
|
|
|
hr = HrOpenSharedMemory(g_PDI.wszGlobalSMName,
|
|
(sizeof(DWORD) * g_PDI.cGlobalCounters),
|
|
psa,
|
|
&g_hsmGlobalCntr,
|
|
(LPVOID *) &g_rgdwGlobalCntr,
|
|
&fExist);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
//HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrInitSharedMemory1);
|
|
goto ret;
|
|
}
|
|
|
|
if (!fExist)
|
|
ZeroMemory(g_rgdwGlobalCntr, (g_PDI.cGlobalCounters * sizeof(DWORD)));
|
|
}
|
|
|
|
// Shared Memory for Instance Counters
|
|
if (g_PDI.cInstCounters)
|
|
{
|
|
DWORD cbAdm;
|
|
DWORD cbCntr;
|
|
WCHAR szAdmName[MAX_PATH]; // Admin SM Name
|
|
WCHAR szCntrName[MAX_PATH]; // Counter SM Name
|
|
|
|
// Calc required memory sizes
|
|
// BUGBUG: Since we're puntin on dynamic instances, we use g_cMaxInst
|
|
cbAdm = sizeof(INSTCNTR_DATA) + (sizeof(INSTREC) * g_cMaxInst);
|
|
cbCntr = ((sizeof(DWORD) * g_PDI.cInstCounters) * g_cMaxInst);
|
|
|
|
// Build SM names for Admin and Counters
|
|
wsprintf(szAdmName, L"%s_ADM", g_PDI.wszInstSMName);
|
|
wsprintf(szCntrName,L"%s_CNTR", g_PDI.wszInstSMName);
|
|
|
|
// Open Instance Admin Memory
|
|
hr = HrOpenSharedMemory(szAdmName,
|
|
cbAdm,
|
|
psa,
|
|
&g_hsmInstAdm,
|
|
(LPVOID *)&g_pic,
|
|
&fExist);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
//HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrInitSharedMemory2);
|
|
goto ret;
|
|
}
|
|
|
|
// Fixup Pointers
|
|
g_rgInstRec = (INSTREC *) ((LPBYTE) g_pic + sizeof(INSTCNTR_DATA));
|
|
|
|
if (!fExist)
|
|
{
|
|
ZeroMemory(g_pic, cbAdm);
|
|
g_pic->cMaxInstRec = g_cMaxInst;
|
|
g_pic->cInstRecInUse = 0;
|
|
}
|
|
|
|
// Because we don't support dynamic instances, We should *always*
|
|
// have a MaxInstRec of g_cMaxInst
|
|
//Assert(g_cMaxInst == g_pic->cMaxInstRec);
|
|
|
|
// Open Instance Counter Memory
|
|
hr = HrOpenSharedMemory(szCntrName,
|
|
cbCntr,
|
|
psa,
|
|
&g_hsmInstCntr,
|
|
(LPVOID *)&g_rgdwInstCntr,
|
|
&fExist);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
//HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrInitSharedMemory2);
|
|
goto ret;
|
|
}
|
|
|
|
if (!fExist)
|
|
ZeroMemory(g_rgdwInstCntr, cbCntr);
|
|
}
|
|
|
|
ret:
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
- HrGetCounterIDsFromReg
|
|
-
|
|
* Purpose:
|
|
* Get the "First Name" and "First Help" inicies from the Registry in the
|
|
* special place for the configured service.
|
|
*/
|
|
|
|
HRESULT
|
|
HrGetCounterIDsFromReg(HANDLE hEventLog, DWORD * pdwFirstCntr, DWORD * pdwFirstHelp)
|
|
{
|
|
HRESULT hr;
|
|
HKEY hKey = NULL;
|
|
WCHAR wszServicePerfKey[MAX_PATH];
|
|
DWORD dwSize;
|
|
DWORD dwType;
|
|
|
|
if (!g_fInitCalled)
|
|
return E_FAIL;
|
|
|
|
if (!pdwFirstCntr || !pdwFirstHelp)
|
|
return E_INVALIDARG;
|
|
|
|
// Get the First Counter and First Help from the registry
|
|
wsprintf(wszServicePerfKey, L"SYSTEM\\CurrentControlSet\\Services\\%s\\Performance", g_PDI.wszSvcName);
|
|
|
|
hr = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
wszServicePerfKey,
|
|
0L,
|
|
KEY_READ,
|
|
&hKey);
|
|
|
|
if (hr != ERROR_SUCCESS)
|
|
{
|
|
//HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrOpenRegistry);
|
|
goto ret;
|
|
}
|
|
|
|
dwSize = sizeof(DWORD);
|
|
|
|
hr = RegQueryValueExW(hKey,
|
|
L"First Counter",
|
|
0L,
|
|
&dwType,
|
|
(LPBYTE)pdwFirstCntr,
|
|
&dwSize);
|
|
|
|
if (hr != ERROR_SUCCESS)
|
|
{
|
|
//HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrQueryRegistry1);
|
|
goto ret;
|
|
}
|
|
|
|
dwSize = sizeof(DWORD);
|
|
|
|
hr = RegQueryValueExW(hKey,
|
|
L"First Help",
|
|
0L,
|
|
&dwType,
|
|
(LPBYTE)pdwFirstHelp,
|
|
&dwSize);
|
|
|
|
if (hr != ERROR_SUCCESS)
|
|
{
|
|
//HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrQueryRegistry2);
|
|
goto ret;
|
|
}
|
|
|
|
ret:
|
|
|
|
if (hKey)
|
|
RegCloseKey(hKey);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
- HrAllocPerfCounterMem
|
|
-
|
|
* Purpose:
|
|
* Allocates memory for PERF_COUNTER_DEFINITION arrays for both
|
|
* g_perfdata and g_perfinst.
|
|
*
|
|
* Notes:
|
|
* Uses ProcessHeap handle obtained in HrInit.
|
|
*/
|
|
|
|
HRESULT
|
|
HrAllocPerfCounterMem(HANDLE hEventLog)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cb;
|
|
|
|
if (!g_fInitCalled)
|
|
return E_FAIL;
|
|
|
|
// Global Counters
|
|
if (g_PDI.cGlobalCounters)
|
|
{
|
|
// Alloc Global PERF_COUNTER_DEFINITION array
|
|
|
|
cb = (sizeof(PERF_COUNTER_DEFINITION) * g_PDI.cGlobalCounters);
|
|
|
|
g_perfdata.rgCntrDef = (PERF_COUNTER_DEFINITION *) malloc(cb);
|
|
|
|
if(NULL == g_perfdata.rgCntrDef)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto err;
|
|
}
|
|
|
|
}
|
|
|
|
// Instance Counters
|
|
if (g_PDI.cInstCounters)
|
|
{
|
|
// Alloc Inst PERF_COUNTER_DEFINITION array
|
|
|
|
cb = (sizeof(PERF_COUNTER_DEFINITION) * g_PDI.cInstCounters);
|
|
|
|
g_perfinst.rgCntrDef = (PERF_COUNTER_DEFINITION *) malloc(cb);
|
|
if(NULL == g_perfinst.rgCntrDef )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
|
|
return S_OK;
|
|
|
|
err:
|
|
|
|
//HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrAlloc);
|
|
|
|
HrFreePerfCounterMem();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*
|
|
- HrFreePerfCounterMem
|
|
-
|
|
* Purpose:
|
|
* Companion to HrAllocPerfCounterMem
|
|
*
|
|
* Note:
|
|
* Uses ProcessHeap handle obtained in HrInit.
|
|
*/
|
|
|
|
HRESULT
|
|
HrFreePerfCounterMem(void)
|
|
{
|
|
if (!g_fInitCalled)
|
|
return E_FAIL;
|
|
|
|
// We must invalidate the DLL if we release the memory in the g_PDI
|
|
g_fInitCalled = FALSE;
|
|
|
|
if (g_perfdata.rgCntrDef)
|
|
{
|
|
free(g_perfdata.rgCntrDef);
|
|
g_perfdata.rgCntrDef = NULL;
|
|
}
|
|
|
|
if (g_perfinst.rgCntrDef)
|
|
{
|
|
free(g_perfinst.rgCntrDef);
|
|
g_perfinst.rgCntrDef = NULL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
HrUninstallPerfDll(
|
|
IN LPCWSTR szService )
|
|
{
|
|
// Make sure we've valid input
|
|
if( !szService ) return E_INVALIDARG;
|
|
|
|
|
|
// First, do unlodctr since removing the Performance key without removing
|
|
// counter names and descriptions may toast the perfmon system
|
|
std::wstring wszService = L"x "; // KB Article Q188769
|
|
wszService += szService;
|
|
DWORD dwErr = UnloadPerfCounterTextStringsW(const_cast<LPWSTR>(wszService.c_str()), TRUE);
|
|
if( dwErr != ERROR_SUCCESS )
|
|
{
|
|
// Continue without error if unlodctr has already been called
|
|
if( (dwErr != ERROR_FILE_NOT_FOUND) && (dwErr != ERROR_BADKEY) )
|
|
{
|
|
return HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
}
|
|
|
|
// Now that unlodctr has succeeded, we can start deleting registry keys
|
|
// Start with the Performance key of the service
|
|
std::wstring wszRegKey;
|
|
wszRegKey = szServiceRegKeyPrefix;
|
|
wszRegKey += szService;
|
|
dwErr = SHDeleteKey(HKEY_LOCAL_MACHINE, wszRegKey.c_str());
|
|
if( (dwErr != ERROR_SUCCESS) && (dwErr != ERROR_FILE_NOT_FOUND) )
|
|
{
|
|
return HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
|
|
|
|
// Use WIN32 API's to get the path to the module name
|
|
wchar_t szFileName[_MAX_PATH+1] ;
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
VirtualQuery(HrUninstallPerfDll, &mbi, sizeof(mbi));
|
|
DWORD dwRet = GetModuleFileName( reinterpret_cast<HINSTANCE>(mbi.AllocationBase), szFileName, sizeof(szFileName)/sizeof(wchar_t)-1) ;
|
|
if (dwRet == 0)
|
|
{
|
|
// Wow, don't know what happened,
|
|
return HRESULT_FROM_WIN32(::GetLastError());
|
|
}
|
|
|
|
szFileName[_MAX_PATH]=0;
|
|
// Split the module path to get the filename
|
|
wchar_t szDrive[_MAX_DRIVE] ;
|
|
wchar_t szDir[_MAX_DIR ] ;
|
|
wchar_t szPerfFilename[ _MAX_FNAME ] ;
|
|
wchar_t szExt[_MAX_EXT ] ;
|
|
_wsplitpath( szFileName, szDrive, szDir, szPerfFilename, szExt ) ;
|
|
|
|
|
|
// Delete the Event Log key for the service
|
|
wszRegKey = szEventLogRegKeyPrefix;
|
|
wszRegKey += szPerfFilename;
|
|
dwErr = SHDeleteKey(HKEY_LOCAL_MACHINE, wszRegKey.c_str());
|
|
if( ERROR_FILE_NOT_FOUND == dwErr ) dwErr = ERROR_SUCCESS;
|
|
|
|
|
|
return HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
|