635 lines
16 KiB
C++
635 lines
16 KiB
C++
#define UNICODE
|
|
#define _UNICODE
|
|
|
|
#define DBNTWIN32
|
|
#include <windows.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <rpc.h>
|
|
#include "cs.h"
|
|
|
|
#define DllExport __declspec( dllexport )
|
|
#define PERF_QUEUE_OBJECT TEXT("MSMQ Queue")
|
|
|
|
typedef PERF_DATA_BLOCK PERF_DATA, *PPERF_DATA;
|
|
typedef PERF_OBJECT_TYPE PERF_OBJECT, *PPERF_OBJECT;
|
|
typedef PERF_INSTANCE_DEFINITION PERF_INSTANCE, *PPERF_INSTANCE;
|
|
typedef PERF_COUNTER_DEFINITION PERF_COUNTER, *PPERF_COUNTER;
|
|
|
|
LPWSTR* aszTitleArray; // Array of pointers to title strings
|
|
DWORD dwLastCounter=0;
|
|
WCHAR wcsObjectID[32];
|
|
WCHAR wcsInstanceName[100];
|
|
DWORD dwObjectIndex; // Holds the the entry of the object in the title array
|
|
CCriticalSection g_critRegistry;
|
|
|
|
|
|
//*********************************************************************
|
|
//
|
|
// CounterData
|
|
//
|
|
// Returns counter data for an object instance. If pInst or pCount
|
|
// is NULL then NULL is returne.
|
|
//
|
|
PVOID CounterData (PPERF_INSTANCE pInst, PPERF_COUNTER pCount)
|
|
{
|
|
PPERF_COUNTER_BLOCK pCounterBlock;
|
|
|
|
if (pCount && pInst)
|
|
{
|
|
pCounterBlock = (PPERF_COUNTER_BLOCK)((PCHAR)pInst + pInst->ByteLength);
|
|
return (PVOID)((PCHAR)pCounterBlock + pCount->CounterOffset);
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// FirstCounter
|
|
//
|
|
// Find the first counter in pObject.
|
|
//
|
|
// Returns a pointer to the first counter. If pObject is NULL
|
|
// then NULL is returned.
|
|
//
|
|
PPERF_COUNTER FirstCounter (PPERF_OBJECT pObject)
|
|
{
|
|
if (pObject)
|
|
return (PPERF_COUNTER)((PCHAR) pObject + pObject->HeaderLength);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//*********************************************************************
|
|
//
|
|
// NextCounter
|
|
//
|
|
// Find the next counter of pCounter.
|
|
//
|
|
// If pCounter is the last counter of an object type, bogus data
|
|
// maybe returned. The caller should do the checking.
|
|
//
|
|
// Returns a pointer to a counter. If pCounter is NULL then
|
|
// NULL is returned.
|
|
//
|
|
PPERF_COUNTER NextCounter (PPERF_COUNTER pCounter)
|
|
{
|
|
if (pCounter)
|
|
return (PPERF_COUNTER)((PCHAR) pCounter + pCounter->ByteLength);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//*********************************************************************
|
|
//
|
|
// FirstInstance
|
|
//
|
|
// Returns pointer to the first instance of pObject type.
|
|
// If pObject is NULL then NULL is returned.
|
|
//
|
|
PPERF_INSTANCE FirstInstance (PPERF_OBJECT pObject)
|
|
{
|
|
if (pObject)
|
|
return (PPERF_INSTANCE)((PCHAR) pObject + pObject->DefinitionLength);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//*********************************************************************
|
|
//
|
|
// NextInstance
|
|
//
|
|
// Returns pointer to the next instance following pInst.
|
|
//
|
|
// If pInst is the last instance, bogus data maybe returned.
|
|
// The caller should do the checking.
|
|
//
|
|
// If pInst is NULL, then NULL is returned.
|
|
//
|
|
PPERF_INSTANCE NextInstance (PPERF_INSTANCE pInst)
|
|
{
|
|
PERF_COUNTER_BLOCK *pCounterBlock;
|
|
|
|
if (pInst)
|
|
{
|
|
pCounterBlock = (PERF_COUNTER_BLOCK *)((PCHAR) pInst + pInst->ByteLength);
|
|
return (PPERF_INSTANCE)((PCHAR) pCounterBlock + pCounterBlock->ByteLength);
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
//*********************************************************************
|
|
//
|
|
// InstanceName
|
|
//
|
|
// Returns the name of the pInst.
|
|
//
|
|
// If pInst is NULL then NULL is returned.
|
|
//
|
|
LPTSTR InstanceName (PPERF_INSTANCE pInst)
|
|
{
|
|
if (pInst)
|
|
return (LPTSTR) ((PCHAR) pInst + pInst->NameOffset);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
BOOL GetPerformanceData(PPERF_DATA pPerfData,
|
|
PPERF_OBJECT pObject,
|
|
LPWSTR* aszTitleArray,
|
|
LPWSTR lpcsInstanceName,
|
|
DWORD *pdwMsgInQueue)
|
|
{
|
|
|
|
PPERF_INSTANCE pInstance = NULL;
|
|
|
|
*pdwMsgInQueue = 0;
|
|
|
|
pInstance = FirstInstance (pObject);
|
|
for (int i=0;i<pObject->NumInstances;i++)
|
|
{
|
|
|
|
LPWSTR lpwcsInstanceName = InstanceName(pInstance);
|
|
if (wcsicmp(lpcsInstanceName, lpwcsInstanceName) == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pInstance = NextInstance(pInstance);
|
|
}
|
|
|
|
if (i==pObject->NumInstances)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
PPERF_COUNTER pCounter;
|
|
pCounter = FirstCounter (pObject);
|
|
LPWSTR lpwcsNameTitle;
|
|
|
|
for (DWORD Index =0; Index < pObject->NumCounters; Index++)
|
|
{
|
|
lpwcsNameTitle = aszTitleArray[pCounter->CounterNameTitleIndex];
|
|
if (wcscmp(L"Messages in Queue", lpwcsNameTitle))
|
|
{
|
|
pCounter = NextCounter (pCounter);
|
|
continue;
|
|
}
|
|
|
|
|
|
DWORD dwCounterValue;
|
|
|
|
//
|
|
// update counters value
|
|
//
|
|
|
|
if (pInstance)
|
|
{
|
|
dwCounterValue = *(DWORD *)CounterData(pInstance,pCounter);
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*pdwMsgInQueue = dwCounterValue;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// FirstObject
|
|
//
|
|
// Returns pointer to the first object in pData.
|
|
// If pData is NULL then NULL is returned.
|
|
//
|
|
PPERF_OBJECT FirstObject (PPERF_DATA pData)
|
|
{
|
|
if (pData)
|
|
return ((PPERF_OBJECT) ((PBYTE) pData + pData->HeaderLength));
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//*********************************************************************
|
|
//
|
|
// NextObject
|
|
//
|
|
// Returns pointer to the next object following pObject.
|
|
//
|
|
// If pObject is the last object, bogus data maybe returned.
|
|
// The caller should do the checking.
|
|
//
|
|
// If pObject is NULL, then NULL is returned.
|
|
//
|
|
PPERF_OBJECT NextObject (PPERF_OBJECT pObject)
|
|
{
|
|
if (pObject)
|
|
return ((PPERF_OBJECT) ((PBYTE) pObject + pObject->TotalByteLength));
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//*********************************************************************
|
|
//
|
|
// FindObject
|
|
//
|
|
// Returns pointer to object with TitleIndex. If not found, NULL
|
|
// is returned.
|
|
//
|
|
PPERF_OBJECT FindObject (PPERF_DATA pData, DWORD TitleIndex)
|
|
{
|
|
PPERF_OBJECT pObject;
|
|
DWORD i = 0;
|
|
|
|
if (pObject = FirstObject (pData))
|
|
while (i < pData->NumObjectTypes)
|
|
{
|
|
if (pObject->ObjectNameTitleIndex == TitleIndex)
|
|
return pObject;
|
|
|
|
pObject = NextObject (pObject);
|
|
i++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// GetPerfData
|
|
//
|
|
// Get a new set of performance data.
|
|
//
|
|
// *ppData should be NULL initially.
|
|
// This function will allocate a buffer big enough to hold the
|
|
// data requested by szObjectIndex.
|
|
//
|
|
// *pDataSize specifies the initial buffer size. If the size is
|
|
// too small, the function will increase it until it is big enough
|
|
// then return the size through *pDataSize. Caller should
|
|
// deallocate *ppData if it is no longer being used.
|
|
//
|
|
// Returns ERROR_SUCCESS if no error occurs.
|
|
//
|
|
// Note: the trial and error loop is quite different from the normal
|
|
// registry operation. Normally if the buffer is too small,
|
|
// RegQueryValueEx returns the required size. In this case,
|
|
// the perflib, since the data is dynamic, a buffer big enough
|
|
// for the moment may not be enough for the next. Therefor,
|
|
// the required size is not returned.
|
|
//
|
|
// One should start with a resonable size to avoid the overhead
|
|
// of reallocation of memory.
|
|
//
|
|
DWORD
|
|
GetPerfData(
|
|
HKEY hPerfKey,
|
|
LPTSTR szObjectIndex,
|
|
PPERF_DATA* ppData,
|
|
DWORD* pDataSize
|
|
)
|
|
{
|
|
DWORD DataSize;
|
|
DWORD dwR;
|
|
DWORD Type;
|
|
|
|
*ppData = NULL;
|
|
do {
|
|
DataSize = *pDataSize;
|
|
dwR = RegQueryValueEx (hPerfKey,
|
|
szObjectIndex,
|
|
NULL,
|
|
&Type,
|
|
(BYTE*) *ppData,
|
|
&DataSize);
|
|
|
|
|
|
if (dwR == ERROR_MORE_DATA)
|
|
{
|
|
*pDataSize += 1024;
|
|
LocalFree (*ppData);
|
|
*ppData = (PPERF_DATA) LocalAlloc (LMEM_FIXED, *pDataSize);
|
|
}
|
|
|
|
if (!*ppData)
|
|
{
|
|
LocalFree (*ppData);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
} while (dwR == ERROR_MORE_DATA);
|
|
|
|
return dwR;
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// RefreshPerfData
|
|
//
|
|
// Get a new set of performance data. pData should be NULL initially.
|
|
//
|
|
PPERF_DATA RefreshPerfData (HKEY hPerfKey,
|
|
LPTSTR szObjectIndex,
|
|
PPERF_DATA pData,
|
|
DWORD *pDataSize)
|
|
{
|
|
if (GetPerfData (hPerfKey, szObjectIndex, &pData, pDataSize) == ERROR_SUCCESS)
|
|
return pData;
|
|
else
|
|
return NULL;
|
|
}
|
|
//*********************************************************************
|
|
//
|
|
// GetPerfTitleSz
|
|
//
|
|
// Retrieves the performance data title strings.
|
|
//
|
|
// This call retrieves english version of the title strings.
|
|
//
|
|
// For NT 3.1, the counter names are stored in the "Counters" value
|
|
// in the ...\perflib\009 key. For 3.5 and later, the 009 key is no
|
|
// longer used. The counter names should be retrieved from "Counter 009"
|
|
// value of HKEY_PERFORMANCE_KEY.
|
|
//
|
|
// Caller should provide two pointers, one for buffering the title
|
|
// strings the other for indexing the title strings. This function will
|
|
// allocate memory for the TitleBuffer and TitleSz. To get the title
|
|
// string for a particular title index one would just index the TitleSz.
|
|
// *TitleLastIdx returns the highest index can be used. If TitleSz[N] is
|
|
// NULL then there is no Title for index N.
|
|
//
|
|
// Example: TitleSz[20] points to titile string for title index 20.
|
|
//
|
|
// When done with the TitleSz, caller should LocalFree(*TitleBuffer).
|
|
//
|
|
// This function returns ERROR_SUCCESS if no error.
|
|
//
|
|
DWORD GetPerfTitleSz (HKEY hKeyMachine,
|
|
HKEY hKeyPerf,
|
|
LPWSTR *TitleBuffer,
|
|
LPWSTR *TitleSz[],
|
|
DWORD *TitleLastIdx)
|
|
{
|
|
HKEY hKey1;
|
|
HKEY hKey2;
|
|
DWORD Type;
|
|
DWORD DataSize;
|
|
DWORD dwR;
|
|
DWORD Len;
|
|
DWORD Index;
|
|
DWORD dwTemp;
|
|
BOOL bNT10;
|
|
LPWSTR szCounterValueName;
|
|
LPWSTR szTitle;
|
|
|
|
// Initialize
|
|
//
|
|
hKey1 = NULL;
|
|
hKey2 = NULL;
|
|
*TitleBuffer = NULL;
|
|
*TitleSz = NULL;
|
|
|
|
|
|
|
|
|
|
// Open the perflib key to find out the last counter's index and system version.
|
|
//
|
|
dwR = RegOpenKeyEx (hKeyMachine,
|
|
TEXT("software\\microsoft\\windows nt\\currentversion\\perflib"),
|
|
0,
|
|
KEY_READ,
|
|
&hKey1);
|
|
if (dwR != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
|
|
|
|
// Get the last counter's index so we know how much memory to allocate for TitleSz
|
|
//
|
|
DataSize = sizeof (DWORD);
|
|
dwR = RegQueryValueEx (hKey1, TEXT("Last Counter"), 0, &Type, (LPBYTE)TitleLastIdx, &DataSize);
|
|
if (dwR != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
|
|
|
|
// Find system version, for system earlier than 1.0a, there's no version value.
|
|
//
|
|
dwR = RegQueryValueEx (hKey1, TEXT("Version"), 0, &Type, (LPBYTE)&dwTemp, &DataSize);
|
|
|
|
if (dwR != ERROR_SUCCESS)
|
|
// unable to read the value, assume NT 1.0
|
|
bNT10 = TRUE;
|
|
else
|
|
// found the value, so, NT 1.0a or later
|
|
bNT10 = FALSE;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Now, get ready for the counter names and indexes.
|
|
//
|
|
if (bNT10)
|
|
{
|
|
// NT 1.0, so make hKey2 point to ...\perflib\009 and get
|
|
// the counters from value "Counters"
|
|
//
|
|
szCounterValueName = TEXT("Counters");
|
|
dwR = RegOpenKeyEx (hKeyMachine,
|
|
TEXT("software\\microsoft\\windows nt\\currentversion\\perflib\\009"),
|
|
0,
|
|
KEY_READ,
|
|
&hKey2);
|
|
if (dwR != ERROR_SUCCESS)
|
|
goto done;
|
|
}
|
|
else
|
|
{
|
|
// NT 1.0a or later. Get the counters in key HKEY_PERFORMANCE_KEY
|
|
// and from value "Counter 009"
|
|
//
|
|
szCounterValueName = TEXT("Counter 009");
|
|
hKey2 = hKeyPerf;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Find out the size of the data.
|
|
//
|
|
dwR = RegQueryValueEx (hKey2, szCounterValueName, 0, &Type, 0, &DataSize);
|
|
if (dwR != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
|
|
|
|
// Allocate memory
|
|
//
|
|
*TitleBuffer = (LPTSTR)LocalAlloc (LMEM_FIXED, DataSize);
|
|
if (!*TitleBuffer)
|
|
{
|
|
dwR = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
*TitleSz = (LPTSTR *)LocalAlloc (LPTR, (*TitleLastIdx+1) * sizeof (LPTSTR));
|
|
if (!*TitleSz)
|
|
{
|
|
dwR = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Query the data
|
|
//
|
|
dwR = RegQueryValueEx (hKey2, szCounterValueName, 0, &Type, (BYTE *)*TitleBuffer, &DataSize);
|
|
if (dwR != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
|
|
|
|
|
|
// Setup the TitleSz array of pointers to point to beginning of each title string.
|
|
// TitleBuffer is type REG_MULTI_SZ.
|
|
//
|
|
szTitle = *TitleBuffer;
|
|
|
|
while (Len = lstrlen (szTitle))
|
|
{
|
|
Index = _wtoi (szTitle);
|
|
|
|
szTitle = szTitle + Len +1;
|
|
|
|
if (Index <= *TitleLastIdx)
|
|
(*TitleSz)[Index] = szTitle;
|
|
|
|
szTitle = szTitle + lstrlen (szTitle) +1;
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
// Done. Now cleanup!
|
|
//
|
|
if (dwR != ERROR_SUCCESS)
|
|
{
|
|
// There was an error, free the allocated memory
|
|
//
|
|
if (*TitleBuffer) LocalFree (*TitleBuffer);
|
|
if (*TitleSz) LocalFree (*TitleSz);
|
|
}
|
|
|
|
// Close the hKeys.
|
|
//
|
|
if (hKey1) RegCloseKey (hKey1);
|
|
if (hKey2 && hKey2 != hKeyPerf) RegCloseKey (hKey2);
|
|
|
|
|
|
|
|
return dwR;
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
GetPerfmonInfo(LPCSTR lpwcsInstanceName)
|
|
{
|
|
DWORD dwR = 0;
|
|
DWORD Index;
|
|
static fFirst = TRUE;
|
|
LPWSTR sTitleBuffer; // Buffer which holds title strings
|
|
DWORD dwMsgInQueue;
|
|
WCHAR wcsInstanceName[100];
|
|
|
|
swprintf(wcsInstanceName,L"%S", lpwcsInstanceName);
|
|
|
|
|
|
if (fFirst)
|
|
{
|
|
dwR = GetPerfTitleSz (HKEY_LOCAL_MACHINE,
|
|
HKEY_PERFORMANCE_DATA,
|
|
&sTitleBuffer,
|
|
&aszTitleArray,
|
|
&dwLastCounter);
|
|
|
|
if (FAILED(dwR))
|
|
{
|
|
return 0;
|
|
}
|
|
fFirst = FALSE;
|
|
//
|
|
// Initiate the objects Index
|
|
//
|
|
dwObjectIndex = (DWORD)-1;
|
|
|
|
for (Index = 0; Index <= dwLastCounter; Index++)
|
|
{
|
|
if (aszTitleArray[Index] &&
|
|
(lstrcmpi (aszTitleArray[Index], PERF_QUEUE_OBJECT) == 0))
|
|
{
|
|
dwObjectIndex = Index;
|
|
}
|
|
}
|
|
|
|
if ((DWORD)-1 == dwObjectIndex)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
swprintf(wcsObjectID,L"%ld", dwObjectIndex);
|
|
}
|
|
//
|
|
// Get the number of counters the object has
|
|
//
|
|
PPERF_OBJECT pObject;
|
|
PPERF_DATA pPerfData;
|
|
DWORD dwPerfDataSize = 0;
|
|
|
|
pPerfData = RefreshPerfData (HKEY_PERFORMANCE_DATA, wcsObjectID, pPerfData, &dwPerfDataSize);
|
|
|
|
pObject= FindObject (pPerfData, dwObjectIndex);
|
|
if (pObject == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
GetPerformanceData(pPerfData,pObject,aszTitleArray, wcsInstanceName,&dwMsgInQueue);
|
|
LocalFree (pPerfData);
|
|
return dwMsgInQueue;
|
|
}
|