• 第3章 窗口与消息_3.1Windows编程模型


    第3章窗口与消息

    3.1 Windows_编程模型
     (1)窗口程序的运行过程
       ①设计窗口
       ②注册窗口类(RegisterClassEx)。在注册之前,要先填写RegisterClassEx的参数WNDCLASSEX结构。
       ③建立窗口(CreateWindowEx)。
       ④显示窗口(ShowWindows)。
       ⑤刷新窗口客户区(UpdateWindow)。
       ⑥进入无限的消息获取和处理的循环。首先获取消息(GetMessage),如果有消息到达,则将消息分派到回调函数处理(DispatchMessage),如果消息是WM_QUIT,则退出循环。

    (2)窗口消息

      1、进队消息
        由Windows放入消息队列中的。在程序的消息循环中,重新返回并给配给窗体过程。(如键盘、鼠标、PostMessage等)。不进队的消息在Windows调用窗口时直接发给窗口过程(如SendMessage)。也就是说,进队的消息被直接发给应用程序的消息队列。而不进队的消息发给窗口过程。在多数情况下,进队消息是用户输入的结果,比如键盘击键、鼠标移动或单击等消息。进队消息包括时钟消息、刷新消息和退出消息等。

    2、不进队消息
        在多数情况下,不进队消息来自调用特定的Windows函数。例如CreateWindowEx函数会内部发送一个WM_CREATE消息、GetWindowText内部也会发送WM_GETTEXT消息获取标题内容。

    (3)Window窗口和消息详细注解

      1 #include <Windows.h>
      2  #include<tchar.h>
      3 
      4 /*********************************回调函数******************************************
      5 LRESULT:返回值,被宏定义为long型
      6 CALLBACK:函数参数进栈顺序(从右到左,己反汇编证实)
      7 4个参数:为MSG结构体的前4个成员变量
      8 ***********************************************************************************/
      9 LRESULT CALLBACK WndProc(HWND, int, WPARAM, LPARAM);
     10  
     11 
     12 /*************************程序入口 ******************************************
     13 int:返回值类型;
     14 WINAPI:            函数调用约定,进栈的顺序与CALLBACK一致
     15 hInstance:        应用程序(进程)句柄,与指针、引用差不多,但又不同。(句柄无类型,其值会因操作系统中内存页面转换而不同,
     16                           由操作 系统维护)。
     17 hPrevInstance:通过此参数可查看是否另一个实例进程正在运行。现在一般不用,为NULL
     18 lpCmdLine:      命令行
     19 nCmdShow:     窗体显示方式—正常、最大化、最小化、全屏等
     20 ***************************************************************************/
     21 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
     22 {
     23 static TCHAR szAppName[] = TEXT("Hello Windows!"); //定义一个字符串数组,以结尾,用来保存程序名称
     24                                                                                         //要注册到操作系统的名称,一般为进程名称!
     25  
     26 //***********************************1、设计窗口类*****************************
     27 WNDCLASS wc;             //WNDCLASS窗体结构,wc为结构体变量
     28 wc.style = CS_HREDRAW | CS_VREDRAW; //水平、垂直重绘,即改变窗口大小时自动发送WM_PAINT消息,而不必手动发送
     29 wc.lpfnWndProc = (WNDPROC)WndProc;  //填入回调函数的首地址
     30 wc.cbClsExtra = 0;                    //预留空间的附加值,此程序没用到
     31 wc.cbWndExtra = 0;                  //预留空间的附加值,此程序没用到
     32 wc.hInstance = hInstance;           //填入应用程序的句柄hInstance
     33 wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);   //装载标题栏图标
     34 wc.hCursor = LoadCursor(NULL, IDC_CROSS); //装载鼠标
     35 wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); //初始化窗体背景(白色),
     36                                                                                                        //从备用的库存从获取对象(句柄)
     37 wc.lpszMenuName = NULL;                                 //装载菜单,此处程序没有
     38 wc.lpszClassName = szAppName;//程序的名字
     39  
     40 //********************************2、注册窗口类*****************************
     41 if (!RegisterClass(&wc))
     42 {
     43     MessageBox(NULL, TEXT("应用程序须运行于32位操作系统上!"),
     44                          szAppName, MB_ICONERROR | MB_OK);
     45     return 0;
     46 }
     47  
     48 //*****************************3、创建窗口类************************************
     49 HWND hwnd;
     50 hwnd = CreateWindow(
     51                                     szAppName,    //应用程序在操作系统注册的名称,
     52                                                            //通过这里知道是wc设计好的窗口。
     53                                    TEXT("我的第一个窗口程序!"),//窗体标题名称
     54                                     WS_OVERLAPPEDWINDOW, //窗体风格
     55                                     CW_USEDEFAULT, //窗体左上角x坐标
     56                                     CW_USEDEFAULT, //窗体左上角y坐标
     57                                     CW_USEDEFAULT, //窗体右下角x坐标
     58                                     CW_USEDEFAULT, //窗体右下角y坐标
     59                                     NULL,   //父窗口句柄,此处NULL
     60                                     NULL,   //菜单句柄,此处NULL
     61                                     hInstance,//应用程序句柄
     62                                     NULL
     63                                     );
     64 /*创建窗口完成(只存在于内存),系统会发送第一条消息WM_CREATE,注意此时,程序并未执行到消息循环处,
     65 操作系统绕过应用程序的消息队列,直接向应用程序发出。该消息在所有的消息之前,目的是为了在程序程序执行之
     66 前可以有机会进行一些初始化工作或装载动态链接库等操作。
     67 */
     68  
     69 //**********************************4、显示窗口和更新窗口************************
     70 ShowWindow(hwnd, nCmdShow);
     71 /*1、单纯一个ShowWindow,照样会正确画出窗口内容,只不过是在消息队列取空之后才画的,
     72 有时 我们希望窗口被立即重画,而不是去等待那个不确定的消息队列,此时就需要
     73 用到UpdateWindow。UpdateWindow的作用只有一个:假若当前被标记为重画的区域存在(不
     74 存在的话它什么也不做), 那么立刻让Windows使用SendMessage的方式来对你的窗口
     75  发送WM_PAINT。
     76 2、ShowWindow本身不发送重绘消息,它的作用仅仅是把窗口显示出来。不过,当窗口显示的时
     77 候,Windows会自动探测窗口的内容是否需要重画、以及需要重画的区域
     78 3、ShowWindow调用中会发送WM_SIZE和WM_SHOWWINDOW消息给窗体过程 
     79                                                         */
     80  
     81 UpdateWindow(hwnd); 
     82 /*执行UpdateWindow时会立即发送一条WM_PAINT消息,同样由操作系统直接发出。UpdateWindow执行
     83  时,先判断无效区域是否为空,不为空,则发送,为空为不发送。可见,将上面的ShowWindow注释掉,则
     84 不发送。因为未显示出来,当然没有无效区域,即无效区域为空。
     85  */
     86 /*UpdateWindow和I​n​v​a​l​i​d​a​t​e​的区别:
     87 1、UpdateWindow()的作用是使窗口立即重绘(因为操作系统绕应用程序的消息队列,直接给就目标窗口发WM_PAINT,从而导致窗口立即重绘。
     88     而Invalidate等函数后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,它需要等消息队列中的其它消息发送完后才能被处理。
     89 2、UpdateWindow执行时,先判断无效区域是否为空,不为空才发送消息,Invalidate的无效区为整个客户区(注意与InvalidRect的区别)
     90 */
     91  
     92 //**********************消息循环*****************************
     93 MSG msg;
     94 /*1、调用GetMessage函数时,传入msg的地址,从应用程序的消息对列中获取消息,其中的
     95 消息结构的内容由操作系统自动填充。
     96 2、这是windows程序的核心,消息循环处理过程,只有在收到WM_QUIT里才退出,结束程
     97  序。
     98 */
     99 while (GetMessage(&msg, NULL, 0, 0)) 
    100 {
    101         TranslateMessage(&msg);  //翻译键盘消息
    102         DispatchMessage(&msg);   //发送消息函数,先把msg发送给操作系统,然后由操作系统再调用Wndproc函数!
    103                                                       //Dispatch函数的内部实现大体流程
    104                                                      //push 参数进栈
    105                                                      //............
    106                                                      //call WndProc(...)  //调用窗体回调函数,注意这里是个回调函数。
    107                                                      //................
    108                                                      //结束,返回   
    109 }
    110  
    111 return msg.wParam;
    112 }
    113  
    114 //*****************************回调函数——消息处理过程*******************************
    115 //窗口回调函数,此函数只有声明和定义,没有调用!这说明此函数确实是由操作系统调用的!
    116 LRESULT CALLBACK WndProc(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam)
    117 {
    118         HDC hDC;       //设备环境——绘图的地方
    119         PAINTSTRUCT ps; //绘图结构体变量
    120         RECT rect;      //绘图区范围
    121         switch (uMsg)   //分别处理不同的消息
    122         {
    123         case WM_CREATE://此消息是一个应用程序发送的第一个消息,也是唯一的一次!
    124                 PlaySound(TEXT("HelloWin.wav"), NULL, SND_FILENAME | SND_SYNC);
    125                 break;
    126         case WM_SIZE:
    127                 //GetClientRect(hWnd, &rect);
    128                 //InvalidateRect(hWnd, &rect, TRUE);
    129                 break;
    130         case WM_CHAR:
    131                 TCHAR szChar[20];
    132                 _stprintf_s(szChar, TEXT("您按下了%c"), wParam);
    133                 MessageBox(hWnd, szChar, TEXT("地平线工作室!"), 0);
    134                 break;
    135         case WM_PAINT://绘图函数,在窗口上画画儿!
    136                 //PlaySound(TEXT("HelloWin.wav"), NULL, SND_FILENAME | SND_SYNC);
    137                
    138                 hDC = BeginPaint(hWnd, &ps); 
    139                  /*ps结构的第3个成员为RECT rcPaint,是指一个无效区域当调用BeginPaint函数时,由操
    140                   作系 统填充。因此,严格来讲,hDC为无效区效的“设备环境”,在此区域外的作图,都
    141                   将被忽略(即xy坐标在该区域外的)。  */
    142  
    143                 GetClientRect(hWnd, &rect);   //获得客户区大小
    144                 TextOut(hDC, 0, 0, TEXT("地平线工作室!"), lstrlen(TEXT("地平线工作室!")));
    145                 DrawText(hDC, TEXT("我的第一个窗口程序!"), -1, &rect,DT_CENTER | DT_VCENTER | DT_SINGLELINE);
    146                 Ellipse(hDC, 50, 50, 200, 100);
    147                 EndPaint(hWnd, &ps);
    148                 break;
    149         case WM_LBUTTONDOWN:
    150                 MessageBox(hWnd,TEXT("单击左键!"), TEXT("地平线工作室!"), 0);
    151                 break;
    152         case WM_CLOSE:
    153                 if (IDYES == MessageBox(hWnd,TEXT("是否真的要退出!"),TEXT("温馨提醒!"),MB_YESNO))
    154                 {
    155                         DestroyWindow(hWnd);
    156                 }
    157                 break;
    158         case WM_DESTROY://处理退出消息
    159                 PostQuitMessage(0);//此消息直接进入消息队列的头部!
    160                 break;
    161         default:
    162                 return DefWindowProc(hWnd,uMsg,wParam,lParam);
    163         }
    164         return 0;
    165 }
    166 /**********************************************注意*******************************************
    167 1:所有的消息都由操作系统负责管理,所有的消息先进入总的系统消息队列,再由系统消息队列向各个小应用程序队列发消息!
    168 2:窗口回调函数Wndproc是由操作系统调用的,所有的消息进入消息循环,由消息循环把消息发到操作系统的队列中,在由操作系统
    169 根据消息来调用Wndproc函数,而Wndproc函数根据message的不同而调用相应的处理过程!
    170 3:上面这个程序你可以改造一下,在添加几个消息处理过程,比如鼠标左键按下WM_LBULLONDOWN,鼠标左键抬起WM_LBULLONUP
    171 定时器消息WM_TIMER,绘图消息WM_PAINT消息等等,然后把PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC)
    172 这个函数添加到以上各个消息处理过程中,看看结果是不是进行了相应的操作!比如,点击一下鼠标左键看看是不是发出声音了!
    173 可以自己试试!
    174 4:其实,WINDOWS程序也可以自己去创建函数!自己去自定义消息处理过程!比如,你自己定义了一个函数,想在鼠标左键点击后
    175 去调用这个函数,那就可以在WM_LBULLONDOWN这个消息处理过程中加上你自己定义的函数调用就可以了!
    176 5:上面这个程序是个非常简单的程序,但是他足以说明WINDOWS程序的消息机制!几乎所有的程序都包括上面这段代码(除了PLAYSOUND函数和DrawText函数),不管多么复杂的WINDOWS程序都必须包括上面这段代码,因为他是再简单不过的了,只是创建了一个窗口!但是,再复杂的程序无非也就是在Wndproc函数中的switch多加几个case 吗!无非就是加上什么鼠标,键盘,定时器等等,但原里都是一样的!他的消息处理过程都是一样的!主要是把他的消息机制弄懂,其他的都非常easy!
    177 6:PlaySound函数需要用到WINMM.LIB库,在链接时需要加入到项目中。方法:“解决方案”->“项目”->右键->"属性"-> "配置属性"->"链接器"->"输入"->在“附加依赖项”中填入“WINMM.LIB;”
    178     或者:
    179           在#include<windows.h>后面加上(注意:不能加在前面):
    180          #include <mmsystem.h>
    181          #pragma comment(lib, "WINMM.LIB")
    182 ******************************************************************************************/
  • 相关阅读:
    CentOS7.2中安装MongoDB
    django 面试题
    python pandas库——pivot使用心得
    归并排序
    python实现归并排序,归并排序的详细分析
    二分法查找
    二叉树的遍历
    RabbitMQ(python实现)学习之一:简单两点传输“Hello World”的实现
    邻接表存储图,DFS遍历图的java代码实现
    五、python使用模块
  • 原文地址:https://www.cnblogs.com/5iedu/p/4565166.html
Copyright © 2020-2023  润新知