发布新日志

  • 网站性能测试指标

    2009-03-24 21:42:40

    通用指标(指Web应用服务器、数据库服务器必需测试项)

    指标

    说明

    ProcessorTime 服务器CPU占用率,一般平均达到70%时,服务就接近饱和
    Memory Available Mbyte 可用内存数,如果测试时发现内存有变化情况也要注意,如果是内存泄露则比较严重
    Physicsdisk Time 物理磁盘读写时间情况

    Web服务器指标

    指标

    说明

    Requests Per Second(Avg Rps) 平均每秒钟响应次数=总请求时间 / 秒数
    Avg time to last byte per terstion (mstes) 平均每秒业务脚本的迭代次数 ,有人会把上面那个混淆
    Successful Rounds 成功的请求
    Failed Requests 失败的请求
    Successful Hits 成功的点击次数
    Failed Hits 失败的点击次数
    Hits Per Second 每秒点击次数
    Successful Hits Per Second 每秒成功的点击次数
    Failed Hits Per Second 每秒失败的点击次数
    Attempted Connections 尝试链接数

    数据库服务器性能指标

    指标

    说明

    User 0 Connections 用户连接数,也就是数据库的连接数量
    Number of deadlocks 数据库死锁
    Butter Cache hit 数据库Cache的命中情况

    系统的瓶颈定义

    性能项

    命令

    指标

    CPU限制 vmstat 当%user+%sys超过80%时
    磁盘I/O限制 Vmstat 当%iowait超过40%(AIX4.3.3或更高版本)时
    应用磁盘限制 Iostat 当%tm_act超过70%时
    虚存空间少 Lsps,-a 当分页空间的活动率超过70%时
    换页限制 Iostat, stat 虚存逻辑卷%tm_act超过I/O(iostat)的30%,激活的虚存率超过CPU数量(vmstat)的10倍时
    系统失效 Vmstat, sar 页交换增大、CPU等待并运行队列

    稳定系统的资源状态

    性能项

    资源

    评价

    CPU占用率 70%
    85%
    90%+ 很差
    磁盘I/0 <30%
    <40%
    <50%+ 很差
    网络 <30%带宽
    运行队列 <2*CPU数量
    内存 没有页交换
    每个CPU每秒10个页交换
    更多的页交换 很差

      通俗理解:

      日访问量

      常用页面最大并发数

      同时在线人数

      访问相应时间

      案例:

      最近公司一个项目,是个门户网站,需要做性能测试,根据项目特点定出了主要测试项和测试方案:

      一种是测试几个常用页面能接受的最大并发数(用户名参数化,设置集合点策略)

      一种是测试服务器长时间压力下,用户能否正常操作(用户名参数化,迭代运行脚本)

      一种则需要测试服务器能否接受10万用户同时在线操作,如果是用IIS做应用服务器的话,单台可承受的最大并发数不可能达到10万级,那就必须要使用集群,通过多台机器做负载均衡来实现;如果是用websphere之类的应用服务器的话,单台可承受的最大并发数可以达到10万级,但为性能考虑还是必须要使用集群,通过多台机器做负载均衡来实现;通常有1个简单的计算方式,1个连接产生1个session,每个session在服务器上有个内存空间大小的设置,在NT上是3M,那么10万并发就需要300G内存,当然实际使用中考虑其他程序也占用内存,所以准备的内存数量要求比这个还要多一些。还有10万个用户同时在线,跟10万个并发数是完全不同的2个概念。这个楼上已经说了。但如何做这个转换将10万个同时在线用户转换成多少个并发数呢?这就必须要有大量的历史日志信息来支撑了。系统日志需要有同时在线用户数量的日志信息,还需要有用户操作次数的日志信息,这2个数据的比例就是你同时在线用户转换到并发数的比例。另外根据经验统计,对于1个JAVA开发的WEB系统(别的我没统计过,给不出数据),一般1台双CPU、2G内存的服务器上可支持的最大并发数不超过500个(这个状态下大部分操作都是超时报错而且服务器很容易宕机,其实没什么实际意义),可正常使用(单步非大数据量操作等待时间不超过20秒)的最大并发数不超过300个。假设你的10万同时在线用户转换的并发数是9000个,那么你最少需要这样的机器18台,建议不少于30台。当然,你要是买个大型服务器,里面装有200个CPU、256G的内存,千兆光纤带宽,就算是10万个并发用户,那速度,也绝对是嗖嗖的。

      另外暴寒1下,光设置全部进入运行状态就需要接近6个小时。具体的可以拿1个系统来压一下看看,可能会出现以下情况:

      1、服务器宕机;

      2、客户端宕机;

      3、从某个时间开始服务器拒绝请求,客户端上显示的全是错误;

      4、勉强测试完成,但网络堵塞或测试结果显示时间非常长。假设客户端和服务器之间百兆带宽,百兆/10000=10K,那每个用户只能得到10K,这个速度接近1个64K的MODEM上网的速度;另外以上分析全都没考虑系统的后台,比如数据库、中间件等。

      1、服务器方面:上面说的那样的PC SERVER需要50台;

      2、网络方面:按每个用户50K,那至少5根百兆带宽独享,估计仅仅网络延迟就大概是秒一级的;

      3、如果有数据库,至少是ORACLE,最好是SYSBASE,SQL SERVER是肯定顶不住的。数据库服务器至少需要10台4CPU、16G内存的机器;

      4、如果有CORBA,那至少再准备10台4CPU、16G内存的机器;再加上负载均衡、防火墙、路由器和各种软件等,总之没个1000万的资金投入,肯定搞不定。

      这样的门户系统,由于有用户权限,所以并不象jackie所说大多是静态页面。但只要是多服务器的集群,那么我们就可以通过1台机器的测试结果来计算多台机器集群后的负载能力的,最多额外考虑一下负载均衡和路由上的压力,比如带宽、速度、延迟等。但如果都是在1台机器上变化,那我们只能做一些指标上的计算,可以从这些指标上简单判断一下是否不可行,比如10万并发用户却只有1根百兆带宽,那我们可以计算出每个用户只有1K带宽,这显然是不可行的。但实际的结果还是需要测试了才知道,毕竟系统压力和用户数量不是线性变化的。

      这一类系统的普遍的成熟的使用,以及很多软件在方案设计后就能够大致估算出系统的性能特点,都导致了系统在软件性能方面调优的比例并不大(当然不完全排除后期针对某些代码和配置进行优化后性能的进一步提高),更多的都是从硬件方面来考虑,比如增加内存、硬盘做RAID、增加带宽、甚至增加机器等。

      网络技术中的10M 带宽指的是以位计算, 就是 10M bit /秒 ,而下载时的速度看到的是以字节(Byte)计算的,所以10M带宽换算成字节理论上最快下载速度为: 1.25 M Byte/秒!

  • 如何获取性能需求

    2009-03-06 23:21:36

    本文档是为了给需要了解如何填写性能需求或需要引导客户填写性能需求项目组成员参考性文档(为公司内部人员)。

      1. 系统的性能

      1)概念

      系统的性能是一个很大的概念,覆盖面非常广泛,对一个软件系统而言,包括:执行效率、资源占用、系统稳定性、安全性、兼容性、可靠性、可扩展性。

      2)主要指标

      响应时间、点击数、吞吐量、并发用户数、资源利用率、每秒连接数

      3)不同视角的性能

      * 用户角度

      响应时间(最关心的指标)2/5/10原则,过长的时间等待让客户烦燥不安

      系统的稳定性 HTTP500、数据库崩溃、应用服务器崩溃

      * 系统角度

      网络延迟、数据延迟

      系统资源的使用情况 硬件配备情况 软件的配置情况(应用服务器、数据库、系统)

      * 开发角度

      代码实现(算法等)

      数据库实现(数据模型设计、语句的实现方式等)

      2. 我们的目标

      我们的目标是要开发出符合用户的应用系统,其中包括性能的需求并且通过测试其他验证等手段验证系统达到用户的要求。首先要了解客户,向客户了解应用系统运行目标硬件配置、应用环境及网络环境等。了解系统业务逻辑(可能有多种业务)及处理业务量时间分布图。然后由性能测试人员分析以上的数据制定测试策略及测试方法。例如分析出在某月的某一天10:00-11:00是系统处理业务高峰,平均处理50000个A业务量,那么性能测试人员需要根据这个数据设计一个处理业务A测试场景,验证(系统在目标硬件环境)在1个小时内至少能成功处理50000个A业务量。

      3. 用户类型

      * 专业型:熟悉开发应用系统,具有深厚的计算机功底,并且非常清楚系统处理业务量的分布,即能明确的提出具体的性能需求。很遗憾,一般很难遇到这么“牛人”的客户。

      * 普通型:了解开发的应用系统,具有一般系统管理能力。不了解性能指标,不能明确提出具体性能需求。需求项目经理或需求人员引导客户明确性能需求,甚至需要我们开发方为客户制定符合要求的性能需求。

      4. 填写性能需求调研表

      专业型客户很容易向我们提供明确的性能需求,而普通型客户一般最关注的是响应时间(遵循2/5/10原则)、必须支持多少在线用户、应用服务的稳定性(如运行1周或1月服务器后应用服务还能正常提供服务等)。

      为了性能测试更真实的接近系统实现的应用,性能测试人员还必须获取更为详尽的信息。可以从以下三个方面获取:

      * 向项目经理调研信息:如应用系统目标运行硬件配置(此硬件设备是否运行其他服务)、应用环境(是否有其它的服务共用服务)、网络环境、数据库规模等。

      * 向业务经理调研信息:业务量分布图及业务量增长分布图、关键业务量(即场景)

      * 向技术经理调研信息:关键场景交易路径及对数据库操作的动作(见模板)

      根据模板项目经理向客户调研时一般都能很方便获取必要的信息(即使用户不能直接明确提出性能需求),项目组也能很容易制定出符合用户的性能需求。

      接下来的问题是量化性能需求、如何制定性能测试及性能调优……

     

    本文出自oscarli的51Testing软件测试博客:http://www.51testing.com/?67755

  • 软件测试中性能调优的过程解析

    2009-03-06 23:17:34

    性能调优无疑是个庞大的话题,也是很多项目中非常重要的一环,性能调优的难做是众所周知的,毕竟性能调优涵盖的面实在是太多了,在这篇文章中我们蜻蜓点水般的来看看性能调优这项庞大的工程都有些什么过程,同时也看看这些过程中常见的一些做法。

      确定性能调优的目标

      性能调优,首先是要确定性能调优的目标是什么,如果现在应用已经满足了需求,就没必要去做性能调优了,毕竟不经过一个系统的过程,其实是无法确定你所做的性能调整是否真的调优了性能,是否没有造成应用中其他的问题,所以确定性能目标是非常重要的,在定义性能目标的时候通常这么定义的呢:

      1、最大并发数

      2、Quality of Service

      服务的质量,在软件系统方面我们认为主要表现在请求的出错率,系统的load等。

      3、最长响应时间

      对于任何请求所能承受的最大响应时间。

      4、TPS

      每秒需要支持的最大事务数,最典型的指标是:“某页面最高需要支撑每秒7000次的访问次数”。

      例如一个web系统,需要定义出来的目标是:

      并发目标:最高支撑200并发;

      QoS:出错率须控制在万分之一,系统的load最高只能到达10;

      TPS:每秒完成7000次请求的处理;

      最大响应时间:最长允许的响应时间为5秒。

      至于请求的平均响应时间这些就不在性能调优目标中定义,因为要达到TPS的要求,响应时间是必须要达到一个级别的,而且响应时间随着高并发是会出现劣化的。

      当然,还可以把性能指标定到更为细节,例如某个方法的TPS在100并发时需要达到多少。

      在确定好了性能目标后,重要的就是如何来测量系统的性能了。

      测量系统性能

      对于新系统而言,需要评估出其正式运行时的数据量的增长情况;而对于已运行的系统,则需要根据监控获取到系统的运行数据(例如高峰并发数、系统的响应速度情况、系统的load、网络流量、每类请求在总的请求中所占的百分比等)。

      对于新系统而言,要评估出具体的性能相对来说稍微好做一点,因为此时系统通常较为单纯,数据量的增长也不可能是一夜之间增长的,因此基本可以按照一种正常的方法在测试环境评估出其正式运行的性能。

      而对于已运行的系统而言,则较为麻烦,因为通常来讲要在测试环境中模拟正式运行环境基本是不太可能的,因此这个时候通常要采取一些模拟的方法或更高压力的方法来尽量更为准确的评估出系统的性能。

      在测试系统性能时,通常可采用的方法有:

      1、单元测试

      可借助单元测试来测试某个请求的性能;

      2、压力测试;

      压力测试无疑是测量系统性能中最常采用的方式,根据定义的性能目标对系统进行压力测试,以确定系统是否满足性能要求,同时也可以根据压力测试的结果来分析系统的瓶颈,进而进行对应的调优,可用于做压力测试的工具还是不少的,像loadrunner、jmeter等等,不过压力测试这个话题实在太大了,不在这里展开去讲了,不过我也不怎么懂就是,呵呵。

      分析系统性能瓶颈

      根据测量系统性能的结果,多数是可以分析出系统性能的瓶颈,同时还可以结合像jvm堆栈、jprofiler、系统日志等来进行进一步的确定,另外也可以根据性能调优人员的经验,例如可以去了解开发人员是否采用了不适合的数据结构等。

      简单说一个线程分析的例子:

      借助kill -3 pid来获取到目前jvm的线程堆栈信息,特别需要关注的是里面wait for monitor这样的线程,这种线程是指在等待锁的线程,等待一两分钟后再次kill -3 pid,看看这些wait for monitor的线程的变化情况,这对于分析线程中是否存在不合理的竞争过高的锁的分析是非常重要的。

      这一步无疑也是性能调优过程中最难的一步了,分析系统性能瓶颈这种基本只能结合实际例子来讲了,正确在后续抽取一两个例子来进行讲解。

      性能调优

      在分析出系统性能的瓶颈后,其实这一步相对来说还好做些,当然,需要建立在对软硬件知识都有很好的深入了解的基础上,在这里列举一些比较常见的性能调优的手段,多数是抄来或google来的,自己在这方面的经验还不多,希望大家多加指点。

      Redhat Linux内核

      Redhat linux内核版本升级到2.6,2.6和2.4的差别还是很多的,例如对epoll的支持、NPTL的采用;epoll的支持对于java而言也是很重要的,在高并发的情况下nio是否采用epoll还是有挺大的差别的;而NPTL的采用对于多线程程序而言更是极为重要。

      另外需要关注像linux的File Handles是多少、network buffer是多少、MTU是多少、Memory Page size是多少等等。

      JVM

      JVM调优的文章相对来说比较多,大家需要了解的主要是-Xms/-Xmx、并行GC、-XX:MaxPermSize/-XX:MaxNewSize、-XX:ThreadStackSize、NIO采用epoll等等。

      简单的列这两个,其实性能调优的手段还有非常的多,例如简单的增加CPU、买更快速度的硬盘、增加内存、提升网络带宽等这些从硬件角度下手的方式,还有像数据库调优、应用服务器调优等等。

  • loadrunner - winsock 函数

    2008-09-11 22:15:37

    lrs_accept_connection 接受侦听套接字连接


            lrs_close_socket 关闭打开的套接字


            lrs_create_socket 初始化套接字


            lrs_disable_socket 禁用套接字操作


            lrs_exclude_socket 重播期间排除套接字


            lrs_get_socket_attrib 获取套接字属性


            lrs_get_socket_handler 获取指定套接字的套接字处理程序


            lrs_length_receive 接收来自指定长度的缓冲区的数据


            lrs_receive 接收来自套接字的数据


            lrs_receive_ex 接收来自数据报或流套接字的数据(具有特定长度)


            lrs_send 将数据发送到数据报上或流套接字中


            lrs_set_receive_option 设置套接字接收选项


            lrs_set_socket_handler 设置特定套接字的套接字处理程序


            lrs_set_socket_options 设置套接字选项


    缓冲区函数

            lrs_free_buffer 释放分配给缓冲区的内存


            lrs_get_buffer_by_name 从数据文件中获取缓冲区及其大小


            lrs_get_last_received_buffer 获取套接字上接收到的最后的缓冲区及其大小


            lrs_get_last_received_buffer_size 获取套接字上接收到的最后一个缓冲区的大小


            lrs_get_received_buffer 获取最后接收到的缓冲区或其一部分


            lrs_get_static_buffer 获取静态缓冲区或其一部分


            lrs_get_user_buffer 获取套接字的用户数据的内容


            lrs_get_user_buffer_size 获取套接字的用户数据的大小


            lrs_set_send_buffer 指定要在套接字上发送的缓冲区


    环境函数

            lrs_cleanup 终止 Windows 套接字 DLL 的使用


            lrs_startup 初始化 Windows 套接字 DLL


    关联语句函数

            lrs_save_param 将静态或接收到的缓冲区(或缓冲区部分)保存到参数中


            lrs_save_param_ex 将用户、静态或接收到的缓冲区(或缓冲区部分)保存到参数中


            lrs_save_searched_string 在静态或接收到的缓冲区中搜索出现的字符串,将出现字符串的缓冲区部分保存到参数中


    转换函数

            lrs_ascii_to_ebcdic 将缓冲区数据从 ASCII 格式转换成 EBCDIC 格式


            lrs_decimal_to_hex_string 将十进制整数转换为十六进制字符串


            lrs_ebcdic_to_ascii 将缓冲区数据从 EBCDIC 格式转换成ASCII 格式


            lrs_hex_string_to_int 将十六进制字符串转换为整数


    超时函数

            lrs_set_accept_timeout 为接受套接字设置超时


            lrs_set_connect_timeout 为连接到套接字设置超时


            lrs_set_recv_timeout 为接收套接字上的初始预期数据设置超时


            lrs_set_recv_timeout 为建立连接后接收套接字上的预期数据设置超时


            lrs_set_send_timeout 为发送套接字数据设置超时


            录制会话之后,通过 VuGen 的内置编辑器可以查看录制的代码。您可以在脚本中滚动,查看应用程序生成的函数,并检查传输的数据。在主窗口中查看脚本时,可以看到VuGen 录制活动的顺序。在典型的会话期间,将录制下列函数顺序:


            lrs_startup 初始化 WinSock DLL


            lrs_create_socket 初始化套接字


            lrs_send 在数据报上或者向流套接字发送数据


            lrs_receive 接收来自数据报或流套接字的数据


            lrs_disable_socket 禁用套接字操作


            lrs_close_socket 关闭打开的套接字


            lrs_cleanup 终止 WinSock DLL 的使用

            VuGen 在 Windows 上使用 Windows 套接字协议支持应用程序的录制和重播;而在UNIX 平台上仅支持重播。

  • 深入浅出HOOKS

    2008-09-11 21:08:01

    hook是WINDOWS提供的一种消息处理机制,它使得程序员可以使用子过程来监视系统消息,并在消息到达目标过程前得到处理。

    下面将介绍WINNDOWS HOOKS并且说明如何在WINDOWS 程序中使用它。

    使用HOOK 将会降低系统效率,因为它增加了系统处量消息的工作量。建议在必要时才使用HOOK,并在消息处理完成后立即移去该HOOK。

    HOOK链

    WINDOWS提供了几种不同类型的HOOKS;不同的HOOK可以处理不同的消息。例如,WH_MOUSE HOOK用来监视鼠标消息。

    WINDOWS为这几种HOOKS维护着各自的HOOK链。HOOK链是一个由应用程序定义的回调函数队列,当某种类型的消息发生时,WINDOWS向此种类型的HOOK链的第一个函数发送该消息,在第一函数处理完该消息后由该函数向链表中的下一个函数传递消息,依次向下。如果链中某个函数没有向下传送该消息,那么链表中后面的函数将得不到此消息。(对于某些类型的HOOK,不管HOOK链中的函数是否向下传递消息,与此类型HOOK联系的所有HOOK函数都会收到系统发送的消息)

    ===========================HOOK过程========================

    为了拦截特定的消息,你可以使用SetWindowsHookEx函数在该类型的HOOK链中安装你自己的HOOK函数。该函数语法如下:

    public function MyHook(nCode,wParam,iParam) as long

    ‘加入代码

    end function

    其中MyHook可以随便命名,其它不能变。该函数必须放在模块段。nCode指定HOOK类型。wParam,iParam的取值随nCode不同而不同,它代表了某种类型的HOOK的某个特定的动作。

    SetWindowsHookEx总是将你的HOOK函数放置在HOOK链的顶端。你可以使用CallNextHookEx函数将系统消息传递给HOOK链中的下一个函数。

    [注释]对于某些类型的HOOK,系统将向该类的所有HOOK函数发送消息,这时,HOOK函数中的CallNextHookEx语句将被忽略。 

    全局HOOK函数可以拦截系统中所有线程的某个特定的消息(此时该HOOK函数必须放置在DLL中),局部HOOK函数可以拦截指定线程的某特定消息(此时该HOOK函数可以放置在DLL中,也可以放置在应用程序的模块段)。

    [注释] 建议只在调试时使用全局HOOK函数。全局HOOK函数将降低系统效率,并且会同其它使用该类HOOK的应用程序产生冲突。

    ========================HOOK类型=================================

    WH_CALLWNDPROC 和 WH_CALLWNDPROCRET HOOK

    WH_CALLWNDPROC 和WH_CALLWNDPROCRET HOOK可以监视SendMessage发送的消息。系统在向窗体过程发送消息前,将调用WH_CALLWNDPROC;在窗体过程处理完该消息后系统将调用WH_CALLWNDPROCRET。

    WH_CALLWNDPROCRET HOOK会向HOOK过程传送一个CWPRETSTRUCT结构的地址。该结构包含了窗体过程处理系统消息后的一些信息。

    WH_CBT Hook

    系统在激活,创建,消毁,最小化,最大化,移动,改变窗体前;在完成一条系统命令前;在从系统消息队列中移去鼠标或键盘事件前;在设置输入焦点前,或同步系统消息队列前,将调用WH_CBT HOOK。你可以在你的HOOK 过程拦截该类HOOK,并返回一个值,告诉系统,是否继续执行上面的操作。

    WH_DEBUG HOOK

    系统在调用与某种HOOK类型联系的HOOK过程前,将调用WH_DEBUG ,应用程序可以使用该HOOK决定是否让系统执行某种类型的HOOK。

    WH_FOREGROUNDIDLE Hook

    系统在空闲时调用该HOOK,在后台执行优先权较低的应用程序。

    WH_GETMESSAGE Hook

    WH_GETMESSAGE Hook使应用程序可以拦截GetMessage 或 PeekMessage的消息。应用程序使用WH_GETMESSAGE HOOK监视鼠标、键盘输入和发送到队列中的其它消息。

    WH_JOURNALRECORD Hook

    WH_JOURNALRECORD Hook使应用程序可以监视输入事件。典型地,应用程序使用该HOOK记录鼠标、键盘输入事件以供以后回放。该HOOK是全局HOOK,并且不能在指定线程中使用。

    WH_JOURNALPLAYBACK Hook

    ` WH_JOURNALPLAYBACK Hook使应用程序可以向系统消息队列中插入消息。该HOOK可以回放以前由WH_JOURNALRECORD HOOK录制的鼠标、键盘输入事件。在WH_JOURNALPLAYBACK Hook安装到系统时,鼠标、键盘输入事件将被屏蔽。该HOOK同样是一个全局HOOK,不能在指定线程中使用。

    WH_JOURNALPLAYBACK Hook返回一个时间暂停值,它告诉系统,在处理当前回放的消息时,系统等待百分之几秒。这使得此HOOK可以控制在回放时的时间事件。

    WH_KEYBOARD Hook

    WH_KEYBOARD Hook使应用程序可以监视由GetMessage和PeekMessage返回的WM_KEYDOWN 及WM_KEYUP消息。应用程序使用该HOOK监视发送到消息队列中的键盘输入。

    WH_MOUSE Hook

    WH_MOUSE Hook 使应用程序可以监视由GetMessage和PeekMessage返回的消息。应用程序使用该HOOK监视发送到消息队列中的鼠标输入。

    WH_MSGFILTER and WH_SYSMSGFILTER Hooks

    WH_MSGFILTER 和WH_SYSMSGFILTER Hooks使应用程序可以监视菜单、滚动条、消息框、对话框,当用户使用ALT+TAB或ALT+ESC来切换窗体时,该HOOK也可以拦截到消息。WH_MSGFILTER仅在应用程序内部监视菜单、滚动条、消息框、对话框,而WH_SYSMSGFILTER则可以在系统内监视所有应用程序的这些事件。

    WH_SHELL Hook

    一个SHELL程序可以使用WH_SHELL Hook来接收重要的信息。当一个SHELL程序被激活前或当前窗体被创建、消毁时,系统会调用WH_SHELL Hook过程。

    =======================使用HOOK============================

    安装、消毁HOOK过程

    监视系统事件

    安装、消毁HOOK过程

    使用SetWindowsHookEx函数,指定一个HOOK类型,自己的HOOK过程是全局还是局部HOOK,同时给出HOOK过程的进入点,就可以轻松的安装你自己的HOOK过程。

    为了安装一个全局HOOK过程,必须在应用程序外建立一个DLL,并将该HOOK函数封装到其中,应用程序在安装全局HOOK过程时必须先得到该DLL模块的句柄。将DLL名传递给LoadLibrary 函数,就会得到该DLL模块的句柄;得到该句柄 后,使用GetProcAddress函数可以得到HOOK过程的地址。最后,使用SetWindowsHookEx将HOOK过程的首址嵌入相应的HOOK链中,SetWindowsHookEx传递一个模块句柄,它为HOOK过程的进入点,线程标识符置为0,指出:该HOOK过程同系统中的所有线程关联。

    以下是C写的例程,大家可以方便的转换为VB程序。

    HOOKPROC hkprcSysMsg; 

    static HINSTANCE hinstDLL; 

    static HHOOK hhookSysMsg; 







    hinstDLL = LoadLibrary((LPCTSTR) "c:\\windows\\sysmsg.dll"); 

    hkprcSysMsg = (HOOKPROC)GetProcAddress(hinstDLL, "SysMessageProc"); 

    hhookSysMsg = SetWindowsHookEx(WH_SYSMSGFILTER, 

    hkprcSysMsg, hinstDLL, 0); 

     
    Hook简介

    2000-03-18· -·cww

      Hook这个东西有时令人又爱又怕,Hook是用来拦截系统某些信息之用,例如说,我们想 让系统不管在什麽地方只要按个Ctl-B便执行NotePad,或许您会使用Form的KeyPreview,设定为True,但在其他Process中按Ctl-B呢?那就没有用,这时就得设一个Keyboard Hook来拦截所有Key in的键;再如:MouseMove的Event只在该Form或Control上有效,如 果希望在Form的外面也能得知Mouse Move的信息,那只好使用Mouse Hook来栏截Mouse 的信息。再如:您想记录方才使用者的所有键盘动作或Mosue动作,以便录巨集,那就使用JournalRecordHook,如果想停止所有Mosue键盘的动作,而放(执行)巨集,那就使用JournalPlayBack Hook;Hook呢,可以是整个系统为范围(Remote Hook),即其他 Process的动作您也可以拦截,也可以是LocalHook,它的拦截范围只有Process本身。Remote Hook的Hook Function要在.Dll之中,Local Hook则在.Bas中。

    在VB如何设定Hook呢?使用SetWindowsHookEx()

    Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA"(ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long

    idHook代表是何种Hook,有以下几种

    Public Const WH_CALLWNDPROC = 4

    Public Const WH_CALLWNDPROCRET = 12

    Public Const WH_CBT = 5

    Public Const WH_DEBUG = 9

    Public Const WH_FOREGROUNDIDLE = 11

    Public Const WH_GETMESSAGE = 3

    Public Const WH_HARDWARE = 8

    Public Const WH_JOURNALPLAYBACK = 1

    Public Const WH_JOURNALRECORD = 0

    Public Const WH_KEYBOARD = 2

    Public Const WH_MOUSE = 7

    Public Const WH_MSGFILTER = (-1)

    Public Const WH_SHELL = 10

    Public Const WH_SYSMSGFILTER = 6

    pfn代表Hook Function所在的Address,这是一个CallBack Fucnction,当挂上某个Hook时,我们便得定义一个Function来当作某个信息产生时,来处理它的Function,这个Hook Function有一定的参数格式
    Private Function HookFunc(ByVal nCode As Long,ByVal wParam As Long, ByVal lParam As Long ) As Long

    nCode 代表是什麽请况之下所产生的Hook,随Hook的不同而有不同组的可能值wParam lParam 传回值则随Hook的种类和nCode的值之不同而不同。

    因这个参数是一个 Function的Address所以我们固定将Hook Function放在.Bas中,并以AddressOf HookFunc传入。至于Hook Function的名称我们可以任意给定,不一定叫HookFunchmod 代表.DLL的hInstance,如果是Local Hook,该值可以是Null(VB中可传0进去),而如果是Remote Hook,则可以使用GetModuleHandle(".dll名称")来传入。

    dwThreadId 代表执行这个Hook的ThreadId,如果不设定是那个Thread来做,则传0(所以 一般来说,Remote Hook传0进去),而VB的Local Hook一般可传App.ThreadId进去值回值 如果SetWindowsHookEx()成功,它会传回一个值,代表目前的Hook的Handle, 这个值要记录下来。

    因为A程序可以有一个System Hook(Remote Hook),如KeyBoard Hook,而B程序也来设一 个Remote的KeyBoard Hook,那麽到底KeyBoard的信息谁所拦截?答案是,最後的那一个 所拦截,也就是说A先做keyboard Hook,而後B才做,那信息被B拦截,那A呢?就看B的 Hook Function如何做。如果B想让A的Hook Function也得这个信息,那B就得呼叫 CallNextHookEx()将这信息Pass给A,於是产生Hook的一个连线。如果B中不想Pass这信息给A,那就不要呼叫CallNextHookEx()。

    Declare Function CallNextHookEx Lib "user32" Alias "CallNextHookEx" _

    (ByVal hHook As Long, _

    ByVal ncode As Long, _

    ByVal wParam As Long, _

    lParam As Any) As Long

    hHook值是SetWindowsHookEx()的传回值,nCode, wParam, lParam则是Hook Procedure 中的三个参数。

    最後是将这Hook去除掉,请呼叫UnHookWindowHookEx()

    Declare Function UnhookWindowsHookEx Lib "user32" Alias "UnhookWindowsHookEx" _

    (ByVal hHook As Long) As Long

    hHook便是SetWindowsHookEx()的传回值。此时,以上例来说,B程序结束Hook,则换A可 以直接拦截信息。

    KeyBoard Hook的范例

    Hook Function的三个参数

    nCode wParam lParam 传回值 

    HC_ACTION或HC_NOREMOVE 表按键Virtual Key 与WM_KEYDOWN同 若信息要被处理传0 反之传1

    Public hHook as Long

    Public Sub UnHookKBD()

    If hnexthookproc <> 0 Then

    UnhookWindowsHookEx hHook

    hHook = 0

    End If

    End Sub

    Public Function EnableKBDHook()

    If hHook <> 0 Then Exit Function

    hhook = SetWindowsHookEx(WH_KEYBOARD, AddressOf MyKBHFunc, App.hInstance, App.ThreadId)

    End Function

    Public Function MyKBHFunc(ByVal iCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

    MyKBHfunc = 0 '表示要处理这个信息

    If wParam = vbKeySnapshot Then '侦测 有没有按到PrintScreen键

    MyKBHFunc = 1 '在这个Hook便吃掉这个信息

    End If

    Call CallNextHookEx(hHook, iCode, wParam, lParam) '传给下一个Hook

    End Function

      至於其他的 Hook的详细资料与nCode,wParam, lParam的意义,请查Win32 Help

       如何获得密码窗口的内容------揭开Hook的面纱 

     

                                              Pan Ying(zero world)

     

      现在的不少程序都有取得密码窗口的内容的功能,你有没有想过是如何做到这一切的呢?我有一个同学就问过我这样一个问题,当时我也回答不出来,现在我研究了视窗的Hook函数之后总算有了一定的了解。下面就有我来揭开Hook函数的神秘面纱。 

     
      先来介绍Hook的含义: 

      Hook是指在程序正常运行中接受信息之前预先启动的函数,用来检查和修改传给该程序的信息。举例来说,当你在一个窗口上点击鼠标以后,首先接收信息的是相应的Hook函数,然后才传到相应的应用程序。 

       如何定义Hook函数: 

    SetWindowsHookEx: 

      用来装入新的Hook函数,其参数列表为下: 

    int idHook:装入Hook类型,有WH_MOUSE 等。 

    HOOKPROC lpfn:要装入的Hook函数地址。 

    HINSTANCE hMod:函数所在的进程,如果为全局Hook函数该函数必须在Dll中。 

    DWORD dwThreadId:装入哪个进程,如果为全局,则为0。 

      返回相应Hook标识。 

    HookProc: 

      您自定义的Hook函数,应具有的参数如下: 

    int nCode:传入的信息类型。 

    WPARAM wParam:短整型参数。 

    LPARAM lParam:长整型参数。 

      要在函数中调用CallNextHookEx把信息传给下一个Hook函数。 

    CallNextHookEx: 

      用于将信息传给下一个Hook函数,需要将得到的Hook标识和相应参数传入。 

    UnhookWindowsHookEx: 

      用于将装入的Hook函数从Hook链表中剔除,应传入得到的Hook标识。 

    下面我们来看一个例子: 

      例子原码的下载:HookTest.zip 本程序在Window98和Delphi5下通过。 

     一、调用LoadLibrary和GetProcAddress取得函数地址。 

      hinstDLL := LoadLibrary(LPCTSTR( 'hooktest.dll')); 

      @hkprcSysMsg:=GetProcAddress(hinstDLL, 'MouseProc'); 

    二、用SetWindowsHookEx将得到的函数装入。 

      hhk:= SetWindowsHookEx(WH_MOUSE,@hkprcSysMsg,hinstDLL, 0); 

    三、Windows会将函数装入到每一个进程中。 

    四、每当鼠标点击,自定义的Hook函数会将点击的窗口标题传给程序的标题。 

    if (nCode=HC_ACTION)and(WPARAM=WM_LBUTTONDOWN) 

     then 

      begin 

       MyMouse:=CPointer(lPARAM); 

       MyHandle2:=MyMouse^.hwnd; 

       GetMem(MyString,GetWindowTextlength(myhandle2)+1); 

       GetWindowText(MyHandle2,MyString,GetWindowTextlength(myhandle2)+1); 

       TempHandle:=myhandle2; 

       while (TempHandle<>0) do 

       begin 

        myHandle2:=TempHandle; 

        TempHandle:=GetParent(TempHandle); 

       end; 

       if (myhandle2<>0) 

       then 

        SetWindowText(myhandle2,MyString); 

       FreeMem(MyString); 

      end 

    就这样完成了得到密码窗口内容的功能。 



    关于 HOOK 
    [ 作者: 陆麟   添加时间: 2001-6-2 0:04:44 ]
    来源:lu0.126.com

    大家讨论HOOK太多了.在网络中,概凡谈论到进程控制,十有八九最后会得到一句话:写HOOK. 
    嘿嘿,知道HOOK运作机理的有几个呢?下面,本人就MSDN文档中没有写到的一点东西稍微讲几句. 

    以下讲述乃针对全局HOOK而发. 

    1.设置HOOK过程时返回的前一HOOK地址必须被保存到一个全局共享的内存地址段中.这个共享段的地址不是什么本进程的全局变量,而是所有进程都可以看见的变量.因为,进程级变量进在本进程内可见.当其他进程加载HOOK DLL时,HOOK DLL里的所有变量都会被RESET.这也就是说: 

    HHOOK hk; 

    //set and get HHOOK here 

    return hk(); 

    这样的描述是不能跳转到前一HOOK的.这一点,甚至在Jeffrey Richter的经典书籍<>里都描述错了. 

    正确的做法是: 

    #pragma data_seg("dt") 

    HHOOK prehook=0; 

    #pragma data_seg() 

    然后到VC的LINK OPITION里加上: 

    -SECTION:dt,RWS 

    这样,prehook就被搞到系统中被共享了.记住,一定要给prehook初始化.否则,MS编译器的编译器会LINK错误. 

    2.只有使用USER32.DLL的进程才会被INJECT.所以,HOOK并不是万能的.而且,用了USER32.DLL,也不一定会被INJECT.这里有个很好的例子就是整个OS启动中第一个被启动的WIN32进程:KERNEL32.DLL.大家很奇怪,KERNEL32.DLL是个DLL,怎么也会被作为进程加载?但是事实的确是这样的,顺便给大家再上一节98启动课吧.KERNEL32.DLL作为一个独立的进程,启动时加载了MSGSRV32.EXE.而MSGSRV32.EXE又启动了SPOOL32.EXE, SPOOL32.EXE启动了MPREXE.EXE.MPREXE.EXE可不能小看.我敢担保全中国没几个人真正知道它的作用的.MPREXE.EXE不仅是网络客户端部件启动的核心,更是WIN98的SERVICE的SCM.所有的WIN98的SERVICE都是由MPREXE.EXE启动的.WIN98的SERVICE都在HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices里呆者哩.大家都傻眼了吧.:DDD 有一点很令人奇怪, 那就是如果MPREXE.EXE运作不正常,那么SHELL绝对起不来.SHELL却是有MSGSRV32.EXE启动的.看来MPREXE和MSGSRV32有一套内部沟通机制啊.有了SHELL,就什么都有了.其他的东西被SHELL启动就难说准了.反正80%的程序是由SHELL启动的.好了,WIN98启动暂且讲到这里.我们继续原先的话题.KERNEL32.DLL居然就无法用HOOK入侵.大家如果不信的话,就试试看吧. 

    3.尽管使用USER32.DLL的进程会被INJECT. 但是这里还有一个技巧,那就是HOOK DLL是在进程第一次发出USER32调用的时候才被加载. 大家又目瞪口呆了吧.:) 这也就是说,在你发出USER32调用之前, HOOK DLL根本拿你没办法. 哇,真够幽默啊.:)))由于在启动HOOK前的进程绝对都是在调用GetMessage(...)/PeekMessage(...)中,那么HOOK DLL一下子满足了加载条件了. 

     
    4.加载HOOK时,HMODULE一定要正确,否则,系统就无法正确加载HOOK过程.千万不要用0代替HMODULE,因为0代表的是EXE映象的HINSTANCE(其实就是HMODULE). 

    好了.今天就写到这里.此文该算本主页里又一篇经典了吧.:) 

    利用VB建立鼠标键盘操作回放

        很多的教学软件或系统监视软件可以自动记录回放用户的输入文字或点击按钮等操作操作,这个功能的实现是使用

    了Windows的Hook函数。本文介绍如何通过使用VB来实现鼠标键盘操作的纪录和回放。

        Windows提供API函数SetwindowsHookEx来建立一个Hook,通过这个函数可以将一个程序添加到Hook链中监视Windows

    消息,函数语法为:

        Public Declare Function SetWindowsHookEx Lib "user32" _



    用消息拦截技术制作系统日志

     

    康帕斯(中国)国际信息服务有限公司 马文骞  

    01-6-7 下午 03:33:05

     

    --------------------------------------------------------------------------------

     

     

    能够完整记录电脑使用情况的日志文件在 Windows系统安全管理方面的作用是不可低估的。本文介绍了利用消息拦截技术制作日志文件的方法,其中的关键函数是一个未公开的 API系统调用。 

     

     

    一、利用钩子(Hook)拦截系统消息 

     

    日志文件对于一个大企业内部网络的维护与管理是至关重要的。另外还有许多其它场合也离不开日志的使用,例如:多人共享一台电脑,或在家庭中要记录儿童使用电脑的细节,等等。 

     

    日志程序若想完整记录电脑运行期间有哪些软件启动过、各使用了多长时间、以及使用浏览器访问互联网的情况等,必须对系统级消息进行拦截。RegisterShellHook是一个未公开的 API系统函数,它可以帮助日志程序在整个 Windows系统范围内感知到其它窗体的创建、激活或关闭等消息,而且不要求这些窗体与日志程序有父子关系,哪怕是 Windows最高级别的窗体也可以。RegisterShellHook 调用方法为: 

     

    Public Declare Function RegisterShellHook Lib "Shell32" Alias "#181" _ 

    (ByVal hwnd As Long, ByVal nAction As Long) As Long 

     

    其中参数hwnd为日志程序的句柄,参数 nAction为所要进行操作的代码。具体的调用细节参见下面的例子及其注释。 

     

     

    二、将日志程序隐藏起来 

     

    把日志程序的Visible属性设为False当然是必要的一步。然后是 ShowInTaskbar属性也设为 False,以便其在 Windows的任务栏中不出现。最后,为了在 CTRL+ALT+DEL 所弹出的列表中隐藏日志程序,需要调用RegisterServiceProcess函数: 

     

    Public Declare Function RegisterServiceProcess Lib "kernel32" _ 

    (ByVal dwProcessID As Long, ByVal dwType As Long) As Long 

     

    其中参数dwType是操作代码,值“1”表示从CTRL+ALT+DEL列表中去除,值“0”表示在列表中恢复;参数 dwProcessID是要在列表中去除或恢复的进程标识,可以用GetCurrentProcessId() API 函数得到日志程序的进程标识,也可以用更简便的方法,即把 dwProcessID参数置为空值,其含义是用当前程序的进程标识作为参数(见下例)。 

     

    另外,为了让日志程序在 Windows每次启动时都能自动运行,需要修改注册表,即在注册表的下述位置新建一个以日志程序的路径及名称为值的“串值”: 

     

    \HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run 

     

    此外,产生的日志文件也应妥为隐藏,最好用 Winsock控件随时向服务器传送。 

    为了简洁,下面的例子仅将日志文件放在了根目录,并且略去了用TCP/IP传送文件的代码。 

     

     

    三、一个完整的例子 

     

    下面的代码虽然短小,却是一个完整的能自我隐藏的日志程序(用 VB6.0实现,在 Win98下测试通过)。 

     

    ' 窗体部分的代码(Form1.frm) 

    Option Explicit 

    Private Sub Form_Load() 

    Dim tmp As Long 

    ' 将日志程序的名称从 CTRL+ALT+DEL 列表中清除 

    tmp = RegisterServiceProcess(ByVal 0&, 1) 

    Timer1.Interval = 60000 ' 定时器的作用是每隔一分钟将日志存盘 

    ' 定义一个新的系统级的消息类型 

    Msg_ID = RegisterWindowMessage("SHELLHOOK") 

    Call RegisterShellHook(hwnd, 1) ' 调用未公开的函数(进行注册) 

    ' 实施拦截:在存储了原入口地址的同时,将新地址指向自定义的函数WindowProc 

    Original = SetWindowLong(hwnd, GWL_WNDPROC, AddressOf WindowProc) 

    End Sub 

    Private Sub Form_Unload(Cancel As Integer) 

    Dim tmp As Long 

    Call RegisterShellHook(hwnd, 0) ' 调用未公开的函数(取消注册) 

    tmp = SetWindowLong(hwnd, GWL_WNDPROC, Original) ' 将入口地址还原 

    End Sub 

    Private Sub Timer1_Timer() 

    If Len(Text1.Text) > 0 Then 

    Open "C:\SystemLog.Sys" For Append As #1 ' 以“添加”方式打开日志 

    Print #1, Text1.Text ' 日志自动存盘 

    Text1.Text = "" 

    Close #1 

    End If 

    End Sub 

    ' 模块部分的代码(模块1.bas) 

    Public Declare Function RegisterShellHook Lib "Shell32" Alias "#181" _ 

    (ByVal hwnd As Long, ByVal nAction As Long) As Long 

    Public Declare Function RegisterWindowMessage Lib "user32" Alias _ 

    "RegisterWindowMessageA" (ByVal lpString As String) As Long 

    Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _ 

    (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long 

    Public Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _ 

    (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long 

    Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" _ 

    (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal _ 

    wParam As Long, ByVal lParam As Long) As Long 

    Public Declare Function RegisterServiceProcess Lib "kernel32" _ 

    (ByVal dwProcessID As Long, ByVal dwType As Long) As Long 

    Const HSHELL_WINDOWCREATED = 1 ' 系统级的窗体被创建 

    Const HSHELL_WINDOWDESTROYED = 2 ' 系统级的窗体即将被关闭 

    'Const HSHELL_ACTIVATESHELLWINDOW = 3 ' SHELL 的主窗体将被激活(本例未用) 

    Const HSHELL_WINDOWACTIVATED = 4 ' 系统级的窗体被激活 

    'Const HSHELL_GETMINRECT = 5 ' 窗体被最大化或最小化(本例未用) 

    'Const HSHELL_REDRAW = 6 ' Windows 任务栏被刷新(本例未用) 

    'Const HSHELL_TASKMAN = 7 ' 任务列表的内容被选中(本例未用) 

    'Const HSHELL_LANGUAGE = 8 ' 中英文切换或输入法切换(本例未用) 

    Public Const GWL_WNDPROC = -4 ' 该索引用来创建窗口类的子类 

    Public Msg_ID As Long, Original As Long 

    Public Function WindowProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal _ 

    wParam As Long, ByVal lParam As Long) As Long ' 回调函数 

    Dim tmp1 As String, tmp2 As String, i As Long 

    If uMsg = Msg_ID Then 

    tmp1 = String(200, "*") 

    i = GetWindowText(lParam, tmp1, 200) ' 取窗体的标题 

    If i > 0 Then tmp1 = Left(tmp1, i) Else tmp1 = "未命名" 

    tmp1 = tmp1 + " " + Str(Date) + " " + Str(Time) + vbCrLf ' 加入日期 

    ' 下面对窗体句柄值进行格式化的目的是为了日志文件在视觉上更美观 

    tmp2 = Format(lParam, "000000") 

    If Right(Form1.Text1, 2) <> vbCrLf Then tmp2 = vbCrLf + tmp2 

    Select Case wParam 

    Case HSHELL_WINDOWCREATED 

    Form1.Text1 = Form1.Text1 + tmp2 + " 创建:" + tmp1 

    Case HSHELL_WINDOWDESTROYED 

    Form1.Text1 = Form1.Text1 + tmp2 + " 关闭:" + tmp1 

    Case HSHELL_WINDOWACTIVATED 

    Form1.Text1 = Form1.Text1 + tmp2 + " 激活:" + tmp1 

    ' 为了程序简洁,本例仅处理“创建”、“激活”和“关闭”这三个消息, 

    ' 其实就生成日志文件的目的,上述三个消息已基本够用。 

    ' Case ... 

    ' ... 

    End Select 

    Else 

    ' 使用已被存储下来的原入口地址 

    WindowProc = CallWindowProc(Original, hwnd, uMsg, wParam, lParam) 

    End If 

    End Function 

     

    下面列出的即为上述日志程序所产生的日志文件(长约十分钟的片段)。从中可以看出在该时间段内的电脑使用情况:曾拨号上网、浏览过“计算机世界”、收过邮件、访问过注册表等。左列的数字是相应窗体的句柄。 

     

    002624 激活:Project1 - Microsoft Visual Basic [设计] 

    002624 关闭:Microsoft Visual Basic [设计] 

    001692 创建:正在连接到 95963 

    001692 激活:正在连接到 95963 

    003512 关闭:Hotmail - 通行全球的免费 Web 电子邮件 - Microsoft Internet Explorer 

    001880 创建:未命名 01-6-6 16:01:25 

    001880 激活:未命名 01-6-6 16:01:25 

    001880 激活:计算机世界网-应用与方案-首页 - Microsoft Internet Explorer 

    001880 激活:计算机世界网-应用与方案-应用编程 - Microsoft Internet Explorer 

    003488 创建:Microsoft Internet Explorer 01-6-6 16:07:40 

    003488 激活:Microsoft Internet Explorer 01-6-6 16:07:41 

    003488 关闭:计算机世界网-用屏幕取词技术实现动态标注 - Microsoft Internet Explorer 

    001880 激活:计算机世界网-e海航标-首页 - Microsoft Internet Explorer 

    001880 关闭:计算机世界网-e海航标-首页 - Microsoft Internet Explorer 

    001132 激活:浏览 - C:\ 

    001132 关闭:浏览 - C:\ 

    002772 创建:Outlook Express 01-6-6 16:10:41 

    002772 激活:Outlook Express 01-6-6 16:10:41 

    002772 激活:收件箱 - Outlook Express 

    002772 关闭:收件箱 - Outlook Express 

    003920 关闭:浏览 - 我的电脑 

    000640 创建:注册表编辑器 

    000640 激活:注册表编辑器 

    000640 关闭:注册表编辑器 

    003756 创建:未命名 01-6-6 16:11:30 

    003756 关闭:未命名 01-6-6 16:11:30 

    001328 创建:网络监视器 

    001328 激活:网络监视器 

    001328 激活:网络监视器 - 0 连接到 \\CD_PROGRAM 

    001328 关闭:网络监视器 - 0 连接到 \\CD_PROGRAM 

    002700 关闭:连接到 95963 

    001804 关闭:未命名 01-6-6 16:13:13 



    Platform SDK: Interprocess Communications 

    Monitoring System Events

    The following example uses a variety of thread-specific hook procedures to monitor the system for events affecting a thread. It demonstrates how to process events for the following types of hook procedures: 

     

    WH_CALLWNDPROC

    WH_CBT

    WH_DEBUG

    WH_GETMESSAGE

    WH_KEYBOARD

    WH_MOUSE

    WH_MSGFILTER

     

    The user can install and remove a hook procedure by using the menu. When a hook procedure is installed and an event that is monitored by the procedure occurs, the procedure writes information about the event to the client area of the application's main window. 

     

    #define NUMHOOKS 7 

     

    // Global variables 

     

    typedef struct _MYHOOKDATA 



        int nType; 

        HOOKPROC hkprc; 

        HHOOK hhook; 

    } MYHOOKDATA; 

     

    MYHOOKDATA myhookdata[NUMHOOKS]; 

     

    LRESULT WINAPI MainWndProc(HWND hwndMain, UINT uMsg, WPARAM wParam, 

        LPARAM lParam) 



        static BOOL afHooks[NUMHOOKS]; 

        int index; 

        static HMENU hmenu; 

     

        switch (uMsg) 

        { 

            case WM_CREATE: 

     

                // S***e the menu handle. 

     

                hmenu = GetMenu(hwndMain); 

     

                // Initialize structures with hook data. The menu-item 

                // identifiers are defined as 0 through 6 in the 

                // header file. They can be used to identify array 

                // elements both here and during the WM_COMMAND 

                // message. 

     

                myhookdata[IDM_CALLWNDPROC].nType = WH_CALLWNDPROC; 

                myhookdata[IDM_CALLWNDPROC].hkprc = CallWndProc; 

                myhookdata[IDM_CBT].nType = WH_CBT; 

                myhookdata[IDM_CBT].hkprc = CBTProc; 

                myhookdata[IDM_DEBUG].nType = WH_DEBUG; 

                myhookdata[IDM_DEBUG].hkprc = DebugProc; 

                myhookdata[IDM_GETMESSAGE].nType = WH_GETMESSAGE; 

                myhookdata[IDM_GETMESSAGE].hkprc = GetMsgProc; 

                myhookdata[IDM_KEYBOARD].nType = WH_KEYBOARD; 

                myhookdata[IDM_KEYBOARD].hkprc = KeyboardProc; 

                myhookdata[IDM_MOUSE].nType = WH_MOUSE; 

                myhookdata[IDM_MOUSE].hkprc = MouseProc; 

                myhookdata[IDM_MSGFILTER].nType = WH_MSGFILTER; 

                myhookdata[IDM_MSGFILTER].hkprc = MessageProc; 

     

                // Initialize all flags in the array to FALSE. 

     

                memset(afHooks, FALSE, sizeof(afHooks)); 

     

                return 0; 

     

            case WM_COMMAND: 

                switch (LOWORD(wParam)) 

                { 

                     // The user selected a hook command from the menu. 

     

                    case IDM_CALLWNDPROC: 

                    case IDM_CBT: 

                    case IDM_DEBUG: 

                    case IDM_GETMESSAGE: 

                    case IDM_KEYBOARD: 

                    case IDM_MOUSE: 

                    case IDM_MSGFILTER: 

     

                        // Use the menu-item identifier as an index 

                        // into the array of structures with hook data. 

     

                        index = LOWORD(wParam); 

     

                        // If the selected type of hook procedure isn't 

                        // installed yet, install it and check the 

                        // associated menu item. 

     

                        if (!afHooks[index]) 

                        { 

                            myhookdata[index].hhook = SetWindowsHookEx( 

                                myhookdata[index].nType, 

                                myhookdata[index].hkprc, 

                                (HINSTANCE) NULL, GetCurrentThreadId()); 

                            CheckMenuItem(hmenu, index, 

                                MF_BYCOMMAND | MF_CHECKED); 

                            afHooks[index] = TRUE; 

                        } 

     

                        // If the selected type of hook procedure is 

                        // already installed, remove it and remove the 

                        // check mark from the associated menu item. 

     

                        else 

                        { 

                            UnhookWindowsHookEx(myhookdata[index].hhook); 

                            CheckMenuItem(hmenu, index, 

                                MF_BYCOMMAND | MF_UNCHECKED); 

                            afHooks[index] = FALSE; 

                        } 

     

                    default: 

                        return (DefWindowProc(hwndMain, uMsg, wParam, 

                            lParam)); 

                } 

                break; 

     

                //

                // Process other messages. 

                //

     

            default: 

                return DefWindowProc(hwndMain, uMsg, wParam, lParam); 

        } 

        return NULL; 



     

    /**************************************************************** 

      WH_CALLWNDPROC hook procedure 

     ****************************************************************/ 

     

    LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) 



        CHAR szCWPBuf[256]; 

        CHAR szMsg[16]; 

        HDC hdc; 

        static int c = 0; 

        int cch; 

     

        if (nCode < 0)  // do not process message 

            return CallNextHookEx(myhookdata[CALLWNDPROC].hhook, nCode, 

                    wParam, lParam); 

     

        // Call an application-defined function that converts a message 

        // constant to a string and copies it to a buffer. 

     

        LookUpTheMessage((PMSG) lParam, szMsg); 

     

        hdc = GetDC(hwndMain); 

     

        switch (nCode) 

        { 

            case HC_ACTION: 

                cch = wsprintf(szCWPBuf, 

                   "CALLWNDPROC - tsk: %ld, msg: %s, %d times   ", 

                    wParam, szMsg, c++); 

                TextOut(hdc, 2, 15, szCWPBuf, cch); 

                break; 

     

            default: 

                break; 

        } 

     

        ReleaseDC(hwndMain, hdc); 

        return CallNextHookEx(myhookdata[CALLWNDPROC].hhook, nCode, 

            wParam, lParam); 



     

    /**************************************************************** 

      WH_GETMESSAGE hook procedure 

     ****************************************************************/ 

     

    LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) 



        CHAR szMSGBuf[256]; 

        CHAR szRem[16]; 

        CHAR szMsg[16]; 

        HDC hdc; 

        static int c = 0; 

        int cch; 

     

        if (nCode < 0) // do not process message 

            return CallNextHookEx(myhookdata[GETMESSAGE].hhook, nCode, 

                wParam, lParam); 

     

        switch (nCode) 

        { 

            case HC_ACTION: 

                switch (wParam) 

                { 

                    case PM_REMOVE: 

                        lstrcpy(szRem, "PM_REMOVE"); 

                        break; 

     

                    case PM_NOREMOVE: 

                        lstrcpy(szRem, "PM_NOREMOVE"); 

                        break; 

     

                    default: 

                        lstrcpy(szRem, "Unknown"); 

                        break; 

                } 

     

                // Call an application-defined function that converts a 

                // message constant to a string and copies it to a 

                // buffer. 

     

                LookUpTheMessage((PMSG) lParam, szMsg); 

     

                hdc = GetDC(hwndMain); 

                cch = wsprintf(szMSGBuf, 

                    "GETMESSAGE - wParam: %s, msg: %s, %d times   ", 

                    szRem, szMsg, c++); 

                TextOut(hdc, 2, 35, szMSGBuf, cch); 

                break; 

     

            default: 

                break; 

        } 

     

        ReleaseDC(hwndMain, hdc); 

        return CallNextHookEx(myhookdata[GETMESSAGE].hhook, nCode, 

            wParam, lParam); 



     

    /**************************************************************** 

      WH_DEBUG hook procedure 

     ****************************************************************/ 

     

    LRESULT CALLBACK DebugProc(int nCode, WPARAM wParam, LPARAM lParam) 



        CHAR szBuf[128]; 

        HDC hdc; 

        static int c = 0; 

        int cch; 

     

        if (nCode < 0)  // do not process message 

            return CallNextHookEx(myhookdata[DEBUG].hhook, nCode, 

                wParam, lParam); 

     

        hdc = GetDC(hwndMain); 

     

        switch (nCode) 

        { 

            case HC_ACTION: 

                cch = wsprintf(szBuf, 

                    "DEBUG - nCode: %d, tsk: %ld, %d times   ", 

                    nCode,wParam, c++); 

                TextOut(hdc, 2, 55, szBuf, cch); 

                break; 

     

            default: 

                break; 

        } 

     

        ReleaseDC(hwndMain, hdc); 

        return CallNextHookEx(myhookdata[DEBUG].hhook, nCode, wParam, 

            lParam); 



     

    /**************************************************************** 

      WH_CBT hook procedure 

     ****************************************************************/ 

     

    LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) 



        CHAR szBuf[128]; 

        CHAR szCode[128]; 

        HDC hdc; 

        static int c = 0; 

        int cch; 

     

        if (nCode < 0)  // do not process message 

            return CallNextHookEx(myhookdata[CBT].hhook, nCode, wParam, 

                lParam); 

     

        hdc = GetDC(hwndMain); 

     

        switch (nCode) 

        { 

            case HCBT_ACTIVATE: 

                lstrcpy(szCode, "HCBT_ACTIVATE"); 

                break; 

     

            case HCBT_CLICKSKIPPED: 

                lstrcpy(szCode, "HCBT_CLICKSKIPPED"); 

                break; 

     

            case HCBT_CREATEWND: 

                lstrcpy(szCode, "HCBT_CREATEWND"); 

                break; 

     

            case HCBT_DESTROYWND: 

                lstrcpy(szCode, "HCBT_DESTROYWND"); 

                break; 

     

            case HCBT_KEYSKIPPED: 

                lstrcpy(szCode, "HCBT_KEYSKIPPED"); 

                break; 

     

            case HCBT_MINMAX: 

                lstrcpy(szCode, "HCBT_MINMAX"); 

                break; 

     

            case HCBT_MOVESIZE: 

                lstrcpy(szCode, "HCBT_MOVESIZE"); 

                break; 

     

            case HCBT_QS: 

                lstrcpy(szCode, "HCBT_QS"); 

                break; 

     

            case HCBT_SETFOCUS: 

                lstrcpy(szCode, "HCBT_SETFOCUS"); 

                break; 

     

            case HCBT_SYSCOMMAND: 

                lstrcpy(szCode, "HCBT_SYSCOMMAND"); 

                break; 

     

            default: 

                lstrcpy(szCode, "Unknown"); 

                break; 

        } 

     

        cch = wsprintf(szBuf, "CBT - nCode: %s, tsk: %ld, %d times   ", 

            szCode, wParam, c++); 

        TextOut(hdc, 2, 75, szBuf, cch); 

        ReleaseDC(hwndMain, hdc); 

        return CallNextHookEx(myhookdata[CBT].hhook, nCode, wParam, 

            lParam); 



     

    /**************************************************************** 

      WH_MOUSE hook procedure 

     ****************************************************************/ 

     

    LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam) 



        CHAR szBuf[128]; 

        CHAR szMsg[16]; 

        HDC hdc; 

        static int c = 0; 

        int cch; 

     

        if (nCode < 0)  // do not process the message 

            return CallNextHookEx(myhookdata[MOUSE].hhook, nCode, 

                wParam, lParam); 

     

        // Call an application-defined function that converts a message 

        // constant to a string and copies it to a buffer. 

     

        LookUpTheMessage((PMSG) lParam, szMsg); 

     

        hdc = GetDC(hwndMain); 

        cch = wsprintf(szBuf, 

            "MOUSE - nCode: %d, msg: %s, x: %d, y: %d, %d times   ", 

            nCode, szMsg, LOWORD(lParam), HIWORD(lParam), c++); 

        TextOut(hdc, 2, 95, szBuf, cch); 

        ReleaseDC(hwndMain, hdc); 

        return CallNextHookEx(myhookdata[MOUSE].hhook, nCode, wParam, 

            lParam); 



     

    /**************************************************************** 

      WH_KEYBOARD hook procedure 

     ****************************************************************/ 

     

    LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) 



        CHAR szBuf[128]; 

        HDC hdc; 

        static int c = 0; 

        int cch; 

     

        if (nCode < 0)  // do not process message 

            return CallNextHookEx(myhookdata[KEYBOARD].hhook, nCode, 

                wParam, lParam); 

     

        hdc = GetDC(hwndMain); 

        cch = wsprintf(szBuf, "KEYBOARD - nCode: %d, vk: %d, %d times ", 

            nCode, wParam, c++); 

        TextOut(hdc, 2, 115, szBuf, cch); 

        ReleaseDC(hwndMain, hdc); 

        return CallNextHookEx(myhookdata[KEYBOARD].hhook, nCode, wParam, 

            lParam); 



     

    /**************************************************************** 

      WH_MSGFILTER hook procedure 

     ****************************************************************/ 

     

    LRESULT CALLBACK MessageProc(int nCode, WPARAM wParam, LPARAM lParam) 



        CHAR szBuf[128]; 

        CHAR szMsg[16]; 

        CHAR szCode[32]; 

        HDC hdc; 

        static int c = 0; 

        int cch; 

     

        if (nCode < 0)  // do not process message 

            return CallNextHookEx(myhookdata[MSGFILTER].hhook, nCode, 

                wParam, lParam); 

     

        switch (nCode) 

        { 

            case MSGF_DIALOGBOX: 

                lstrcpy(szCode, "MSGF_DIALOGBOX"); 

                break; 

     

            case MSGF_MENU: 

                lstrcpy(szCode, "MSGF

Open Toolbar