VC++6.0消息含义.doc_第1页
VC++6.0消息含义.doc_第2页
VC++6.0消息含义.doc_第3页
VC++6.0消息含义.doc_第4页
VC++6.0消息含义.doc_第5页
已阅读5页,还剩14页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

消息的意思1. 窗口过程 每个窗口会有一个称为窗口过程的回调函数(WndProc),它带有四个参数,分别为:窗口句柄(Window Handle),消息ID(Message ID),和两个消息参数(wParam, lParam), 当窗口收到消息时系统就会调用此窗口过程来处理消息。(所以叫回调函数) 2 消息类型 1) 系统定义消息(System-Defined Messages) 在SDK中事先定义好的消息,非用户定义的,其范围在0x0000, 0x03ff之间, 可以分为以下三类: 1 窗口消息(Windows Message) 与窗口的内部运作有关,如创建窗口,绘制窗口,销毁窗口等。可以是一般的窗口,也可以是Dialog,控件等。 如:WM_CREATE, WM_PAINT, WM_MOUSEMOVE, WM_CTLCOLOR, WM_HSCROLL. 2 命令消息(Command Message) 与处理用户请求有关, 如单击菜单项或工具栏或控件时, 就会产生命令消息。 WM_COMMAND, LOWORD(wParam)表示菜单项,工具栏按钮或控件的ID。如果是控件, HIWORD(wParam)表示控件消息类型 3 控件通知(Notify Message) 控件通知消息, 这是最灵活的消息格式, 其Message, wParam, lParam分别为:WM_NOTIFY, 控件ID,指向NMHDR的指针。NMHDR包含控件通知的内容, 可以任意扩展。 2) 程序定义消息(Application-Defined Messages) 用户自定义的消息, 对于其范围有如下规定: WM_USER: 0x0400-0x7FFF (ex. WM_USER+10) WM_APP(winver 4.0): 0x8000-0xBFFF (ex.WM_APP+4) RegisterWindowMessage: 0xC000-0xFFFF 3 消息队列(Message Queues) Windows中有两种类型的消息队列 1) 系统消息队列(System Message Queue) 这是一个系统唯一的Queue,设备驱动(mouse, keyboard)会把操作输入转化成消息存在系统队列中,然后系统会把此消息放到目标窗口所在的线程的消息队列(thread-specific message queue)中等待处理 2) 线程消息队列(Thread-specific Message Queue) 每一个GUI线程都会维护这样一个线程消息队列。(这个队列只有在线程调用GDI函数时才会创建,默认不创建)。然后线程消息队列中的消息会被送到相应的窗口过程(WndProc)处理. 注意: 线程消息队列中WM_PAINT,WM_TIMER只有在Queue中没有其他消息的时候才会被处理,WM_PAINT消息还会被合并以提高效率。其他所有消息以先进先出(FIFO)的方式被处理。一、队列消息(Queued Messages)和非队列消息(Non-Queued Messages) 1)队列消息(Queued Messages) 消息会先保存在消息队列中,消息循环会从此队列中取消息并分发到各窗口处理 如鼠标,键盘消息。 2) 非队列消息(NonQueued Messages) 消息会绕过系统消息队列和线程消息队列直接发送到窗口过程被处理 如: WM_ACTIVATE(刺激,激活), WM_SETFOCUS(焦点、焦距), WM_SETCURSOR(游标、指针), WM_WINDOWPOSCHANGED(), 注意: postMessage发送的消息是队列消息,它会把消息Post到消息队列中; SendMessage发送的消息是非队列消息, 被直接送到窗口过程处理 二、5 PostMessage(PostThreadMessage), SendMessage PostMessage:把消息放到指定窗口所在的线程消息队列中后立即返回。 PostThreadMessage:把消息放到指定线程的消息队列中后立即返回。 SendMessage:直接把消息送到窗口过程处理, 处理完了才返回。 三、GetMessage, PeekMessage PeekMessage会立即返回 可以保留消息 GetMessage在有消息时返回 会删除消息四、TranslateMessage, TranslateAccelerator TranslateMessage: 把一个virtual-key消息转化成字符消息(character message),并放到当前线程的消息队列中,消息循环下一次取出处理。 TranslateAccelerator: 将快捷键对应到相应的菜单命令。它会把WM_KEYDOWN 或 WM_SYSKEYDOWN转化成快捷键表中相应的WM_COMMAND 或WM_SYSCOMMAND消息, 然后把转化后的 WM_COMMAND或WM_SYSCOMMAND直接发送到窗口过程处理, 处理完后才会返回。五、(消息死锁( Message Deadlocks) 假设有线程A和B, 现在有以下下步骤 1) 线程A SendMessage给线程B, A等待消息在线程B中处理后返回 2) 线程B收到了线程A发来的消息,并进行处理, 在处理过程中,B也向线程A SendMessgae,然后等待从A返回。 因为此时, 线程A正等待从线程B返回, 无法处理B发来的消息, 从而导致了线程A,B相互等待, 形成死锁。多个线程也可以形成环形死锁。 可以使用 SendNotifyMessage()、SendMessageTimeout()或SendMessageCallback()来避免出现死锁。 六、BroadcastSystemMessage 我们一般所接触到的消息都是发送给窗口的, 其实, 消息的接收者可以是多种多样的,它可以是应用程序(applications), 可安装驱动(installable drivers), 网络设备(network drivers), 系统级设备驱动(system-level device drivers)等, BroadcastSystemMessage这个API可以对以上系统组件发送消息。对Windows操作系统下的消息运行机制做较为深入的剖析一、 Windows事件驱动机制DOS和Windows这两种操作系统的运行机制是截然不同的,DOS下的任何程序都是使用顺序的、过程驱动的程序设计方法。这种程序都有一个明显的开始、明显的过程以及一个明显的结束,因此通过程序就能直接控制程序事件或过程的全部顺序。即使是在处理异常时,处理过程也仍然是顺序的、过程驱动的结构。而Windows的驱动方式则是事件驱动的,即程序的流程不是由事件的顺序来控制,而是由事件的发生来控制,所有的事件是无序的,所为一个程序员,在编写程序时,并不知道用户会先按下哪个按纽,也就不知道程序先触发哪个消息。因此我们的主要任务就是对正在开发的应用程序要发出的或要接收的消息进行排序和管理。事件驱动程序设计是密切围绕消息的产生与处理而展开的,一条消息是关于发生的事件的消息。二、 Windows的消息循环Windows操作系统为每一个正在运行的应用程序保持有一个消息队列。当有事件发生后,Windows并不是将这个激发事件直接送给应用程序,而是先将其翻译成一个Windows消息,然后再把这个消息加入到这个应用程序的消息队列中去。应用程序需要通过消息循环来接收这些消息。在MFC中使用了对 WinAPI进行了很好封装的类库,虽然可以为编程提供一个面向对象的界面,使Windows程序员能够以面象对象的方式进行编程,把那些进行SDK编程时最繁琐的部分提供给程序员,使之专注于功能的实现,但是由于引入了很好的封装特性,使我们不能直接操纵部分核心代码。对于消息的循环和接收也只是通过类似于下面的消息映射予以很简单的表示: BEGIN_MESSAGE_MAP(CTEMMSView, CFormView) / AFX_MSG_MAP(CTEMMSView) ON_WM_LBUTTONDOWN() ON_COMMAND(ID_OPENDATA, OnOpenData) ON_WM_TIMER() ON_WM_PAINT() / AFX_MSG_MAP END_MESSAGE_MAP() 虽然上述消息映射在编程过程中处理消息非常简练方便,但显然是难于理解消息是如何参与循环和分发的。因此有必要通过SDK(Software Developers Kit,软件开发工具箱)代码深入到被MFC封装的Windows编程的核心中来研究其具体是如何工作的。在SDK编程中,一般是在Windows应用程序的入口点WinMain函数中添加处理消息循环的代码以检索Windows送来的消息,然后WinMain再把这些消息分配给相应的窗口函数并处理它们: MSG msg; /定义消息名 while (GetMessage (& msg, NULL, 0, 0) TranslateMessage (& msg) ; /翻译消息 DispatchMessage (& msg) ; /撤去消息 return msg.wParam ; 上述几句虽然简单但却是所有Windows程序的关键代码,担负着获取、解释和分发消息的任务,下面就重点对其功能和作用进行分析: MSG结构在头文件中定义如下: typedef struct tagMSG HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; MSG, *PMSG; 其数据成员的具体意义如下: hwnd:消息将要发送到的那个窗口的句柄,用这个参数可以决定让哪个窗口接收消息。 message:消息号,它唯一标识了一种消息类型。每种消息类型都在Windows文件进行了预定义。 wParam:一个32位的消息参数,这个值的确切意义取决于消息本身。 lParam:同上。 time:消息放入消息队列中的时间,在这个域中写入的并非当时日期,而是从Windows启动后所测量的时间值。Windows用 这个域来使用消息保持正确的顺序。 pt:消息放入消息队列时的鼠标坐标。消息循环以GetMessage调用开始,它从消息队列中取出一个消息。该函数的四个参数可以有控制地获取消息,第一个参数指定要接收消息的MSG结构的地址,第二个参数表示窗口句柄,一般将其设置为空,表示要获取该应用程序创建的所有窗口的消息;第三、四参数用于指定消息范围。后面三个参数被设置为默认值,用于接收发送到属于这个应用程序的任何一个窗口的所有消息。在接收到除WM_QUIT之外的任何一个消息后,GetMessage()返回 TRUE;如果GetMessage收到一个WM_QUIT消息,则返回FALSE以退出消息循环,终止程序运行。因此,在接收到WM_QUIT之前,带有GetMessage()的消息循环可以一直循环下去。当除WM_QUIT的消息用GetMessage读入后,首先要经过函数 TranslateMessage()对其进行解释,但对大多数消息来说并不起什么作用。这里起关键作用的是DispatchMessage()函数,把由GetMessage获取的Windows消息传送给在MSG结构中为窗口所指定的窗口过程。在消息处理函数处理完消息之后,代码又循环到开始去接收另一个消息,这样就完成了一个完整的消息循环。由于Windows操作系统是一种非剥夺式多任务操作系统。只有在应用程序主动交出CPU控制权后,Windows才能把控制权交给其他应用程序。在消息循环中,一定要有能交出控制的系统函数才能实现协同式多任务操作。能完成该功能的只有GetMessage、PeekMessage和 WaitMessage这三个函数,如果在应用程序中长期不去调用这三个函数之一其他任务则无法执行。GetMessage函数在找不到等待应用程序处理的消息时,会自动交出控制权,由Windows把CPU的控制权交给其他等待获取控制权的应用程序。由于任何Windows应用程序都含有一个消息循环,这种隐式交出控制权的方式可以保证合并各个应用程序共享控制权。一旦发往该应用程序的消息到达应用程序队列,即开始执行GetMessage语句的下一条语句。使用GetMessage函数的消息循环在消息队列中没有消息时将等待,如果需要,可以利用这段时间进行I/O端口操作等耗时操作,不过需要在消息循环中使用PeekMessage函数来代替GetMessage。使用PeekMessage的方法同GetMessage类似,下面是一段使用 PeekMessage函数的消息循环的典型例子:同时和GetMessage作比较PeekMessage函数的消息循环的典型例子: MSG msg; BOOL bDone=FALSE; do if(PeekMessage(& msg,NULL,0,0,PM_REMOVE) if(msg.message=WM_QUIT) bDone=TRUE; else TranslateMessage(& msg); DispatchMessage(& msg); /无消息处理,进行长时间操作 else /长时间操作 while(!bDone) 无论应用程序消息队列中是否有消息,PeekMessage函数都立即返回,如果希望等待新消息入队,可以利用无返回值的函数WaitMessage配合PeekMessage进行消息循环。三、 对Windowds消息的处理窗口过程处理消息通常以switch语句开始,对于它要处理的每一条消息ID都跟有一条case语句,这在功能上同MFC的消息映射有些类似: switch(uMsgId) case WM_TIMER: /对WM_TIMER定时器消息的处理过程 return 0; case WM_LBUTTONDOWN: /对WM_ LBUTTONDOWN鼠标左键单击消息的处理过程 ruturn 0; default: /其他消息由这个默认处理函数来处理 return DefWindowProc(hwnd,uMsgId,wParam,lParam); 在处理完消息后必须返回0,这很重要,否则Windows将要不停地重试下去。对于那些在程序中不准备处理的消息,窗口过程会把它们都扔给 DefWindowProc进行缺省处理,而且还要返回那个函数的返回值。在消息传递层次中,可以认为DefWindowProc函数是最顶层的函数。该函数发出WM_SYSCOMMAND消息,由系统执行Windows环境中多数窗口所公用的各种通用操作,如更新窗口的正文标题等等。在MFC下可以用下述部分代码实现与上述SDK代码相同的功能: BEGIN_MESSAGE_MAP(CTEMMSView, CFormView) / AFX_MSG_MAP(CTEMMSView) ON_WM_LBUTTONDOWN() ON_WM_TIMER() / AFX_MSG_MAP END_MESSAGE_MAP()A、消息的是什么样子的?消息由一个叫MSG的结构体定义,包括窗口句柄(HWND),消息ID(UINT),参数(WPARAM, LPARAM)等等:struct MSG HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; ;消息ID是消息的类型标识符,由系统或应用程序定义,消息ID为消息划分了类型。同时,也可以看出消息是对应于特定的窗口(窗口句柄)的。(此话并不完全对,其实我们除了可以向某个窗口投递消息,也可以向某个线程投递消息,这个时候,消息是没有对应的窗口句柄的,句柄为空,要处理此类消息,必须在GetMessage或PostMessage之后,判断窗口句柄是否为空,如果为空,就要处理该线程消息,不必再往下DispatchMessage 。B、消息是如何分类的?其前缀都代表什么含义?消息ID只是一个整数,Windows系统预定义了很多消息ID,以不同的前缀来划分,比如WM_*,CB_*等等。具体见下表:Prefix Message category ABM Application desktop toolbar BM Button control CB Combo box control CBEM Extended combo box control CDM Common dialog box DBT Device DL Drag list box DM Default push button control DTM Date and time picker control EM Edit control HDM Header control HKM Hot key control IPM IP address control LB List box control LVM List view control MCM Month calendar control PBM Progress bar PGM Pager control PSM Property sheet RB Rebar control SB Status bar window SBM Scroll bar control STM Static control TB Toolbar TBM Trackbar TCM Tab control TTM Tooltip control TVM Tree-view control UDM Up-down control WM General window 应用程序可以定义自己的消息,其取值范围必须大于WM_USER。如何通过消息传递任何参数?Windows系统的消息机制都包含2个长整型的参数:WPARAM, LPARAM,可以存放指针,也就是说可以指向任何内容了。C、消息队列和线程的关系是什么?消息队列的结构是什么样子的?Windows系统本身会维护一个唯一的消息队列,以便于发送给各个线程,这是系统内部的实现方式。而对于线程来说,每个线程可以拥有自己的消息队列,它和线程一一对应。在线程刚创建时,消息队列并不会被创建,而是当GDI的函数调用发生时,Windows系统才认为有必要为线程创建消息队列。消息队列包含在一个叫THREADINFO的结构中,有四个队列:Sent Message QueuePosted Message QueueVisualized Input QueueReply Message Queue之所以维护多个队列,是因为不同消息的处理方式和处理顺序是不同的。线程和窗口是一一对应的吗?如果想要有两个不同的窗口对消息作出不同反应,但是他们属于同一个线程,可能吗?窗口由线程创建,一个线程可以创建多个窗口。窗口可由CreateWindow()函数创建,但前提是需要提供一个已注册的窗口类(Window Class),每一个窗口类在注册时需要指定一个窗口处理函数(Window Procedure),这个函数是一个回调函数,就是用来处理消息的。而由一个线程来创建对应于不同的窗口类的窗口是可以的。由此可见,只要注册多个窗口类,每个窗口都可以拥有自己的消息处理函数,而同时,他们属于同一个线程。如何发送消息?消息的发送终归通过函数调用,比较常用的有PostMessage(),SendMessage(),另外还有一些Post*或Send*的函数。函数的调用者即发送消息的人。SendMessage()和PostMessage()的原型:LRESULT SendMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT PostMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);SendMessage()要求立即处理,所以它会直接调用窗口的消息处理函数(Window Procedure),完成后返回处理结果。但这仅限于线程内的情况,跨线程时它调不到处理函数,只能把消息发送到接收线程的队列Sent Message Queue里。如果接收线程正在处理别的消息,那么它不会被打断,直到它主动去获取队列里的下一条消息时,它会拿到这一条消息,并开始处理,完成后他会通知发送线程结果消息可以不进队列吗?什么消息不进队列?可以。实际上MSDN把消息分为队列型(Queued Message)和非队列型(Non-queued Message),这只是不同的路由方式,但最终都会由消息处理函数来处理。队列型消息包括硬件的输入(WM_KEY*等)、WM_TIMER消息、WM_PAINT消息等;非队列型的一些例子有WM_SETFOCUS, WM_ACTIVE, WM_SETCURSOR等,它们被直接发送给处理函数。其实,按照MSDN的说法和消息的路由过程可以理解为,Posted Message Queue里的消息是真正的队列型消息,而通过SendMessage()发送到消息,即使它进入了Sent Message Queue,由于SendMessage要求的同步处理,这些消息也应该算非队列型消息。也许,Windows系统会特殊处理,使消息强行绕过队列。谁来发送消息?硬件输入是如何被响应的?消息可以由Windows系统发送,也可以由应用程序本身;可以向线程内发送,也可以夸线程。主要是看发送函数的调用者。对于硬件消息,Windows系统启动时会运行一个叫Raw Input Thread的线程,简称RIT。这个线程负责处理System Hardware Input Queue(SHIQ)里面的消息,这些消息由硬件驱动发送。RIT负责把SHIQ里的消息分发到线程的消息队列里面,那RIT是如何知道该发给谁呢?如果是鼠标事件,那就看鼠标指针所指的窗口属于哪个线程,如果是键盘那就看哪个窗口当前是激活的。一些特殊的按键会有所不同,比如 Alt+Tab,Ctrl+Alt+Del等,RIT能保证他们不受当前线程的影响而死锁。RIT只能同时和一个线程关联起来。有可能,Windows系统还维护了除SHIQ外地其他队列,分发给线程的队列,或者直接发给窗口的处理函数。消息循环是什么样子?线程何时挂起?何时醒来?想象一个通常的Windows应用程序启动后,会显示一个窗口,它在等待用户的操作,并作出反应。它其实是在一个不断等待消息的循环中,这个循环会不断去获取消息并作出处理,当没有消息的时候线程会挂起进入等待状态。这就是通常所说的消息循环。一个典型的消息循环如下所示(注意这里没有处理GetMessage出错的情况):while(GetMessage(&msg, NULL, 0, 0 ) != FALSE) TranslateMessage(&msg); DispatchMessage(&msg); 这里GetMessage()从队列里取出一条消息,经过TranslateMessage(),主要是将虚拟按键消息(WM_KEYDOWN等)翻译成字符消息(WM_CHAR等)。DispatchMessage()将调用消息处理函数。这里有一个灵活性,消息从队列拿出之后,也可以不分发,进行一些别的特殊操作。下面在看看GetMessage()的细节:BOOL GetMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax );GetMessage()会从队列中取出消息,填到MSG结构中通过参数返回。如果此时的消息是WM_QUIT,也就标识线程需要结束,则 GetMessage()返回FALSE,那么while循环会终止。返回TRUE表示取到其他消息,可以继续循环并运行里面的内容。如果返回-1表示 GetMessage()出错。其他几个参数是用来过滤消息的,可以指定接收消息的窗口,以及确定消息的类型范围。这里还需要提到一个概念是线程的Wake Flag,这是一个整型值,保存在THREADINFO里面和4个消息队列平级的位置。它的每一位(bit)代表一个开关,比如QS_QUIT, QS_SENDMESSAGE等等,这些开关根据不同的情况会被打开或关闭。GetMessage()在处理的时候会依赖这些开关。GetMessage()的处理流程如下:1. 处理Sent Message Queue里的消息,这些消息主要是由其他线程的SendMessage()发送,因为他们不能直接调用本线程的处理函数,而本线程调用 SendMessage()时会直接调用处理函数。一旦调用GetMessage(),所有的Sent Message都会被处理掉,并且GetMessage()不会返回;2. 处理Posted Message Queue里的消息,这里拿到一个消息后,GetMessage()将它拷贝到MSG结构中并返回TRUE。注意有三个消息WM_QUIT, WM_PAINT, WM_TIMER会被特殊处理,他们总是放在队列的最后面,直到没有其他消息的时候才被处理,连续的WM_PAINT消息甚至会被合并成一个以提高效率。从后面讨论的这三个消息的发送方式可以看出,使用Send或Post消息到队列里情况不多。3. 处理QS_QUIT开关,这个开关由PostQuitMessage()函数设置,表示线程需要结束。这里为什么不用Send或Post一个 WM_QUIT消息呢?据称:一个原因是处理内存紧缺的特殊情况,在这种情况下Send和Post很可能失败;其次是可以保证线程结束之前,所有Sent 和Posted消息都得到了处理,这是因为要保证程序运行的正确性,或者数据丢失?不得而知。如果QS_QUIT打开,GetMessage()会填充一个WM_QUIT消息并返回FALSE。4. 处理Virtualized Input Queue里的消息,主要包括硬件输入和系统内部消息,并返回TRUE;5. 再次处理Sent Message Queue,来自MSDN却没有解释。难道在检查2、3、4步骤的时候可能出现新的Sent Message?或者是要保证推后处理后面两个消息;6. 处理QS_PAINT开关,这个开关只和线程拥有的窗口的有效性(Validated)有关,不受WM_PAINT的影响,当窗口无效需要重画的时候这个开关就会打开。当QS_PAINT打开的时候,GetMessage()会返回一个WM_PAINT消息。处理QS_PAINT放在后面,因为重绘一般比较慢,这样有助于提高效率;7. 处理QS_TIMER开关,和QS_PAINT类似,返回WM_TIMER消息,之所以它放在QS_PAINT之后是因为其优先级更低,如果Timer消息要求重绘但优先级又比Paint高,那么Paint就没有机会运行了。如果GetMessage()中任何消息可处理,GetMessage()不会返回,而是将线程挂起,也就不会占用CPU时间了。类似的WaitMessage()函数也是这个作用。还有一个PeekMessage(),其原型为:BOOL PeekMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg );它的处理方式和GetMessage()一样,只是多了一个参数wRemoveMsg,可以指定是否移除队列里的消息。最大的不同应该是,当没有消息可处理时,PeekMessage()不是挂起等待消息的到来,而是立即返回FALSE。WM_DESTROY, WM_QUIT, WM_CLOSE消息有什么不同?而其他两个消息是关于窗口的,WM_CLOSE会首先发送,一般情况程序接到该消息后可以有机会询问用户是否确认关闭窗口,如果用户确认后才调用 DestroyWindow()销毁窗口,此时会发送WM_DESTROY消息,这时窗口已经不显示了,在处理WM_DESTROY消息是可以发送 PostQuitMessage()来设置QS_QUIT开关,WM_QUIT消息会由GetMessage()函数返回,不过此时线程的消息循环可能也即将结束。窗口内的消息的路由是怎样的?窗口和其控件的关系是什么?一个窗口(Window)可以有一个Parent属性,对一个Parent窗口来说,属于它的窗口被称为子窗口(Child Window)。控件(Control)或对话框(Dialog)也是窗口,他们一般属于某个父窗口。所有的窗口都有自己的句柄(HWND),消息被发送时,这个句柄就已经被指定了。所以当子窗口收到一个消息时,其父窗口不会也收到这个消息,除非子窗口手动的转发。关于更详细的窗口和控件,会在另一篇中讨论。PS:关系的消息的一个很详细的描述文章在最新新出的windows编程启示录里面有。作者模拟了 SendMessage(),PostMessage()PeekMessage(),GetMessage()的实现,相当于给出了源码,一目了然!谁来处理消息?消息处理函数能发送消息么?由消息处理函数(Window Procedure)来处理。消息处理函数是一个回调函数,其地址在注册窗口类的时候注册,只有在线程内才能调用。其原型为:typedef LRESULT (CALLBACK* WNDPROC)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);处理函数内部一般是一个switch-case结构,来针对不同的消息类型进行处理。Windows系统还为所有窗口预定义了一个默认的处理函数 DefWindowProc(),它提供了最基本的消息处理,一般在不需要特殊处理的时候(即在switch的default分支)会调用这个函数。由同一个窗口类创建的一组窗口共享一个消息处理函数,所以在编写处理函数的时候要小心处理窗口实例的局部变量。处理函数里可以发送消息,但是可以想象有可能出现循环。另外处理函数还常常被递归调用,所以要减少局部变量的使用,以避免递归过深是栈溢出。消息是指什么? 消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉。一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向Windows发出一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。 消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息。例如,对于单击鼠标所产生的消息来说,这个记录中包含了单击鼠标时的坐标。这个记录类型叫做MSG,MSG含有来自windows应用程序消息队列的消息信息,它在Windows中声明如下:typedef struct tagMsg HWND hwnd; 接受该消息的窗口句柄 UINT message; 消息常量标识符,也就是我们通常所说的消息号 WPARAM wParam; 32位消息的特定附加信息,确切含义依赖于消息值 LPARAM lParam; 32位消息的特定附加信息,确切含义依赖于消息值 DWORD time; 消息创建时的时间 POINT pt; 消息创建时的鼠标/光标在屏幕坐标系中的位置MSG; 消息可以由系统或者应用程序产生。系统在发生输入事件时产生消息。举个例子, 当用户敲键, 移动鼠标或者单击控件。系统也产生消息以响应由应用程序带来的变化, 比如应用程序改变系统字体改变窗体大小。应用程序可以产生消息使窗体执行任务,或者与其他应用程序中的窗口通讯。消息中有什么? 我们给出了上面的注释,是不是会对消息结构有了一个比较清楚的认识?如果还没有,那么我们再试着给出下面的解释: hwnd 32位的窗口句柄。窗口可以是任何类型的屏幕对象,因为Win32能够维护大多数可视对象的句柄(窗口、对话框、按钮、编辑框等)。 message用于区别其他消息的常量值,这些常量可以是Windows单元中预定义的常量,也可以是自定义的常量。消息标识符以常量命名的方式指出消息的含义。当窗口过程接收到消息之后,他就会使用消息标识符来决定如何处理消息。例如、WM_PAINT告诉窗口过程窗体客户区被改变了需要重绘。符号常量指定系统消息属于的类别,其前缀指明了处理解释消息的窗体的类型。 wParam 通常是一个与消息有关的常量值,也可能是窗口或控件的句柄。 lParam 通常是一个指向内存中数据的指针。由于WParam、lParam和Pointer都是32位的,因此,它们之间可以相互转换。消息标识符的值 系统保留消息标识符的值在0x0000在0x03ff(WM_USER-1)范围。这些值被系统定义消息使用。 应用程序不能使用这些值给自己的消息。应用程序消息从WM_USER(0X0400)到0X7FFF,或0XC000到0XFFFF;WM_USER到0X7FFF范围的消息由应用程序自己使用;0XC000到0XFFFF范围的消息用来和其他应用程序通信,我们顺便说一下具有标志性的消息值: WM_NULL-0x0000 空消息。 0x0001-0x0087 主要是窗口消息。 0x00A0-0x00A9 非客户区消息 0x0100-0x0108 键盘消息 0x0111-0x0126 菜单消息 0x0132-0x0138 颜色控制消息 0x0200-0x020A 鼠标消息 0x0211-0x0213 菜单循环消息 0x0220-0x0230 多文档消息 0x03E0-0x03E8 DDE消息 0x0400 WM_USER 0x8000 WM_APP 0x0400-0x7FFF 应用程序自定义私有消息消息有哪几种? 其实,windows中的消息虽然很多,但是种类并不繁杂,大体上有3种:窗口消息、命令消息和控件通知消息。 窗口消息大概是系统中最为常见的消息,它是指由操作系统和控制其他窗口的窗口所使用的消息。例如CreateWindow、DestroyWindow和MoveWindow等都会激发窗口消息,还有我们在上面谈到的单击鼠标所产生的消息也是一种窗口消息。 命令消息,这是一种特殊的窗口消息,他用来处理从一个窗口发送到另一个窗口的用户请求,例如按下一个按钮,他就会向主窗口发送一个命令消息。 控件通知消息,是指这样一种消息,一个窗口内的子控件发生了一些事情,需要通知父窗口。通知消息只适用于标准的窗口控件如按钮、列表框、组合框、编辑框,以及Windows公共控件如树状视图、列表视图等。例如,单击或双击一个控件、在控件中选择部分文本、操作控件的滚动条都会产生通知消息。 她类似于命令消息,当用户与控件窗口交互时,那么控件通知消息就会从控件窗口发送到它的主窗口。但是这种消息的存在并不是为了处理用户命令,而是为了让主窗口能够改变控件,例如加载、显示数据。例如按下一个按钮,他向父窗口发送的消息也可以看作是一个控件通知消息;单击鼠标所产生的消息可以由主窗口直接处理,然后交给控件窗口处理。 其中窗口消息及控件通知消息主要由窗口类即直接或间接由CWND类派生类处理。相对窗口消息及控件通知消息而言,命令消息的处理对象范围就广得多,它不仅可以由窗口类处理,还可以由文档类,文档模板类及应用类所处理。 由于控件通知消息很重要的,人们用的也比较多,但是具体的含义往往令初学者晕头转向,所以我决定把常见的几个列出来供大家参考:按扭控件BN_CLICKED 用户单击了按钮BN_DISABLE 按钮被禁止BN_DOUBLECLICKED 用户双击了按钮BN_HILITE 用户加亮了按钮BN_PAINT 按钮应当重画BN_UNHILITE 加亮应当去掉组合框控件CBN_CLOSEUP 组合框的列表框被关闭CBN_DBLCLK 用户双击了一个字符串CBN_DROPDOWN 组合框的列表框被拉出CBN_EDITCHANGE 用户修改了编辑框中的文本CBN_EDITUPDATE 编辑框内的文本即将更新CBN_ERRSPACE 组合框内存不足CBN_KILLFOCUS 组合框失去输入焦点CBN_SELCHANGE 在组合框中选择了一项CBN_SELENDCANCEL 用户的选择应当被取消CBN_SELENDOK 用户的选择是合法的CBN_SETFOCUS 组合框获得输入焦点编辑框控件EN_CHANGE 编辑框中的文本己更新EN_ERRSPACE 编辑框内存不足EN_HSCROLL 用户点击了水平滚动条EN_KILLFOCUS 编辑框正在失去输入焦点EN_MAXTEXT 插入的内容被截断EN_SETFOCUS 编辑框获得输入焦点EN_UPDATE 编辑框中的文本将要更新EN_VSCROLL 用户点击了垂直滚动条消息含义列表框控件LBN_DBLCLK 用户双击了一项LBN_ERRSPACE 列表框内存不够LBN_KILLFOCUS 列表框正在失去输入焦点LBN_SELCANCEL 选择被取消LBN_SELCHANGE 选择了另一项LBN_SETFOCUS 列表框获得输入焦点队列消息和非队列消息 从消息的发送途径来看,消息可以分成2种:队列消息和非队列消息。消息队列由可以分成系统消息队列和线程消息队列。系统消息队列由Windows维护,线程消息队列则由每个GUI(Graphical User Interface)线程自己进行维护,为避免给non-GUI现成创建消息队列,所有线程产生时并没有消息队列,仅当线程第一次调用GDI(Graphics Device Interface)函数数系统给线程创建一个消息队列。队列消息送到系统消息队列,然后到线程消息队列;非队列消息直接送给目的窗口过程。 对于队列消息,最常见的是鼠标和键盘触发的消息,例如WM_MOUSERMOVE,WM_CHAR等消息,还有一些其它的消息,例如:WM_PAINT、WM_TIMER和WM_QUIT。当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由Windows系统去进行处理。Windows系统则在适当的时机,从系统消息队列中取出一个消息,根据前面我们所说的MSG消息结构确定消息是要被送往那个窗口,然后把取出的消息送往创建窗口的线程的相应队列,下面的事情就该由线程消息队列操心了,Windows开始忙自己的事情去了。线程看到自己的消息队列中有消息,就从队列中取出来,通过操作系统发送到合适的窗口过程去处理。 一般来讲,系统总是将消息Post在消息队列的末尾。这样保证窗口以先进先出的顺序接受消息。然而,WM_PAINT是一个例外,同一个窗口的多个 WM_PAINT被合并成一个 WM_PAINT 消息, 合并所有的无效区域到一个无效区域。合并WM_PAIN的目的是为了减少刷新窗口的次数。 非队列消息将会绕过系统队列和消息队列,直接将消息发送到窗口过程,。系统发送非队列消息通知窗口,系统发送消息通知窗口。 例如,当用户激活一个窗口系统发送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。这些消息通知窗口它被激活了。非队列消息也可以由当应用程序调用系统函数产生。例如,当程序调用SetWindowPos系统发送WM_WINDOWPOSCHANGED消息。一些函数也发送非队列消息,例如下面我们要谈到的函数。 消息的发送 了解了上面的这些基础理论之后,我们就可以进行一下简单的消息发送与接收。 把一个消息发送到窗口有3种方式:发送、寄送和广播。 发送消息的函数有SendMessage、SendMessageCallback、SendNotifyMessage、SendMessageTimeout;寄送消息的函数主要有PostMessage、PostThreadMessage、PostQuitMessage;广播消息的函数我知道的只有Broadcas

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论