因工作需要,需要写个对进程性能进行监控的DLL,为防止以后忘记,留在这里。:) 头文件 #pragma once #ifndef PERFORMANCE_SDK_H #define PERFORMANCE_SDK_H #ifndef PERFORMANCE_EXPORT #define PERFORMANCE_EXPORT _declspec(dllexport) #else #define PERFORMANCE_EXPORT _declspec(dllimport) #endif #include <Windows.h> #include <Psapi.h> #include <tlhelp32.h> #include <stdio.h> #include "CpuUsage.h" extern "C" { ////brief 根据进程名称获得进程CPU使用率 ////param pProcessName[in] 进程名称 ////return 成功则返回进程的CPU使用率,否则返回-1 PERFORMANCE_EXPORT int __stdcall GetCpuUsageByProcessName(LPCTSTR pProcessName); ////brief 根据进程ID获得进程CPU使用率 ////param dwProcessID[in] 进程ID ////return 成功则返回进程的CPU使用率,否则返回-1 PERFORMANCE_EXPORT int __stdcall GetCpuUsageByProcessID(DWORD dwProcessID); ////brief 根据进程ID获得进程内存使用情况 ////param dwProcessID[in] 进程ID ////return 成功则返回进程使用的内存数,否则返回-1 PERFORMANCE_EXPORT int __stdcall GetMemoryUseInfoByProcessID(DWORD dwProcessID); ////brief 根据进程ID获得进程IO使用情况 ////param dwProcessID[in] 进程ID ////param ioRead[in] I/O读取 ////param ioWrite[in] I/O写入 ////param ioOther[in] I/O其他 ////param ioReadTrans[in] I/O读取字节数 ////param ioWriteTrans[in] I/0写入字节数 ////param ioTransOther[in] I/0其他字节 ////return 成功则返回TRUE,否则返回FALSE PERFORMANCE_EXPORT bool __stdcall GetIOsByProcessID(DWORD dwProcessID, ULONGLONG* ioRead, ULONGLONG* ioWrite, ULONGLONG* ioOther = 0, ULONGLONG* ioReadTrans = 0, ULONGLONG* ioWriteTrans = 0, ULONGLONG* ioTransOther = 0); ////brief 根据进程ID获得进程句柄使用情况 ////param dwProcessID[in] 进程ID ////return 成功则返回进程使用的句柄数,否则返回-1 PERFORMANCE_EXPORT int __stdcall GetHandlesByProcessID(DWORD dwProcessID); ////brief 根据进程ID获得进程线程使用情况 ////param dwProcessID[in] 进程ID ////return 成功则返回进程使用的线程数,否则返回-1 PERFORMANCE_EXPORT int __stdcall GetThreadsByProcessID(DWORD dwProcessID); ////brief 获得网络流量 ////return 成功则返回当前网络流量,否则返回-1 PERFORMANCE_EXPORT int __stdcall GetNetworkFlowRate(); }; #endif 实现文件 #include "stdafx.h" #include "PerformanceInterface.h" int __stdcall GetCpuUsageByProcessName(LPCTSTR pProcessName) { CCpuUsage cpuUsage; return cpuUsage.GetCpuUsage(pProcessName); } int __stdcall GetCpuUsageByProcessID(DWORD dwProcessID) { CCpuUsage cpuUsage; return cpuUsage.GetCpuUsage(dwProcessID); } int __stdcall GetMemoryUseInfoByProcessID(DWORD dwProcessID) { size_t memSize = -1; HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID); if (hProcess == NULL) { return -1; } PROCESS_MEMORY_COUNTERS pmc; BOOL bGetMemInfo = ::GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)); if(!bGetMemInfo) { CloseHandle(hProcess); return -1; } memSize = pmc.WorkingSetSize; CloseHandle(hProcess); return memSize; } bool __stdcall GetIOsByProcessID(DWORD dwProcessID, ULONGLONG* ioRead, ULONGLONG* ioWrite, ULONGLONG* ioOther, ULONGLONG* ioReadTrans, ULONGLONG* ioWriteTrans, ULONGLONG* ioTransOther) { HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID); if (hProcess == NULL) { return false; } IO_COUNTERS ioCount; BOOL bGetIoCount = ::GetProcessIoCounters(hProcess, &ioCount); if (!bGetIoCount) { CloseHandle(hProcess); return false; } *ioRead = ioCount.ReadOperationCount; *ioWrite = ioCount.WriteOperationCount; if (ioOther != 0) { *ioOther = ioCount.OtherOperationCount; } if (ioReadTrans != 0) { *ioReadTrans = ioCount.ReadTransferCount; } if (ioWriteTrans != 0) { *ioWriteTrans = ioCount.WriteTransferCount; } if (ioTransOther != 0) { *ioTransOther = ioCount.OtherTransferCount; } CloseHandle(hProcess); return true; } int __stdcall GetHandlesByProcessID(DWORD dwProcessID) { HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID); if (hProcess == NULL) { return -1; } DWORD dwHandles; BOOL bGetHandles = ::GetProcessHandleCount(hProcess, &dwHandles); if (!bGetHandles) { CloseHandle(hProcess); return -1; } CloseHandle(hProcess); return dwHandles; } int __stdcall GetThreadsByProcessID(DWORD dwProcessID) { int nThreadCount = 0; HANDLE hThreadSnap = INVALID_HANDLE_VALUE; THREADENTRY32 te32; // Take a snapshot of all running threads hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); if( hThreadSnap == INVALID_HANDLE_VALUE ) return -1; // Fill in the size of the structure before using it. te32.dwSize = sizeof(THREADENTRY32 ); // Retrieve information about the first thread, // and exit if unsuccessful if( !Thread32First( hThreadSnap, &te32 ) ) { CloseHandle( hThreadSnap ); // Must clean up the snapshot object! return -1; } // Now walk the thread list of the system, // and display information about each thread // associated with the specified process do { if( te32.th32OwnerProcessID == dwProcessID ) { nThreadCount++; } } while( Thread32Next(hThreadSnap, &te32 ) ); // Don't forget to clean up the snapshot object. CloseHandle( hThreadSnap ); return nThreadCount; } int __stdcall GetNetworkFlowRate(DWORD dwProcessID) { return 0; 实现CPU使用的类,CodeProject上下载的 头文件: #ifndef _CPUUSAGE_H #define _CPUUSAGE_H #include <windows.h> class CCpuUsage { public: CCpuUsage(); virtual ~CCpuUsage(); // Methods int GetCpuUsage(); int GetCpuUsage(LPCTSTR pProcessName); int GetCpuUsage(DWORD dwProcessID); BOOL EnablePerformaceCounters(BOOL bEnable = TRUE); // Attributes private: bool m_bFirstTime; LONGLONG m_lnOldValue ; LARGE_INTEGER m_OldPerfTime100nSec; DWORD m_cpuCount; }; 实现文件 #include "stdafx.h" #include <atlbase.h> // for CRegKey use #include "CpuUsage.h" #pragma pack(push,8) #include "PerfCounters.h" #pragma pack(pop) #pragma warning(disable : 4996) #define SYSTEM_OBJECT_INDEX 2 // 'System' object #define PROCESS_OBJECT_INDEX 230 // 'Process' object #define PROCESSOR_OBJECT_INDEX 238 // 'Processor' object #define TOTAL_PROCESSOR_TIME_COUNTER_INDEX 240 // '% Total processor time' counter (valid in WinNT under 'System' object) #define PROCESSOR_TIME_COUNTER_INDEX 6 // '% processor time' counter (for Win2K/XP) /////////////////////////////////////////////////////////////////// // // GetCpuUsage uses the performance counters to retrieve the // system cpu usage. // The cpu usage counter is of type PERF_100NSEC_TIMER_INV // which as the following calculation: // // Element Value // ======= =========== // X CounterData // Y 100NsTime // Data Size 8 Bytes // Time base 100Ns // Calculation 100*(1-(X1-X0)/(Y1-Y0)) // // where the denominator (Y) represents the total elapsed time of the // sample interval and the numerator (X) represents the time during // the interval when the monitored components were inactive. // // // Note: // ==== // On windows NT, cpu usage counter is '% Total processor time' // under 'System' object. However, in Win2K/XP Microsoft moved // that counter to '% processor time' under '_Total' instance // of 'Processor' object. // Read 'INFO: Percent Total Performance Counter Changes on Windows 2000' // Q259390 in MSDN. // /////////////////////////////////////////////////////////////////// typedef enum { WINNT, WIN2K_XP, WIN9X, UNKNOWN }PLATFORM; typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); DWORD GetNumberOfProcessors() { SYSTEM_INFO si; // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise. PGNSI pfnGNSI = (PGNSI) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo"); if(pfnGNSI) { pfnGNSI(&si); } else { GetSystemInfo(&si); } return si.dwNumberOfProcessors; } PLATFORM GetPlatform() { OSVERSIONINFO osvi; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (!GetVersionEx(&osvi)) return UNKNOWN; switch (osvi.dwPlatformId) { case VER_PLATFORM_WIN32_WINDOWS: return WIN9X; case VER_PLATFORM_WIN32_NT: if (osvi.dwMajorVersion == 4) return WINNT; else return WIN2K_XP; } return UNKNOWN; } CCpuUsage::CCpuUsage() { m_bFirstTime = true; m_lnOldValue = 0; m_cpuCount = 0; memset(&m_OldPerfTime100nSec, 0, sizeof(m_OldPerfTime100nSec)); m_cpuCount = GetNumberOfProcessors(); if (m_cpuCount <= 0) { m_cpuCount = 1; } } CCpuUsage::~CCpuUsage() { } BOOL CCpuUsage::EnablePerformaceCounters(BOOL bEnable) { if (GetPlatform() != WIN2K_XP) return TRUE; CRegKey regKey; if (regKey.Open(HKEY_LOCAL_MACHINE, "SYSTEM//CurrentControlSet//Services//PerfOS//Performance") != ERROR_SUCCESS) return FALSE; regKey.SetValue(!bEnable, "Disable Performance Counters"); regKey.Close(); if (regKey.Open(HKEY_LOCAL_MACHINE, "SYSTEM//CurrentControlSet//Services//PerfProc//Performance") != ERROR_SUCCESS) return FALSE; regKey.SetValue(!bEnable, "Disable Performance Counters"); regKey.Close(); return TRUE; } // // GetCpuUsage returns the system-wide cpu usage. // Since we calculate the cpu usage by two samplings, the first // call to GetCpuUsage() returns 0 and keeps the values for the next // sampling. // Read the comment at the beginning of this file for the formula. // int CCpuUsage::GetCpuUsage() { static PLATFORM Platform = GetPlatform(); if (m_bFirstTime) EnablePerformaceCounters(); // Cpu usage counter is 8 byte length. CPerfCounters<LONGLONG> PerfCounters; char szInstance[256] = {0}; // Note: // ==== // On windows NT, cpu usage counter is '% Total processor time' // under 'System' object. However, in Win2K/XP Microsoft moved // that counter to '% processor time' under '_Total' instance // of 'Processor' object. // Read 'INFO: Percent Total Performance Counter Changes on Windows 2000' // Q259390 in MSDN. DWORD dwObjectIndex; DWORD dwCpuUsageIndex; switch (Platform) { case WINNT: dwObjectIndex = SYSTEM_OBJECT_INDEX; dwCpuUsageIndex = TOTAL_PROCESSOR_TIME_COUNTER_INDEX; break; case WIN2K_XP: dwObjectIndex = PROCESSOR_OBJECT_INDEX; dwCpuUsageIndex = PROCESSOR_TIME_COUNTER_INDEX; strcpy(szInstance,"_Total"); break; default: return -1; } int CpuUsage = 0; LONGLONG lnNewValue = 0; PPERF_DATA_BLOCK pPerfData = NULL; LARGE_INTEGER NewPerfTime100nSec = {0}; lnNewValue = PerfCounters.GetCounterValue(&pPerfData, dwObjectIndex, dwCpuUsageIndex, szInstance); NewPerfTime100nSec = pPerfData->PerfTime100nSec; if (m_bFirstTime) { m_bFirstTime = false; m_lnOldValue = lnNewValue; m_OldPerfTime100nSec = NewPerfTime100nSec; return 0; } LONGLONG lnValueDelta = lnNewValue - m_lnOldValue; double DeltaPerfTime100nSec = (double)NewPerfTime100nSec.QuadPart - (double)m_OldPerfTime100nSec.QuadPart; m_lnOldValue = lnNewValue; m_OldPerfTime100nSec = NewPerfTime100nSec; double a = (double)lnValueDelta / DeltaPerfTime100nSec; double f = (1.0 - a) * 100.0; CpuUsage = (int)(f + 0.5); // rounding the result if (CpuUsage < 0) return 0; return CpuUsage / m_cpuCount; } int CCpuUsage::GetCpuUsage(LPCTSTR pProcessName) { static PLATFORM Platform = GetPlatform(); if (m_bFirstTime) EnablePerformaceCounters(); // Cpu usage counter is 8 byte length. CPerfCounters<LONGLONG> PerfCounters; char szInstance[256] = {0}; DWORD dwObjectIndex = PROCESS_OBJECT_INDEX; DWORD dwCpuUsageIndex = PROCESSOR_TIME_COUNTER_INDEX; strcpy(szInstance,pProcessName); int CpuUsage = 0; LONGLONG lnNewValue = 0; PPERF_DATA_BLOCK pPerfData = NULL; LARGE_INTEGER NewPerfTime100nSec = {0}; lnNewValue = PerfCounters.GetCounterValue(&pPerfData, dwObjectIndex, dwCpuUsageIndex, szInstance); NewPerfTime100nSec = pPerfData->PerfTime100nSec; if (m_bFirstTime) { m_bFirstTime = false; m_lnOldValue = lnNewValue; m_OldPerfTime100nSec = NewPerfTime100nSec; return 0; } LONGLONG lnValueDelta = lnNewValue - m_lnOldValue; double DeltaPerfTime100nSec = (double)NewPerfTime100nSec.QuadPart - (double)m_OldPerfTime100nSec.QuadPart; m_lnOldValue = lnNewValue; m_OldPerfTime100nSec = NewPerfTime100nSec; double a = (double)lnValueDelta / DeltaPerfTime100nSec; CpuUsage = (int) (a*100); if (CpuUsage < 0) return 0; return CpuUsage / m_cpuCount; } int CCpuUsage::GetCpuUsage(DWORD dwProcessID) { static PLATFORM Platform = GetPlatform(); if (m_bFirstTime) EnablePerformaceCounters(); // Cpu usage counter is 8 byte length. CPerfCounters<LONGLONG> PerfCounters; DWORD dwObjectIndex = PROCESS_OBJECT_INDEX; DWORD dwCpuUsageIndex = PROCESSOR_TIME_COUNTER_INDEX; int CpuUsage = 0; LONGLONG lnNewValue = 0; PPERF_DATA_BLOCK pPerfData = NULL; LARGE_INTEGER NewPerfTime100nSec = {0}; lnNewValue = PerfCounters.GetCounterValueForProcessID(&pPerfData, dwObjectIndex, dwCpuUsageIndex, dwProcessID); NewPerfTime100nSec = pPerfData->PerfTime100nSec; if (m_bFirstTime) { m_bFirstTime = false; m_lnOldValue = lnNewValue; m_OldPerfTime100nSec = NewPerfTime100nSec; return 0; } LONGLONG lnValueDelta = lnNewValue - m_lnOldValue; double DeltaPerfTime100nSec = (double)NewPerfTime100nSec.QuadPart - (double)m_OldPerfTime100nSec.QuadPart; m_lnOldValue = lnNewValue; m_OldPerfTime100nSec = NewPerfTime100nSec; double a = (double)lnValueDelta / DeltaPerfTime100nSec; CpuUsage = (int) (a*100); if (CpuUsage < 0) return 0; return CpuUsage / m_cpuCount; } 还有一个模板类,实现从注册表中取值和遍历的功能 #include <windows.h> #include <stdio.h> #include <comdef.h> // for using bstr_t class #include <vector> #include <WinPerf.h> #pragma warning(disable : 4996) #pragma warning(disable : 4244) #define TOTALBYTES 100*1024 #define BYTEINCREMENT 10*1024 template <class T> class CPerfCounters { public: CPerfCounters() { } ~CPerfCounters() { } T GetCounterValue(PERF_DATA_BLOCK **pPerfData, DWORD dwObjectIndex, DWORD dwCounterIndex, LPCTSTR pInstanceName = NULL) { QueryPerformanceData(pPerfData, dwObjectIndex, dwCounterIndex); PPERF_OBJECT_TYPE pPerfObj = NULL; T lnValue = {0}; // Get the first object type. pPerfObj = FirstObject( *pPerfData ); // Look for the given object index for( DWORD i=0; i < (*pPerfData)->NumObjectTypes; i++ ) { if (pPerfObj->ObjectNameTitleIndex == dwObjectIndex) { lnValue = GetCounterValue(pPerfObj, dwCounterIndex, pInstanceName); break; } pPerfObj = NextObject( pPerfObj ); } return lnValue; } T GetCounterValueForProcessID(PERF_DATA_BLOCK **pPerfData, DWORD dwObjectIndex, DWORD dwCounterIndex, DWORD dwProcessID) { QueryPerformanceData(pPerfData, dwObjectIndex, dwCounterIndex); PPERF_OBJECT_TYPE pPerfObj = NULL; T lnValue = {0}; // Get the first object type. pPerfObj = FirstObject( *pPerfData ); // Look for the given object index for( DWORD i=0; i < (*pPerfData)->NumObjectTypes; i++ ) { if (pPerfObj->ObjectNameTitleIndex == dwObjectIndex) { lnValue = GetCounterValueForProcessID(pPerfObj, dwCounterIndex, dwProcessID); break; } pPerfObj = NextObject( pPerfObj ); } return lnValue; } protected: class CBuffer { public: CBuffer(UINT Size) { m_Size = Size; m_pBuffer = (LPBYTE) malloc( Size*sizeof(BYTE) ); } ~CBuffer() { free(m_pBuffer); } void *Realloc(UINT Size) { m_Size = Size; m_pBuffer = (LPBYTE) realloc( m_pBuffer, Size ); return m_pBuffer; } void Reset() { memset(m_pBuffer,NULL,m_Size); } operator LPBYTE () { return m_pBuffer; } UINT GetSize() { return m_Size; } public: LPBYTE m_pBuffer; private: UINT m_Size; }; // // The performance data is accessed through the registry key // HKEY_PEFORMANCE_DATA. // However, although we use the registry to collect performance data, // the data is not stored in the registry database. // Instead, calling the registry functions with the HKEY_PEFORMANCE_DATA key // causes the system to collect the data from the appropriate system // object managers. // // QueryPerformanceData allocates memory block for getting the // performance data. // // void QueryPerformanceData(PERF_DATA_BLOCK **pPerfData, DWORD dwObjectIndex, DWORD dwCounterIndex) { // // Since i want to use the same allocated area for each query, // i declare CBuffer as static. // The allocated is changed only when RegQueryValueEx return ERROR_MORE_DATA // static CBuffer Buffer(TOTALBYTES); DWORD BufferSize = Buffer.GetSize(); LONG lRes; char keyName[32]; sprintf_s(keyName, 32, "%d",dwObjectIndex); Buffer.Reset(); while( (lRes = RegQueryValueEx( HKEY_PERFORMANCE_DATA, keyName, NULL, NULL, Buffer, &BufferSize )) == ERROR_MORE_DATA ) { // Get a buffer that is big enough. BufferSize += BYTEINCREMENT; Buffer.Realloc(BufferSize); } *pPerfData = (PPERF_DATA_BLOCK) Buffer.m_pBuffer; } // // GetCounterValue gets performance object structure // and returns the value of given counter index . // This functions iterates through the counters of the input object // structure and looks for the given counter index. // // For objects that have instances, this function returns the counter value // of the instance pInstanceName. // T GetCounterValue(PPERF_OBJECT_TYPE pPerfObj, DWORD dwCounterIndex, LPCTSTR pInstanceName) { PPERF_COUNTER_DEFINITION pPerfCntr = NULL; PPERF_INSTANCE_DEFINITION pPerfInst = NULL; PPERF_COUNTER_BLOCK pCounterBlock = NULL; // Get the first counter. pPerfCntr = FirstCounter( pPerfObj ); for( DWORD j=0; j < pPerfObj->NumCounters; j++ ) { if (pPerfCntr->CounterNameTitleIndex == dwCounterIndex) break; // Get the next counter. pPerfCntr = NextCounter( pPerfCntr ); } if( pPerfObj->NumInstances == PERF_NO_INSTANCES ) { pCounterBlock = (PPERF_COUNTER_BLOCK) ((LPBYTE) pPerfObj + pPerfObj->DefinitionLength); } else { pPerfInst = FirstInstance( pPerfObj ); // Look for instance pInstanceName _bstr_t bstrInstance; _bstr_t bstrInputInstance = pInstanceName; for( int k=0; k < pPerfObj->NumInstances; k++ ) { bstrInstance = (wchar_t *)((PBYTE)pPerfInst + pPerfInst->NameOffset); if (!_stricmp((LPCTSTR)bstrInstance, (LPCTSTR)bstrInputInstance)) { pCounterBlock = (PPERF_COUNTER_BLOCK) ((LPBYTE) pPerfInst + pPerfInst->ByteLength); break; } // Get the next instance. pPerfInst = NextInstance( pPerfInst ); } } if (pCounterBlock) { T *lnValue = NULL; lnValue = (T*)((LPBYTE) pCounterBlock + pPerfCntr->CounterOffset); return *lnValue; } return -1; } T GetCounterValueForProcessID(PPERF_OBJECT_TYPE pPerfObj, DWORD dwCounterIndex, DWORD dwProcessID) { int PROC_ID_COUNTER = 784; BOOL bProcessIDExist = FALSE; PPERF_COUNTER_DEFINITION pPerfCntr = NULL; PPERF_COUNTER_DEFINITION pTheRequestedPerfCntr = NULL; PPERF_COUNTER_DEFINITION pProcIDPerfCntr = NULL; PPERF_INSTANCE_DEFINITION pPerfInst = NULL; PPERF_COUNTER_BLOCK pCounterBlock = NULL; // Get the first counter. pPerfCntr = FirstCounter( pPerfObj ); for( DWORD j=0; j < pPerfObj->NumCounters; j++ ) { if (pPerfCntr->CounterNameTitleIndex == PROC_ID_COUNTER) { pProcIDPerfCntr = pPerfCntr; if (pTheRequestedPerfCntr) break; } if (pPerfCntr->CounterNameTitleIndex == dwCounterIndex) { pTheRequestedPerfCntr = pPerfCntr; if (pProcIDPerfCntr) break; } // Get the next counter. pPerfCntr = NextCounter( pPerfCntr ); } if( pPerfObj->NumInstances == PERF_NO_INSTANCES ) { pCounterBlock = (PPERF_COUNTER_BLOCK) ((LPBYTE) pPerfObj + pPerfObj->DefinitionLength); } else { pPerfInst = FirstInstance( pPerfObj ); for( int k=0; k < pPerfObj->NumInstances; k++ ) { pCounterBlock = (PPERF_COUNTER_BLOCK) ((LPBYTE) pPerfInst + pPerfInst->ByteLength); if (pCounterBlock) { int processID = 0; processID = *(T*)((LPBYTE) pCounterBlock + pProcIDPerfCntr->CounterOffset); if (processID == dwProcessID) { bProcessIDExist = TRUE; break; } } // Get the next instance. pPerfInst = NextInstance( pPerfInst ); } } if (bProcessIDExist && pCounterBlock) { T *lnValue = NULL; lnValue = (T*)((LPBYTE) pCounterBlock + pTheRequestedPerfCntr->CounterOffset); return *lnValue; } return -1; } /***************************************************************** * * * Functions used to navigate through the performance data. * * * *****************************************************************/ PPERF_OBJECT_TYPE FirstObject( PPERF_DATA_BLOCK PerfData ) { return( (PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength) ); } PPERF_OBJECT_TYPE NextObject( PPERF_OBJECT_TYPE PerfObj ) { return( (PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength) ); } PPERF_COUNTER_DEFINITION FirstCounter( PPERF_OBJECT_TYPE PerfObj ) { return( (PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength) ); } PPERF_COUNTER_DEFINITION NextCounter( PPERF_COUNTER_DEFINITION PerfCntr ) { return( (PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength) ); } PPERF_INSTANCE_DEFINITION FirstInstance( PPERF_OBJECT_TYPE PerfObj ) { return( (PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength) ); } PPERF_INSTANCE_DEFINITION NextInstance( PPERF_INSTANCE_DEFINITION PerfInst ) { PPERF_COUNTER_BLOCK PerfCntrBlk; PerfCntrBlk = (PPERF_COUNTER_BLOCK)((PBYTE)PerfInst + PerfInst->ByteLength); return( (PPERF_INSTANCE_DEFINITION)((PBYTE)PerfCntrBlk + PerfCntrBlk->ByteLength) ); } };