WINCE下消息传递和消息队列_第1页
WINCE下消息传递和消息队列_第2页
WINCE下消息传递和消息队列_第3页
WINCE下消息传递和消息队列_第4页
WINCE下消息传递和消息队列_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

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

文档简介

1、WINCE下消息传递和消息队列WINCE下消息传递和消息队列2010-04-06 13:42Windows CE是事件驱动的操作系统,所有的消息都是使用一个名为MSG的结构来传递。MSG结构共包含六个字段,MSG定义如下:typedef struct tagMSGHWND hwnd;UINT message;WPARAM wParam;LPARAM lParam;DWORD time;POSITION pt;MSG;Hwnd:是用以检索消息的窗口名柄。也就是也就是用来处理消息的窗口的窗口名柄。Message:代表一个消息的消息值,每一个Windows消息都有一个消息值勤,该值由windows.

2、h头文件中的宏定义来标识wParam:用来包含与消息有关的附加信息,它随不同的消息而有所不同。lParam:用来存放有关消息的附加消息。Time:指定消息送到消息队列的时间Pt:指定消息发送时,屏幕光标的位置。定义新的消息:Windows CE支持系统定义的信息和应用程序定义的消息。系统定义消息(System-defined messges)的标识符的值是从0到0X3ff,而从0x400到0x7fff的值给应用程序(application-defined messages)定义的消息使用。WM_USER这个消息的值(message number)是0x400,如果我们要定义应用程序的用户消息(

3、custom message),可以人这个值开始,方法如下:#define WM_MYMESSAGE(WM_USER+999)而为了确保我们的应用程序定义的信义不会跟随其他应用程序相同而冲突,可以呼叫RegisterWindowMessage函数来检查。参数:lpString:指定将被注册的消息的以O结束的字符串指针。返回值如果消息被成功注册,返回值是在范围OxCOOO到OxFFFF的消息标识符;如果函数调用失败,返回值是零。要得到更多的错误信息,调用函数GetLastError。返回的消息值可在调用函数SendMessage或PostMessage时使用。传送消息我们可以呼叫SendMess

4、age函数来传送信息到窗口。SendMessage函数是一个同步函数,函数的呼叫会一直等到接收此消息的窗口处理程序处理完这个送来的消息后才会返回。函数原型:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);参数:hWnd:其窗口程序将接收消息的窗口的句柄。如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。Msg:指定被发送的消息。wParam:指定附加的消息指定信息。IParam:指定附加的消息指

5、定信息。返回值:返回值指定消息处理的结果,依赖于所发送的消息。张贴消息相反地,PostMessage函数是一个异步(asynchronous)函数。呼叫PostMessage函数时,PostMessage函数送出消息到消息队列后马上返回,而不会等到接收此消息的窗口的窗口处理程序处理完这个消息后才返回。消息队列里的消息通过调用GetMessage和PeekMessage取得。函数原型:B00L PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);参数hWnd:其窗口程序接收消息的窗口的句柄。可取有特定含义的两个值:HWND_BR

6、OADCAST:消息被寄送到系统的所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口。消息不被寄送到子窗口。NULL:此函数的操作和调用参数dwThread设置为当前线程的标识符PostThreadMessage函数一样。Msg:指定被寄送的消息。wParam:指定附加的消息特定的信息。IParam:指定附加的消息特定的信息。返回值:如果函数调用成功,返回非零值:如果函数调用失败,返回值是零。若想获得更多的错误信息,请调用GetLastError函数。队列消息和非队列消息从消息的发送途径来看,消息可以分成2种:队列消息和非队列消息。消息队列由可以分成系统消息队列和线程消

7、息队列。系统消息队列由Windows维护,线程消息队列则由每个GUI线程自己进行维护,为避免给non-GUI现成创建消息队列,所有线程产生时并没有消息队列,仅当线程第一次调用GDI函数数系统给线程创建一个消息队列。队列消息送到系统消息队列,然后到线程消息队列;非队列消息直接送给目的窗口过程。对于队列消息,最常见的是鼠标和键盘触发的消息,例如WM_MOUSERMOVE,WM_CHAR等消息,还有一些其它的消息,例如:WM_PAINT、WM_TIMER和WM_QUIT。当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由Windows系统去进

8、行处理。Windows系统则在适当的时机,从系统消息队列中取出一个消息,根据前面我们所说的MSG消息结构确定消息是要被送往那个窗口,然后把取出的消息送往创建窗口的线程的相应队列,下面的事情就该由线程消息队列操心了,Windows开始忙自己的事情去了。线程看到自己的消息队列中有消息,就从队列中取出来,通过操作系统发送到合适的窗口过程去处理。一般来讲,系统总是将消息Post在消息队列的末尾。这样保证窗口以先进先出的顺序接受消息。然而,WM_PAINT是一个例外,同一个窗口的多个WM_PAINT被合并成一个WM_PAINT消息,合并所有的无效区域到一个无效区域。合并WM_PAIN的目的是为了减少刷新

9、窗口的次数。非队列消息将会绕过系统队列和消息队列,直接将消息发送到窗口过程,。系统发送非队列消息通知窗口,系统发送消息通知窗口。例如,当用户激活一个窗口系统发送WM_ACTIVATE,WM_SETFOCUS,and WM_SETCURSOR。这些消息通知窗口它被激活了。非队列消息也可以由当应用程序调用系统函数产生。例如,当程序调用SetWindowPos系统发送WM_WINDOWPOSCHANGED消息。一些函数也发送非队列消息,例如下面我们要谈到的函数。消息的发送了解了上面的这些基础理论之后,我们就可以进行一下简单的消息发送与接收。把一个消息发送到窗口有3种方式:发送、寄送和广播。发送消息的

10、函数有SendMessage、SendMessageCallback、SendNotifyMessage、SendMessageTimeout;寄送消息的函数主要有PostMessage、PostThreadMessage、PostQuitMessage;广播消息的函数我知道的只有BroadcastSystemMessage、BroadcastSystemMessageEx。SendMessage的原型如下:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam),这个函数主要是向一个或多个窗口发送一条消息,一直等到

11、消息被处理之后才会返回。不过需要注意的是,如果接收消息的窗口是同一个应用程序的一部分,那么这个窗口的窗口函数就被作为一个子程序马上被调用;如果接收消息的窗口是被另外的线程所创建的,那么窗口系统就切换到相应的线程并且调用相应的窗口函数,这条消息不会被放进目标应用程序队列中。函数的返回值是由接收消息的窗口的窗口函数返回,返回的值取决于被发送的消息。PostMessage的原型如下:BOOL PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam),该函数把一条消息放置到创建hWnd窗口的线程的消息队列中,该函数不等消息被处理就马上将控制

12、返回。需要注意的是,如果hWnd参数为HWND_BROADCAST,那么,消息将被寄送给系统中的所有的重叠窗口和弹出窗口,但是子窗口不会收到该消息;如果hWnd参数为NULL,则该函数类似于将dwThreadID参数设置成当前线程的标志来调用PostThreadMEssage函数。从上面的这2个具有代表性的函数,我们可以看出消息的发送方式和寄送方式的区别所在:被发送的消息是否会被立即处理,函数是否立即返回。被发送的消息会被立即处理,处理完毕后函数才会返回;被寄送的消息不会被立即处理,他被放到一个先进先出的队列中,一直等到应用程序空线的时候才会被处理,不过函数放置消息后立即返回。实际上,发送消息

13、到一个窗口处理过程和直接调用窗口处理过程之间并没有太大的区别,他们直接的唯一区别就在于你可以要求操作系统截获所有被发送的消息,但是不能够截获对窗口处理过程的直接调用。以寄送方式发送的消息通常是与用户输入事件相对应的,因为这些事件不是十分紧迫,可以进行缓慢的缓冲处理,例如鼠标、键盘消息会被寄送,而按钮等消息则会被发送。广播消息用得比较少,BroadcastSystemMessage函数原型如下:long BroadcastSystemMessage(DWORD dwFlags,LPDWORD lpdwRecipients,UINT uiMessage,WPARAM wParam,LPARAM l

14、Param);该函数可以向指定的接收者发送一条消息,这些接收者可以是应用程序、可安装的驱动程序、网络驱动程序、系统级别的设备驱动消息和他们的任意组合。需要注意的是,如果dwFlags参数是BSF_QUERY并且至少一个接收者返回了BROADCAST_QUERY_DENY,则返回值为0,如果没有指定BSF_QUERY,则函数将消息发送给所有接收者,并且忽略其返回值。消息的接收消息的接收主要有3个函数:GetMessage、PeekMessage、WaitMessage。GetMessage原型如下:BOOL GetMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFi

15、lterMin,UINT wMsgFilterMax);该函数用来获取与hWnd参数所指定的窗口相关的且wMsgFilterMin和wMsgFilterMax参数所给出的消息值范围内的消息。需要注意的是,如果hWnd为NULL,则GetMessage获取属于调用该函数应用程序的任一窗口的消息,如果wMsgFilterMin和wMsgFilterMax都是0,则GetMessage就返回所有可得到的消息。函数获取之后将删除消息队列中的除WM_PAINT消息之外的其他消息,至于WM_PAINT则只有在其处理之后才被删除。PeekMessage原型如下:BOOL PeekMessage(LPMSG

16、lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg);该函数用于查看应用程序的消息队列,如果其中有消息就将其放入lpMsg所指的结构中,不过,与GetMessage不同的是,PeekMessage函数不会等到有消息放入队列时才返回。同样,如果hWnd为NULL,则PeekMessage获取属于调用该函数应用程序的任一窗口的消息,如果hWnd=-1,那么函数只返回把hWnd参数为NULL的PostAppMessage函数送去的消息。如果wMsgFilterMin和wMsgFilterMax都是0,则PeekM

17、essage就返回所有可得到的消息。函数获取之后将删除消息队列中的除WM_PAINT消息之外的其他消息,至于WM_PAINT则只有在其处理之后才被删除。WaitMessage原型如下:BOOL VaitMessage();当一个应用程序无事可做时,该函数就将控制权交给另外的应用程序,同时将该应用程序挂起,直到一个新的消息被放入应用程序的队列之中才返回。消息的处理接下来我们谈一下消息的处理,首先我们来看一下VC中的消息泵:while(GetMessage(&msg,NULL,0,0)if(!TranslateAccelerator(msg.hWnd,hAccelTable,&msg)Transl

18、ateMessage(&msg);DispatchMessage(&msg);首先,GetMessage从进程的主线程的消息队列中获取一个消息并将它复制到MSG结构,如果队列中没有消息,则GetMessage函数将等待一个消息的到来以后才返回。如果你将一个窗口句柄作为第二个参数传入GetMessage,那么只有指定窗口的的消息可以从队列中获得。GetMessage也可以从消息队列中过滤消息只接受消息队列中落在范围内的消息。这时候就要利用GetMessage/PeekMessage指定一个消息过滤器。这个过滤器是一个消息标识符的范围或者是一个窗体句柄,或者两者同时指定。当应用程序要查找一个后入消

19、息队列的消息是很有用。WM_KEYFIRST和WM_KEYLAST常量用于接受所有的键盘消息。WM_MOUSEFIRST和WM_MOUSELAST常量用于接受所有的鼠标消息。然后TranslateAccelerator判断该消息是不是一个按键消息并且是一个加速键消息,如果是,则该函数将把几个按键消息转换成一个加速键消息传递给窗口的回调函数。处理了加速键之后,函数TranslateMessage将把两个按键消息WM_KEYDOWN和WM_KEYUP转换成一个WM_CHAR,不过需要注意的是,消息WM_KEYDOWN,WM_KEYUP仍然将传递给窗口的回调函数。处理完之后,DispatchMess

20、age函数将把此消息发送给该消息指定的窗口中已设定的回调函数。如果消息是WM_QUIT,则GetMessage返回0,从而退出循环体。应用程序可以使用PostQuitMessage来结束自己的消息循环。通常在主窗口的WM_DESTROY消息中调用。下面我们举一个常见的小例子来说明这个消息泵的运用:if(:PeekMessage(&msg,m_hWnd,WM_KEYFIRST,WM_KEYLAST,PM_REMOVE)if(msg.message=WM_KEYDOWN&msg.wParam=VK_ESCAPE).这里我们接受所有的键盘消息,所以就用WM_KEYFIRST和WM_KEYLAST作为

21、参数。最后一个参数可以是PM_NOREMOVE或者PM_REMOVE,表示消息信息是否应该从消息队列中删除。所以这段小代码就是判断是否按下了Esc键,如果是就进行处理。窗口过程窗口过程是一个用于处理所有发送到这个窗口的消息的函数。任何一个窗口类都有一个窗口过程。同一个类的窗口使用同样的窗口过程来响应消息。系统发送消息给窗口过程将消息数据作为参数传递给他,消息到来之后,按照消息类型排序进行处理,其中的参数则用来区分不同的消息,窗口过程使用参数产生合适行为。一个窗口过程不经常忽略消息,如果他不处理,它会将消息传回到执行默认的处理。窗口过程通过调用DefWindowProc来做这个处理。窗口过程必须

22、return一个值作为它的消息处理结果。大多数窗口只处理小部分消息和将其他的通过DefWindowProc传递给系统做默认的处理。窗口过程被所有属于同一个类的窗口共享,能为不同的窗口处理消息。下面我们来看一下具体的实例:LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)int wmId,wmEvent;PAINTSTRUCT ps;HDC hdc;TCHAR szHelloMAX_LOADSTRING;LoadString(hInst,IDS_HELLO,szHello,MAX_LOADSTR

23、ING);switch(message)case WM_COMMAND:wmId=LOWORD(wParam);wmEvent=HIWORD(wParam);/Parse the menu selections:switch(wmId)case IDM_ABOUT:DialogBox(hInst,(LPCTSTR)IDD_ABOUTBOX,hWnd,(DLGPROC)About);break;case IDM_EXIT:DestroyWindow(hWnd);break;default:return DefWindowProc(hWnd,message,wParam,lParam);break

24、;case WM_PAINT:hdc=BeginPaint(hWnd,&ps);/TODO:Add any drawing code here.RECT rt;GetClientRect(hWnd,&rt);DrawText(hdc,szHello,strlen(szHello),&rt,DT_CENTER);EndPaint(hWnd,&ps);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd,message,wParam,lParam);return 0;消息分流器通常的窗口过程

25、是通过一个switch语句来实现的,这个事情很烦,有没有更简便的方法呢?有,那就是消息分流器,利用消息分流器,我们可以把switch语句分成更小的函数,每一个消息都对应一个小函数,这样做的好处就是对消息更容易管理。之所以被称为消息分流器,就是因为它可以对任何消息进行分流。下面我们做一个函数就很清楚了:void MsgCracker(HWND hWnd,int id,HWND hWndCtl,UINT codeNotify)switch(id)case ID_A:if(codeNotify=EN_CHANGE).break;case ID_B:if(codeNotify=BN_CLICKED).

26、break;.然后我们修改一下窗口过程:LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)switch(message)HANDLE_MSG(hWnd,WM_COMMAND,MsgCracker);HANDLE_MSG(hWnd,WM_DESTROY,MsgCracker);default:return DefWindowProc(hWnd,message,wParam,lParam);return 0;在WindowsX.h中定义了如下的HANDLE_MSG宏:#define HANDLE_

27、MSG(hwnd,msg,fn)switch(msg):return HANDLE_#msg(hwnd),(wParam),(lParam),(fn);实际上,HANDLE_WM_XXXX都是宏,例如:HANDLE_MSG(hWnd,WM_COMMAND,MsgCracker);将被转换成如下定义:#define HANDLE_WM_COMMAND(hwnd,wParam,lParam,fn)(fn)(hwnd),(int)(LOWORD(wParam),(HWND)(lParam),(UINT)HIWORD(wParam),0L);好了,事情到了这一步,应该一切都明朗了。不过,我们发现在wi

28、ndowsx.h里面还有一个宏:FORWARD_WM_XXXX,我们还是那WM_COMMAND为例,进行分析:#define FORWARD_WM_COMMAND(hwnd,id,hwndCtl,codeNotify,fn)(void)(fn)(hwnd),WM_COMMAND,MAKEWPARAM(UINT)(id),(UINT)(codeNotify),(LPARAM)(HWND)(hwndCtl)所以实际上,FORWARD_WM_XXXX将消息参数进行了重新构造,生成了wParam&lParam,然后调用了我们定义的函数。MFC消息的处理实现方式初看MFC中的各种消息,以及在头脑中根深蒂

29、固的C+的影响,我们可能很自然的就会想到利用C+的三大特性之一:虚拟机制来实现消息的传递,但是经过分析,我们看到事情并不是想我们想象的那样,在MFC中消息是通过一种所谓的消息映射机制来处理的。为什么呢?在潘爱民老师翻译的Visual C+技术内幕(第4版)中给出了详细的原因说明,我再简要的说一遍。在CWnd类中大约有110个消息,还有其它的MFC的类呢,算起来消息太多了,在C+中对程序中用到的每一个派生类都要有一个vtable,每一个虚函数在vtable中都要占用一个4字节大小的入口地址,这样一来,对于每个特定类型的窗口或控件,应用程序都需要一个440KB大小的表来支持虚拟消息控件函数。如果说

30、上面的窗口或控件可以勉强实现的话,那么对于菜单命令消息及按钮命令消息呢?因为不同的应用程序有不同的菜单和按钮,我们怎么处理呢?在MFC库的这种消息映射系统就避免了使用大的vtable,并且能够在处理常规Windows消息的同时处理各种各样的应用程序的命令消息。说白了,MFC中的消息机制其实质是一张巨大的消息及其处理函数的一一对应表,然后加上分析处理这张表的应用框架内部的一些程序代码.这样就可以避免在SDK编程中用到的繁琐的CASE语句。MFC的消息映射的基类CCmdTarget如果你想让你的控件能够进行消息映射,就必须从CCmdTarget类中派生。CCmdTarget类是MFC处理命令消息的

31、基础、核心。MFC为该类设计了许多成员函数和一些成员数据,基本上是为了解决消息映射问题的,所有响应消息或事件的类都从它派生,例如:应用程序类、框架类、文档类、视图类和各种各样的控件类等等,还有很多。不过这个类里面有2个函数对消息映射非常重要,一个是静态成员函数DispatchCmdMsg,另一个是虚函数OnCmdMsg。DispatchCmdMsg专门供MFC内部使用,用来分发Windows消息。OnCmdMsg用来传递和发送消息、更新用户界面对象的状态。CCmdTarget对OnCmdMsg的默认实现:在当前命令目标(this所指的类和基类的消息映射数组里搜索指定命令消息的消息处理函数。这里

32、使用虚拟函数GetMessageMap得到命令目标类的消息映射入口数组_messageEntries,然后在数组里匹配命令消息ID相同、控制通知代码也相同的消息映射条目。其中GetMessageMap是虚拟函数,所以可以确认当前命令目标的确切类。如果找到了一个匹配的消息映射条目,则使用DispachCmdMsg调用这个处理函数;如果没有找到,则使用_GetBaseMessageMap得到基类的消息映射数组,查找,直到找到或搜寻了所有的基类(到CCmdTarget)为止;如果最后没有找到,则返回FASLE。每个从CCmdTarget派生的命令目标类都可以覆盖OnCmdMsg,利用它来确定是否可以

33、处理某条命令,如果不能,就通过调用下一命令目标的OnCmdMsg,把该命令送给下一个命令目标处理。通常,派生类覆盖OnCmdMsg时,要调用基类的被覆盖的OnCmdMsg。在MFC框架中,一些MFC命令目标类覆盖了OnCmdMsg,如框架窗口类覆盖了该函数,实现了MFC的标准命令消息发送路径。必要的话,应用程序也可以覆盖OnCmdMsg,改变一个或多个类中的发送规定,实现与标准框架发送规定不同的发送路径。例如,在以下情况可以作这样的处理:在要打断发送顺序的类中把命令传给一个非MFC默认对象;在新的非默认对象中或在可能要传出命令的命令目标中。消息映射的内容通过ClassWizard为我们生成的代码,我们可以看到,消息映射基本上分为2大部分:在头文件(.h)中有一个宏DECLARE_MESSAGE_MAP(),他被放在了类的末尾,是一个public属性的;与之对应的是在实现部分(.cpp)增加了一章消息映射表,内容如下:BEGIN_MESSAGE_MAP(当前类,当前类的基类)AFX_MSG_MAP(CMainFrame)消息的入口项AFX_MSG_MAP E

温馨提示

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

评论

0/150

提交评论