windows的窗口消息机制(读windows核心编程笔记):基础篇

http://blog.csdn.net/zhangyq73/archive/2007/06/19/1658086.aspx

 

当线程调用函数来建立某个对象时,则该对象就归这个线程的进程所拥有。这样,当进程结束时,如果没有明确删除这个对象,则操作系统会自动删除这个对象。对窗口和挂钩( h o o k )这两种U s e r对象,它们分别由建立窗口和安装挂钩的线程所拥有。
如果一个线程建立一个窗口或安装一个挂钩,线程结束,操作系统会自动删除窗口或卸载挂钩。这意味:建立窗口的线程必须为窗口处理所有消息。这也意味着每个线程,如果至少建立了一个窗口,都由系统对它分配一个消息队列。这个队列用于窗口消息的派送( d i s p a t c h)。为了使窗口接收这些消息,线程必须有它自己的消息循环。

线程调用一个与图形用户界面有关的函数(例如检查它的消息队列或建立一个窗口),系统就会为该线程分配一些另外的资源,以便它能够执行与用户界面有关的任务。特别是,系统分配一个T H R E A D I N F O结构,并将这个数据结构与线程联系起来。这个T H R E A D I N F O结构包含一组成员变量,利用这组成员,线程可以认为它是在自己独享的环境中运行。T H R E A D I N F O是一个内部的、未公开的数据结构,用来指定线程的登记消息队列(posted-message queue)、发送消息队列( send-message queue)、应答消息队列( r e p l y -message queue)、虚拟输入队列(virtualized-input queue)、唤醒标志(wake flag)、以及用来描述线程局部输入状态的若干变量,离开代码(int nExitCode).这个T H R E A D I N F O结构是窗口消息系统的基础.

BOOL PostMessage(
  HWND hWnd,      // handle of destination window-------Long,接收消息的那个窗口的句柄。
  UINT Msg,       // message to post----------------------------Long,消息标识符
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
); 作用是消息被放置在线程的登记消息队列中。将一条消息投递到指定窗口的消息队列。投递的消息会在Windows事件处理过程中得到处理。特别适合那些不需要立即处理的窗口消息的发送。
hWnd如设为HWND_BROADCAST,表示投递给系统中的所有顶级窗口。如设为零,表示投递一条线程消息。
当一个线程调用这个函数时,系统要确定是哪一个线程建立了用h w n d参数标识的窗口。然后系统分配一块内存,将这个消息参数存储在这块内存中,并将这块内存增加到相应线程的登记消息队列中。并且,这个函数还设置Q S _ P O S T M E S S A G E唤醒位。
函数在登记了消息之后立即返回,调用该函数的线程不知道登记的消息是否被指定窗口的窗口过程所处理。实际上,有可能这个指定的窗口永远不会收到登记的消息。如果建立这个特定窗口的线程在处理完它的消息队列中的所有消息之前就结束了,就会发生这种事。


DWORD GetWindowThreadProcessId(
  HWND hWnd,             // handle to window
  LPDWORD lpdwProcessId // address of variable for process identifier
);函数返回线程的I D,这个线程建立了h w n d参数所标识的窗口。线程I D在全系统范围内是唯一的。可以通过对p d w P r o c e s s I d参数传递一个D W O R D地址来获取拥有该线程的进程I D,这个进程I D在全系统范围内也是唯一的。通常,不需要进程I D,只须对这个参数传递N U L L。

BOOL PostThreadMessage(
  DWORD idThread, // thread identifier
  UINT Msg,       // message to post
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
);也可以将消息放置在线程的登记消息队列中。PostThreadMessage是一个线程体发送一个消息到指定的线程ID。
当消息被设置到队列中时,M S G结构的h w n d成员将设置成N U L L。当程序要在主消息循环中执行一些特殊处理时要调用这个函数。这个函数既可以发送消息给工作线程,也可以发送给UI线程。
接受PostThreadMessage的线程必须已经有了一个message queue,否则调用PostThreadMessage会失败。因为此原因使用GetLastError会得到错误码为1444,这种情况经常出现,解决方法有如下两种:调用PostThreadMessage,如果失败,就Sleep一段时间再次调PostThreadMessage直到调用成功;创建一个Event对象,让PostThreadMessage等待接受的线程创建一个message queue。可以通过调用PeekMessage强制系统创建一个message queue。
PostThreadMessage传递的消息如果要包含信息,要注意在结束的时候释放消息中的信息。P o s t T h r e a d M e s s a g e在向线程的队列登记了消息之后就立即返回。调用该函数的线程不知道消息是否被处理。

VOID PostQuitMessage(  int nExitCode   // exit code);为了终止线程的消息循环,应该是由DestroyWindow()中发出的WM_QUIT消息来引起调用的。PostQuitMessage()是posts一个WM_QUIT消息给消息队列并立即返回;进程收到WM_QUIT消息后退出消息循环,程序结束。DestroyWindow()是发送一个WM_DESTROY消息,结束掉此窗口,但是程序并没有结束。
LRESULT SendMessage(
  HWND hWnd,      // handle of destination window
  UINT Msg,       // message to send
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
);
函数功能是将窗口消息直接发送给一个窗口过程:只有当消息被处理之后, S e n d M e s s a g e才能返回到调用程序。
当一个线程向其他线程所建立的窗口发送消息时, S e n d M e s s a g e的内部工作就复杂得多(即使两个线程在同一进程中也是如此)。当一个线程调用S e n d M e s s a g e向一个由其他进程所建立的窗口发送一个消息,发送线程不可能处理窗口消息,因为发送线程不是运行在接收进程的地址空间中,因此不能访问相应窗口过程的代码和数据。实际上,发送线程要挂起,而由另外的线程处理消息。所以为了向其他线程建立的窗口发送一个窗口消息,系统必须执行下面将讨论的动作。
首先,发送的消息要追加到接收线程的发送消息队列,同时还为这个线程设定Q S _ S E N D M E S S A GE标志。
其次,如果接收线程已经在执行代码并且没有等待消息(如调用G e t M e s s a g e、P e e k M e s s a g e或Wa i t M e s s a g e),发送的消息不会被处理,系统不能中断线程来立即处理消息。
当接收进程在等待消息时,系统首先检查Q S _ S E N D M E S S A G E唤醒标志是否被设定,如果是,系统扫描发送消息队列中消息的列表,并找到第一个发送的消息。几个线程可以同时向一个窗口分别发送消息。这时,系统只是将这些消息追加到接收线程的发送消息队列中。
当接收线程等待消息时,系统从发送消息队列中取出第一个消息并调用适当的窗口过程来处理消息。如果在发送消息队列中再没有消息了,则Q S _ S E N D M E S S A G E唤醒标志被关闭。当接收线程处理消息的时候,调用S e n d M e s s a g e的线程被设置成空闲状态( i d l e),等待一个消息出现在它的应答消息队列中。在发送的消息处理之后,窗口过程的返回值被登记到发送线程的应答消息队列中。发送线程现在被唤醒,取出包含在应答消息队列中的返回值。这个

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值