Microsoft Windwos NT/2000 提供了一個強大的API集來訪問系統事件和性能數據的眾多計數器。我們既可以實時地得到計數器的值,也可以從一個日志文件中讀取計數器數據。功能可為強大,而且使用簡單。
下面我就簡單談談在vc中如何使用windows的性能計數器。好,廢話少說,我們開始:
我們用一個簡單的例子來說明性能計數器的使用方法。
比如:我們如何獲取當前正在運行的某個進程的CPU使用率呢?你一定會說:“這還不簡單,方法有很多”。當然,我承認這個不難,而且的確有很多方法。但是哪種方法最簡單?效率最高呢?我猜大概是使用性能計數器了。
要使用性能計數器的基本步驟是:
1.打開計數器PdhOpenQuery;
2.為計數器句柄分配空間;
3.把感興趣的計數器添加進來PdhAddCounter;
4.收集數據PdhCollectQueryData
5.得到計數器的數值PdhGetFormattedCounterValue;
6.關閉計數器PdhCloseQuery。
下面是用代碼實現的步驟
第一步:
在頭文件中
#include
#pragma comment ( lib , "Pdh.lib" )
HQUERY hQuery = NULL ;
PDH_STATUS pdhStatus ;
HCOUNTER * pCounterHandle = NULL ;
__try
{
pdhStatus = PdhOpenQuery ( 0 , 0 , & hQuery ) ;
if ( pdhStatus != ERROR_SUCCESS )
{
__leave ;
}
pCounterHandle = ( HCOUNTER * ) GlobalAlloc ( GPTR , sizeof ( HCOUNTER
) ) ;
if ( pCounterHandle == NULL )
{
__leave ;
}
}
__finally
{
if ( AbnormalTermination () )
{
}
}
第三步:創建計數器(假設要獲取QQ程序的CPU使用率)
PDH_FMT_COUNTERVALUE fmtValue ;
DWORD dwctrType ;
__try
{
pdhStatus = PdhAddCounter ( hQuery , _TEXT ( "Process(_TEXT ( "QQ"
))\\%Processor Time" ) , 0 , pCounterHandle ) ;
if ( pdhStatus != ERROR_SUCCESS )
{
__leave ;
}
pdhStatus = PdhCollectQueryData ( hQuery ) ;
if ( pdhStatus != ERROR_SUCCESS )
{
__leave ;
}
pdhStatus = PdhGetFormattedCounterValue ( * pCounterHandle
, PDH_FMT_DOUBLE , & dwctrType , & fmtValue ) ;
if ( pdhStatus != ERROR_SUCCESS )
{
__leave ;
}
// fmtValue.doubleValue就是當前此時此刻該程序的CPU使用率(循環調用就可得到實時數據)
}
__finally
{
if ( AbnormalTermination () )
{
}
}
pdhStatus = PdhCloseQuery ( hQuery ) ;
if ( pdhStatus == ERROR_SUCCESS )
{
// 關閉成功
}
else
{
// 關閉失敗
}
/*
是不是很簡單呀!上面例子中PdhAddCounter函數是添加計數器,它的第二個參數就是計數器地址,我們可以更換其它的,以獲得其它計數數據。(詳細請查詢MSDN)
windows的性能計數器可以獲得好幾百項系統計數信息,幾乎所有和計數有關的信息都可以得到。說到這裡一定有朋友要問:“我還能得到哪些信息?這麼多的計數器又代表什麼含義?”,我們繼續向下看。
上面說過了,要獲取其它技術信息只需更改計數器地址(就是PdhAddCounter函數中的第二個參數“\Process(( "QQ"
))\%Processor Time”),每個計數器地址包含三個部分(計數器對象Process、計數器%Processor Time、計數器實例QQ),我們只要知道你的系統中都有哪些計數器對象、每個計數器對象有包含哪些計數器、每個計數器又有哪些計數器實例,按照上面的調用格式就可以得到你想要的所有計數信息。
Microsoft為我們提供了方便獲取計數器對象、計數器、實例信息的方法---杖舉。
要杖舉計數器需要用到以下幾個API:
1.杖舉計數器對象
PdhEnumObjects (
NULL , // [IN]數據源,NT4.0必須為NULL
szMachineName , // [IN]機器名。本地機器為NULL
szObjectListBuffer , // [OUT]接收計數器列表的緩沖區,如果計數器列表長度為0,則該項為空
& dwObjectListSize , // [IN/OUT]設置或接收計數器列表長度
dwDetailLevel , // 獲取信息的級別
// PERF_DETAIL_NOVICE 初級級別
// PERF_DETAIL_ADVANCE 高級級別(包含初級)
// PERF_DETAIL_EXPERT 專家級別(包含初級和高級)
// PERF_DETAIL_WIZARD 系統級別(包含所有級別)
TRUE ) ;
2.枚舉計數器和計數器實例
PdhEnumObjectItems (
NULL , // [IN]數據源,NT4.0必須為NULL
szMachineName , // [IN]機器名。本地機器為NULL
pctCounter , // [IN]計數器名
szCounterListBuffer , // [OUT]接收計數器列表的緩沖區,如果計數器列表長度為0,則該項為空
& dwCounterListSize , // [IN/OUT]設置或接收計數器列表長度
szInstanceListBuffer , // [OUT]接收實例列表的緩沖區,如果計數器列表長度為0,則該項為空
& dwInstanceListSize , // [IN/OUT]設置或接收實例列表長度
dwDetailLevel , // 獲取信息的級別
// PERF_DETAIL_NOVICE 初級級別
// PERF_DETAIL_ADVANCE 高級級別(包含初級)
// PERF_DETAIL_EXPERT 專家級別(包含初級和高級)
// PERF_DETAIL_WIZARD 系統級別(包含所有級別)
0 ) ; // 最後一個參數系統保留為0
更詳細信息請參閱MSDN
*/
杖舉計數器對象的基本步驟是:
1.獲取計數器對象列表大小
2.為計數器列表分配緩沖區
3.開始杖舉
以下是編程實現:
第一步:獲取計數器對象列表大小
LPTSTR szObjectListBuffer = NULL ;
DWORD dwObjectListSize = 0 ;
LPTSTR szThisObject = NULL ;
__try
{
pdhStatus = PdhEnumObjects (
true ) ;
if ( pdhStatus != ERROR_SUCCESS )
{
__leave ;
}
// 根據得到的緩沖區大小分配計數器對象列表緩沖區內存
szObjectListBuffer = ( LPTSTR ) malloc ( ( dwObjectListSize * sizeof
( TCHAR ) ) ) ;
if ( szObjectListBuffer == NULL )
{
__leave ;
}
TRUE ) ;
if ( pdhStatus != ERROR_SUCCESS )
{
__leave ;
}
szThisObject = szObjectListBuffer ;
// 開始杖舉
for ( ; * szThisObject != 0 ; szThisObject += ( lstrlen ( szThisObject
) + 1 ) )
{
// 每循環一次 szThisObject 就是杖舉到的計數器對象
}
}
__finally
{
if ( AbnormalTermination () )
{
// 如果失敗
if ( szObjectListBuffer != NULL )
{
free ( szObjectListBuffer ) ;
szObjectListBuffer = NULL ;
}
}
else
{
// 如果成功
}
}
// 最後別忘了 free
/*
通過剛才杖舉得到計數器對象就可以繼續杖舉該對象下的計數器和計數器實例,方法和上面基本雷同,有興趣的朋友可以自己來做,限於篇幅我就不重復了。
我在說說如何知道計數器的描述信息(可是中文的哦!),也就是每個計數器都代表什麼含義?干什麼用的?要知道每個計數器描述信息需要用到PdhGetCounterInfo函數(都是在pdh開頭的API中打轉)。
*/
// 基本步驟如下:
// 1.格式化某一個計數器地址(字符串)
/*
在這裡需要說明一下:有很多計數器是沒有實例的。有實例和沒有實例的格式化形式略有不同。
比如:
(有實例的)獲取當前寫入操作時傳送到磁盤上的字節速度:需要用到”PhysicalDisk“計數器對象、該計數器對象下的"Disk Write
Bytes/sec"計數器、以及計數器實例(在我的機子上主硬盤的實例為 "0 C: D: E: F:" ) ,那麼獲取我傳送到主硬盤上的字節速度的計數器地址為
: "\PhysicalDisk("0 C: D: E: F:")\Disk Write Bytes/sec" 。
(無實例的)獲取本計算機自上次啟動後已經運行的時間(單位秒):需要用到"System"計數器對象、蓋計數器對象下的"System Up
Time"計數器、無實例,那麼這個地址為: "\System\System Up Time" 。
// 2.創建計數器PdhAddCounter
// 3.分配接收描述信息的緩沖區
// 4.獲取描述信息
*/
// 以下是程序實現:
__try
{
// 創建計數器
pdhStatus = PdhAddCounter ( hQuery , _TEXT ( "\\System\\System Up
Time" ) , 0 , pCounterHandle ) ;
if ( pdhStatus != ERROR_SUCCESS )
{
__leave ;
}
DWORD dwCounterBuff ;
BYTE byCounterBuff [ sizeof ( PDH_COUNTER_INFO ) + sizeof ( TCHAR
) * 2048 ] ;
dwCounterBuff = sizeof ( byCounterBuff ) ;
pdhStatus = PdhGetCounterInfo ( * pCounterHandle , TRUE
, & dwCounterBuff , ( PPDH_COUNTER_INFO ) byCounterBuff ) ;
if ( pdhStatus != ERROR_SUCCESS )
{
__leave ;
}
PDH_COUNTER_INFO pdhCounterInfo = * ( PPDH_COUNTER_INFO ) byCounterBuff
;
// 有關PDH_COUNTER_INFO結構的信息請參閱MSDN
// PDH_COUNTER_INFO結構中包含了很多關於計數器的信息,其中szExplainText為計數器描述信息
// pdhCounterInfo.szExplainText
}
__finally
{
if ( AbnormalTermination () )
{
// 如果失敗
}
else
{
// 如果成功
}
}
/*