窗口是在线程环境下产生的,在线程DispatchMessage时调用窗口过程,好像有些消息是直接调用窗口过程的,这是在什么情况下发生的?既然窗口过程是在线程中执行的,为什么在某个线程的窗口过程中陷入死循环整个程序都没有响应了,而在某个线程中陷入死循环,程序还是可以响应的?如果我上面说的正确的话,为什么窗口过程在一个消息没有处理完成之前还可以处理下一条?(如窗口中可以弹出多个模式对话框)?

解决方案 »

  1.   

    整个程序都没有响应只是一个假象,实际上是运行WinMain那个线程没有响应,程序的主要界面没有相应看起来就像整个程序没有相应。如果你用SendMessage,则直接调用窗口函数,不通过消息循环的DispatchMessage。
      

  2.   


    (转载)
    essage Routing
    系统有两个方法将消息传递到窗口过程。Post一个消息到先进先出的消息队列。系统定义的临时内存对象。和直接 send消息到窗口过程。被发送到消息队列的消息称做 queued messages。他们主要是由鼠标键盘输入,例如 WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_KEYDOWN, and WM_CHAR messages。还包括定时器, 刷新, 退出: WM_TIMER, WM_PAINT, and WM_QUIT。其他直接发送到窗口过程的消息被称为 nonqueued messages. Queued Messages系统可以显示任何数量的窗体同一时间。为了传递鼠标键盘消息到合适的窗口,系统使用消息队列。 
    系统维护一个系统消息队列和每一个GUI线程的消息队列,为避免给non-GUI现成创建消息队列,所有线程产生时并没有消息队列
    仅当线程第一次调用GDI函数数系统给线程创建一个消息队列。只要用户移动鼠标,点击鼠标,敲键,驱动程序将其转换为消息将他们放在系统消息队列中。系统将他们从系统消息队列中移走,检查他们的目标窗口,然后将他们发送到创建目标窗口的线程的消息队列。现成消息队列接受由这个线程创建的所有窗口的消息队列。线程删除消息系统调用窗口过程进行处理。
    WM_PAINT是一个例外,系统总是将消息Post在消息队列的末尾。这样保证窗口以先进先出的顺序接受消息。然而, is kept in the queue and is forwarded to the window procedure only when the queue contains no other messages.同一个窗口的多个 WM_PAINT被合并成一个 WM_PAINT 消息, 合并所有的无效区域到一个无效区域。合并WM_PAIN减少了刷新窗口的次数。 系统通过填充MSG结构并将它复制到消息队列来发送消息到线程队列。MSG结构包括:窗口句柄,消息标识符,两个消息参数。 the time the message was posted, and the mouse cursor position. 线程可以使用PostMessage和PostThreadMessage来给发送消息到自己消息队列或者另一个线成的消息队列。 
    应用程序可以使用GetMessage从消息队列删除消息。可以使用 PeekMessage来检查一个消息而不删除它。 这个函数将消息队列的消息填充到MSG结构。 
    在从消息队列删除了一个消息,应用程序可以使用DispatchMessage使系统将消息发送到窗口过程来处理。DispatchMessage拥有一个由GetMessage或者PeekMessage填充的结构的指针,传递窗口句柄,消息标识符,消息参数给窗口过程。但它并不传递消息发送的时间和鼠标的位置(???),应用程序可以通过GetMessageTime和GetMessagePos来得到这些信息。
    A thread can use the WaitMessage function to yield control to other threads when it has no messages in its message queue. 这个函数挂起线程,并不返回,直到新的消息放置于消息队列中。 
    你可以调用SetMessageExtraInfo函数来关联一个值到消息队列。调用GetMessageExtraInfo来得到这个值。
      

  3.   

    Nonqueued Messages非入队消息即直接发送到窗口过程的消息,绕过系统队列和消息队列。系统发送非入队消息通知窗口,系统发送消息通知窗口(The system typically sends nonqueued messages to notify a window of events that affect it)。 例如,当用户激活一个窗口系统发送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。这些消息通知窗口它被激活了。that keyboard input is being directed to the window, and that the mouse cursor has been moved within the borders of the window. 非入队消息也可以由当应用程序调用系统函数产生。例如,当程序调用SetWindowPos系统发送WM_WINDOWPOSCHANGED消息。
    一些函数也发送非入队消息如 BroadcastSystemMessage, BroadcastSystemMessageEx, SendMessage, SendMessageTimeout, and SendNotifyMessage. Message Handling
    应用程序必须移除和处理被发送到消息队列的消息。但线程应用程序通常在WinMain使用消息循环来移除和分发消息到何时窗口过程来处理。多线程应用程序可以在每一个创建窗口的线程中中包含消息循环。Message Loop
    A simple message loop consists of one function call to each of these three functions: GetMessage, TranslateMessage, and DispatchMessage. 注意如果有错误GetMessage 返回 -1 -thus the need for the special testing.例子MSG msg;
    BOOL bRet;while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)

        if (bRet == -1)
        {
            // handle the error and possibly exit
        }
        else
        {
            TranslateMessage(&msg); 
            DispatchMessage(&msg); 
        }
    }
    GetMessage 函数从消息队列获得一个消息并将它复制到MSG结构。他返回一个非0值,除非遇到WM_QUIT消息。否则它返回0然后结束循环。In a single-threaded application, ending the message loop is often the first step in closing the application. 应用程序可以使用PostQuitMessage来结束自己的消息循环。通常在主窗口的WM_DESTROY消息中调用。 
    如果你将一个窗口句柄作为第二个参数传入GetMessage,那么只有指定窗口的的消息可以从队列中获得。GetMessage也可以从消息队列中过滤消息只接受消息队列中落在范围内的消息。详细见消息过滤。
    一个线程循环必须包括TranslateMessage如果线程接受键盘的字符输入。每一次用户按键系统产生虚拟键消息,一个虚拟键消息包含虚拟键用来标志那个键被按下,要获得这个值消息循环必须调用TranslateMessage,用于将虚拟键转换为字符消息WM_CHAR然后将它放回应用程序消息队列。The character message can then be removed upon a subsequent iteration of the message loop and dispatched to a window procedure. 
    DispatchMessage 函数分发消息到MSG结构中的窗口句柄关联的窗口过程。如果窗口句柄是HWND_TOPMOST,DispatchMessage分发消息到系统中的所有的top-level窗口的窗口过程。如果句柄是NULL,DispatchMessage不做任何事。
    应用程序得主线程在初始化击创建至少一个窗口后启动它的消息,一旦启动消息循环持续从线程队列中获得消息,然后分发他们到合适的窗口。消息循环在得到WM_QUIT后结束。
    Only one message loop is needed for a message queue, even if an application contains many windows. DispatchMessage 总是分发消息到合适的窗口,这是因为MSG结构包含消息所属的窗口的句柄。
    你可以用各种方式修改消息循环。例如,从消息队列获得消息消息但不把他们分发到窗口中去。当应用程序发送一个消息但不指定窗口时是有用的。你可以用GetMessage获得一个特定的消息,而保留其他消息在消息队列。当你需要改变先进先出的顺序时是有用的。
    应用程序使用加速键必须将键盘消息转换为WM_COMMAND消息。所以消息循环必须包括TranslateAccelerator函数。详细信息参见加速键。
    如果线程使用非模态对话框,消息循环必须包括 IsDialogMessage 以使非模态对话框获得键盘输入。 Window Procedure窗口过程是一个用于处理所有发送到这个窗口的消息的函数。任何一个窗口类都有一个窗口过程。同一个类的窗口使用同样的窗口过程来响应消息。 
    系统发送消息给窗口过程将消息数据作为参数传递给他,窗口过程使用参数产生合适行为。
    一个窗口过程不经常忽略消息,如果他不处理,它会将消息传回到执行默认的处理。窗口过程通过调用DefWindowProc来做这个处理。窗口过程必须return一个值作为它的消息处理结果。大多数窗口只处理小部分消息和将其他的通过DefWindowProc传递给系统做默认的处理。
    窗口过程被所有属于同一个类的窗口共享,能为不同的窗口处理消息。Message Filter
    应用程序可以从消息队列选择特定的消息。使用GetMessage或者PeekMessage并指定一个消息过滤器。这个过滤器是一个消息标识符的范围或者是一个窗体句柄,或者两者同时指定。当应用程序要查找一个后入消息队列的消息是很有用。 It is also useful if an application must process input (hardware) messages before processing posted messages. 
    WM_KEYFIRST 和 WM_KEYLAST 常量用于接受所有的键盘消息。 the WM_MOUSEFIRST 和 WM_MOUSELAST 常量用于接受所有的鼠标消息。 
    Any application that filters messages must ensure that a message satisfying the message filter can be posted. For example, if an application filters for a WM_CHAR message in a window that does not receive keyboard input, the GetMessage function does not return. This effectively "hangs" the application. Posting and Sending Messages
    应用程序可以post和send消息,通过将消息复制到消息队列即post消息,send消息则绕过了消息队列直接传递消息数据到窗口过程。
    可以使用PostMessage来post消息,SendMessage来send消息。
      

  4.   

    Posting Messages
    应用程序post消息通知指定窗体执行任务。PostMessage可以创建MSG结构并将它Copy到消息队列。消息循环最终捕获消息并分发到合适的窗口过程。
    给PostMessage传递一个NULL句柄不指定哪一个窗口,这个消息就被发送到当前线程消息队列,应用程序必须在消息处理中处理这个消息。这是为整个应用程序发送消息的一个方法。
    偶尔你可以使用HWND_TOPMOST 这个参数作为句柄参数向所有的top-level窗口发送消息。
    当消息队列满的时候PostMessage并不发送消息,应用程序需要检查PostMessage函数的返回值来确定消息是否被发送,或者没有需要重发。Sending Messages
    通过Send消息来通知窗口过程立即执行任务。SendMessage将消息发送给指定窗口的窗口过程。函数将等待窗口过程处理完才返回一个消息结果。父窗口和子窗口通常使用Send消息来互相通讯。例如,一个父窗口拥有以一个文本框作为它的子窗口,它可以通过发送消息到子窗口来给文本框设置文字。子窗口也可将文字被用户改变的消息发送给父窗口。
    SendMessageCallback也将消息发送给指定窗口的窗口过程,但是他立即返回。在窗口过程处理完消息后,系统调用指定的回调函数,回调函数的详细资料参见SendAsyncProc
    偶尔,你可以发送消息系统中到所有的top-level窗口,例如,应用程序改变了系统时间。它必须以HWND_TOPMOST作为句柄参数发送一个WM_TIMECHANGE 消息通知所有的top-level窗口,你也可以使用以BSM_APPLICATIONS作为参数的BroadcastSystemMessage函数向所有应用程序广播。
    可以使用InSendMessage或者InSendMessageEx函数,窗口过程可以判断它处理的消息是否是由其他线程调用SendMessage发送过来的。This capability is useful when message processing depends on the origin of the message. Message Deadlocks
    一个线程调用SendMessage函数发送一个消息给另一个线程不能执行知道接受消息的窗口过程返回。If the receiving thread yields control while processing the message, 发送线程不能继续执行,因为它在等待SendMessage返回。If the receiving thread is attached to the same queue as the sender, 将引起死锁。
    Note that the receiving thread need not yield control explicitly; calling any of the following functions can cause a thread to yield control implicitly. DialogBox
    DialogBoxIndirect
    DialogBoxIndirectParam
    DialogBoxParam
    GetMessage
    MessageBox
    PeekMessage
    SendMessage
    为了避免潜在的死锁,可以使用SendNotifyMessage 或者 SendMessageTimeout,否则窗口过程,将用InSendMessage或者InSendMessageEx判断消息是否由另一个线程发送过来。当调用前面任何一个函数窗口过程将首先调用InSendMessage或者InSendMessagEx,如果函数返回true窗口过程必须调用ReplyMessage before any function that causes the thread to yield control. Broadcasting Messages
    每一个消息包括消息标识符和两个参数,wParam和lParam,消息标识符是唯一的代表这个消息的含义。参数提供与消息相关的额外的信息,但是wParam参数通常是一个类型值提供更多的消息信息。
    消息广播是简单的将消息发送到系统中的多个接收者。使用BroadcastSystemMessage函数来广播消息,你必须制定一个或者多个接收者类型,这些类型可以是applications, installable drivers, network drivers, and system-level device drivers。系统将消息发送给指定类型的所有成员。
    当系统级的设备驱动或者相关组件发生变化了系统通常用广播消息来响应这些变化。For example, the component responsible for disk drives broadcasts a message whenever the device driver for the floppy disk drive detects a change of media such as when the user inserts a disk in the drive.
    The system broadcasts messages to recipients in this order: system-level device drivers, network drivers, installable drivers, and applications. This means that system-level device drivers, if chosen as recipients, always get the first opportunity to respond to a message. Within a given recipient type, no driver is guaranteed to receive a given message before any other driver. This means that a message intended for a specific driver must have a globally-unique message identifier so that no other driver unintentionally processes it.
    You can also broadcast messages to all top-level windows by specifying HWND_BROADCAST in the SendMessage, SendMessageCallback, SendMessageTimeout, or SendNotifyMessage function. 
    Applications receive messages through the window procedure of their top-level windows. Messages are not sent to child windows. Services can receive messages through a window procedure or their service control handlers.
    Note  System-level device drivers use a related, system-level function to broadcast system messages. Query Messages
    You can create your own custom messages and use them to coordinate activities between your applications and other components in the system. This is especially useful if you have created your own installable drivers or system-level device drivers. Your custom messages can carry information to and from your driver and the applications that use the driver. To poll recipients for permission to carry out a given action, use a query message. You can generate your own query messages by setting the BSF_QUERY value in the dwFlags parameter when calling BroadcastSystemMessage. Each recipient of the query message must return TRUE for the function to send the message to the next recipient. If any recipient returns BROADCAST_QUERY_DENY, the broadcast ends immediately and the function returns a zero.Windows 95/98/Me: You can create installable drivers that broadcast and process messages. An installable driver is a dynamic-link library (DLL) that exports a DriverProc function. The driver receives messages through its DriverProc function and can broadcast messages using BroadcastSystemMessage. Installable drivers are typically used to support multimedia devices, such as sound boards, but can be used for other devices and purposes too. Windows 95/98/Me: Network drivers are DLLs that provide the underlying support for applications that use the network functions to connect to and browse network resources. System-level device drivers are system-specific executable components that provide direct access to and management of the hardware devices of the computer. The details regarding how these components process system messages is beyond the scope of this overview.
    在复习windows程序设计时看了一下msdn并翻译了一下,水平很烂,太累了,先到这里了。希望网友能给我补充或者指出错误。文章出至platform sdk帮助中的About Messages and Message Queues 一章。
      

  5.   

    to stonespace(stonespace) 
    为什么在其他线程创建的窗口过程陷入死循环,而运行WinMain的线程(应该是主线程了)会没有响应了呢?是不是因为占用了GDI资源?