分享
 
 
 

vc下使用windows的性能计数器简介

王朝vc·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

/* vc下使用windows的性能计数器简介 */

/* 作者:wlzqin */

/*

Microsoft Windwos NT/2000 提供了一个强大的API集来访问系统事件和性能数据的众多计数器。我们既可以实时地得到计数器的值,也可以从一个日志文件中读取计数器数据。功能可为强大,而且使用简单。

下面我就简单谈谈在vc中如何使用windows的性能计数器。好,废话少说,我们开始:

我们用一个简单的例子来说明性能计数器的使用方法。

比如:我们如何获取当前正在运行的某个进程的CPU使用率呢?你一定会说:“这还不简单,方法有很多”。当然,我承认这个不难,而且的确有很多方法。但是哪种方法最简单?效率最高呢?我猜大概

是使用性能计数器了。

*/

// 要使用性能计数器的基本步骤是:

// 1.打开计数器PdhOpenQuery;

// 2.为计数器句柄分配空间;

// 3.把感兴趣的计数器添加进来PdhAddCounter;

// 4.收集数据PdhCollectQueryData

// 4.得到计数器的数值PdhGetFormattedCounterValue;

// 5.关闭计数器PdhCloseQuery。

// 下面是用代码实现的步骤

// 第一步:

// 在头文件中

#include <Pdh.h>

// 在实现文件中

#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 (

NULL , // [IN]数据源,NT4.0必须为NULL

NULL , // [IN]机器名。本地机器为NULL

szObjectListBuffer , // [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必须为NULL

NULL , // [IN]机器名。本地机器为NULL

szObjectListBuffer , // [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编程经验的人肯定对此文嗤之以鼻,希望看在广大初学者的份上(包括我)请不要言语攻击我。

希望有经验的朋友多提宝贵意见、多斧正。

最后说明:

1.本文采用的是UNICODE编码,其中用到了一些宏,如果要不加修改直接通过编译请在编译器中选择UNICODE编码,并在头文件中添加#include <TChar.h> (略作修改就可在ANSI编码下运行)

2.本文用到的__try {} __finally {} 只是结构化异常处理SEH,可以不要。

3.需要本文例子工程及源代码的朋友请到我的主页上来下载 www.Clock.5888.com

4.性能计数器只能用在2000/Xp系统(2003没试过)

5.本文代码编译环境 Windows 2000 + VC.net

6.欢迎转载,转载请注明文章作者和出处。

*/

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有