zoukankan      html  css  js  c++  java
  • 窗口函数你的第一个Windows程序——窗口消息

    改章节是一篇关于窗口函数的帖子

        MSDN在线原文(英文)

        

    窗口消息

        

    一个GUI应用程序必须响应用户和操作系统的事件。

        


        

    来自用户的事件 包含所有的使他人能够与你的程序互动的方法:鼠标点击,键盘输入,触屏手势等。

        

    来自操作系统的事件 包含任何可以影响程序行为的“外部”程序,比如用户可以插入一个新的硬件,或者Windows可能会进入低功耗状态(就寝或休眠)。

        


        

    在程序运行时,这些事件可以在恣意次序任何时光发生,你如何构建一个执行流程不能提前预测的程序?

        

    为懂得决这个问题,Windows应用一个“消息传递”模块,操作系统和你的窗口应用程序传递消息给这个模块。消息是一个简单的数字代码,它指定一个特定的事件。例如,用户按压了鼠标左键,窗口收到以下消息代码的消息:

        


    #define WM_LBUTTONDOWN 0x0201

    有一些消息具有与它们相关的数据,比如 WM_LBUTTONDOWWN 消息包含了鼠标以后位置的X坐标与Y坐标。
    为了传递一个消息给窗口,操作系统将调用注册该窗口的窗口进程(现在你晓得什么是窗口进程).

        


        

    消息循环

        

    一个应用程序在它运行时,会收到数以千记的消息(想想看,每一次的击键和鼠标点击生成一条消息),而且一个应用程序有多个窗口,每个都有自己的窗口进程。程序如何获得所有这些消息并供给个正确的窗口进程?应用程序须要一个循环来获得消息并把它们发送到正确的窗口。对于创建窗口的每个线程,操作系统创建一个窗口消息队列,这个队列保存所有在这个线程创建的窗口消息。在你的程序里,队列本身是隐藏的,你不能直接操纵队列,但你可以调用GetMessage函数把消息从队列拉出来。

        


    MSG msg;
    GetMessage(&msg, NULL, 0, 0);
    

    这个函数从队列头部删除第一条消息,如果队列是空的,直到另外一个功能块的消息停止排队。事实上,GetMessage阻塞并不会使你的程序没有反应。如果没有消息,程序不做任何事情。如果你须要执行后台处理,你可以创建额外的线程继续运行,而GetMessage函数等待另外一条消息。(查看写窗口进程)

        

    GetMessage函数的第一个参数是一个MSG结构的地址,如果函数调用成功,它填充MSG结构有关的消息中的信息,包含目标窗口和消息代码。其它三个参数让你能够从消息队列中停止过滤。在几乎所有的情况下,将参数设置为0(零)。

        


        

    虽然MSG结构包含消息有关的信息,你几乎不用直接检查这类结构,相反,你要把它直接传递给两个函数
    TranslateMessage(&msg); 
    DispatchMessage(&msg);
    

    TranslateMessage函数与键盘输入关联,它把键盘的键击(键按下,按键摊开)转换成字符,你不须要真正的晓得这个函数是如何工作的,只须要晓得在调用DispatchMessage函数之前调用它。如果你须要更深入的懂得,MSDN文档的链接会给你更多的信息。
        每日一道理
    聪明人学习,像搏击长空的雄鹰,仰视一望无际的大地;愚笨的人学习,漫无目的,犹如乱飞乱撞的无头飞蛾;刻苦的人学习,像弯弯的河流,虽有曲折,但终会流入大海;懒惰的人学习,像水中的木头,阻力越大倒退得越快。
     

        

    DispatchMessage函数告知操作系统调用消息的目标窗口的窗口进程,换句话说,操作系统查找窗口表的窗口句柄,发明与窗口关联的函数指针,并调用函数。

        

    例如,假设用户按下鼠标左键,这引发一连串的事件:

        

    1. 操作系统在消息队列放置一个WM_LBUTTONDOWN消息。
    2. 你的程序调用GetMessage函数。
    3. GetMessage中队列中提取WM_LBUTTONDOWN并填充MSG结构。
    4. 你的程序调用TranslateMessage和DispatchMessage函数。
    5. 在DispatchMessage,操作系统调用你的窗口进程。
    6. 你的窗口函数可以响应消息或忽略它。

        当窗口函数返回,它将返回到DispatchMessage,下一条消息再回到消息循环。只要你的程序在运行,消息将继续达到队列。因此,你须要一个循环,不断的从队列中提取消息并调度它们。你可能想到的循环执行以下操作:

    //警告:实际上不要这样写循环
    while (1)      
    {
        GetMessage(&msg, NULL, 0,  0);
        TranslateMessage(&msg); 
        DispatchMessage(&msg);
    }

        
    正如以上所写,这个循环永远不会结束。这是GetMessage函数放在循环内的返回值。正常情况下,GetMessage函数返回一个非零值。当你想要退出程序和中断消息循环,简单的调用PostQuitMessage函数。

    PostQuitMessage(0);

        PostQuitMessage函数将一个WM_QUIT消息放入队列,WM_QUIT是一条特别的消息,它引发GetMessage函数返回零值,消息循环结束的信号。这是修改后的消息循环:

    // 正确的消息循环
    
    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

        
    只要GetMessage函数返回非零值,在while循环中表达式的值就是真值,在调用PostQuitMessage函数后,表达式值为假值程序跳出循环。(这类行为有一个有趣的结果,你的窗口进程从未收到WM_QUIT消息,所以并不须要在窗口进程中为这个消息应用case语句。)

        下一个显著的问题是:你应该在什么时候调用PostQuitMessage函数?我们回到本主题中的窗口关闭的问题。但我们首先要写我们的窗口进程。

        

     宣布消息和发送消息

        上一节谈到进入队列的消息,在某些情况下,操作系统将绕过消息队列,直接调用窗口进程。

        停止这类辨别的术语有可能造成混杂:

        宣布(Post)一条消息意味着消息在消息队列中,并通过消息循环调度(GetMessage和DispatchMessage)。

        发送(Send)一条消息意味着跳过消息队列,操作系统直接调用窗口进程。

        现在,区别不是很主要。窗口进程处理所有消息,但某些消息绕过消息队列,直接进入你的窗口进程。然而,如果你的应用程序窗口之间停止通信它可以有所作为。你可以在关于消息和消息队列(About Messages and Message Queues)的主题中找到更深入的探讨。

        下一节:誊写窗口进程

    文章结束给大家分享下程序员的一些笑话语录: 警告
    有一个小伙子在一个办公大楼的门口抽着烟,一个妇女路过他身边,并对他 说, “你知道不知道这个东西会危害你的健康?我是说, 你有没有注意到香烟 盒上的那个警告(Warning)?”
    小伙子说,“没事儿,我是一个程序员”。
    那妇女说,“这又怎样?”
    程序员说,“我们从来不关心 Warning,只关心 Error”

    --------------------------------- 原创文章 By
    窗口和函数
    ---------------------------------

  • 相关阅读:
    复习一些奇怪的题目
    NOIP 考前 KMP练习
    NOIP 考前 并查集复习
    NOIP 考前 Tarjan复习
    NOIP 考前 图论练习
    BZOJ 1468 树分治
    Codeforces Round #376 (Div. 2)
    CodeVS 线段覆盖1~5
    Luogu 3396 权值分块
    BZOJ 2743 树状数组
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3100622.html
Copyright © 2011-2022 走看看