《MFC程序开发参考大全》学习笔记_1

Windows应用程序是以消息为基础、以事件为驱动的应用程序。应用程序在运行时,会不断的等待操作系统发送给自身的消息(利用一个while循环),然后依据不同的消息进行相应的处理(switch语句)。

接收并处理消息的主角是窗口,每一个窗口都有一个负责处理消息的函数——称“窗口函数”或“回调函数”。当窗口获得一个消息时,“窗口函数”会判断消息的类型,并确定如何处理消息。

1.消息结构

typedef struct tagMSG
{
    HWND hWnd;
    UINT message;
    WPARAM wParam;
    LPARAM lParam;
    DWORD time;
    POINT pt;
}MSG;
  • hWnd:窗口句柄,操作系统可以通过它将消息分派到正确的窗口中(即可以确定是哪个窗口有此消息)。句柄起到标识对象的作用,系统在创建内核对象(进程、线程、事件等)或GDI对象(窗口、图标、光标等)时,会为其分配内存,同事返回这些对象的标识,即句柄。此后,程序对这些对象进行的操作,都是通过句柄进行的。
  • message:消息标识符,确定是哪个消息。例如:WM_PAINT、WM_CREATE。
  • wParam:表示消息的附加信息。
  • lParam:表示消息的附加信息。

例如:以消息WM_MOUSEMOVE为例,wParam表示的是被用户按下的虚拟键;lParam的低字节表示鼠标的横坐标,高字节表示鼠标的纵坐标。如果要查看某一个具体消息的附加信息所表示的含义,可以在MSDN中查看。

  • time:消息被放入消息队列中的时间。
  • pt:消息被放入消息队列的鼠标位置(以屏幕坐标表示)。

2.消息类型

Windows系统将消息分为两大类,一类是系统预定义的消息,一类是用户自定义的消息。

系统消息由前缀和后缀两部分组成,前缀表示处理该消息的窗口类别,后缀描述消息的内容。例如:WM_CLOSE,"WM"表示处理该消息的窗口为一般窗口,"CLOSE"表示窗口关闭。

用户自定义消息的时候,应保证消息值是唯一的,不能与系统中定义的消息冲突。系统保留了0x0000~0x03FF的消息值,应用程序不能使用。用户可以使用0x0400(WM_USER)~0x7FFF的消息值。

3.消息队列

为了保证消息不被遗失,操作系统必须为应用程序暂时存储消息,等待应用程序依次进行处理。为此,操作系统为应用程序建立了消息队列。每当有消息发生时,系统将消息分派到消息队列中,等待应用程序从中取出来进行处理。

消息队列是由线程拥有的,应用程序是一个进程,进程除了拥有主线程外,还可以创建多个线程。线程一旦调用了与GDI有关的函数或检查消息队列的时候,系统就会为线程建立消息队列。系统为线程建立消息队列,实际是分配一个THREADINFO结构的数据,使其与线程关联。

问:消息时如何传递到消息队列中的?

答:可以通过调用PostMessage函数将消息放入到线程的等级消息队列中。

BOOL PostMessage(
    HWND hWnd,      //窗口句柄
    UINT Msg,       //消息标识
    WPARAM wParam,  //消息附加信息
    LPARAM lParam   //消息附加信息 
);

在调用PostMessage函数时,系统首先确定是哪个线程创建了hWnd参数标识的窗口(如果判断某一个窗口是由哪个线程创建的,可以调用GetWindowThreadProcessId函数实现,该函数的返回值是创建窗口的线程ID),然后系统分配一块内存区域,将消息信息存储在内存区域中,并将该内存区域添加到线程的等级消息队列中。PostMessage函数在执行后会立即返回,因此,调用该函数的线程并不知道放入到登记消息队列中的消息是否被处理了。

系统除了为每个线程维护一个消息队列,还维护一个全局的消息队列,称为系统硬件输入队列,用于存储系统中硬件触发的消息。在系统初始化时,会建立一个特殊的线程——原始输入线程(Raw Input Thread, RIT),并且建立一个系统硬件输入队列(System Hardware Input Queue, SHIQ)。RIT与SHIQ构成了系统的硬件输入模型核心。

问:系统硬件消息是如何传递到线程的消息队列的?

答:以鼠标消息为例,系统通过设备驱动程序将消息放入到SHIQ,此时,RIT会唤醒,通过当前鼠标光标之下的窗口,获取创建窗口的线程ID,然后将鼠标消息放入到线程的虚拟输入队列中。

4.消息循环

拥有消息队列的线程,需要不断的从消息队列中取出消息,进行处理。在处理消息的过程中,可能会有新的消息被放入到消息队列中,如此反复构成消息循环。

MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

GetMessage函数从当前的线程消息队列中获取消息,将消息信息存储在参数中。

BOOL GetMessage(
    LPMSG lpMsg,       //是MSG结构指针,用于存储获取的消息信息
    HWND hWnd,         //表示函数返回与指定窗口有关的消息,为NULL时,函数返回与所有窗口有关的消息
    UINT wMsgFilterMin,//表示返回消息的最小值 
    UINT wMsgFilterMax //表示返回消息的最大值
);

函数返回值:如果获得的消息为WM_QUIT,返回值为零(即FALSE,消息循环函数结束),如果函数执行过程中出现错误,返回值为-1(消息循环不会结束),其他情况返回值为非零。

TranslateMessage函数用于将虚拟键消息转换为字符消息。该函数检查是否有WM_KEYDOWN或WM_SYSKEYDOWN消息从虚拟输入队列中取出,如果有,系统检测虚拟键是否能够转换为等价的字符,如果能够转换,则TranslateMessage函数调用PostMessage函数将WM_CHAR或WM_SYSCHAR消息放入到登记消息队列中。下次执行GetMessage函数时,发现登记消息队列中有WM_CHAR或WM_SYSCHAR消息,将其取出并返回。

DispatchMessage函数用于将获取的消息回传给操作系统,由于消息中已经包含了窗口的句柄等信息,因此操作系统能够正确的调用窗口函数处理消息。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值