当一个线程第一次被创建时,系统假定线程不会用于任何与用户相关的任务。这样可以减少线程对系统资源的要求。但是,一旦该线程调用一个与图形用户界面有关的函数 (如检查它的消息队列或建立一个窗口 ),系统就会为该线程分配一些另外的资源,以便它能够执行与用户界面有关的任务。特别是,系统分配了一个THREADINFO结构,并将这个数据结构与线程联系起来。
THREADINFO结构体如下:
1.将消息发送到线程的消息队列
当线程有了与之联系的THREADINFO结构时,消息就有自己的消息队列集合。
通过调用函数 BOOL PostMesssage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
可以将消息放置在线程的登记消息队列中。
当一个线程调用这个函数时,系统要确定是哪个线程建立了用 hwnd 参数标识的窗口。然后系统分配一块内存,将这个消息参数存储在这块内存中,并将这块内存增加到相应线程的登记消息队列中。并且该函数还设置QS_POSTMESSAGE唤醒位。函数 PostMesssage 在登记了消息后立即返回,调用该函数的线程不知道登记的消息是否被指定窗口的窗口过程所处理。
还可通过调用函数 BOOL PostThreadMesssage(DWORD dwThreadId, UINT uMsg, WPARAM wParam, LPARAM lParam)将消息放置在线程的登记消息队列中,同 PostMesssage 函数一样,该函数在向线程的队列登记消息后立即返回,调用该函数的线程不知道消息是否被处理。
向线程的队列发送消息的函数还有 VOID PostQuitMesssage(int nExitCode) ;
该函数可以终止线程消息的循环,调用该函数类似于调用:PostThreadMesssage(GetCurrenThreadId( ), WM_QUIT, nExitCode, 0);但 PostQuitMesssage 并不实际登记一个消息到任何队列中。只是在内部,该函数设定 QS_QUIT 唤醒标志,并设置 THREADINFO 结构的 nExitCode 成员。
2.向窗口发送消息
将窗口消息直接发送给一个窗口过程可以使用函数 LRESULT SendMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)窗口过程将处理这个消息,只有当消息被处理后,该函数才能返回。即具有同步的特性。
该函数的工作机制:
2.1 如果调用该函数的线程向该线程所建立的窗口发送了一个消息,SendMessage就很简单:它只是调用指定窗口的窗口过程,将其作为一个子例程。当窗口过程完成对消息的处理时,它向 SendMessage 返回一个值。SendMessage 再将这个值返回给调用线程。
2.2当一个线程向其他线程所建立的窗口发送消息时,SendMessage就复杂很多(即使两个线程在同一个进程中也是如此)。windows 要求建立窗口的线程处理窗口的消息。所以当一个线程调用 SendMessage 向一个由其他进程所建立的窗口发送一个消息,也就是向其他线程发送消息,发送线程不可能处理该窗口消息,因为发送线程不是运行在接收进程的地址空间中,因此不能访问相应窗口的过程的代码和数据。(对于这个,我有点疑问:同一个进程的不同线程是运行在相同进程的地址空间中,它也采用这种机制,又作何解释呢?)实际上,发送线程要挂起,而由另外的线程处理消息。所以为了向其他线程建立的窗口发送一个窗口消息,系统必须执行一些复杂的动作。
由于windows使用上述方法处理线程之间的发送消息,所以有可能造成线程挂起,严重的会出现死锁。
利用一下4个函数可以编写保护性代码防护出现这种情况。
1. LRESULT SendMessageTimeout( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT fuFlags, UINT uTimeout , PDWORD_PTR pdwResult);
2. BOOL SendMessageCallback( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, SENDSYNCPROC pfnResultCallback, ULONG_PTR dwData);
3. BOOL SendNotifyMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
4.BOOL ReplyMessage( LRESULT lResult);
另外可以使用函数 BOOL InSendMessage( ) 判断是在处理线程间的消息发送,还是在处理线程内的消息发送
转自 http://www.cnblogs.com/yewsky/articles/1882730.html
几种消息队列:
系统消息队列由Windows维护,线程消息队列则由每个GUI线程自己进行维护,为避免给non-GUI线程创建消息队列,所有线程产生时并没有消息队列,仅当线程第一次调用GDI函数时系统给线程创建一个消息队列。队列消息送到系统消息队列,然后到线程消息队列;非队列消息直接送给目的窗口过程。
对于队列消息,最常见的是鼠标和键盘触发的消息,例如WM_MOUSERMOVE,WM_CHAR等消息,还有一些其它的消息,例如:WM_PAINT、WM_TIMER和WM_QUIT。当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由Windows系统去进行处理。Windows系统则在适当的时机,从系统消息队列中取出一个消息,根据前面我们所说的MSG消息结构确定消息是要被送往那个窗口,然后把取出的消息送往创建窗口的线程的相应队列,下面的事情就该由线程消息队列操心了,Windows开始忙自己的事情去了。线程看到自己的消息队列中有消息,就从队列中取出来,通过操作系统发送到合适的窗口过程去处理。
一般来讲,系统总是将消息Post在消息队列的末尾。这样保证窗口以先进先出的顺序接受消息。然而,WM_PAINT是一个例外,同一个窗口的多个 WM_PAINT被合并成一个 WM_PAINT 消息, 合并所有的无效区域到一个无效区域。合并WM_PAIN的目的是为了减少刷新窗口的次数。
系统消息队列,就是windows系统自己控制的队列,编程人员管不了也不看不到
系统消息队列就是解析和分发每个消息到 哪一个应用程序当中
关键是 系统中运行的每一个应用程序,都有一个或只能有一个主线程,这个主线程里面就是一个死循环的解析消息队列(winmain里面),那么应用程序中的其他所有线程或者其他所有窗口都共用主线程中的一个消息队列,当然也可以在线程中去写消息队列解析,但默认是都共用主线程的消息队列,然后解析中分发消息到各自窗体的消息映射函数