跳转至

SOUI消息过滤机制

本文介绍SOUI框架中的消息过滤机制,这类似于MFC中的PreTranslateMessage功能。

消息过滤概述

消息过滤机制允许在消息到达目标窗口之前对其进行预处理或拦截,常用于: - 实现工具提示(Tooltip)控制 - 快捷键处理 - 特殊消息拦截 - 全局消息监控

实现消息过滤器

1. 定义消息过滤器接口

struct IMessageFilter
{
    virtual BOOL PreTranslateMessage(MSG* pMsg) = 0;
};

2. 实现消息过滤器

class SMyMessageFilter : public IMessageFilter
{
public:
    // 实现PreTranslateMessage接口
    virtual BOOL PreTranslateMessage(MSG* pMsg) override
    {
        // 返回TRUE表示消息已被处理
        // 返回FALSE表示继续传递消息
        switch(pMsg->message)
        {
        case WM_KEYDOWN:
            // 处理按键消息
            return TRUE;
        }
        return FALSE;
    }
};

3. 注册消息过滤器

在SOUI中需要将消息过滤器注册到MessageLoop:

// 在SHostWnd中
SMessageLoop* pLoop = GetMsgLoop();
pLoop->AddMessageFilter(pMessageFilter);

// 在SWindow中
SMessageLoop* pLoop = GetContainer()->GetMsgLoop();
pLoop->AddMessageFilter(pMessageFilter);

4. 注销消息过滤器

pLoop->RemoveMessageFilter(pMessageFilter);

使用场景示例

1. 工具提示控制

class STooltipFilter : public IMessageFilter
{
public:
    virtual BOOL PreTranslateMessage(MSG* pMsg) override
    {
        switch(pMsg->message)
        {
        case WM_MOUSEMOVE:
            // 控制工具提示显示
            UpdateTooltip(pMsg->pt);
            return FALSE;  // 继续传递消息

        case WM_MOUSEWHEEL:
            // 隐藏工具提示
            HideTooltip();
            return FALSE;
        }
        return FALSE;
    }

private:
    void UpdateTooltip(POINT pt)
    {
        // 实现工具提示更新逻辑
    }

    void HideTooltip()
    {
        // 实现工具提示隐藏逻辑
    }
};

2. 快捷键处理

class SHotkeyFilter : public IMessageFilter
{
public:
    virtual BOOL PreTranslateMessage(MSG* pMsg) override
    {
        if(pMsg->message == WM_KEYDOWN)
        {
            // 检查Ctrl+C快捷键
            if(pMsg->wParam == 'C' && (GetKeyState(VK_CONTROL) & 0x8000))
            {
                OnCopy();
                return TRUE;  // 消息已处理
            }
        }
        return FALSE;
    }

private:
    void OnCopy()
    {
        // 实现复制功能
    }
};

3. 下拉窗口示例

class SDropDownWnd : public SHostWnd, public IMessageFilter
{
public:
    SDropDownWnd()
    {
        // 构造函数中注册消息过滤器
        GetMsgLoop()->AddMessageFilter(this);
    }

    ~SDropDownWnd()
    {
        // 析构函数中注销消息过滤器
        GetMsgLoop()->RemoveMessageFilter(this);
    }

    // 实现消息过滤
    virtual BOOL PreTranslateMessage(MSG* pMsg) override
    {
        // 处理鼠标消息
        if(pMsg->message >= WM_MOUSEFIRST && pMsg->message <= WM_MOUSELAST)
        {
            // 检查鼠标是否在窗口区域内
            POINT pt = {GET_X_LPARAM(pMsg->lParam), GET_Y_LPARAM(pMsg->lParam)};
            if(!IsMouseInWindow(pt))
            {
                OnMouseLeave();
                return TRUE;
            }
        }
        return FALSE;
    }

protected:
    bool IsMouseInWindow(POINT pt)
    {
        CRect rcWnd;
        GetWindowRect(&rcWnd);
        return rcWnd.PtInRect(pt);
    }

    void OnMouseLeave()
    {
        // 处理鼠标离开事件
    }
};

最佳实践

1. 合理使用

  • 只过滤必要的消息
  • 避免过多的消息过滤器
  • 及时注销不需要的过滤器

2. 性能考虑

class SOptimizedFilter : public IMessageFilter
{
public:
    virtual BOOL PreTranslateMessage(MSG* pMsg) override
    {
        // 快速检查消息类型
        if(pMsg->message < WM_MOUSEFIRST || pMsg->message > WM_MOUSELAST)
            return FALSE;

        // 只处理感兴趣的消息
        // ...

        return FALSE;
    }
};

3. 生命周期管理

class SMessageFilterManager
{
public:
    void AddFilter(IMessageFilter* pFilter)
    {
        if(!m_filters.Find(pFilter))
        {
            m_pLoop->AddMessageFilter(pFilter);
            m_filters.Add(pFilter);
        }
    }

    void RemoveFilter(IMessageFilter* pFilter)
    {
        if(m_filters.Find(pFilter))
        {
            m_pLoop->RemoveMessageFilter(pFilter);
            m_filters.Remove(pFilter);
        }
    }

private:
    SArray<IMessageFilter*> m_filters;
    SMessageLoop* m_pLoop;
};

调试技巧

  1. 消息跟踪

    virtual BOOL PreTranslateMessage(MSG* pMsg) override
    {
        STRACE(_T("Message: %d, wParam: %d, lParam: %d"),
            pMsg->message, pMsg->wParam, pMsg->lParam);
        return FALSE;
    }
    

  2. 性能监控

    virtual BOOL PreTranslateMessage(MSG* pMsg) override
    {
        DWORD dwStart = GetTickCount();
        BOOL bRet = ProcessMessage(pMsg);
        DWORD dwEnd = GetTickCount();
    
        if(dwEnd - dwStart > 100)
        {
            // 记录性能问题
            STRACE(_T("Message processing too slow: %dms"), dwEnd - dwStart);
        }
    
        return bRet;
    }
    

注意事项

  1. 消息处理
  2. 返回TRUE表示消息已处理
  3. 返回FALSE继续传递消息
  4. 不要无故拦截消息

  5. 生命周期

  6. 构造时注册
  7. 析构时注销
  8. 避免内存泄漏

  9. 性能影响

  10. 减少不必要的处理
  11. 优化处理逻辑
  12. 监控处理时间