win32消息映射5-msg_struct的变异体

4. msg_struct的变异体

“变异体”这个名词不那么准确,但没有找到更好的名词之前,暂且先叫“变异体”。什么叫msg_struct的变异体,就是结构的大小和内存布局和msg_struct一样的,就叫做msg_struct的变异体。重温msg_struct的定义:

struct msg_struct
{
    HWND    hwnd;
    UINT    message;

    union
    {
        WPARAM    wParam;

        struct
        {
            WORD    wParamLo;
            WORD    wParamHi;
        };
    };

    union
    {
        LPARAM    lParam;

        struct
        {
            WORD    lParamLo;
            WORD    lParamHi;
        };
    };

    LRESULT result;
};

所有的windows消息,都对应着这个结构体,message的不同,对应的wParam和lParam也不同。对WM_DESTROY消息来说,wParam和lParam是没意义的,我们可以引入一个msg_struct的别名,对应WM_DESTROY消息:

typedef msg_struct msg_destroy;

而WM_KEYDOWN消息,wParam和lParam是有意义的(具体意义请查阅MSDN),由此我们可以这么定义msg_key:

struct msg_key
{
    HWND    hWnd;
    UINT    message;

    union
    {
        WPARAM    key_code;
        WPARAM    wParm;
    };

    union
    {
        struct {
            LPARAM    repeat_count : 16;
            LPARAM    scan_code : 8;
            LPARAM    is_extended_key : 1;
            LPARAM    reserve : 4;
            LPARAM    context_code : 1;        // The value is always 0 for a WM_KEYUP message
            LPARAM    previous_key_state : 1;        // The value is always 1 for a WM_KEYUP message
            LPARAM    transition_state : 1;        // The value is always 1 for a WM_KEYUP message
        };
        LPARAM lParam;
    };
    LRESULT result;
};

(为什么叫msg_key而不叫msg_keydown,因为对于按键消息来说,wParam和lParam是一样的解释。同理还有msg_mouse,对应所有的鼠标消息)

对比msg_key和msg_struct,会发现两者之间并无本质上的区别。msg_key把wParam和lParam解释成它对应的含义。在语法上,这两个结构是不存在关系,但在语义上,这两个结构是一回事。

对于大部分的windows消息来说,wParam和lParam都有含义,这就暗示着,对于消息映射函数来说,都需要wParam和lParam,而对于某一个windows消息来说,wParam和lParam要恢复成它的语义,才会方便使用。由此,我们可以规范消息映射函数的类型:

bool on_XXX(msg_struct &msg);    // 为什么要有返回值bool,在讲继承的时候会说到这问题

对于WM_DESTROY消息,它映射的消息函数应该是:

bool on_destroy(msg_destroy &);

而对于WM_KEYDOWN消息,它映射的函数应该是:

bool on_keydown(msg_key &);

这定义的类型可以满足大部分的需求了。但凡事总有例外,比如,WM_PAINT消息。在WM_PAINT消息里,大多数情况下,都是要调用BeginPaint和EndPaint,若消息映射函数只有一个参数,这意味着在每个映射函数里,都有BeginPaint和EndPaint和代码,这未免太过于重复和乏味,所以我们要对这种情况做个特例:

bool on_paint(HDC hdc, const RECT &rtClip, msg_paint &);

既然消息映射函数的类型改了,那么map_XXX里的实现也要改了。

对于WM_DESTROY:

struct map_destroy : msgmap_t
{
    template<typename T>
    static inline size_t map_fun_addr(bool (T::*f)(msg_destroy &))
    {
        return *reinterpret_cast<size_t *>(&f);
    }

    static bool on_map(const msgmap_t &a, void * x, msg_struct &msg)
    {
        msgmap_t &o= *reinterpret_cast<msgmap_t *>(x);
        typedef bool (msgmap_t::*fun_t)(msg_destroy &);
        const fun_t &f = *reinterpret_cast<const fun_t *>(&a.on_message);
        return (o.*f)(reinterpret_cast<msg_destroy &>(msg));
    }
};

这里要注意这句代码:reinterpret_cast<msg_destroy &>(msg),因为msg_destroy是msg_struct的变异体,所以,这里的强行转换是没有问题的。

对于WM_PAINT:

struct map_paint : msgmap_t
{
    template<typename T>
    static inline size_t map_fun_addr(bool (T::*f)(HDC,const RECT &, msg_paint &))
    {
        return *reinterpret_cast<size_t *>(&f);
    }

    static bool on_map(const msgmap_t &a, void * x, msg_struct &msg)
    {
        struct paint_sentry : PAINTSTRUCT
        {
            HWND    m_hWnd;
            HDC    hdc;

            paint_sentry(HWND hWnd) : m_hWnd(hWnd)
            {
                hdc = ::BeginPaint(m_hWnd, this);
            }

            ~paint_sentry()
            {
                ::EndPaint(m_hWnd, this);
            }
        }ps(msg.hWnd);

        msgmap_t &o= *reinterpret_cast<msgmap_t *>(x);
        typedef bool (msgmap_t::*fun_t)(HDC, const RECT &, msg_paint &);
        const fun_t &f = *reinterpret_cast<const fun_t *>(&a.on_message);
        return (o.*f)(ps.hdc, ps.rcPaint,reinterpret_cast<msg_paint &>(msg));
    }
};

在map_paint::on_map里,引入了一个局部类,是为了防止在回调函数中出现了异常而不能调用EndPaint。

宏WABC_ON_DESTROY和WABC_ON_PAINT不用做修改。

对于WM_KEYDOWN:

struct map_key : msgmap_t
{
    template<typename T>
    static inline size_t map_fun_addr(bool (T::*f)(msg_key &))
    {
        return *reinterpret_cast<size_t *>(&f);
    }
};

#define WABC_ON_KEYDOWN(f) \
    { WM_KEYDOWN, map_key::map_fun_addr<map_class>(f), &map_destroy::on_map },

这里,不知道是否有人注意到,在map_key里,并没有on_map函数,它使用的是map_destroy::on_map!msg_key也是msg_struct的变异体,对于map_destroy::on_map来说,msg_destroy和msg_key没有区别,只要它们映射消息函数的类型是一致的。

同理,我们很快就能定义WM_KEYUP、WM_CHAR、WM_SYSKEYUP、WM_SYSKEYDOWN、WM_SYSKEYCHAR消息。

#define WABC_ON_KEYUP(f) \
    { WM_KEYUP, map_key::map_fun_addr<map_class>(f), &map_destroy::on_map },

#define WABC_ON_CHAR(f) \
    { WM_CHAR, map_key::map_fun_addr<map_class>(f), &map_destroy::on_map },

#define WABC_ON_SYSKEYDOWN(f) \
    { WM_SYSKEYDOWN, map_key::map_fun_addr<map_class>(f), &map_destroy::on_map },

#define WABC_ON_SYSKEYUP(f) \
    { WM_SYSKEYUP, map_key::map_fun_addr<map_class>(f), &map_destroy::on_map },

#define WABC_ON_SYSCHAR(f) \
    { WM_SYSCHAR, map_key::map_fun_addr<map_class>(f), &map_destroy::on_map },

鼠标消息类似。

总结:

在msgmap_t::on_message,我们弱化了on_message的语义,然后又规范了消息映射函数的类型,通过msg_struct的变异体,最大限度的在正确性、易用性和内存性能方面达到了完美的平衡。这一设计思想,不单纯可以应用于消息映射机制上,也可应用于在其它类似场合。

请点击这里下载'wabc'库的最终源码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值