中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 编程语言 > 综合其它
win32gui之event handling分析
作者:未知 时间:2005-07-27 23:31 出处:CSDN 责编:chinaitpower
              摘要:win32gui之event handling分析

win32gui之event handling分析

hanlray@hotmail.com

Revision: 1.0 Date: 2005/07/17


1. 前言

win32gui是一个符合C++精神的GUI框架,源自在C++届颇有声望的cuj杂志上的一系列文章,其充分发挥了C++语言的特点,有助于提高C++在GUI开发方面的框架水平。其feature很多,现列举几个:

  • real GUI RAII
  • no more message maps
  • easy handling of events/ event handling
  • thread-safety
  • STL-friendly

在实际使用过程中,发现其event handling机制很独特,下面就对它进行一下分析。

2. easy handling of events

熟悉MFC,包括WTL、wxWindow开发的朋友对它们的消息映射一定不会陌生,要处理一条消息,一般需要三个步骤:

  • 定义消息映射,把消息和消息处理函数关联起来。这一般是用某种形式的宏来实现的。
  • 在窗口类的头文件中声明该消息处理函数。
  • 在窗口类的源文件中实现该消息处理函数

虽然可能有wizard的帮助,这种过程仍然是繁琐的,也是有缺点的:

  • 易出错。比如我想处理WM_PAINT消息,虽然我定义了ON_WM_PAINT宏,但是消息处理函数却写成了OnPaint1,这时编译器是不会 有任何错误或警告的,当然也不会调到你的这个OnPaint1函数里。换句话说,你的消息处理函数必须写得和CWnd类里定义的一模一样,如果不小心写错 了,你是不会收到任何警告的。甚至没有消息映射而只有消息处理函数,编译器也是无法发觉的。
  • 看似magic的宏的大量使用,实际上减弱了代码的可读性。
  • 增加、删除或修改消息映射通常会带来相应头文件的改变,从而增加编译时间。

win32gui是怎么做的呢?假设有一个窗口类child_view,其源文件为child_view.cpp,对其进行事件处理的步骤如下:

  • 在child_view.cpp中定义一个负责事件处理的类,比如叫做child_view_hanlder:
    struct child_view_handler : event_handler<child_view_handler, child_view> {
    };                
    
                
  • 直接写消息处理函数。比如要处理的消息是WM_ERASEBKGND,就这样写:
                    
    struct child_view_handler : event_handler<child_view_handler, child_view> {
        handle_event on_erase_bg(wm::draw::erase_bg_arg arg) {
            ...//some code here
            return event<WM_ERASEBKGND>().HANDLED_BY(&me::on_erase_bg);
        }
    };                    
                    注意该函数名是可以任取的。 
                    

这样的过程也许不比MFC等简单多少,但其优点也是突出的:

  • 实现对某个消息的处理只需要一个消息处理函数,而且该消息处理函数的名称是任意的,这就大大减少了出错的机会。
  • 窗口类和消息处理类是分离的,这意味改变消息处理逻辑不会影响窗口类本身。

至少从表面上看,这里是没有消息映射的,最后的那个return语句看起来有点像,但它在消息处理函数体里,总不会在执行消息处理函数的时候定义消息映射吧?当然也 不会是消息处理函数名称,因为它的名称是任取的。就算win32gui对Win32 GUI封装的再好,也不可能脱离win32窗口系统的原理:系统肯定会向这个 child_view窗口发送一条WM_ERASEBKGND消息,要对该消息进行处理,是一定需要该消息与消息处理函数的对应关系的,不管该关系用什么方式表达。 其实关键就是在那个return语句上,它显然可以是一种消息映射的表达,只是位置有点奇怪而已。其中的HANDLED_BY是一个宏,其定义为

#define HANDLED_BY(f) get_proxy(f).handled_by<f>()

            

先看event类模板的定义:

                        
template<int wm> struct event {
    template<class self_type>
    detail::event_keeper0<wm,self_type> get_proxy(                       handle_event (self_type::*)() ) {
        return detail::event_keeper0<wm,self_type>();
    }
    template<class self_type, class p1>

    detail::event_keeper1<wm,self_type,p1> get_proxy(                    handle_event (self_type::*)(p1) ) {
        return detail::event_keeper1<wm,self_type,p1>();
    }
    template<class self_type, class p1, class p2>
    detail::event_keeper2<wm,self_type,p1,p2> get_proxy(                 handle_event (self_type::*)(p1,p2) ) {
        return detail::event_keeper2<wm,self_type,p1,p2>();
    }
    template<class self_type, class p1, class p2, class p3>

    detail::event_keeper3<wm,self_type,p1,p2,p3> get_proxy(              handle_event (self_type::*)(p1,p2,p3) ) {
        return detail::event_keeper3<wm,self_type,p1,p2,p3>();
    }
    template<class self_type, class p1, class p2, class p3, class p4>
    detail::event_keeper4<wm,self_type,p1,p2,p3,p4> get_proxy(           handle_event (self_type::*)(p1,p2,p3,p4) ) {
        return detail::event_keeper4<wm,self_type,p1,p2,p3,p4>();
    }
    template<class self_type, class p1, class p2, class p3, class p4, class p5>

    detail::event_keeper5<wm,self_type,p1,p2,p3,p4,p5> get_proxy(        handle_event (self_type::*)(p1,p2,p3,p4,p5) ) {
        return detail::event_keeper5<wm,self_type,p1,p2,p3,p4,p5>();
    }
    template<class self_type, class p1, class p2, class p3, class p4, class p5, class p6>
    detail::event_keeper6<wm,self_type,p1,p2,p3,p4,p5,p6> get_proxy(     handle_event (self_type::*)(p1,p2,p3,p4,p5,p6) ) {
        return detail::event_keeper6<wm,self_type,p1,p2,p3,p4,p5,p6>();
    }
};
            
            

其有多个成员含函数模板,分别对应消息处理函数的参数个数从0到6的情况。这里我们的消息处理函数只需要一个参数,因此get_proxy返回的是实例属于 detail::event_keeper1类模板,再看event_keeper1的定义:

                
template<int wm, class me, class p1> struct event_keeper1 {
    template<handle_event (me::*func)(p1)> handle_event handled_by() {
        return event_adder<wm, me, func1_caller<me,p1,func> >();
    }
};
         
            

其返回的是event_adder类模板的实例:

                
template<int wm, class me, class func> struct event_adder : handle_event {
    event_adder() {
        if ( detail::initialize_static_trick() ) {
            assert(false); // should never happen
            s_adder = event_adder_impl<wm,me,func>();
        }
    }
    static event_adder_impl<wm,me,func> s_adder;
};

            

可以看到,event_adder的构造函数好像也没做什么,关键在于s_adder这个static成员上,看看其类型定义:

                

template<int wm, class me, class func> struct event_adder_impl : register_possible_dialog<me> {
    typedef typename func::first_arg first_arg;

    event_adder_impl() {
        WIN32GUI_LOG("adding event " << wm << " for " << typeid(me).name());
        if ( initialize_static_trick() ) {
            // force the compiler to generate the constructor for the 'me' class
            new me;
            // v1.5+
            // extra safety for the first argument...
            typedef typename ignore_enhanced_msg<first_arg>::arg func_first_arg;
            int first_argument_and_return_DONOT_match_please_check_again = check_event<wm,func_first_arg>( (func_first_arg*)0);
        }
        // note: for the same message, we might have multiple functions
        me::s_events()[ (UINT)wm].push_back( &func::call );        
    }
};

            

真相大白了!最后一句清楚地表明,这里是有消息映射的!实际上win32gui对于每个event handler类型,在内部都为其维护了一个消息映射表,使用的数据结构是stl::map。消息处理函数的return语句在运行时实际什么也没做,它的存在只是为了让编译器产生event_adder_impl的静态变量,在这些 静态变量的初始化时刻,其构造函数被调用,从而建立消息映射关系。

3. 结束语

虽然win32gui号称no more message maps,但从上分析,其还是存在消息映射的,这也是由win32窗口系统本身决定的;只不过其表达方式有些特别,利用了模板和静态变量技术,较之MFC之类的方式,其优点也是相当明显的。


关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有