zoukankan      html  css  js  c++  java
  • Windows程序设计2(消息机制、菜单)

     

    、小记;

      PostQuitMessage(0);

      产生WM_QUIT消息给进程队列,且立即返回,同时使得消息循环退出,使得进程终止。(其实它通过PostMessage(hWnd,WM_QUIT,0,0)发送消息)

      MoveWindow();//移动窗口

      BOOL  MoveWindow(

          HWND hWnd,      // 窗口句柄

          int X,          // 水平位置

          int Y,          // 垂直位置

          int nWidth,     // 宽度

          int nHeight,    // 高度

          BOOL bRepaint   // 重绘标识

        );

    、消息机制

    1.程序执行机制:

      过程驱动:程序的执行是有序的,按照预先预定义的顺序执行。

      事件驱动:程序执行是无序的,用户可以按照需要随机触发相应的事件。

      Win32窗口程序就是按照事件驱动方式执行,即消息机制。当系统通知窗口时,采用消息的方式派发给窗口。

      每个窗口都要有个消息处理函数。

      当系统通知窗口时,会调用窗口处理函数同时,将消息ID和消息参数传递给窗口处理函数。

      在窗口处理函数中,不处理的消息,使用缺省窗口处理函数,例如DefWindowProc 。

    2.消息相关函数

      回调机制:程序员自己定义的函数,但交给操作系统调用。

      获取消息:

      BOOL GetMessage(

        LPMSG lpMsg, //存放获取到的消息BUFF

        HWND hWnd,   //窗口句柄

        UINT wMsgFilterMin, //获取消息的最小ID范围

        UINT wMsgFilterMax    //获取消息的最大ID范围

      );

      lpMsg - 当获取到消息后,将消息的参数存放到MSG结构中。

      hWnd - 获取hWnd所指定窗口的消息,若NULL,则获取所有消息。

      wMsgFilterMin和wMsgFilterMax -只能获取到由它们指定的消息范围内的消息,如果都为0,表示没有范围。

      若接受的消息为WM_QUIT,则返回值为0,则退出消息循环。

      GetMessage - 从系统获取消息,将消息从系统中移除,阻塞函数。当系统无消息时,GetMessage会等候下一条消息。

      PeekMessage - 以查看的方式从系统获取消息,可以不将消息从系统移除,非阻塞函数。当系统无消息时,返回FALSE,继续执行后续代码。

      使用PeekMessage()可以在不锁住我们的程序的前提下对消息进行检查。许多程序使用GetMessage(),也可以很好的工作。但使用GetMessage(),程序在收到paint消息或其他别的什么窗口消息之前不会做任何事。

      翻译消息:将按键消息,翻译成字符消息。

      BOOL TranslateMessage(

           CONST MSG *lpMsg //要翻译的消息地址

          );

      检查消息是否是按键的消息,若是,则产生一个字符消息。如果不是按键消息,不做任何处理,继续执行。

      派发消息:  将消息派发到该消息所属窗口的窗口处理函数上。

      DispatchMessage(&msg)

      {

      1. 获取消息是属于本进程哪个窗口(&msg)->hWnd;
      2. 根据窗口句柄获取内存中的信息 如:类名
      3. 将类名与操作系统中去匹配 窗口类名
      4. 若匹配,调用被匹配的哪个窗口类中存的函数(消息处理函数)。
      5. 将其中的4个参数一一传递给处理函数。

      }

    3.常用Windows消息

      WM_DESTROY - 窗口被销毁时的消息,无消息参数。常用于在窗口被销毁之前,做相应的善后处理,例如资源、内存等。

      WM_SYSCOMMAND - 系统命令消息,当点击窗口的最大化、最小化、关闭等命令时,收到这个消息。

      常用在窗口关闭时,提示用户处理。

          wParam - 具体命令,例如关闭SC_CLOSE等.( wParam==SC_CLOSE)

        lParam - 鼠标位置

           LOWORD 低字 - 水平位置 (LOWORD(lParam))

        HIWORD 高字 - 垂直位置 (HIWORD(lParam))

      WM_CREATE - 在窗口创建成功还未显示之前,收到这个消息。

        常用于初始化窗口的参数、资源等等,包括创建子窗口等。

        WPARAM - 不使用,LPARAM - 是CREATESTRUCT结构的指针,保存了CreatWindowEx中的12个参数。若使用它,注意要强制转换类型。(CREATESTRUCT*)lParam。

      WM_SIZE --常用于窗口大小变化后,调整窗口内各个部分的布局

        WPARAM - 窗口大小变化的原因,LPARAM-变化窗口客户区的大小LOWORD - 变化后的宽度    (LOWORD(lParam))

        HIOWORD- 变化后的高度    (HIWORD(lParam))

      WM_QUIT - 用于结束消息循环处理。

        wParam - PostQuitMessage函数传递的参数,lParam -不使用。

        当GetMessage收到这个消息后,会返回FALSE,结束while处理,退出消息循环。

      WM_PAINT - 绘图消息

      键盘消息

      鼠标消息

      定时器消息

    4.消息的发送(仅此两种,其他形式的发送消息都是基于这两种)

      SendMessage - 发送消息,会等候消息处理的结果。

      PostMessage - 投递消息,消息发出后立刻返回,不等候消息执行结果。

      LRESULT SendMessage / BOOL PostMessage(

        HWND hWnd,//消息发送的目的窗口

        UINT Msg, //消息ID

        WPARAM wParam, //消息参数

        LPARAM lParam  //消息参数

        );

    5.消息的分类

    1 系统消息 - ID范围 0 - 0x03FF (0~1023)

      由系统定义好的消息,可以在程序中直接使用。

    2 用户自定义消息 - ID范围 0x0400 - 0x7FFF (31743个)

      由用户自己定义,满足用户自己的需求。由用户自己发出消息,并响应处理。

      自定义消息宏:WM_USER (其实已定义为0x0400)

    3 应用程序消息 - ID范围 0x8000 - 0xBFFF

      程序之间通讯时使用的消息。

      应用程序消息宏:WM_APP

    4 系统注册消息 - ID范围 0xC000 - 0xFFFF

      在系统注册并生成相应消息,然后可以在各个程序中使用这个消息。

    用户使用自定义消息时:

      #define WM_MYMESG WM_USER+N  (0<N<=31743)

    6.消息队列

      消息队列用于存放消息的一个队列,消息在队列中先入先出。所有窗口程序都具有消息队列。程序可以从队列中获取消息。

      消息队列的类型

        系统消息队列-由系统维护的消息队列。存放系统产生的消息,例如鼠标、键盘等。

        程序消息队列-属于每一个应用程序(线程)的消息队列。由应用程序(线程)维护。

      消息队列的关系

      1 当鼠标、键盘产生消息时,会将消息存放到系统消息队列

      2 系统会根据存放的消息,找到对应窗口的消息队列。

      3 将消息投递到程序的消息队列中。

      操作系统如何将一个消息正确转发?

    1. 将系统消息队列中的消息提取出来。
    2. 根据消息的hWnd,获取内存。
    3. 在内存中获取“Main”。
    4. 拿着“Main”去操作系统中去匹配窗口类名。
    5. 一旦匹配到窗口类,可以确定这窗口类的句柄g_hInstance。
    6. 将消息转发给相应的窗口程序的消息队列中。

    7.消息和消息队列

      根据消息和消息队列之间使用关系,将消息分成两类:

      队列消息 - 消息的发送和获取,都是通过消息队列完成。

      非队列消息 - 消息的发送和获取,是直接调用消息的窗口处理完成。

      队列消息 -  消息发送后,首先放入队列,然后通过消息循环,从队列当中获取。

      GetMessage - 从消息队列中获取消息

      PostMessage - 将消息投递到(系统的)消息队列,后直接返回,不管对方是否获取消息并成功执行。以后交给操作系统转发给相应的窗口的消息队列,此后交给GetMessage()去获取。

      常见队列消息:WM_PAINT、键盘、鼠标、定时器。

      非队列消息 消息发送时,首先查找消息接收窗口的窗口处理函数,直接调用处理函数,完成消息,(不用排队,且无需Getmessage()去自己的消息队列去获取消息)。

      SendMessage - 直接将消息发送给窗口的处理函数,并等候处理结果。

      常见消息:WM_CREATE、WM_SIZE等。

    如何找到相应的处理函数呢?(类似于DispatchMessage())

    1. 根据hWnd,找到内存。
    2. 在内存中获取 “Main”。
    3. 拿着“Main”在操作系统中去匹配窗口类。
    4. 匹配成功,具体的窗口类。
    5. return 那个窗口类中的处理函数(WndProc)

    8.消息获取:GetMessage  /PeekMessage次序

      1 .在程序(线程)消息队列查找消息,如果队列有消息,检查消息是否满足指定条件(HWND,ID范围),不满足条件就不会取出该消息,否则从队列取出消息返回。

      2 .如果程序(线程)消息队列没有消息,向系统消息队列获取属于本程序的消息。如果系统队列的当前消息属于本程序,系统会将消息转发到程序

      3 .如果系统消息队列也没有消息,检查当前窗口的需要重新绘制的区域,如果发现有需要绘制的区域,产生WM_PAINT消息,取得消息返回处理。

      4 .如果没有重新绘制区域,检查定时器如果有到时的定时器,产生WM_TIMER,返回处理执行。

      5 .如果没有到时的定时器,整理程序的资源、内存等等。

      6 .GetMessage会继续等候下一条消息。PeekMessage会返回FALSE,交出程序的控制权。

      注意:GetMessage如果获取到是WM_QUIT,函数会返回FALSE。

    9. WM_PAINT - 绘图消息

      WM_PAINT -- 当窗口需要绘制的时候,会发送窗口处理函数。

      窗口无效区域:

      被声明成需要重新绘制的区域。

      BOOL InvalidateRect(

             HWND hWnd,  //窗口句柄

             CONST RECT* lpRect,  //区域的矩形坐标

             BOOL bErase  //重绘前是否先擦除

        );

      在程序中,如果需要绘制窗口,调用函数声明窗口无效区域。

      WM_PAINT参数:WPARAM – 设备上下文,LPARAM - 不使用。

      消息处理步骤:

      1 开始绘图处理

        HDC BeginPaint(

          HWND hwnd, //绘图窗口

          LPPAINTSTRUCT lpPaint //绘图参数的BUFF

          ); 返回绘图设备句柄HDC

      2 绘图

      3 结束绘图处理

        BOOL EndPaint(

          HWND hWnd, //绘图窗口

          CONST PAINTSTRUCT *lpPaint  //绘图参数的指针BeginPaint返回

          );

    10.键盘消息

    • 1 键盘消息

        WM_KEYDOWN - 按键被按下时产生

        WM_KEYUP - 按键被放开时产生

        WM_SYSKEYDOWN - 系统键按下时产生  比如ALT、F10等

        WM_SYSKEYUP - 系统键放开时产生

        WM_CHAR - 字符消息

    • 2 消息参数

      按键消息:

             WPARAM - 按键的Virtual Key

        LPARAM - 按键的参数,例如按下次数

      WM_CHAR消息:

             WPARAM - 输入的字符

        LPARAM - 按键的相关参数

      

      1  KEYDOWN可以重复出现,KEYUP只能在按键松开时出现1次。

      2  TranslateMessage在转换WM_KEYDOWN消息时,对于可见字符可以产生WM_CHAR,不可见字符无此消息。

      过程:

        1、先判断是否是WM_KEYDOWN消息(msg.message==WM_KEYDOWN)。若是进入2,否则返回。

        2、判断是否是可见字符(通过msg.wParam(虚拟键码),判断是否为可见字符按键),若不是,则返回。若是进入3。

        3、产生可见字符的WM_CHAR消息给操作系统,然后操作系统再转发给窗口的消息队列,再通过GetMessage()抓取,此时跳过TranslateMessage(),由DispatchMessage()分派消息给窗口的处理函数。(判断Caps Lock按键是否打开,根据Caps Lock按键的状态将虚拟键码转换为ASCII码)。(其实对于可见)

      3  WM_KEYDOWN/UP的wParam是表示的按键,WM_CHAR是表示输入的字符。

    11.鼠标消息

    1 基本鼠标消息

      WM_LBUTTONDOWN - 鼠标左键按下

      WM_LBUTTONUP - 鼠标左键抬起

      WM_RBUTTONDOWN - 鼠标右键按下

      WM_RBUTTONUP - 鼠标右键抬起

      WM_MOUSEMOVE - 鼠标移动消息

    2 双击消息

      WM_LBUTTONDBLCLK - 鼠标左键双击

      WM_RBUTTONDBLCLK - 鼠标右键双击

    3 滚轮消息

      WM_MOUSEWHEEL - 鼠标滚轮消息

    基本鼠标消息:

      消息参数

        WPARAM - 其他按键的状态,例如Ctrl/Shift等

        LPARAM - 鼠标的位置,窗口客户区坐标系。

        LOWORD   X坐标位置

        HIWORD   Y坐标位置

      鼠标消息使用

        一般情况鼠标按下/抬起成对出现。在鼠标移动过程中,会根据移动速度产生一系列的WM_MOUSEMOVE消息。

    双击消息:

      消息参数:

        WPARAM - 其他按键的状态,例如Ctrl/Shift等

        LPARAM - 鼠标的位置,窗口客户区坐标系。

        LOWORD X坐标位置

        HIWORD Y坐标位置

      使用时需要在注册窗口类的时候添加CS_DBLCLKS 风格,否则不会有(鼠标左右键)双击效果。

    消息产生的先后顺序:

       WM_LBUTTONDBLCLK为例:

      WM_LBUTTONDOWN

      WM_LBUTTONUP

      WM_LBUTTONDBLCLK

      WM_LBUTTONUP

    滚轮消息:

      消息参数

        WPARAM:

        LOWORD - 其他按键的状态

        HIWORD - 滚轮的偏移量,是120的倍数,通过正负值表示表示滚动方向。

          正:向前滚动

          负:向后滚动

        LPARAM:

          鼠标当前的位置,屏幕坐标系(注意不是当前窗口的坐标系)

          LOWORD - X坐标

          HIWORD - Y坐标

          通过偏移量,获取滚动的方向和倍数。

    12.定时器消息:

    定时器消息(WM_TIMER):

      可以在程序中设置定时器,当到达时间间隔时,定时器会向程序发送一个WM_TIMER消息。定时器的精度是毫秒,但是准确度很低。例如设置时间间隔为1000ms,但是会在非1000毫秒到达。

      消息的参数:

        WPARAM - 定时器ID

        LPARAM - 定时器处理函数的指针

    定时器使用:

      创建定时器:

        UINT SetTimer(

          HWND hWnd,//定时器窗口句柄

          UINT nIDEvent, //定时器ID

          UINT uElapse,//时间间隔(毫秒为单位)

          TIMERPROC lpTimerFunc //定时器处理函数指针

          );创建成功,返回非0。

        若lpTimerFunc为NULL,则使用窗口处理函数做为定时器处理函数,否则使用定时器处理函数处理定时器消息。

      关闭定时器:

        BOOL KillTimer(

          HWND hWnd,//定时器窗口句柄

          UINT uIDEvent //定时器ID

          );

    附:GetClientRect 获取窗口客户区大小.

    RECT rect={0};

    GetClientRect(hWnd,&rect);

    定时器处理函数原型:

      VOID   CALLBACK   TimerProc(

          HWND   hwnd,     // 定时器窗口句柄

          UINT    uMsg,     // WM_TIMER消息

          UINT    idEvent,  //定时器ID

          DWORD  dwTime   // 自系统开启到当前流逝的时间(毫秒为单位)

        );

    三、 菜单

    1. 菜单的分类

    1.1  窗口的顶层菜单

    1.2  弹出式菜单

    1.3  系统菜单

    HMENU类型表示菜单窗口的顶层菜单,菜单每一项有相应的ID

    窗口的顶层菜单:

    创建菜单:

      HMENU CreateMenu(VOID); //创建成功返回菜单句柄

    增加菜单项

      BOOL AppendMenu(

        HMENU hMenu, //菜单句柄

        UINT uFlags, //菜单项风格

        UINT_PTR uIDNewItem, //菜单项ID

        LPCTSTR lpNewItem //菜单项的名称

        );

    弹出式菜单 Popup

    创建菜单

      HMENU CreatePopupMenu(VOID);//创建成功返回菜单句柄

    加入顶层菜单

      BOOL AppendMenu(

        HMENU hMenu, //菜单句柄

        UINT uFlags, //菜单项增加选项,此时为MF_POPUP

        UINT_PTR uIDNewItem, //弹出式菜单的句柄

        LPCTSTR lpNewItem //菜单项的名称

        );

    注意:或是InsertMenu();

      MF_POPUP  -弹出下拉菜单或子菜单,uIDNewItem必须填写下拉菜单或子菜单句柄。

      MF_SEPARATOR  -分割线

      MF_STRING   -具有这种风格的菜单项,被点击后会发出消息(WM_COMMAND)

    设置到窗口:

      BOOL SetMenu(

        HWND hWnd,  //窗口句柄

        HMENU hMenu //菜单句柄

        );

    如:

      HMENU  hMain=CreateMenu();//创建菜单

      HMENU hFile=CreatePopupMenu();//创建弹出式菜单

      AppendMenu(hFile,MF_STRING|MF_CHECKED,1003,"新建");//新建前打钩

      AppendMenu(hFile,MF_SEPARATOR,0,"");//分割线

      AppendMenu(hFile,MF_STRING|MF_MENUBREAK,1004,"退出");//退出在弹出菜单中 下换列在一列

      HMENU hHelp=CreatePopupMenu();

      AppendMenu(hHelp,MF_STRING|MF_GRAYED,1005,"关于");//关于变灰,使其不可用

      AppendMenu(hMain,MF_POPUP,(UINT)hFile,"文件");

      AppendMenu(hMain,MF_POPUP,(UINT)hHelp,"帮助");

      SetMenu(hWnd,hMain);

    菜单命令处理:

      WM_COMMAND 消息

        WPARAM

          HIWORD(16) – 当对于菜单为0

          LOWORD (16)- 菜单项的ID

          LPARAM – 当对于菜单为NULL

    命令处理过程

    获取WPARAM菜单ID,根据ID处理。

    菜单的使用

    菜单项的状态

    在增加菜单项可以设置菜单项的状态。

    可以使用菜单API 修改状态

    CheckMenuItem

    EnableMenuItem

    DWORD CheckMenuItem(

        HMENU hmenu,       // 菜单句柄

        UINT uIDCheckItem,   // 测试的菜单项

        UINT uCheck         // 菜单项标识

    );

        函数使用时,需要通过设置uCheck MF_BYPOSITION或者MF_BYCOMMAND,确定使用菜单索引或者菜单ID。若为MF_BYPOSITION,则uIDCheckItem应为相应菜单的索引序号,注意,此序号为以0开始,分割线也要计数的,另外使用了MF_MENUBREAK的那一列项计入在第二列,即从第一列计数完再第二列。若为MF_BYCOMMAND,则uIDCheckItem应为菜单的ID号。EnableMenuItem()相似用法。

      在菜单被激活但是未显示(比如点击下拉菜单时里面的菜单项),窗口会收到WM_INITMENUPOPUP消息

        WPARAM - 菜单句柄(即将显示出来的菜单句柄)

        LPARAM - LOWORD 是菜单项索引(被点击的菜单项的索引,从左至右自0开始的…)

             - HIWORD 是窗口菜单标识,(即是否为窗口菜单的标识,顶层菜单、系统菜单为窗口菜单,顶层菜单不是.)

    窗口菜单:。。。

    右键菜单Context Menu

    1  创建菜单

      右键菜单是一个弹出式菜单(鼠标右键弹起触发,可以用WM_RBUTTONUP,但建议用WM_CONTEXTMENU更专业),使用CreatePopupMenu创建。

    2  显示菜单

      BOOL TrackPopupMenu(

        HMENU  hMenu, //菜单句柄

        UINT   uFlags, //显示方式

        int  x, //水平位置,屏幕坐标系,非窗口

        int  y, //垂直位置,屏幕坐标系,非窗口

        int nReserved, //保留,必须为0

        HWND  hWnd, //处理菜单消息的窗口句柄

        CONST RECT  *prcRect  //NULL,忽略

        );

      TrackPopupMenu是阻塞函数。

      使用WM_RBUTTONUP时,由于获得的是窗口坐标参数,故窗口坐标需要转化为屏幕坐标如下。

      //客户区窗口坐标转变为Windows屏幕坐标

      BOOL ClientToScreen(

          HWND hWnd,       // 窗口句柄

          LPPOINT lpPoint  // 坐标(输入、输出同一参数),入为客户窗口,输出为屏幕

        );

      ///Windows屏幕坐标转变为客户区窗口坐标

        BOOL ScreenToClient(

            HWND hWnd,       // 窗口句柄

            LPPOINT lpPoint  // 坐标(输入、输出同一参数)(与上相反过程)

          );

      而使用WM_CONTEXTMENU时,不需要转换,参数已经为屏幕坐标系下的。

      WParam - 右键点击的窗口句柄

      LPARAM -  LOWORD X坐标,屏幕坐标系

                    HIWORD Y坐标,屏幕坐标系

      WM_CONTEXTMENU消息是在WM_RBUTTONUP消息之后产生。

    3   命令处理

      触发WM_COMMAND消息,与窗口菜单一致

      如果Track设置了TPM_RETURNCMD选项,那么被点击的菜单项ID通过函数的返回值获取,另外为阻塞函数且选择该选项,此时不会进入消息循环,返回值的菜单项ID可以处理其他事物。若没有该选项,不会返回被点击菜单项ID,且会进入消息循环。

    4 菜单项的状态

      WM_INITMENUPOPUP,按照弹出菜单处理,用法同WM_INITMENUPOPUP息,即点击右键激活但为显示的状态下。

    系统菜单(比如窗口图标点击弹出的菜单)

    1 系统菜单的获取

      HMENU GetSystemMenu(

        HWND hWnd,//窗口句柄

        BOOL bRevert //重置选项

        ); 返回获取到的系统菜单句柄

      bRevert:  TRUE - 删除旧菜单,恢复到默认的系统菜单

               FALSE - 返回当前的系统菜单句柄。一般下建议使用FALSE

    2 系统菜单的修改

      AppendMenu

      DeleteMenu (也可以删除其他的非系统菜单项)

      BOOL     DeleteMenu(

          HMENU   hMenu,     //菜单句柄

          UINT uPosition,  // 菜单选项或索引

          UINT  uFlags      // 菜单项标识

        );

      使用同CheckMenuItem()相似,

    3 系统菜单命令响应

      通过WM_SYSCOMMAND消息响应菜单命令。

      WPARAMLOWORD是命令ID,即点击的相应菜单项ID

  • 相关阅读:
    代码是什么
    关于程序
    信息系统分析三原则
    设计的一个原则,妥协,不完美
    Algs4-1.4.30一个栈和一个steque实现的双向队列
    Algs4-1.4.31三个栈实现的双向队列
    Algs4-1.4.29两个栈实现的steque
    Algs4-1.4.27两个栈实现队列
    Algs4-1.4.28一个队列实现的栈
    *Algs4-1.4.26-三点共线-(未解决)
  • 原文地址:https://www.cnblogs.com/haomiao/p/6753263.html
Copyright © 2011-2022 走看看