win32消息映射9-inherited

8.inherited

在讲继承的时候我们说过,若映射的回调函数返回false,就继续遍历下一个mapslot链表:

class A : public wabc::wndbase
{
    WABC_DECLARE_MSG_MAP()
public:
    // ...
    bool on_keydown(wabc::msg_keydown &msg)
    {
        // ...
        return true;
    }
};

class B : public A
{
    WABC_DECLARE_MSG_MAP()
public:
    // ...
    bool on_keydown(wabc::msg_keydown &msg)
    {
        // ...
        return false;
    }
};

在B窗口里,按了一个键,会先触发B::on_keydown,等B::on_keydown执行完了,再触发A::on_keydown。若有这样的需求,在B::on_keydown里,先执行A::on_keydown的代码,等A::on_keydown执行完了,再继续B::on_keydown剩余的代码,该如何做。一种可能的实现是:

bool B::on_keydown(wabc::msg_keydown &msg)
{
    A::on_keydown(msg);
    // ...
    return true;
}

这种实现的弊端在于:1.必须知道mapslotB的下一个节点是mapslotA;2.A::on_keydown必须是public或protected。若消息映射机制提供一种方式,先执行下一个链表的映射,再回到原先的地方继续执行,这会好很多。我们为msg增加一个inherited函数,实现下一个映射链表节点的回调:

bool B::on_keydown(wabc::msg_keydown &msg)
{
    msg.inherited();
    // ...
    return true;
}

 inherited实现的关键点在于wndproc::process_WM函数。在wndproc::process_WM函数里,我们现在的代码,是从mapslot链表的第一个节点开始遍历:

 void wndproc::process_WM(wndbase &wnd, msg_struct &msg)
 {
    // ...
    mapslot_node *pSlot= wnd.m_mapslot_head->next;
    // ...
 }

 若pSlot指向的是我们需要的节点,这问题就解决了。在msg里增加两个变量:wnd和cur_slot,就很容易实现msg.inherited()功能。

 struct msg_node
{
    wndbase *wnd;
    mapslot_node * cur_slot;
    // ...

    bool inherited();
};

增加后,wndproc::process_WM将变成:

 void wndproc::process_WM(wndbase &wnd, msg_struct &msg)
 {
    // ...
    mapslot_node *pSlot= msg.cur_slot;
    // ...
 }

由于msg_node增加了一个变量增加了wnd变量,所以process_WM第一个参数就不需要了,同理,其它process_XXX也一样。

class wndproc
{
    static bool process_WM_CREATE(msg_struct &msg);

    static bool process_WM(msg_struct &msg);

    static bool process(msg_struct &msg);

    friend msg_node;
public:
    static LRESULT CALLBACK WndProc(...);
};

增加一个process函数,里面放的是WndProc主逻辑的代码。

bool wndproc::process(msg_struct &msg)
{
    struct special_msg
    {
        typedef bool (*fun_t)(msg_struct &);
        UINT message;
        fun_t fun;
    };

    static special_msg items[] = {
        WM_CREATE, &wndproc::process_WM_CREATE,
    };

    const size_t nSize = countof(items);
    size_t first = 0, last = nSize, n = nSize, m, tmp;

#ifdef _DEBUG
    for (m = 1; m < countof(items); ++m)
    {
        assert(items[m - 1].message < items[m].message);
    }
#endif

    while (0 < n)
    {
        m = n / 2;
        tmp = first + m;

        if (items[tmp].message < msg.message)
        {
            first = tmp + 1;
            n -= m + 1;
        }
        else if (msg.message < items[tmp].message)
            n = m;
        else
            return items[tmp].fun(msg);
    }

    return process_WM(msg);
}

LRESULT CALLBACK  wndproc::WndProc(...)
{
    msg_struct msg={0};
    // ...
    if(msg.wnd)
    {
        msg.cur_slot= msg.wnd->m_mapslot_head->next;
        if(process(msg))
            return msg.result;
    }
    return ::DefWindowProc(hWnd, message, wParam, lParam);
}

这样,一切又回到原先的功能上,所作的改动,是为了实现msg_struct::inherited的功能。至此,已经万事俱备,水到渠成。

bool msg_node::inherited()
{
    mapslot *p = static_cast<mapslot*>(cur_slot);
    if(p->next != p)
    {
        msg_struct tmp = static_cast<msg_struct &>(*this);
        tmp.cur_slot= p->next;
        return wndproc::process(tmp);
    }
    return false;
}

inherited是msg_node的成员函数,不能在msg_node上定义,原因在于msg_struct存在很多“变异体”,若在msg_struct上定义,这会导致在所有的变异体都定义一次。这过程是乏味的且代码重复。

对于WM_PAINT消息,有必要增加对inherited的支持。在讲继承的时候说过,对于WM_PAINT的映射消息,只能返回true,以避免对BeginPaint和EndPaint的重复调用。在调用inherited的时候,依然要避免这种情况。WM_PAINT消息的wParam和lParam没有意义,我们可以利用这两个参数,来判断BeginPaint和EndPaint是否被调用了。

bool map_paint::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);
        }
    };

    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);

    if(msg.wParam == 0 && msg.lParam == 0)
    {
        paint_sentry ps(msg.hWnd);
        msg.wParam = reinterpret_cast<WPARAM>(hdc);
        msg.lParam = reinterpret_cast<LPARAM>(&ps.rcPaint);
        (o.*f)(ps.hdc, ps.rcPaint, reinterpret_cast<msg_paint &>(msg));
    }
    else
    {
        HDC dc = reinterpret_cast<HDC>(msg.wParam);
        const RECT &rt = *reinterpret_cast<RECT *>(msg.lParam);
        (o.*f)(hdc, rt, reinterpret_cast<msg_paint &>(msg));
    }
    return true;
}

msg_struct::inherited的操作,只会遍历mapslot链表里的映射,若没有找到对应的映射函数,将一直遍历到链表尾,DefWindowProc api不会被调用。

msg_struct::inherited的操作,是一把双刃剑,很强大,但却可能造成一些意外。原因在于,有可能无法判断下一个映射函数的行为。若不能正确判断下一个映射函数执行完后对inherited()后面的代码造成的影响,这时候可能就会有一些奇怪的问题。调用msg.inherited(),一定要明白这么做的意义。

总结:

在有些语言里,在语法里提供了inherited的支持,我们在这里提供了一个模拟的实现,花费的代价不大,但在使用上却十分便捷。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值