windows开发必备之消息循环【c++】

消息映射机制

消息映射机制是事件驱动编程中的一种技术,广泛用于处理图形用户界面(GUI)中的用户输入、系统事件等。其核心思想是将特定的消息(如用户点击、键盘输入等)与特定的处理函数关联起来,以实现消息的响应和处理。在C++的Microsoft Foundation Class Library(MFC)中,消息映射机制被广泛应用于Windows应用程序的开发。

消息映射机制的原理

  1. 消息的生成与传递:

    • 在Windows操作系统中,用户的各种操作(如鼠标点击、键盘输入等)和系统事件(如窗口重绘、定时器事件)会生成对应的消息。操作系统将这些消息发送到应用程序的消息队列中,等待处理。
  2. 消息循环:

    • 应用程序的主消息循环(Message Loop)从消息队列中取出消息,并将其传递给对应的窗口过程(Window Procedure)。在MFC中,这个过程由框架管理。
  3. 消息映射表:

    • 每个MFC类都有一个消息映射表(Message Map),它将消息与特定的消息处理函数关联起来。这个表是由一系列宏定义构成的,例如BEGIN_MESSAGE_MAPEND_MESSAGE_MAP之间的ON_WM_PAINTON_WM_LBUTTONDOWN等宏。
     
    BEGIN_MESSAGE_MAP(CMyWndClass, CWnd)
        ON_WM_PAINT()       // 将WM_PAINT消息映射到OnPaint函数
        ON_WM_LBUTTONDOWN() // 将WM_LBUTTONDOWN消息映射到OnLButtonDown函数
    END_MESSAGE_MAP()
    

  4. 消息分发:

    • 当一个消息到达某个窗口时,MFC框架会根据消息映射表查找与该消息对应的处理函数。如果找到了对应的处理函数,框架将调用该函数来处理消息。
  5. 消息处理函数:

    • 消息处理函数是应用程序对特定消息的响应逻辑,通常是类的成员函数。例如,WM_PAINT消息会调用OnPaint()函数来进行窗口的重绘,WM_LBUTTONDOWN消息会调用OnLButtonDown()函数来处理鼠标左键的点击。
  6. 消息传递:

    • 如果一个类的消息映射表中没有对应某个消息的处理函数,MFC会将消息传递给基类进行处理,直到找到合适的处理函数为止。如果最终未找到处理函数,则消息会被忽略或执行默认的处理操作。

消息映射机制的优点

  • 简洁的代码结构:通过消息映射机制,程序员可以轻松将消息与处理函数关联起来,避免使用大量的switch-case结构,从而使代码更加清晰和易于维护。
  • 扩展性强:消息映射机制支持类的继承和扩展,子类可以通过重载或添加新的消息处理函数来扩展基类的功能。

消息映射机制的应用

消息映射机制广泛应用于Windows应用程序开发,特别是在处理GUI事件时,如窗口重绘、用户输入等。MFC的消息映射机制是基于宏和类的继承实现的,通过这一机制,可以灵活地管理和处理各种消息。

在其他框架或语言中,也有类似的机制,例如Qt的信号与槽机制、Java的事件监听器等,虽然具体实现不同,但其核心思想都是通过某种方式将事件与处理逻辑关联起来。

为了在C++中实现类似于MFC中的消息映射机制,我们可以设计一个简单的类来模拟这种功能。该类会包含一个消息映射表,用于将特定的消息映射到相应的处理函数。这是一种基于函数指针或函数对象的实现。

以下是一个基本的实现示例:

#include <iostream>
#include <map>
#include <functional>

// 定义消息类型
enum class MessageType
{
    MSG_PAINT,
    MSG_LBUTTONDOWN,
    MSG_KEYDOWN,
    // 其他消息类型...
};

// 基础窗口类
class BaseWindow
{
public:
    // 消息映射表类型
    using MessageMap = std::map<MessageType, std::function<void()>>;

    // 构造函数,设置消息映射表
    BaseWindow()
    {
        initializeMessageMap();
    }

    // 处理消息的函数
    void handleMessage(MessageType msg)
    {
        auto it = messageMap.find(msg);
        if (it != messageMap.end())
        {
            it->second(); // 调用映射的处理函数
        }
        else
        {
            std::cout << "Message not handled!" << std::endl;
        }
    }

protected:
    // 消息映射表
    MessageMap messageMap;

    // 初始化消息映射表的函数
    virtual void initializeMessageMap()
    {
        // 基类可以映射一些通用消息
    }
};

// 自定义窗口类,继承自基础窗口类
class MyWindow : public BaseWindow
{
public:
    MyWindow()
    {
        // 自定义初始化
    }

protected:
    // 重写初始化消息映射表的函数
    void initializeMessageMap() override
    {
        messageMap[MessageType::MSG_PAINT] = [this]() { onPaint(); };
        messageMap[MessageType::MSG_LBUTTONDOWN] = [this]() { onLButtonDown(); };
        messageMap[MessageType::MSG_KEYDOWN] = [this]() { onKeyDown(); };
    }

    // 消息处理函数
    void onPaint()
    {
        std::cout << "Handling WM_PAINT message." << std::endl;
    }

    void onLButtonDown()
    {
        std::cout << "Handling WM_LBUTTONDOWN message." << std::endl;
    }

    void onKeyDown()
    {
        std::cout << "Handling WM_KEYDOWN message." << std::endl;
    }
};

int main()
{
    MyWindow window;

    window.handleMessage(MessageType::MSG_PAINT);
    window.handleMessage(MessageType::MSG_LBUTTONDOWN);
    window.handleMessage(MessageType::MSG_KEYDOWN);
    window.handleMessage(static_cast<MessageType>(999));  // 未映射的消息

    return 0;
}

代码解释

  1. MessageType枚举:定义了一些模拟的消息类型,例如MSG_PAINTMSG_LBUTTONDOWNMSG_KEYDOWN等。

  2. BaseWindow类:这个基类包含了一个消息映射表MessageMap,用于将消息类型映射到相应的处理函数。handleMessage函数用于处理传入的消息,查找消息映射表并调用相应的函数。

  3. MyWindow类:这个类继承自BaseWindow,并重写了initializeMessageMap函数。在这个函数中,我们将消息类型映射到具体的处理函数,如onPaintonLButtonDown等。

  4. lambda表达式:用于将成员函数绑定到消息映射表中。因为这些成员函数可能需要访问类的其他成员,所以lambda表达式捕获this指针。

  5. main函数:在主函数中,创建了MyWindow对象,并调用handleMessage来模拟处理不同的消息。

特点和扩展

  • 灵活性:通过使用std::function,消息映射机制可以轻松映射到任何兼容的函数对象或lambda表达式上,使得机制非常灵活。
  • 可扩展性:可以进一步扩展此机制,支持传递参数、异步消息处理等高级功能。
  • 类型安全:由于使用std::functionlambda,这个实现相对类型安全,避免了传统函数指针容易引发的错误。

进阶之——开源界面库duilib

DuiLib是一个轻量级的Windows UI库,广泛应用于Windows桌面应用程序开发。它通过消息机制和事件处理来响应用户操作。以下是DuiLib响应用户操作的基本原理:

1. 消息传递机制

DuiLib使用Windows的消息机制来处理用户操作。所有的用户输入(如鼠标点击、键盘输入)都会以Windows消息的形式传递给应用程序。DuiLib通过窗口过程(Window Procedure)拦截这些消息,并将其分发给相应的UI控件处理。

2. 控件的消息处理

DuiLib中的每个UI控件都可以处理特定的消息。例如,按钮控件会处理与鼠标点击相关的消息,文本框控件会处理键盘输入消息。

  • 消息映射:每个控件都有一个消息映射表,类似于MFC的消息映射机制。这张表将特定的Windows消息与控件的处理函数关联起来。

  • 事件触发:当控件接收到特定的消息时,它会触发相应的事件。例如,当用户点击按钮时,按钮控件会触发一个“点击事件”,并调用对应的回调函数。

3. 事件机制

DuiLib还支持事件机制,允许开发者为控件的特定操作设置回调函数。事件机制通常通过回调函数或委托实现,开发者可以在控件上注册处理特定事件的函数。

  • 事件绑定:开发者可以将特定的回调函数绑定到控件的事件上,例如绑定到按钮的点击事件。当事件发生时,DuiLib会自动调用绑定的回调函数。
 

// 例如,绑定按钮的点击事件 pButton->OnClick += MakeDelegate(this, &CMyWindow::OnButtonClick);

  • 事件传播:如果控件没有处理某个消息或事件,DuiLib会将消息向上传递到父控件或窗口,直到找到能够处理该消息的对象。这种机制确保了用户操作能够被正确处理。

4. 自定义消息与扩展

DuiLib允许开发者定义自定义消息,以处理特定的用户操作或系统事件。开发者可以扩展DuiLib的控件,添加自定义的消息处理函数来响应这些自定义消息。

5. 示例

当用户点击一个按钮时,DuiLib的处理流程如下:

  1. 消息传递:用户点击按钮,Windows生成WM_LBUTTONDOWN消息并传递给DuiLib窗口。
  2. 消息分发:DuiLib的窗口过程将WM_LBUTTONDOWN消息传递给相应的按钮控件。
  3. 事件触发:按钮控件处理WM_LBUTTONDOWN消息并触发点击事件(OnClick)。
  4. 回调调用:如果开发者为按钮注册了点击事件的回调函数,DuiLib将调用该回调函数,执行开发者定义的操作。

通过这种方式,DuiLib能够高效地响应用户的各种操作,并允许开发者灵活定制应用程序的行为。

为了模拟DuiLib的消息循环和事件处理机制,我们可以使用C++编写一个简化的类。这将模拟消息的接收、分发以及控件的事件处理流程。这个类将展示如何处理基本的用户操作,如按钮点击。

模拟DuiLib消息循环的C++类

#include <iostream>
#include <map>
#include <functional>

// 模拟的消息类型
enum class MessageType
{
    MSG_PAINT,
    MSG_LBUTTONDOWN,
    MSG_LBUTTONUP,
    MSG_CLICK,
};

// 基础控件类
class UIControl
{
public:
    using MessageMap = std::map<MessageType, std::function<void()>>;

    virtual void handleMessage(MessageType msg)
    {
        auto it = messageMap.find(msg);
        if (it != messageMap.end())
        {
            it->second();
        }
        else
        {
            std::cout << "Message not handled!" << std::endl;
        }
    }

protected:
    MessageMap messageMap;

    // 虚函数,用于子类覆盖消息映射表的初始化
    virtual void initializeMessageMap() = 0;
};

// 按钮控件类
class UIButton : public UIControl
{
public:
    UIButton()
    {
        initializeMessageMap();
    }

    std::function<void()> onClick; // 绑定按钮点击事件的回调函数

protected:
    void initializeMessageMap() override
    {
        messageMap[MessageType::MSG_LBUTTONDOWN] = [this]() { onLButtonDown(); };
        messageMap[MessageType::MSG_LBUTTONUP] = [this]() { onLButtonUp(); };
    }

    void onLButtonDown()
    {
        std::cout << "Button down." << std::endl;
    }

    void onLButtonUp()
    {
        std::cout << "Button up." << std::endl;
        if (onClick)
        {
            onClick(); // 触发点击事件
        }
    }
};

// 模拟的消息循环类
class MessageLoop
{
public:
    void addControl(UIControl* control)
    {
        controls.push_back(control);
    }

    void sendMessage(MessageType msg)
    {
        for (auto control : controls)
        {
            control->handleMessage(msg);
        }
    }

private:
    std::vector<UIControl*> controls;
};

int main()
{
    // 创建消息循环和按钮控件
    MessageLoop loop;
    UIButton button;

    // 绑定按钮的点击事件回调
    button.onClick = []() { std::cout << "Button clicked!" << std::endl; };

    // 添加控件到消息循环
    loop.addControl(&button);

    // 模拟用户操作
    loop.sendMessage(MessageType::MSG_LBUTTONDOWN);
    loop.sendMessage(MessageType::MSG_LBUTTONUP); // 这将触发按钮的点击事件

    return 0;
}

代码解释

  1. MessageType枚举:定义了一个模拟的消息类型,包括MSG_LBUTTONDOWN(鼠标左键按下)、MSG_LBUTTONUP(鼠标左键松开)、MSG_CLICK(点击事件)等。

  2. UIControl类:基础控件类,包含一个消息映射表messageMap,用于将消息映射到对应的处理函数。handleMessage函数根据消息类型查找并调用相应的处理函数。

  3. UIButton类:继承自UIControl,代表一个按钮控件。它重写了initializeMessageMap函数,将鼠标按下和松开的消息映射到处理函数onLButtonDownonLButtonUp。在onLButtonUp中,如果定义了点击事件回调onClick,则会调用它。

  4. MessageLoop类:模拟消息循环机制,包含一个控件列表,并通过sendMessage函数将消息分发给所有控件。每个控件根据自身的消息映射表处理收到的消息。

  5. main函数:在主函数中,创建一个消息循环和一个按钮控件,绑定按钮的点击事件回调,然后模拟用户操作(按下和松开按钮),触发点击事件。

特点和扩展

  • 灵活性:通过使用std::function和lambda表达式,能够灵活地将用户操作与对应的处理逻辑绑定。
  • 可扩展性:可以进一步扩展此机制,添加更多的控件类型(如文本框、滑块等)以及处理更多的消息类型。
  • 18
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值