VC下使用 Windows 的效能計數器簡介
作者:wlzqin
下載原始碼
前言
Microsoft Windwos NT/2000 提供了一個強大的API集來訪問系統事件和效能資料的眾多計數器。我們既可以即時地得到計數器的值,也可以從一個記錄檔中讀取計數器資料。功能可為強大,而且使用簡單 。下面我就簡單談談在VC中如何使用Windows的效能計數器。好,廢話少說,我們開始。
我們用一個簡單的例子來說明效能計數器的使用方法。比如:我們如何擷取當前正在啟動並執行某個進程的CPU使用率呢?你一定會說:“這還不簡單,方法有很多”。當然,我承認這個不難,而且的確有很多方法。但是哪種方法最簡單?效率最高呢?我猜大概是使用效能計數器了。
要使用效能計數器的基本步驟是:
- 開啟計數器 PdhOpenQuery;
- 為計數器控制代碼分配空間;
- 把感興趣的計數器添加進來 PdhAddCounter;
- 收集資料 PdhCollectQueryData;
- 得到計數器的數值 PdhGetFormattedCounterValue;
- 關閉計數器 PdhCloseQuery;
下面是用代碼實現的步驟:
第一步:
在標頭檔中
#include <"Pdh.h">
在實現檔案中,
#pragma comment ( lib , "Pdh.lib" )
第二步:開啟計數器,並給計數器控制代碼分配空間
HQUERYhQuery = 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_COUNTERVALUEfmtValue ;DWORDdwctrType ;__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必須為NULLszMachineName , // [IN]機器名。本地機器為NULLszObjectListBuffer , // [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必須為NULLszMachineName , // [IN]機器名。本地機器為NULLpctCounter , // [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
枚舉計數器對象的基本步驟是:
- 擷取計數器對象列表大小;
- 為計數器列表分配緩衝區;
- 開始杖舉;
以下是編程實現:
第一步:擷取計數器對象列表大小
LPTSTRszObjectListBuffer = NULL ;DWORDdwObjectListSize= 0 ;LPTSTRszThisObject= NULL ;__try{// 第一次調用該函數獲得接收效能計數器對象列表的緩衝區大小pdhStatus = PdhEnumObjects (NULL , // [IN]資料來源,NT4.0必須為NULLNULL , // [IN]機器名。本地機器為NULLszObjectListBuffer , // [OUT]接收計數器列表的緩衝區,如果計數器列表長度為0,則該項為空白& dwObjectListSize , // [IN/OUT]設定或接收計數器列表長度PERF_DETAIL_WIZARD ,// 擷取資訊的層級// PERF_DETAIL_NOVICE 初級層級 // PERF_DETAIL_ADVANCE 進階層級(包含初級)// PERF_DETAIL_EXPERT 專家層級(包含初級和進階)// PERF_DETAIL_WIZARD 系統層級(包含所有層級)true ) ;if ( pdhStatus != ERROR_SUCCESS ){__leave ;}// 根據得到的緩衝區大小分配計數器對象列表緩衝區記憶體szObjectListBuffer = ( LPTSTR ) malloc ( ( dwObjectListSize* sizeof ( TCHAR ) ) ) ;if ( szObjectListBuffer == NULL ){__leave ;}// 第二次調用該函數獲得計數器對象pdhStatus = PdhEnumObjects (NULL , // [IN]資料來源,NT4.0必須為NULLNULL , // [IN]機器名。本地機器為NULLszObjectListBuffer , // [OUT]接收計數器列表的緩衝區,如果計數器列表長度為0,則該項為空白& dwObjectListSize , // [IN/OUT]設定或接收計數器列表長度PERF_DETAIL_WIZARD ,// 擷取資訊的層級// PERF_DETAIL_NOVICE 初級層級 // PERF_DETAIL_ADVANCE 進階層級(包含初級)// PERF_DETAIL_EXPERT 專家層級(包含初級和進階)// PERF_DETAIL_WIZARD 系統層級(包含所有層級)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{// 如果成功}}
至此,關於效能計數器的簡單介紹到此完畢。前面囉裡囉唆說一大串,主要是考慮到剛接觸VC不久的朋友,如果本文能對他們有協助我將不勝榮幸。略有VC編程經驗的人肯定對此文嗤之以鼻,希望看在廣大初學者的份上(包括我)請不要言語攻擊我。希望有經驗的朋友多提寶貴意見、多斧正。
最後說明:
- 本文採用的是UNICODE編碼,其中用到了一些宏,如果要不加修改直接通過編譯請在編譯器中選擇UNICODE編碼,並在標頭檔中添加 #include (略作修改就可在ANSI編碼下運行);
- 本文用到的__try {} __finally {} 只是結構化異常處理SEH,可以不要;
- 需要本文例子工程及原始碼的朋友請到我的首頁上來下載 www.Clock.5888.com;
- 效能計數器只能用在2000/Xp系統(2003沒試過);
- 本文代碼編譯環境 Windows 2000 + VC.net;
- 歡迎轉載,轉載請註明文章作者和出處;