




已阅读5页,还剩17页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Windows消息机制Windows消息机制/ R5 D w2 c/ 5 & 9 I z关键词: Windows 消息 消息机制 x! p% 5 p* a, / 5 j. / i/ s3 v/ J摘要:Windows编程和Dos编程,一个很大的区别就是,Windows编程是事件驱动,消息传递的。所以,要学好Windows编程,必须对消息机制有一个清楚的认识,本文希望能够对消息的传递做一个全面的分析。 ! y, Z. y5 z, O1 ( 什么是消息?, 9 ) p, X* | ?: f! Y % Y+ Z3 G4 h2 j2 x: 8 2 i+ ?! 3 S+ B$ _9 M: i( B8 R + E G/ I5 ! Q消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉。一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向Windows发出一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。: s. D+ s7 g# k7 S3 6 n 2 ?- B8 b 2 z( Z# y2 K5 _0 0 Q4 |1 j7 y: r消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息。例如,对于单击鼠标所产生的消息来说,这个记录中包含了单击鼠标时的坐标。这个记录类型叫做MSG,MSG含有来自windows应用程序消息队列的消息信息,它在Windows中声明如下:# |- P& x% y6 k+ v4 n! 5 u. X, F3 y5 a/ 4 f5 4 t $ n5 x9 o4 $ Fl9 J9 G8 r+ x I1 xtypedef struct tagMsg# Z0 c, m4 C1 Ck1 s; s4 j: Z4 ; F& X7 r4 I$ XHWND hwnd; 接受该消息的窗口句柄9 _ W$ ( S I4 N, b- HUINT message; 消息常量标识符,也就是我们通常所说的消息号e( S! i/ jU N. RWPARAM wParam; 32位消息的特定附加信息,确切含义依赖于消息值% - u6 D- - W4 o, ?LPARAM lParam; 32位消息的特定附加信息,确切含义依赖于消息值* e) l& q3 : c$ J2 cDWORD time; 消息创建时的时间0 Y+ q$ z+ 8 X( y% z) HPOINT pt; 消息创建时的鼠标/光标在屏幕坐标系中的位置M* i9 Y% F1 L1 g# s4 kMSG;& G4 G! J2 |9 J7 O a7 9 P8 s) 0 $ _( J* uq n+ O0 J1 Y8 N+ : C* p0 u. q消息可以由系统或者应用程序产生。系统在发生输入事件时产生消息。举个例子, 当用户敲键, 移动鼠标或者单击控件。系统也产生消息以响应由应用程序带来的变化, 比如应用程序改变系统字体改变窗体大小。应用程序可以产生消息使窗体执行任务,或者与其他应用程序中的窗口通讯。 # L) X0 m% g1 b% L! i- W: D* I, ) x8 X+ p n! , z! L8 z( d0 G4 F+ o1 U6 7 U; d) Q! a; w5 v* a消息中有什么?/ R. l4 K; C0 c, e& H# |2 , C$ n( k o+ k3 W9 ( o4 j+ T, z, H# Q- B s0 W5 x/ _我们给出了上面的注释,是不是会对消息结构有了一个比较清楚的认识?如果还没有,那么我们再试着给出下面的解释:5 O% N! W) F W: I$ _+ Q1 6 L. q; k( r6 ( Q8 g* b% |# & Z/ o) w+ N, d( h, e* ! Q. chwnd 32位的窗口句柄。窗口可以是任何类型的屏幕对象,因为Win32能够维护大多数可视对象的句柄(窗口、对话框、按钮、编辑框等)。O* ?7 & X T7 Y$ I9 g0 V% D b) ; y# F/ R; x; H+ D( r2 J$ T8 B/ p0 |! j. o( C! HQ/ bmessage用于区别其他消息的常量值,这些常量可以是Windows单元中预定义的常量,也可以是自定义的常量。消息标识符以常量命名的方式指出消息的含义。当窗口过程接收到消息之后,他就会使用消息标识符来决定如何处理消息。例如、WM_PAINT告诉窗口过程窗体客户区被改变了需要重绘。符号常量指定系统消息属于的类别,其前缀指明了处理解释消息的窗体的类型。: A, O/ F- F/ o6 P/ v9 fd# e3 R$ H; n0 j 9 v0 K8 Rw Q) p1 y& x( c* ?5 a3 t4 w1 n( Z3 _7 N gwParam 通常是一个与消息有关的常量值,也可能是窗口或控件的句柄。! E- Q7 M7 f4 h& O( u& 4 K0 Z9 $ nn: h1 r 4 S u* A/ Y9 8 e! G8 7 M d% A; S/ j2 o* ClParam 通常是一个指向内存中数据的指针。由于WParam、lParam和Pointer都是32位的,因此,它们之间可以相互转换。! P! E$ a. z+ k/ Ft5 X B% v. & _9 s u9 I 7 XC5 ( q5 J* / r% . ?3 _9 f+ Z& M8 h7 B- 6 H+ / i! Y! E3 e摘要:Windows编程和Dos编程,一个很大的区别就是,Windows编程是事件驱动,消息传递的。所以,要学好Windows编程,必须对消息机制有一个清楚的认识,本文希望能够对消息的传递做一个全面的分析。 9 f, h7 X4 D- |. hg: w- 什么是消息? , ; . o; t9 % q e# B1 H& L7 W M* _8 M/ r; + Pp& w& & f% s7 Y( x! N+ s- Y* H+ C# _消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉。一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向Windows发出一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。8 $ ) * Q* g2 f5 M. mJ4 t5 V6 Y) 9 . - o1 t/ Y! X4 f5 A5 pS8 K1 x3 o7 cd( d3 z9 % N消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息。例如,对于单击鼠标所产生的消息来说,这个记录中包含了单击鼠标时的坐标。这个记录类型叫做MSG,MSG含有来自windows应用程序消息队列的消息信息,它在Windows中声明如下:2 H. E2 |% m5 k$ z/ Q6 b. F) L3 y R! I3 P2 E % MS/ _- m* g9 c, z7 k3 n& W: Stypedef struct tagMsg& S1 + W4 l% j) p9 f7 d/ a9 |& 2 f0 m9 # HWND hwnd; 接受该消息的窗口句柄 D( q& Z8 A5 - s1 : G* pUINT message; 消息常量标识符,也就是我们通常所说的消息号3 Z t; i( x6 K/ . X4 eWPARAM wParam; 32位消息的特定附加信息,确切含义依赖于消息值# g0 R, N1 h! + R0 yLPARAM lParam; 32位消息的特定附加信息,确切含义依赖于消息值 T% a4 LP. d g% xDWORD time; 消息创建时的时间 f2 P0 p3 e: Q9 B7 A. 9 8 ?1 fPOINT pt; 消息创建时的鼠标/光标在屏幕坐标系中的位置 H8 s* z- P0 c- MSG;9 M2 w* w* h- X T2 c0 e) K% u/ M6 q 7 o; x+ & - , |9 d: 5 D( A1 a1 V, jW$ C4 1 p. G: z% f消息可以由系统或者应用程序产生。系统在发生输入事件时产生消息。举个例子, 当用户敲键, 移动鼠标或者单击控件。系统也产生消息以响应由应用程序带来的变化, 比如应用程序改变系统字体改变窗体大小。应用程序可以产生消息使窗体执行任务,或者与其他应用程序中的窗口通讯。 L0 I Sp8 J8 X* l1 D* E4 6 u) zU3 n) q) g; G: $ O $ s?6 ( T1 E0 R. Z8 I2 q3 H3 q! t0 d消息中有什么?! c d, m6 i4 I5 i2 E4 F+ o) R& u. _& w 5 s. $ R4 q$ ?1 f# 7 L7 B( . G; s T$ H. O. e* i0 我们给出了上面的注释,是不是会对消息结构有了一个比较清楚的认识?如果还没有,那么我们再试着给出下面的解释:* g0 ?% H& j* p- h; m4 E9 G) P2 O. + F8 W5 8 + h+ ; s1 , X7 c* m$ c7 n! m8 X( o9 Ihwnd 32位的窗口句柄。窗口可以是任何类型的屏幕对象,因为Win32能够维护大多数可视对象的句柄(窗口、对话框、按钮、编辑框等)。1 D$ _1 E0 H* u! f I# T% & L6 G2 m: z8 0 Q1 R6 E# f! h6 B& x* p% * P2 n& i. ! r J: D) W) Q3 ?message用于区别其他消息的常量值,这些常量可以是Windows单元中预定义的常量,也可以是自定义的常量。消息标识符以常量命名的方式指出消息的含义。当窗口过程接收到消息之后,他就会使用消息标识符来决定如何处理消息。例如、WM_PAINT告诉窗口过程窗体客户区被改变了需要重绘。符号常量指定系统消息属于的类别,其前缀指明了处理解释消息的窗体的类型。7 W0 K% d m2 t0 T% q F! V6 9 g4 e% r * v$ J1 $ c! U9 P! k X3 7 Y o3 1 X& r( lwParam 通常是一个与消息有关的常量值,也可能是窗口或控件的句柄。; K5 Z, l1 h$ _: b: _$ x& s3 _$ i- o% t/ D0 R & Km, 8 w( o% k8 7 |5 mO. V! 1 ?4 w8 D1 LlParam 通常是一个指向内存中数据的指针。由于WParam、lParam和Pointer都是32位的,因此,它们之间可以相互转换。/ b y+ t2 m) w3 : A7 w, H; EU& V+ K r: $ r% a- K4 i. U, F / B, H. f, e. Y- 3 c9 Y8 l队列消息和非队列消息6 8 J) e N$ b: XK0 N3 M6 i+ Q2 e# 6 Q5 f4 M从消息的发送途径来看,消息可以分成2种:队列消息和非队列消息。消息队列由可以分成系统消息队列和线程消息队列。系统消息队列由Windows维护,线程消息队列则由每个GUI线程自己进行维护,为避免给non-GUI现成创建消息队列,所有线程产生时并没有消息队列,仅当线程第一次调用GDI函数数系统给线程创建一个消息队列。队列消息送到系统消息队列,然后到线程消息队列;非队列消息直接送给目的窗口过程。, C X% s4 % C9 n& P) X3 Ef; K* p; D对于队列消息,最常见的是鼠标和键盘触发的消息,例如WM_MOUSERMOVE,WM_CHAR等消息,还有一些其它的消息,例如:WM_PAINT、WM_TIMER和WM_QUIT。当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由Windows系统去进行处理。Windows系统则在适当的时机,从系统消息队列中取出一个消息,根据前面我们所说的MSG消息结构确定消息是要被送往那个窗口,然后把取出的消息送往创建窗口的线程的相应队列,下面的事情就该由线程消息队列操心了,Windows开始忙自己的事情去了。线程看到自己的消息队列中有消息,就从队列中取出来,通过操作系统发送到合适的窗口过程去处理。! x: m& $ E/ z) w) w( h; 1 q8 e4 D2 g) M$ |0 Y& r# Z一般来讲,系统总是将消息Post在消息队列的末尾。这样保证窗口以先进先出的顺序接受消息。然而,WM_PAINT是一个例外,同一个窗口的多个 WM_PAINT被合并成一个 WM_PAINT 消息, 合并所有的无效区域到一个无效区域。合并WM_PAIN的目的是为了减少刷新窗口的次数。2 # z0 L) z/ t7 4 K* d7 F; ! t6 l& J非队列消息将会绕过系统队列和消息队列,直接将消息发送到窗口过程,。系统发送非队列消息通知窗口,系统发送消息通知窗口。 例如,当用户激活一个窗口系统发送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。这些消息通知窗口它被激活了。非队列消息也可以由当应用程序调用系统函数产生。例如,当程序调用SetWindowPos系统发送WM_WINDOWPOSCHANGED消息。一些函数也发送非队列消息,例如下面我们要谈到的函数。$ J. 8 / P5 Q7 c1 u* o- M, S* + l( z消息的发送% f3 v% Pj; H W! |6 P5 E. s c9 P% x3 Q9 z9 U3 t3 q+ m; d! % 了解了上面的这些基础理论之后,我们就可以进行一下简单的消息发送与接收。) K7 o- n- l- o w2 f W6 , M) S5 n& , W! I) P把一个消息发送到窗口有3种方式:发送、寄送和广播。; v t: w y, u! y1 r7 C) y7 N: x& J9 & t$ n- q. n Z. J9 U发送消息的函数有SendMessage、SendMessageCallback、SendNotifyMessage、SendMessageTimeout;寄送消息的函数主要有PostMessage、PostThreadMessage、PostQuitMessage;广播消息的函数我知道的只有BroadcastSystemMessage、BroadcastSystemMessageEx。 G$ c ?* Z _, % u A% F, p, . A2 d4 a c* TSendMessage的原型如下:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam),这个函数主要是向一个或多个窗口发送一条消息,一直等到消息被处理之后才会返回。不过需要注意的是,如果接收消息的窗口是同一个应用程序的一部分,那么这个窗口的窗口函数就被作为一个子程序马上被调用;如果接收消息的窗口是被另外的线程所创建的,那么窗口系统就切换到相应的线程并且调用相应的窗口函数,这条消息不会被放进目标应用程序队列中。函数的返回值是由接收消息的窗口的窗口函数返回,返回的值取决于被发送的消息。& T& B3 |8 W( m3 j2 v& w( K3 x# F6 oPostMessage的原型如下:BOOL PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam),该函数把一条消息放置到创建hWnd窗口的线程的消息队列中,该函数不等消息被处理就马上将控制返回。需要注意的是,如果hWnd参数为HWND_BROADCAST,那么,消息将被寄送给系统中的所有的重叠窗口和弹出窗口,但是子窗口不会收到该消息;如果hWnd参数为NULL,则该函数类似于将dwThreadID参数设置成当前线程的标志来调用PostThreadMEssage函数。) s8 p4 N R2 J1 z w* D9 O6 A& B/ j! G从上面的这2个具有代表性的函数,我们可以看出消息的发送方式和寄送方式的区别所在:被发送的消息是否会被立即处理,函数是否立即返回。被发送的消息会被立即处理,处理完毕后函数才会返回;被寄送的消息不会被立即处理,他被放到一个先进先出的队列中,一直等到应用程序空线的时候才会被处理,不过函数放置消息后立即返回。3 k$ g U# P& D S% I9 i6 S) p3 D# G- Dx实际上,发送消息到一个窗口处理过程和直接调用窗口处理过程之间并没有太大的区别,他们直接的唯一区别就在于你可以要求操作系统截获所有被发送的消息,但是不能够截获对窗口处理过程的直接调用。; s8 e; A3 e6 J7 _9 q( j, h, 4 C$ F% ( u# I: $ O. 以寄送方式发送的消息通常是与用户输入事件相对应的,因为这些事件不是十分紧迫,可以进行缓慢的缓冲处理,例如鼠标、键盘消息会被寄送,而按钮等消息则会被发送。3 D b W) n I2 o7 D: F& p2 j5 5 n+ N3 c u; h广播消息用得比较少,BroadcastSystemMessage函数原型如下:+ q! y4 Z2 4 h7 |/ v# B5 e6 o+ U, q1 Z& j: v3 q/ / f& _: ?long BroadcastSystemMessage(DWORD dwFlags,LPDWORD lpdwRecipients,UINT uiMessage,WPARAM wParam,LPARAM lParam);: v: a4 |% o8 ?6 s( d: D E- + W6 h7 I0 b* m9 W该函数可以向指定的接收者发送一条消息,这些接收者可以是应用程序、可安装的驱动程序、网络驱动程序、系统级别的设备驱动消息和他们的任意组合。需要注意的是,如果dwFlags参数是BSF_QUERY并且至少一个接收者返回了BROADCAST_QUERY_DENY,则返回值为,如果没有指定BSF_QUERY,则函数将消息发送给所有接收者,并且忽略其返回值。 8 1 j) o m8 d, 4 U7 ?8 v* M0 H: 队列消息和非队列消息5 e. T4 l: d2 l( b* C0 V, J 从消息的发送途径来看,消息可以分成2种:队列消息和非队列消息。消息队列由可以分成系统消息队列和线程消息队列。系统消息队列由Windows维护,线程消息队列则由每个GUI线程自己进行维护,为避免给non-GUI现成创建消息队列,所有线程产生时并没有消息队列,仅当线程第一次调用GDI函数数系统给线程创建一个消息队列。队列消息送到系统消息队列,然后到线程消息队列;非队列消息直接送给目的窗口过程。, r5 ?5 z+ X4 a& d7 M# t y2 I0 ! c- C u对于队列消息,最常见的是鼠标和键盘触发的消息,例如WM_MOUSERMOVE,WM_CHAR等消息,还有一些其它的消息,例如:WM_PAINT、WM_TIMER和WM_QUIT。当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由Windows系统去进行处理。Windows系统则在适当的时机,从系统消息队列中取出一个消息,根据前面我们所说的MSG消息结构确定消息是要被送往那个窗口,然后把取出的消息送往创建窗口的线程的相应队列,下面的事情就该由线程消息队列操心了,Windows开始忙自己的事情去了。线程看到自己的消息队列中有消息,就从队列中取出来,通过操作系统发送到合适的窗口过程去处理。3 Z# s% u6 : v: H1 d# W! P+ g) E) Y9 J一般来讲,系统总是将消息Post在消息队列的末尾。这样保证窗口以先进先出的顺序接受消息。然而,WM_PAINT是一个例外,同一个窗口的多个 WM_PAINT被合并成一个 WM_PAINT 消息, 合并所有的无效区域到一个无效区域。合并WM_PAIN的目的是为了减少刷新窗口的次数。* _7 S8 p O- |z4 f. d, h- _/ P% H) Q. h( . U非队列消息将会绕过系统队列和消息队列,直接将消息发送到窗口过程,。系统发送非队列消息通知窗口,系统发送消息通知窗口。 例如,当用户激活一个窗口系统发送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。这些消息通知窗口它被激活了。非队列消息也可以由当应用程序调用系统函数产生。例如,当程序调用SetWindowPos系统发送WM_WINDOWPOSCHANGED消息。一些函数也发送非队列消息,例如下面我们要谈到的函数。* s2 d3 : e- G! ?6 * 1 X# X! z4 I7 _消息的发送. Z: u$ i/ i8 C C2 Q8 l) E4 U/ KS7 m: n F! e n了解了上面的这些基础理论之后,我们就可以进行一下简单的消息发送与接收。8 d8 5 E+ A8 6 a0 M0 D4 w& j( Y5 K C8 d; w8 v. L/ L把一个消息发送到窗口有3种方式:发送、寄送和广播。1 7 ?3 I0 3 ?H- R% J v( l/ i1 A% Z* q7 U+ O$ w- f( h发送消息的函数有SendMessage、SendMessageCallback、SendNotifyMessage、SendMessageTimeout;寄送消息的函数主要有PostMessage、PostThreadMessage、PostQuitMessage;广播消息的函数我知道的只有BroadcastSystemMessage、BroadcastSystemMessageEx。+ W( r- K8 0 |* - Q- k. m% y6 v x4 x5 l3 * h! U( S , OSendMessage的原型如下:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam),这个函数主要是向一个或多个窗口发送一条消息,一直等到消息被处理之后才会返回。不过需要注意的是,如果接收消息的窗口是同一个应用程序的一部分,那么这个窗口的窗口函数就被作为一个子程序马上被调用;如果接收消息的窗口是被另外的线程所创建的,那么窗口系统就切换到相应的线程并且调用相应的窗口函数,这条消息不会被放进目标应用程序队列中。函数的返回值是由接收消息的窗口的窗口函数返回,返回的值取决于被发送的消息。/ S! i! S7 |8 R0 K, m- j2 V# B |% P3 t0 ?7 f3 W3 lPostMessage的原型如下:BOOL PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam),该函数把一条消息放置到创建hWnd窗口的线程的消息队列中,该函数不等消息被处理就马上将控制返回。需要注意的是,如果hWnd参数为HWND_BROADCAST,那么,消息将被寄送给系统中的所有的重叠窗口和弹出窗口,但是子窗口不会收到该消息;如果hWnd参数为NULL,则该函数类似于将dwThreadID参数设置成当前线程的标志来调用PostThreadMEssage函数。- * O1 Y( G N2 F- N R8 M9 n3 M( z* J7 g从上面的这2个具有代表性的函数,我们可以看出消息的发送方式和寄送方式的区别所在:被发送的消息是否会被立即处理,函数是否立即返回。被发送的消息会被立即处理,处理完毕后函数才会返回;被寄送的消息不会被立即处理,他被放到一个先进先出的队列中,一直等到应用程序空线的时候才会被处理,不过函数放置消息后立即返回。& E. F$ I& Q# k- c5 & u( l. h: z: n/ Y2 Y实际上,发送消息到一个窗口处理过程和直接调用窗口处理过程之间并没有太大的区别,他们直接的唯一区别就在于你可以要求操作系统截获所有被发送的消息,但是不能够截获对窗口处理过程的直接调用。9 e& 9 h2 P% & r4 R, 2 k1 r+ C- 以寄送方式发送的消息通常是与用户输入事件相对应的,因为这些事件不是十分紧迫,可以进行缓慢的缓冲处理,例如鼠标、键盘消息会被寄送,而按钮等消息则会被发送。) p0 q J* e) 6 w- A6 I) m5 i5 |$ Cg广播消息用得比较少,BroadcastSystemMessage函数原型如下:* i# l/ L2 v( , J; l8 v2 S. W& j. R: k: jlong BroadcastSystemMessage(DWORD dwFlags,LPDWORD lpdwRecipients,UINT uiMessage,WPARAM wParam,LPARAM lParam);, z5 D w! V7 K. 2 I! F $ N# P/ z3 B f5 j该函数可以向指定的接收者发送一条消息,这些接收者可以是应用程序、可安装的驱动程序、网络驱动程序、系统级别的设备驱动消息和他们的任意组合。需要注意的是,如果dwFlags参数是BSF_QUERY并且至少一个接收者返回了BROADCAST_QUERY_DENY,则返回值为,如果没有指定BSF_QUERY,则函数将消息发送给所有接收者,并且忽略其返回值。 3 Z; L: ; i3 D N# y4 j( q1 KT0 : P: P窗口过程0 b# r+ ; q$ X; m3 n; L3 r; y% R5 U窗口过程是一个用于处理所有发送到这个窗口的消息的函数。任何一个窗口类都有一个窗口过程。同一个类的窗口使用同样的窗口过程来响应消息。 系统发送消息给窗口过程将消息数据作为参数传递给他,消息到来之后,按照消息类型排序进行处理,其中的参数则用来区分不同的消息,窗口过程使用参数产生合适行为。. j: 2 : c5 Qb/ t5 F5 c w2 L# q( m- T/ sp一个窗口过程不经常忽略消息,如果他不处理,它会将消息传回到执行默认的处理。窗口过程通过调用DefWindowProc来做这个处理。窗口过程必须return一个值作为它的消息处理结果。大多数窗口只处理小部分消息和将其他的通过DefWindowProc传递给系统做默认的处理。窗口过程被所有属于同一个类的窗口共享,能为不同的窗口处理消息。下面我们来看一下具体的实例:( t* |( O( v1 S# t$ w; _! a: G8 c ; E9 HLRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)5 H* f% I0 4 l1 u( R b U+ f, K9 A+ S; 6 R) I- Ed2 N. C4 int wmId, wmEvent;# v) j% J( 1 F: iPAINTSTRUCT ps;* E- vo n1 / H- t, C- x) pHDC hdc;- O) Z$ Y) S0 |6 O# j0 TCHAR szHelloMAX_LOADSTRING;+ s e0 N( ?9 L2 h( 9 d8 VLoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); 1 & v) s: g( q3 A, a( h0 b Qswitch (message) ! ( w0 u9 V! j ; c$ c* |: a# ) X+ W- # bcase WM_COMMAND:7 e! G& Y- q r! F7 e$ owmId = LOWORD(wParam); ! y9 W, H% q5 k9 X* $ rwmEvent = HIWORD(wParam); 4 M! L! 9 ; Z) d6 H5 7 / Parse the menu selections:1 KO: X/ |8 ! K6 N2 H7 vswitch (wmId)+ t: j2 % Mh, Q7 W, C8 q/ k6 |6 H7 ci2 z$ |7 T$ mcase IDM_ABOUT:, A9 y: Y: i% |DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);: T# C3 . 6 n3 c d: mbreak;* c% | s3 o& Z# xcase IDM_EXIT: _ y# I8 Q# p+ 2 I! DDestroyWindow(hWnd);( 4 l: ( t4 m, K* 7 break;Sq& $ |# X+ IGdefault:% k0 5 x% T% _return DefWindowProc(hWnd, message, wParam, lParam);1 c8 ?( E) au: P! z7 t8 D/ d3 7 ?. nbreak;! z& N! r3 F9 m2 p y2 Ycase WM_PAINT:4 L# 2 & |+ W: thdc = BeginPaint(hWnd, &ps);* j7 ; H+ F/ I/ TODO: Add any drawing code here.0 : b5 z) t6 ERECT rt;3 M. b3 E$ f& V i3 E Q0 z% d+ k- GetClientRect(hWnd, &rt);5 X$ M/ f& C; a WDrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);5 g/ H; b5 1 M! 3 y, o/ VKEndPaint(hWnd, &ps);% s9 V; x4 w( w4 ( Y4 b3 Ibreak;8 C0 U $ M- K: L& w7 $ fcase WM_DESTROY:% d+ * & R- C& l0 % QPostQuitMessage(0);$ s( v W, V2 % f: Z# ibreak; t! J6 Y c- ?4 Ydefault:, iN5 k- Y9 m9 j( W4 : ! breturn DefWindowProc(hWnd, message, wParam, lParam);0 d& E% S* A6 X; H6 e : A/ . ; Q2 j return 0;0 i! k1 r: o6 O) F9 G( T h4 ! x! m g0 Z$ e9 J5 ?9 D1 z( # F0 b消息分流器, S* a# J8 6 C$ M( F& A% Y # V通常的窗口过程是通过一个switch语句来实现的,这个事情很烦,有没有更简便的方法呢?有,那就是消息分流器,利用消息分流器,我们可以把switch语句分成更小的函数,每一个消息都对应一个小函数,这样做的好处就是对消息更容易管理。# V/ W8 9 ? Q& g2 b* V: R1 _0 w4 R9 sY之所以被称为消息分流器,就是因为它可以对任何消息进行分流。下面我们做一个函数就很清楚了:1 | x$ o& h, , m% y8 Y. M1 a& p: m0 D8 X4 + Pvoid MsgCracker(HWND hWnd,int id,HWND hWndCtl,UINT codeNotify)1 E1 r6 m7 y, Y3 G+ |2 3 T) F9 B* B& K0 cg) s/ switch(id)8 j4 : ! n0 I/ L$ G$ 1 G. Wcase ID_A:3 E- im( Y: ( s, dif(codeNotify=EN_CHANGE).N f; O4 H0 S9 H1 cbreak;/ Y, 5 8 r, n6 t$ |3 bcase ID_B:0 A: r4 t1 r$ Y* H# g4 H% tif(codeNotify=BN_CLICKED).9 |9 H1 s, h9 d$ Mbreak;. Z* n! o M8 N9 ( .1 g) % % G L+ Y b( i0 e# i& y8 Y0 B7 8 G8 u6 U/ t p( U2 U- Q; g- F: X( O! w) l, t+ J8 a9 s然后我们修改一下窗口过程:! q, |2 D* 3 w7 p* e# g5 Q# g- s4 * V- q* P1 D& H8 C, B9 TLRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)1 n* Q i8 R2 Q( i. R% f6 F# x o( i0 e: w! f* _& switch(message)( 2 i6 I) u; l8 k) V% p3 L. g1 o j. j& U) EHANDLE_MSG(hWnd,WM_COMMAND,MsgCracker);1 Y0 a9 0 a. t# LHANDLE_MSG(hWnd,WM_DESTROY,MsgCracker);+ b* 4 s# k0 T8 WIdefault:( r* s- k6 # V$ E. L* Mreturn DefWindowProc(hWnd, message, wParam, lParam);! 1 h6 7 F$ / X+ S 7 . U( F( % J9 h6 F1 e& M! return 0; , T1 2 s! R9 Q T- I5 C1 q# C3 K9 T: e0 Y( F在WindowsX.h中定义了如下的HANDLE_MSG宏:& yj Ke. S2 t8 N1 ?2 6 O4 g) T* f#define HANDLE_MSG(hwnd,msg,fn) 7 U, P1 df6 W y$ E5 o1 switch(msg): return HANDLE_#msg(hwnd),(wParam),(lParam),(fn);$ j; H8 t2 m6 w5 s y% y: i$ S& I6 B+ Q5 T5 i: G实际上,HANDLE_WM_XXXX都是宏,例如:HANDLE_MSG(hWnd,WM_COMMAND,MsgCracker);将被转换成如下定义:& x% L- m) M1 v# s( w% i0 ?+ i# t( N$ n. N& B* Q* D#define HANDLE_WM_COMMAND(hwnd,wParam,lParam,fn) # % T. Q3 6 $ n e7 y(fn)(hwnd),(int)(LOWORD(wParam),(HWND)(lParam),(UINT)HIWORD(wParam),0L);6 h6 I3 l2 , / , d- , j# |5 x) k! n4 S好了,事情到了这一步,应该一切都明朗了。% m: E1 N6 m5 O: u) H4 + A Xi+ j! K. K: i) - D不过,我们发现在windowsx.h里面还有一个宏:FORWARD_WM_XXXX,我们还是那WM_COMMAND为例,进行分析:) S( C- I8 G) f2 G, fG% k: X; _; t- X#define FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, fn) - z1 n8 5 Z2 L3 R4 n(void)(fn)(hwnd), WM_COMMAND, MAKEWPARAM(UINT)(id),(UINT)(codeNotify), (LPARAM)(HWND)(hwndCtl)6 4 a1 z7 D! W9 P# q: C7 3 k5 8 H0 H. J所以实际上,FORWARD_WM_XXXX将消息参数进行了重新构造,生成了wParam & lParam,然后调用了我们定义的函数。 8 R7 M: w6 S _* f$ v$ 2 g# |. r! Q+ + hMFC消息的处理实现方式9 x9 s& w! m+ ; oc A+ |5 m, p8 B1 i4 E# g. q初看MFC中的各种消息,以及在头脑中根深蒂固的C+的影响,我们可能很自然的就会想到利用C+的三大特性之一:虚拟机制来实现消息的传递,但是经过分析,我们看到事情并不是想我们想象的那样,在MFC中消息是通过一种所谓的消息映射机制来处理的。) b& n$ a: M% 1 d7 b0 I# _2 w8 h0 r# L3 g+ O+ T) X. t为什么呢?在潘爱民老师翻译的Visual C+技术内幕(第4版)中给出了详细的原因说明,我再简要的说一遍。在CWnd类中大约有110个消息,还有其它的MFC的类呢,算起来消息太多了,在C+中对程序中用到的每一个派生类都要有一个vtable,每一个虚函数在vtable中都要占用一个4字节大小的入口地址,这样一来,对于每个特定类型的窗口或控件,应用程序都需要一个440KB大小的表来支持虚拟消息控件函数。! V8 d1 S$ e0 x; ?U# w- A+ J5 ( u) Q. A& G如果说上面的窗口或控件可以勉强实现的话,那么对于菜单命令消息及按钮命令消息呢?因为不同的应用程序有不同的菜单和按钮,我们怎么处理呢?在MFC库的这种消息映射系统就避免了使用大的vtable,并且能够在处理常规Windows消息的同时处理各种各样的应用程序的命令消息。6 n ?$ |* q8 $ R, H* h1 6 w7 O6 h& r说白了,MFC中的消息机制其实质是一张巨大的消息及其处理函数的一一对应表,然后加上分析处理这张表的应用框架内部的一些程序代码.这样就可以避免在SDK编程中用到的繁琐的CASE语句。 7 X: a/ K( N t& b0 I$ X U4 H4 |, T* p* t2 yMFC的消息映射的基类CCmdTarget% Y L, j z, Q E4 e0 f( I9 7 p; n0 W3 K如果你想让你的控件能够进行消息映射,就必须从CCmdTarget类中派生。CCmdTarget类是MFC处理命令消息的基础、核心。MFC为该类设计了许多成员函数和一些成员数据,基本上是为了解决消息映射问题的,所有响应消息或事件的类都从它派生,例如:应用程序类、框架类、文档类、视图类和各种各样的控件类等等,还有很多。: d y. v# D1 l$ Z% S# # f& c- V9 K1 c不过这个类里面有个函数对消息映射非常重要,一个是静态成员函数DispatchCmdMsg,另一个是虚函数OnCmdMsg。! $ L- s6 uj6 O. P0 Y/ s$ v6 pDispatchCmdMsg专门供MFC内部使用,用来分发Windows消息。OnCmdMsg用来传递和发送消息、更新用户界面对象的状态。- B: r n2 y7 x3 R9 z8 3 W1 E! , * G+ oCCmdTarget对OnCmdMsg的默认实现:在当前命令目标(this所指)
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 门面租赁合同修复协议书
- 长租公寓租赁合同协议书
- 防护网工程销售合同范本
- 法人替公司还款合同范本
- 消防项目安全施工协议书
- 瑕疵生态板出售合同范本
- 物流人力合作合同协议书
- 销售咨询服务合同协议书
- 用于工作安置的合同协议
- 电梯门框安装合同协议书
- GB/T 29529-2013泵的噪声测量与评价方法
- GB/T 1591-2018低合金高强度结构钢
- D类《职业能力倾向测试》考试试题及答案
- 10000中国普通人名大全
- ISO9001:2015中英文对照版
- 眼部化妆技巧课件
- 置业顾问基础知识培训(最新版)
- TSG-Z7003-2004 特种设备检验检测机构质量管理体系要求-高清正版
- 汽车VIN效验码计算器
- 德州寺北35千伏输变电工程节地评价报告
- 用友NC财务信息系统操作手册(全)
评论
0/150
提交评论