• Windows窗口开发原理(窗口的创建&消息机制)


    在windows应用程序中,窗口是其非常重要的一个元素。并且窗口是通过窗口句柄来标识的。句柄(HANDLE)是windows程序中一个重要的概念,其标识各种资源,包括图标句柄(HICON)、光标句柄(HCURSOR)和画刷句柄(HBRUSH)。

    下面以一个带有自定义的画刷、光标和图标的windows窗口为例,讲解win32窗口的创建过程。

    windows消息机制

    windows程序是基于事件驱动方式的程序设计模式,主要是基于消息的。比如当用户在窗口中画图的时候,按下鼠标左键,此时os会感知到这一事件,于是将此事件包装成一个消息,投递到应用程序的消息队列中,然后应用程序从消息队列中取出消息,经过transltor翻译、分发消息,然后交由os调用

    窗口过程函数(应用程序注册的回调函数)或(DefWindowProc系统默认的回调处理函数)进行处理。

    创建一个窗口的基本流程

    设计窗口类

    窗口的特征是由WNDCLASS结构体来定义的,其定义了这个窗口的基本属性,所以我们只需填充结构体各成员信息即可。

    typedef struct tagWNDCLASSW {
        UINT        style;
        WNDPROC     lpfnWndProc;
        int         cbClsExtra;
        int         cbWndExtra;
        HINSTANCE   hInstance;
        HICON       hIcon;
        HCURSOR     hCursor;
        HBRUSH      hbrBackground;
        LPCWSTR     lpszMenuName;
        LPCWSTR     lpszClassName;
    } WNDCLASSW
    typedef WNDCLASSW WNDCLASS;//WNDCLASS是WNDCLASSW的别名
    View Code

    这里要首先说明下各类型定义

    //LRESULT :long
    // UINT:unsigned int
    //WPARAM:unsigned int
    //LPARAM:unsigned int
    //LPCWSTR:const w_chart_t *;
    // typedef WORD                ATOM;   //BUGBUG - might want to remove this from minwin
    // typedef unsigned short      WORD;
    // typedef unsigned long       DWORD;
    //LPCWSTR const w_char_t *   宽字符
    //LPCSTR  const char *
        wchar_t szAppclassName[] = _T("FirstWin32");
        WNDCLASS wc;
        wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;//窗口类的样式(要让窗口在水平和垂直尺寸发生变化时发生重绘,我们可以使用用位或|操作符将其组合起来)
        //WNDPROC 函数指针类型
        wc.lpfnWndProc = WindowProc;//窗口回调函数/窗口处理函数
        wc.cbClsExtra = 0;//窗口类的附加内存大小
        wc.cbWndExtra = 0;//窗口附加内存大小
        wc.hInstance = hInstance;//当前应用程序实例句柄
        wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));//加载自定义的图标句柄
        //wc.hCursor = LoadCursor(NULL,IDC_CROSS);//光标句柄;加载系统光标,也可以采用下面的方式加载自定义的光标
        wc.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR1));
        wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 0));//红绿蓝三原色0~255,[0最暗,255最亮]
        wc.lpszMenuName = NULL;//菜单名
        wc.lpszClassName = szAppclassName;//窗口类型名 spy++(vs->工具选项)

    其中的第二个成员变量lpfnWndProc是一个函数指针,指向窗口过程函数,窗口过程函数时一个回调函数。该函数签名如下:

     WNDPROC     lpfnWndProc;
    typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);

    故窗口函数按照如下签名定义,实现如下:

    //窗口处理函数
    //第一个参数:当前窗口句柄
    //第二个参数:消息类型
    //第三个参数:附加消息、附加操作
    //第四个参数:附加消息、附加操作
    LRESULT CALLBACK  WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
        case WM_CLOSE://窗口关闭消息
            DestroyWindow(hWnd);//销毁窗口,干掉界面,不会发出WM_QUIT,会发出一个WM_DESTORY消息
            break;
        case WM_QUIT://窗口销毁消息
            PostQuitMessage(0);//发布WM_QUIT
            break;
        default:
            break;
        }
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }

    回调函数机制:

    ⑴定义一个回调函数(WindowProc);
    ⑵提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者(注册wc.lpfnWndProc = WindowProc);
    ⑶当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理(eg双击鼠标按钮时间发生后,os调用窗口过程函数进行处理)。

    注册窗口类

     通过ATOM WINAPI RegisterClass( _In_ CONST WNDCLASS*lpWndClass);进行窗口类注册。形参为WNDCLASS地址。返回值为ATOM.

    创建窗口

     创建窗口通过CreateWindow函数来实现,该函数API信息如下:

    void CreateWindow(
       lpClassName,
       lpWindowName,
       dwStyle,
       x,
       y,
       nWidth,
       nHeight,
       hWndParent,
       hMenu,
       hInstance,
       lpParam
    );
    View Code
    关于返回值:
    类型:HWND 如果函数成功,则返回值是新窗口的句柄。 如果函数失败,则返回值为NULL。要获取扩展错误信息,请调用GetLastError。
    //WS:window style
        HWND hWnd = CreateWindow(szAppclassName,                        //窗口类型名
            _T("这个是我的第一个windows应用程序")               //窗口左上角标题
            , WS_BORDER | WS_CAPTION | WS_MAXIMIZEBOX | WS_SYSMENU, //窗口的风格
            200, 300,                                       //窗口左上角坐标
            800, 600,                                        // //窗口宽和高
            NULL,                                            //父窗口句柄
            NULL,                                            //菜单句柄
            hInstance,                                        //应用程序实例句柄
            NULL);                                        //创建窗口的附加参数,WM_CREATE消息,lparam来接受这个参数
        if (NULL == hWnd)
        {
            MessageBox(NULL, _T("创建窗口失败"), _T("提示"), MB_OK);
        }
    View Code

    显示窗口&更新窗口

     创建完窗口后,要对窗口进行具体的显示。

    显示函数API如下:

    BOOL ShowWindow(
      HWND hWnd,//创建窗口后返回的窗口句柄
      int  nCmdShow //指定窗口显示的状态,包括SW_MAXIMIZE  SW_MINIMIZE SW_NORMAL SW_HIDE  最大化、最小化、正常、隐藏显示
    );

    在调用ShowWindow函数之后,需要调用UpdateWindow来刷新窗口。

    UpdateWindow(hWnd);//hWnd是创建成功后的窗口句柄

    注意:UpdateWindow函数通过发送一个WM_PAINT消息来刷新窗口,UpdateWindow将WM_PAINT消息直接发送给了窗口过程函数进行处理,而没有放到消息队列中。

    消息循环

    在创建窗口、显示窗口、更新窗口后,我们需要编写一个消息循环,调用GetMessage函数不断从消息队列中取出消息,并进行相应。

    消息结构体:

    //        typedef struct tagMSG {
        //HWND        hwnd;      //消息发向窗口的窗口句柄(指的是这个消息发个哪个窗口了,这里指定这个窗口的句柄)
        //UINT        message;   //消息编号
        //WPARAM      wParam;  //附加消息
        //LPARAM      lParam;//附加消息
        //DWORD       time;//消息放入消息队列的时间
        //POINT       pt;//消息放入消息队列时鼠标坐标
    View Code
    BOOL GetMessage(
      LPMSG lpMsg,//消息结构体地址
      HWND  hWnd,//窗口句柄
      UINT  wMsgFilterMin,//要获取的消息的最小值,通常设置为0
      UINT  wMsgFilterMax//要获取的消息的最大值。如果FiterMin,Max两者都为0,则接收所有消息
    );

    代码如下:

    MSG msg;
        //GetMessage:何时返回FALSE
        //当获取到WM_QUIT消息时,返回FALSE,没有获取到这个消息时,返回非0,不会退出循环
        while (GetMessage(&msg, NULL, 0, 0))
        {
            //将虚拟键消息转换为字符消息
            TranslateMessage(&msg);
            //将消息分发给窗口处理函数
            DispatchMessage(&msg);
        }

    此处的Windows应用消息的消息处理机制如下图:

    (1)os接收到应用消息的窗口消息【比如当用户在窗口中画图的时候,按下鼠标左键,此时os会感知到这一事件,于是将此事件包装成一个消息】,将消息投递到该应用消息的消息队列中。

    (2)应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息。取出消息后,应用程序可以对消息进行一些预处理,比如放弃对某些消息的相应或调用TranslateMessage产生新的消息。

    (3)应用程序调用DispatchMessage将消息回传给os。消息MSG结构体中包含接受消息的窗口的句柄。因此DispatchMessage函数总能进行正确传递。

    (4)os利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理(即“系统给应用程序发送了消息”)。

     附录完整代码

    #include<Windows.h>
    #include<tchar.h>
    #include"resource.h"
    //LRESULT :long
    // UINT:unsigned int
    //WPARAM:unsigned int
    //LPARAM:unsigned int
    //LPCWSTR:const w_chart_t *;
    // typedef WORD                ATOM;   //BUGBUG - might want to remove this from minwin
    // typedef unsigned short      WORD;
    // typedef unsigned long       DWORD;
    //LPCWSTR const w_char_t *   宽字符
    //LPCSTR  const char *
    //窗口处理函数
    //第一个参数:当前窗口句柄
    //第二个参数:消息类型
    //第三个参数:附加消息、附加操作
    //第四个参数:附加消息、附加操作
    LRESULT CALLBACK  WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
        case WM_CLOSE://窗口关闭消息
            DestroyWindow(hWnd);//销毁窗口,干掉界面,不会发出WM_QUIT,会发出一个WM_DESTORY消息
            break;
        case WM_QUIT://窗口销毁消息
            PostQuitMessage(0);//发布WM_QUIT
            break;
        default:
            break;
        }
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow)
    {
        //创建一个窗口的流程
        //1设计窗口类
      /*  typedef struct tagWNDCLASSW {
            UINT        style;
            WNDPROC     lpfnWndProc;
            int         cbClsExtra;
            int         cbWndExtra;
            HINSTANCE   hInstance;
            HICON       hIcon;
            HCURSOR     hCursor;
            HBRUSH      hbrBackground;
            LPCWSTR     lpszMenuName;
            LPCWSTR     lpszClassName;
        } WNDCLASSW,*/
        //typedef WNDCLASSW WNDCLASS;
    
        //LRESULT CALLBACK WindowProc(
        //    _In_ HWND   hwnd,
        //    _In_ UINT   uMsg,
        //    _In_ WPARAM wParam,
        //    _In_ LPARAM lParam
        //);
        //typedef LRESULT(CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
        wchar_t szAppclassName[] = _T("FirstWin32");
        WNDCLASS wc;
        wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;//窗口类的风格
        //WNDPROC 函数指针类型
        wc.lpfnWndProc = WindowProc;//窗口回调函数/窗口处理函数
        wc.cbClsExtra = 0;//窗口类的附加内存大小
        wc.cbWndExtra = 0;//窗口附加内存大小
        wc.hInstance = hInstance;//当前应用程序实例句柄
        wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));//加载自定义的图标句柄
        //wc.hCursor = LoadCursor(NULL,IDC_CROSS);//光标句柄;加载系统光标,也可以采用下面的方式加载自定义的光标
        wc.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR1));
        wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 0));//红绿蓝三原色0~255,[0最暗,255最亮]
        wc.lpszMenuName = NULL;//菜单名
        wc.lpszClassName = szAppclassName;//窗口类型名 spy++(vs->工具选项)
    
    
        //2注册窗口类
        //返回值ATOM
        if (0 == RegisterClass(&wc))
        {
            MessageBox(NULL, _T("此程序不能复制在winNT平台"), _T("提示"), MB_OK);
            return 0;
        }
        //3创建窗口
        //WS:window style
        HWND hWnd = CreateWindow(szAppclassName,                        //窗口类型名
            _T("这个是我的第一个windows应用程序")               //窗口左上角标题
            , WS_BORDER | WS_CAPTION | WS_MAXIMIZEBOX | WS_SYSMENU, //窗口的风格
            200, 300,                                       //窗口左上角坐标
            800, 600,                                        // //窗口宽和高
            NULL,                                            //父窗口句柄
            NULL,                                            //菜单句柄
            hInstance,                                        //应用程序实例句柄
            NULL);                                        //创建窗口的附加参数,WM_CREATE消息,lparam来接受这个参数
        if (NULL == hWnd)
        {
            MessageBox(NULL, _T("创建窗口失败"), _T("提示"), MB_OK);
        }
        //4显示窗口
        //SW_SHOW:原来在何处显示,就在此处显示
        //SW_MAXIMIZE  SW_MINIMIZE     SW_NORMAL SW_HIDE  最大化、最小化、正常、隐藏显示
    
    
    
    
        ShowWindow(hWnd, SW_SHOW);
        //5更新窗口
        UpdateWindow(hWnd);
        //6消息循环
        //        typedef struct tagMSG {
        //HWND        hwnd;      //消息发向窗口的窗口句柄(指的是这个消息发个哪个窗口了,这里指定这个窗口的句柄)
        //UINT        message;   //消息编号
        //WPARAM      wParam;  //附加消息
        //LPARAM      lParam;//附加消息
        //DWORD       time;//消息放入消息队列的时间
        //POINT       pt;//消息放入消息队列时鼠标坐标
        //windows程序都是通过消息机制驱动运行的。
        MSG msg;
        //GetMessage:何时返回FALSE
        //当获取到WM_QUIT消息时,返回FALSE,没有获取到这个消息时,返回非0,不会退出循环
        while (GetMessage(&msg, NULL, 0, 0))
        {
            //将虚拟键消息转换为字符消息
            TranslateMessage(&msg);
            //将消息分发给窗口处理函数
            DispatchMessage(&msg);
        }
    
    
        MessageBox(NULL, _T("这是第一个win32应用程序"), _T("提示"), MB_OK);
        return 0;
    }
    View Code
  • 相关阅读:
    ios代码大全
    MYSQL数据库之如何在已经建立好表之后重构数据表
    关于cookie在一个页面设置但是在另外一个页面获取不到的原因
    cookie的那点事儿
    关于a标签不能调用js方法的小细节,你注意到了么?
    关于mysql预处理技术的一点小心得
    关于delete使用limit的一些注意事项
    DP1 等分连续1-N个数的划分种数
    Spring 编程式事务和声明式事务管理
    java https client信任所有证书
  • 原文地址:https://www.cnblogs.com/shuzhongke/p/15364810.html
Copyright © 2020-2023  润新知