注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

沙漠里de烟雨

原创分享,禁止转载

 
 
 

日志

 
 

win32小结  

2012-04-24 12:34:26|  分类: C++ |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

VC,事件驱动

SendMessage发送消息给窗口处理函数,阻塞,等待返回。
PostMessage投递消息给消息队列,立即返回。

消息分类:
1)系统消息;0-1023
2)用户自定义消息;
3)应用程序间消息;驱动
4)系统注册消息。MFC

下面主要讲的都是系统消息,

消息队列:存放消息的一个队列
分:
1)系统消息队列:系统维护的消息队列,存放系统产生的消息。
2)程序消息队列:应用程序主线程维护的消息队列,由GetMessage/PeekMessage获取。

系统消息队列中的消息会根据存放的消息找到对应的窗口消息队列,
将消息投递到程序消息队列中,以让用户可以干预消息的默认处理。

消息队列分:
队列消息--消息的发送与获取,都是通过消息队列完成。WM_PAINT,键盘、鼠标、定时器等都是队列消息。
 // WM_QUIT必须进消息队列(不能在窗口处理函数中出现),否则GetMessage无法退出循环(因为它只能在消息队列中取消息)
非队列消息--消息的发送与获取,是直接调用消息的窗口处理函数完成。
 //WM_CREATE  WM_SIZE 必须不进队列。WM_CREATE WM_SIZE都是非队列消息(需要立马处理的,直接交给窗口处理函数来处理)

PostMessage--投递消息到系统消息队列
SendMessage--投递消息到窗口处理函数
 系统会根据不同的消息从系统消息队列取出放入对应的程序消息队列中以让PeekMessage和GetMessage获取
PeekMessage--从消息队列中获取消息(从本程序消息队列中获取)  矛盾:从系统获取消息
GetMessage --从消息队列中获取消息(从本程序消息队列中获取)  矛盾:以查看方式从系统获取消息

消息循环(处理的是队列消息,对于所有的队列消息,都要先进入系统消息队列):
GetMessage从程序的消息队列当中获取消息(队列消息),并将所有队列消息给
TranslateMessage函数预先处理,如果是键盘消息,此函数中通过调用PostMessage将字符消息发送至系统消息队列中,
如果不是键盘消息,将此消息下传到DispatchMessage函数将此消息派发到消息中指定的窗口处理函数中去处理,因为每个窗口都必须要
有窗口处理函数,如果是系统已经注册了的窗口及其处理函数,可以根据自己需要是否要特殊处理,如果要特殊处理,在窗口处理函数中
作处理,因为默认的窗口处理函数优先调用用户自定义的消息处理,而如果要默认处理,系统会处理该消息中所携带的所有信息。

消息循环是整个窗口程序能持续运行的支撑,一旦GetMessage返回值为Flase,程序会因失去支撑而关闭,有时尽管进程仍活着,但对用户
而言已无多大用处了。因为VC程序是以事件驱动起来的程序,用户通过消息机制实现与程序的交互,而由于消息循环不再循环,无法再获得
消息来处理,无法实现交互,程序也失去用处了。故消息循环是整个程序运行的驱动是一点也不为过的。

而对于两个函数而言,GetMessage更是程序的支柱,VC窗口程序是不能对PeekMessage抱有过高的期望的,因为PeekMessage非阻塞这一致命
的弱点,造成VC窗口程序是不能寄望于一个意志不够坚定的消息获取函数的。唯有GetMessage才能让我们的VC窗口程序有所依赖,因为它的
阻塞函数特性才让VC窗口程序不会一闪而过。其实我们的GetMessage不仅能不辞辛苦地去抓取消息,还能产生消息,如WM_PAINT和WM_TIMER。

下面我们就来看消息循环的运行过程:
1)在程序(进程)消息队列查找消息,如果队列有消息,检查消息是否满足指定条件(HWND,ID范围),不满足条件就不会取出消息,
    若满足则从队列中取出消息并返回。
2)如果程序(进程)消息队列没有消息,向系统消息队列获取属于本程序的消息(并非真正去获取,而去咨询,有的话系统消息队列会将所属
 消息发至程序消息队列中)。如果系统队列的当前消息属于本程序,系统会将消息转发到程序队列,程序获取消息返回,处理消息。  
3)如果系统消息队列也没有消息,检查当前窗口的需要重新绘制的区域,如果发现有需要绘制的区域,产生WM_PAINT消息,取得消息返回处理。   
4)如果没有重新绘制区域,检查定时器如果有到时的定时器,产生WM_TIMER,返回处理执行。   
5)如果没有到时的定时器,整理程序的资源、内存等等。   
6)GetMessage会继续等候下一条消息。PeekMessage会返回FALSE,交出程序的控制权。   
注意:当GetMessage如果获取到是WM_QUIT,函数会返回FALSE。此时说明程序需要正常退出了。
拿到了消息,接下来的事就就是将此消息交给TranslateMessage去筛选键盘消息,如果是键盘消息,再将此消息递送给PostMessage投回到系统
消息队列中并即刻返回。如果不是键盘消息,则递给DispatchMessage函数将消息派发给消息所属的窗口的窗口处理函数。

如果任何事件都走队列,因为队列都是先入先出的,由于队列消息是排队的,这就不能完全体现事件驱动的精神:因为有些消息用户是希望消息
一产生就优先交给窗口处理函数去处理,让用户有无延时性的体验,尽管计算机速度很快。但必竟有些事件是要等用户去触发进而得到回馈的。所以
SendMessage就运用而生了,发送消息到指定的窗口,并等候窗口处理函数处理消息完后返回。
而PostMessage就没有这么耐心了,它将消息投递到系统消息队列中就返回,不等候返回。剩下的事就交给GetMessage去获取了并处理了。因为
PostMessage并不关心它投递的消息有没有处理,什么时候处理,这与它无关,这好比是邮件分发员,他只负责将邮件分发出去,而GetMessage则像
快递员,他要等待有人去签收他的邮件,否则不会回去(当然现实并不完全一样,快递员不会笨到为一件没有人签收的快递而死等...)
由此便可知SendMessage发送到消息是非队列消息,PostMessage发送的消息是队列消息。


一、WM_PAINT消息
当窗口需要绘制的时候(无效区域存在),GetMessage会发送消息到消息队列,最后发送至窗口处理函数。注意:绘图必须在WM_PAINT中画。
InvalidateRect( ...) //窗口处于屏幕的最前面, 且窗口没有变化,却想要重新绘制窗口,可以用此函数。
  此消息处理步骤:
 BeginPaint()
  //在此绘图...
 EndPaint()

附:
GetClientRect 获取窗口客户区大小

  BOOL GetClientRect(
     HWND hWnd,      // 窗口句柄
      LPRECT lpRect   // 接收客户区大小的Buff(结构体指针)
   );

  typedef struct _RECT {
     LONG left;
     LONG top;
     LONG right;
    LONG bottom;
  } RECT, *PRECT;

  Ellipse 绘圆:
   BOOL Ellipse(
    HDC hdc,    //绘图设备
    int nLeftRect,  //左上角X坐标(外切圆的正方形的左上角坐标)
    int nTopRect,   //左上角Y坐标
    int nRightRect, //右下角X坐标
    int nBottomRect //右下角Y坐标
       );

二、键盘消息
 WM_KEYDOWN - 按键被按下时产生
 WM_KEYUP - 按键被放开时产生
 WM_SYSKEYDOWN - 系统键按下时产生 ALT、F10
 WM_SYSKEYUP - 系统键放开时产生
 WM_CHAR - 字符消息 (由TranslateMessage产生)
前四种键盘消息的消息参数依赖:-----------------如:OnKeyDown(wParam){switch(wParam){case VK_LEFT:...}}
 WPARAM - 按键的 Virtual Key(虚拟键值)
 LPARAM - 按键的参数,例如按下次数
  Virtual Key有如下:
 VK_LEFT 向左键的键值(37)
 VK_UP  向上键的键值(38)
 VK_RIGHT 向右键的键值(39)
 VK_DOWN 向下键的键值(40)
 VK_E 字母E键的键值(其它字母键类似)
后一种键盘消息:字符消息的消息参数依赖:
 WPARAM - 输入的字符(真正的字符编码)
 LPARAM - 按键的相关参数 
注意使用:
 1)KEYDOWN可以重复出现,KEYUP只能在按键松开时出现1次
 2)TranslateMessage在转换 WM_KEYWODN 消息时,对于可见字符可以产生WM_CHAR,不可见字符无此消息。
 3)WM_KEYWODN/UP的wParam是表示的按键,WM_CHAR是表示输入的字符。

三、鼠标消息
 单击
 WM_LBUTTONDOWN - 鼠标左键按下
 WM_LBUTTONUP - 鼠标左键抬起
 WM_RBUTTONDOWN - 鼠标右键按下
 WM_RBUTTONUP - 鼠标右键抬起
 WM_MOUSEMOVE - 鼠标移动消息(移动的慢产生的数据更新相对更多)
 双击
 WM_LBUTTONDBLCLK - 鼠标左键双击(WM_LBUTTONDOWN、WM_LBUTTONUP、WM_LBUTTONDBLCLK、WM_LBUTTONUP)
 WM_RBUTTONDBLCLK - 鼠标右键双击(WM_RBUTTONDOWN、WM_RBUTTONUP、WM_RBUTTONDBLCLK、WM_RBUTTONUP)
 滚轮--注意:因为之前没有滚轮的消息,所以应该在StdAfx.h中的"#include "windows.h"这行的上一行中添加 #define _WIN32_WINNT 0x0400  //提升系统文件的版本
 WM_MOUSEWHEEL - 鼠标滚轮消息(偏移量最多120)  
单击鼠标消息参数依赖:
 WPARAM - 其他按键的状态,例如 Ctrl/Shift等
 LPARAM - 鼠标的位置,窗口客户区坐标系。
  LOWORD X坐标位置
  HIWORD Y坐标位置
双击鼠标消息参数依赖:
 WPARAM - 其他按键的状态,例如 Ctrl/Shift等
 LPARAM - 鼠标的位置,窗口客户区坐标系。
  LOWORD X坐标位置
  HIWORD Y坐标位置

注意:需要在注册窗口类的时候添加CS_DBLCLKS风格
消息产生顺序
 以WM_LBUTTONDBLCLK为例:
  WM_LBUTTONDOWN
  WM_LBUTTONUP
  WM_LBUTTONDBLCLK
  WM_LBUTTONUP

滚动消息参数依赖:
 WPARAM:
  LOWORD - 其他按键的状态
  HIWORD - 滚轮的偏移量,是120的倍数
   通过正负值表示表示滚动方向。
   正:向前滚动
   负:向后滚动
 LPARAM:鼠标当前的位置,屏幕坐标系
  LOWORD - X坐标
  HIWORD - Y坐标
通过偏移量,获取滚动的方向和倍数。

四、定时器消息

可以在程序中设置定时器,当到达时间间隔时,定时器会向程序发送一个 WM_TIMER 消息,要处理的事件则放在WM_TIMER消息处理函数中。
因为一般需要一开始就计时,故SetTimer应将放在 WM_CREATE 的消息处理函数中。
SetTimer(...定时处理函数)
KillTimer(...定时处理函数)   //可以在其它处理函数中关闭定时器,看需要。

五、菜单
 窗口的顶层菜单(标题栏下的菜单) --------------------依赖消息:WM_COMMAND
 弹出式菜单(右键弹出式菜单,标题栏下弹出的菜单) ----依赖消息:WM_COMMAND
 系统菜单(标题栏上单击弹出的菜单)----------------依赖消息:WM_SYSCOMMAND
HMENU类型表示菜单,菜单每一项有相应的ID
消息依赖参数:
 WPARAM:
  HIWORD - 对于菜单项为0,对于加速键为1
  LOWORD - 菜单项的ID
获取WPARAM菜单ID,根据ID处理。
CheckMenuItem( )  //动态改变菜单项的状态
WM_INITMENUPOPUP
在菜单被激活但是未显示前,窗口会收到这个WM_INITMENUPOPUP消息     //以此动态改变菜单的状态.
 WPARAM - 菜单句柄(弹出式菜单句柄)   //弹出式菜单不是窗口菜单,顶层菜单和系统菜单(就是标题栏那里的弹出式菜单)都是窗口菜单
 LPARAM - LOWORD 是菜单项索引(顶层菜单句柄) 从0至n
  HIWORD - 是窗口菜单标识(是1,否0)   顶层菜单和系统菜单时均为1,弹出式菜单时为0 .

系统菜单命令响应
 通过WM_SYSCOMMAND响应菜单命令。如: OnSysCommand(hWnd,wParam)
 WPARAM的LOWORD是命令ID

右键菜单是一个弹出式菜单,使用CreatePopupMenu创建
 TrackPopupMenu是阻塞函数 (当右键菜单弹出来后,进入阻塞,右键菜单消失后才能解除阻塞)
消息依赖:WM_COMMAND,与窗口菜单一致
 如果Track设置了TPM_RETURNCMD选项,那么点击的菜单项ID通过函数的返回值获取,并且不发出WM_COMMAND消息。如果不设置此选项,会发出WM_COMMAND消息.
WM_INITMENUPOPUP,按照弹出菜单处理
菜单处理位置
  鼠标右键抬起
  WM_RBUTTONUP 鼠标右键消息窗口坐标系坐标,要使用需要转换成屏幕坐标系ClientToScreen ScreenToClient

  WM_CONTEXTMENU    //对右键菜单处理更专业,因为不用转换坐标系.
   wParam - 右键点击的窗口句柄
   LPARAM - LOWORD X坐标,屏幕坐标系
   HIWORD Y坐标, 屏幕坐标系
  WM_CONTEXTMENU消息是在WM_RBUTTONUP消息之后产生。

六、资源:
菜单资源MENU  ----消息依赖:WM_COMMAND
LoadMenu(  )
SetMenu(  )
图标资源ICON  ----消息依赖:WM_SETICON
LoadIcon(  )
DrawIcon(  )
光标资源CURSOR----消息依赖:WM_SETCURSOR
LoadCursor(  )
SetCursor(  )
SetCapture(  )
ReleaseCapture(  )
LoadCursorFromFile(  )
GetCursorPos(  )
字符串资源
LoadString(  )
加速键资源---消息依赖:WM_COMMAND
 资源添加加速键表,增加命令ID对应的加速键
LoadAccelerators( )
TranslateAccelerator()
TranslateAccelerator处理过程
 检测消息是否是WM_KEYDOWN\WM_SYSKEYDOW,获取按键状态
 根据按键状态,从HACCEL中查找对应命令ID
 找到对应ID,发送WM_COMMAND消息,处理ID所对应的命令
如: void Message(HWND hWnd)
  {
   HACCEL hAccel =  LoadAccelerators(g_hInstance,MAKEINTRESOURCE(IDR_ACCELERATOR1));
   MSG nMsg = {0};
   while(GetMessage(&nMsg,NULL,0,0))
   {
    if(!TranslateAccelerator(hWnd,hAccel,&nMsg))
    {
     TranslateMessage(&nMsg);
     DispatchMessage(&nMsg);
    }
   }
  }

七、绘图对象--WM_PAINT
(颜色、点、直线、圆形、弧线、封闭图形[矩形、圆、圆角矩形、扇形])
画笔
画刷
位图

坐标系与坐标系映射
设备坐标系:屏幕坐标系、窗口坐标系、客户区坐标系
逻辑坐标系

文字和字体
文字颜色和背景
字体

八、对话框----消息依赖:WM_INITDIALOG
对话框窗口处理函数 DialogProc
模式对话框DialogBox(阻塞函数)
无模式对话框CreateDialog(非阻塞函数)
普通窗口CreateWindow/Ex
其中,无模式对话框/普通窗口 - DestroyWindow(可以向系统中发送WM_DESTROY消息,用的是postMessage)

九、子控件
GetDlgItem

十、静态框--消息依赖WM_COMMIT。  STM_SETICON

十一、按钮
下压式按钮
分组框
复选框
单选框

十、编辑框(EDIT)---消息依赖:WM_SETFONT  

十一、组合框-----WM_COMMAND

十二、滚动条

十三、列表框

十四、windows库程序
静态库
动态库

十五、windows文件

十六、windows内存管理
堆内存
栈内存
内存映射文件

十七、Windows进程及线程

十八、线程的同步
 原子锁
 临界区(段)
 事件
 互斥
 信号量
 等候计时器


  评论这张
 
阅读(1067)| 评论(0)
推荐

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018