• Windows窗口程序


    Windows窗口程序

    应用程序分类
    • 控制台程序Console

      DOS程序,没有窗口,通过DOS窗口执行

      入口函数: main

    • 窗口程序

      拥有自己的窗口,可以与用户交互

      入口函数: WinMain

    • 库程序

      存放代码、数据的程序,执行文件可以从中取出代码执行或获取数据

      • 静态库程序:扩展名LIB, 在编译链接程序时,将代码放入到执行文件中

        静态库没有入口函数 --> 没法执行 --> 没法进入内存

      • 动态库程序:扩展名DLL,在文件执行时从中获取代码

        动态库有入口函数–>可以执行,但是不能独立执行( 必须依附其他程序 )

        入口函数: DLLMain

    开发工具和类
    开发工具
    • 编译器

      CL.EXE 将源代码编译成目标文件.obj

    • 链接器

      LINK.EXE 将目标代码,库链接生成最终文件

    • 资源编译器

      RC.EXE (.rc) 将资源编译,最终通过链接器存入最终文件

    Visual Studio 路径: C:\Projram Files(x86)\Microsoft Visual Studio xx\vc\bin

    Windows库

    • kernel32.dll

      提供核心的API, 例如进程,线程,内存管理

    • user32.dll

      提供了窗口,消息等API

    • gdi32.dll

      绘图相关的API

    路径: C:\Windows\System32

    头文件
    • windows.h

      所有windows头文件的集合 --> 包含了其他头文件

    • windef.h

      windows数据类型

    • winbase.h

      kernel32的API

    • wingdi.h

      gdi32的API

    • winuser.h

      user32的API

    • winnt.h

      UNICODE字符集的支持

    相关函数
    int WINAPI WinMain(
    	HINSTANCE hInstance,// 当前程序的实例句柄
        HINSTANCE hPrevInstance, // 当前程序前一个实例句柄  --> 已经废弃
        LPSTR lpCmdLine, // 命令行参数字符串  char* 类型  --> 只能传递一个命令行参数
        int nCmdShow // 窗口的显示方式 最大化显示,最小化显示,原样显示
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    句柄可以找到进程对应的内存 --> 句柄是表的索引

    int MessageBox(
    	HWND hWnd, // 父窗口句柄   
        LPCTSTR lpText, // 显示在消息框中的文字
        LPCTSTR lpCaption, // 显示在标题栏中的文字
        UINT uType // 消息框中的按钮,图标显示类型
    ); // 返回点击的按钮ID
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Hxxx --> 大概率是句柄

    rc资源文件

    后缀: .rc

    100 ICON small.ico
    
    • 1

    100 数字标识 ICON 图标资源 small.ico 文件名称

    编译后称为 .res文件

    .obj & .res 统称为目标文件

    程序编译过程

    image-20221105230332488

    Demo
    #include 
    int WinMain(HINSTANCE hIns,HINSTANCE hPreIns,LPSTR lpCmdLine, int nCmdShow){
    	MessageBox(NULL,"hello world","Information",MB_YESNO);
    	return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    image-20221028212113243

    第一个windows窗口
    步骤
    1. 定义WinMain函数
    2. 定义窗口处理函数(自定义,处理消息)
    3. 注册窗口类(向操作系统写入一些数据)
    4. 创建窗口(内存中创建窗口)
    5. 显示窗口(绘制窗口的图像)
    6. 消息循环(获取/翻译/派发消息)
    7. 消息处理
    #include
    
    // 窗口处理函数(自定义,处理消息)
    LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam){
        return DefWindowProc(hWnd,msgID,wParam,lParam);
    }
    // 入口函数
    int CALLBACK WinMain(HINSTANCE hIns,HINSTANCE hPerIns,LPSTR lpCmdLine,int nCmdShow){
        // 注册窗口类 --> 向系统的内核写入一些数据
        WNDCLASS wc={0}; // 结构体变量
        wc.cbClsExtra = 0; // 开缓冲区  --> n 字节的缓冲区
        wc.cbWndExtra = 0; // 开缓冲区  另一种缓冲区
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); // 背景色
        wc.hCursor = NULL; // 默认光标位置
        wc.hIcon = NULL;  // 默认图标
        wc.hInstance = hIns; // 当前程序实例句柄
        wc.lpfnWndProc = WndProc; // 窗口处理函数
        wc.lpszClassName = "窗口类名字"; // 窗口类的名字
        wc.lpszMenuName = NULL; // 没有菜单
        wc.style = CS_HREDRAW | CS_VREDRAW ; // 水平或者垂直有变化,重画窗口
        // 将上面复制写入操作系统
        RegisterClass(&wc);
        // 内存中创建窗口
        HWND hWnd = CreateWindow("窗口类名字","标题",WS_OVERLAPPEDWINDOW,100,100,100,100,NULL,NULL,hIns,NULL);
        //HWND hWnd = CreateWindow("窗口类名字","标题",WS_OVERLAPPEDWINDOW,x,y,w,h,父窗口,菜单,hIns,NULL);
        // WS_OVERLAPPEDWINDOW最后一个参数窗口风格
        // 父窗口,菜单没有就置位NULL
        // 最后一个参数没用,置为NULL
        // 显示窗口
        ShowWindow(hWnd,SW_SHOW);
        // 风格 SW_SHOW 原样显示
        UpdateWindow(hWnd);
        // 刷新 --> 再画一遍
        // 消息循环
        MSG nMsg = {0};
        while(GetMessage(&nMsg,NULL,0,0)){
    		TranslateMessage(&nMsg); // 翻译消息
            DispatchMessage(&nMsg) ; // 派发消息,交给窗口处理函数来处理
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    字符编码
    历史背景
    • ASC

      7位代表一个字符

    • ASCII

      8位一个字符

    • DBCS

      double byte

      单双字节混合编码

    • UNICODE

      • linux 一般utf8
      • windows 一般 utf16
    宽字节字符
    • wchar_t 每个字符占2个字节

      char每个字符占1个字节

      wchar_t 实际上是 unsigned short 类型,定义时,需要在前面增加"L",通知编译器按照双字节编译字符串,采用UNICODE编译

    • 需要使用支持wchar_t函数操作宽字节字符串

      wchar_t * pwszText = L"Hello wchar";
      wprintf(L"%s\n",pwszText);
      
      • 1
      • 2
    • demo

      #include
      #include
      
      int main(){
          wchar_t * pszText = L"Hello wchar";
          int len = wcslen(pszText); // 返回字符个数
          wprintf(L"%d,%s",len,pszText);
          return 0;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • TCHAR 数据类型

      定义在WINNT.h中:

      #ifdef UNICODE
      	typedef wchar_t TCHAR
          #define _TEXT(quote) L##quote
      #else
          typedef char TCHAR
          #define _TEXT(quote) quote
      #endif
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      ## 是拼接的作用

      TCHAR * pszText = _TEXT("Hello,wkk");
      
      • 1

      定义UNICODE 宏要在window.h头文件前面

    • UNICODE字符打印

      wprintf 对UNICODE 字符打印支持不完善

      在Windows使用WriteConsole API打印UNICODE 字符 GetStdHandle

      //WriteConsole(标准输出句柄,输出的缓冲区,输出长度,实际输出的长度,备用参数);
      wchar_t *pszText = L"哇咔咔";
      WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),pszText,wcslen(pszText),NULL,NULL);
      // GetStdHandle() 获取标准句柄
      // STD_INPUT_HANDLE
      // STD_OUTPUT_HANDLE
      // STD_ERROR_HANDLE
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    • 项目属性设置为UNICODE 字符集

      系统会自动增加UNICODE宏的定义

      如果是TCHAR * 类型增加了UNICODE宏,字符串字面量前面要加L

    • 系统调用函数的参数类型

      LPSTR === char * 
      LPCSTR === const char *
      LPWSTR === wchar_t * 
      LPCWSTR === const wchar_t * 
      LPTSTR === TCHAR *
      LPCTSTR === const TCHAR*
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    注册窗口类
    窗口类

    概念

    • 窗口类包含了窗口的各种参数信息的数据结构
    • 每个窗口都具有窗口类,基于窗口类创建窗口
    • 每个窗口类都具有一个名称,使用前必须注册到系统

    分类

    • 系统窗口类

      系统已经定义好的窗口类,所有应用程序都可以直接使用

    • 应用程序全局窗口类

      由用户自己定义,当前应用程序所有模块都可以使用

    • 应用程序局部窗口类

      由用户自己定义,当前应用程序中本模块可以使用

    系统窗口类

    不需要用户注册,直接使用即可,系统已经注册好了

    • 按钮 - BUTTON
    • 编辑框 - EDIT
    #include
    int CALLBACK WinMain(HINSTANCE hIns,HINSTANCE hPerIns,LPSTR lpCmdLine,int nCmdShow){
    
        HWND hWnd = CreateWindow("Button","按钮",WS_OVERLAPPEDWINDOW,100,100,100,100,NULL,NULL,hIns,NULL);
    
        ShowWindow(hWnd,SW_SHOW);
    
        UpdateWindow(hWnd);
    
        MSG nMsg = {0};
        while(GetMessage(&nMsg,NULL,0,0)){
    		TranslateMessage(&nMsg); // 翻译消息
            DispatchMessage(&nMsg) ; // 派发消息,交给窗口处理函数来处理
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    全局窗口类
    ATOM RegisterClass(
    	CONST WNDCLASS * lpWndClass // 窗口类的数据
    );
    // 注册成功,返回一个数字标识 非0
    // 失败,返回0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    // 注册窗口类的结构体
    typedef struct _WNDCLASS {
        UINT style; // 窗口类的风格
        WNDPROC lpfnWndProc; // 窗口处理函数
        int cbClsExtra ; // 窗口类的附加数据buff大小
        int cbWndExtra;  // 窗口的附加数据buff大小
        HINSTANCE hInstance; // 当前模块的实例了句柄
        HICON hIcon; // 窗口图标句柄
        HCURSOR hCursor; // 鼠标句柄
        HBRUSH hbrBackground; // 绘制窗口背景的画刷句柄
        LPCTSTR lpszMenuName; // 窗口菜单的资源ID字符串
        LpCTSTR lpszClassName; // 窗口类的名称
    }WNDCLASS,*PWNDCLASS;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • style 窗口类风格

      应用程序全局窗口类的注册,需要在窗口类的风格中增加CS_GLOBALCLASS.

      WNDCLASS wce = {0};
      wce.style = ... | CS_GLOBALCLASS;
      
      • 1
      • 2

      应用程序局部窗口类,在注册窗口类时,不添加CS_GLOBALCLASS风格

      CS_HREDRAW 当窗口水平变化时,窗口重新绘制

      CS_VERDRAW 当窗口垂直变化时,窗口重新绘制

      CS_DBLCLKS 允许窗口接收鼠标双击

      CS_NOCLOSE 窗口没有关闭按钮

    一般不建议使用全局窗口类

    局部窗口类

    在注册窗口类时,不添加CS_GLOBALCLASS风格

    窗口创建

    CreateWindow / CreateWindowEx --> 加强版

    加强版增加了扩展风格dwExStyle参数

    HWND CrateWindowEx(
    	DWORD dwExStyle , // 窗口的扩展风格
        LPCTSTR lpClassName, // 已经注册的窗口类名称
        LPCTSTR lpWindowName, // 窗口标题栏的名字
        DWORD dwStyle, // 窗口的基本风格
        int x, // 左上角水平坐标
        int y, // 左上角垂直坐标
        int nWidth,
        int nHeight,
        HWND hWndParent, // 窗口的父窗口句柄  --> 如果是子窗口要写这个参数
        HMENU hMenu, // 窗口菜单句柄
        HINSTANCE hInstance, // 应用程序实例句柄 --> WinMain 第一个参数
        LPVOID lpParam // 窗口创建时附加参数 --> 一般给NULL
    ); // 创建成功返回窗口句柄
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    窗口基本风格

    WS_BORDER   有黑色的边界线
    WS_CAPTION  有标题栏
    WS_CHILD    子窗口
    WS_CHILDWINDOW  同上
    WS_CLIPCHILDREN  裁剪窗口  --> 不规则窗口
    WS_CLIPSIBLINGS  
    WS_DISABLED  禁用 --> 常用按钮控件
    WS_DLGFRAME  对话框
    WS_GROUP     分组
    WS_HSCROLL   水平滚动条
    WS_ICONIC    最初状态最小化状态
    WS_MAXIMIZE  最大化状态
    WS_MAXIMIZEBOX  最大化按钮
    WS_MINIMIZE
    WS_MINIMIZEBOX
    WS_OVERLAPPED 交叠窗口  --> 标题栏+边框
    WS_OVERLAPPEDWINDOW   基本都有 *********
    WS_POPUP  弹出式对话框
    WS_SIZEBOX 可以改变大小    
    WS_TABSTOP   支持tap键顺序
    WS_SYSMENU  系统菜单
    WS_TILED
    WS_VISIBLE  可见的 --> 显式子窗口   主窗口 -> showWindow 函数
    WS_VSCROLL   垂直滚动条
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    CreateWindowEx 内部实现

    • 函数内部根据传入的窗口类名称,在应用程序局部窗口类中查找,如果找到执行2, 没有找到执行3
    • 比较局部窗口类与创建窗口时传入的HINSTANCE变量,如果发现相等,创建和注册的窗口类在同一模块,创建窗口返回。如果不相等执行3
    • 在应用程序全局窗口类,如果找到执行4,如果没有找到执行5
    • 使用找到的窗口类的信息,创建窗口返回
    • 在系统窗口类中查找,如果找到创建窗口返回,否则创建窗口失败
    匹配查找窗口类
    if(找到窗口类){
        申请一大块内存,将窗口的数据信息存入这块内存
        return 内存的句柄
    }else{
        return NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    基本demo
    #include
    
    // 窗口处理函数(自定义,处理消息)
    LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam){
    	switch(msgID){
    		case WM_DESTROY:
    			PostQuitMessage(0); // 可以使GetMessage 函数返回0 
    			break;
    	}
        return DefWindowProc(hWnd,msgID,wParam,lParam);
    }
    // 入口函数
    int CALLBACK WinMain(HINSTANCE hIns,HINSTANCE hPerIns,LPSTR lpCmdLine,int nCmdShow){
        // 注册窗口类 --> 向系统的内核写入一些数据
        WNDCLASS wc={0}; // 结构体变量
        wc.cbClsExtra = 0; // 开缓冲区  --> n 字节的缓冲区
        wc.cbWndExtra = 0; // 开缓冲区  另一种缓冲区
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); // 背景色
        wc.hCursor = NULL; // 默认光标位置
        wc.hIcon = NULL;  // 默认图标
        wc.hInstance = hIns; // 当前程序实例句柄
        wc.lpfnWndProc = WndProc; // 窗口处理函数
        wc.lpszClassName = "窗口类名字"; // 窗口类的名字
        wc.lpszMenuName = NULL; // 没有菜单
        wc.style = CS_HREDRAW | CS_VREDRAW ; // 水平或者垂直有变化,重画窗口
        // 将上面复制写入操作系统
        RegisterClass(&wc);
        // 内存中创建窗口
        HWND hWnd = CreateWindow("窗口类名字","标题",WS_OVERLAPPEDWINDOW,100,100,100,100,NULL,NULL,hIns,NULL);
        //HWND hWnd = CreateWindow("窗口类名字","标题",WS_OVERLAPPEDWINDOW,x,y,w,h,父窗口,菜单,hIns,NULL);
        // WS_OVERLAPPEDWINDOW最后一个参数窗口风格
        // 父窗口,菜单没有就置位NULL
        // 最后一个参数没用,置为NULL
        // 显示窗口
        ShowWindow(hWnd,SW_SHOW);
        // 风格 SW_SHOW 原样显示
        UpdateWindow(hWnd);
        // 刷新 --> 再画一遍
        // 消息循环
        MSG nMsg = {0};
        while(GetMessage(&nMsg,NULL,0,0)){
    		TranslateMessage(&nMsg); // 翻译消息
            DispatchMessage(&nMsg) ; // 派发消息,交给窗口处理函数来处理
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    创建子窗口
    • 创建时要设置父窗口句柄
    • 创建风格要增加WS_CHLID | WS_VISIBLE
    WNDCLASS wc={0}; // 结构体变量
    wc.cbClsExtra = 0; // 开缓冲区  --> n 字节的缓冲区
    wc.cbWndExtra = 0; // 开缓冲区  另一种缓冲区
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); // 背景色
    wc.hCursor = NULL; // 默认光标位置
    wc.hIcon = NULL;  // 默认图标
    wc.hInstance = hIns; // 当前程序实例句柄
    wc.lpfnWndProc = DefWindowProc; // 系统默认处理函数 **************
    wc.lpszClassName = "Child"; // 窗口类的名字
    wc.lpszMenuName = NULL; // 没有菜单
    wc.style = CS_HREDRAW | CS_VREDRAW ; // 水平或者垂直有变化,重画窗口
    // 将上面复制写入操作系统
    RegisterClass(&wc);
    HWND hChlid1 = CreateWindowEx(0,"Child","child1",WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW,0,0,200,200,
          hWnd,NULL,hIns,NULL
    );
    
    HWND hChlid2 = CreateWindowEx(0,"Child","child2",WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW,200,0,200,200,
          hWnd,NULL,hIns,NULL
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    显式窗口
    ShowWindow(hWnd,SW_SHOW);
    // 根据窗口句柄,找到内存,绘制窗口
    UpdateWindow(hWnd);
    
    • 1
    • 2
    • 3
    消息基础
    消息
    • 消息组成(windows下)

      • 窗口句柄
      • 消息ID
      • 消息的两个参数(两个附带信息)
      • 消息的产生的时间
      • 消息产生时的鼠标位置
      typedef struct tagMSG{
          HWND hwnd;
          UINT message;
          WPARAM wparam;
          LPARAM lParam;
          DWORD time;
          POINT pt;
      }MSG;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • 消息的作用

      当系统通知窗口工作时,采用消息的方法派发给窗口的处理函数

      每个窗口都有窗口处理函数

    MSG nMsg = {0};
    while(GetMessage(&nMsg,NULL,0,0)){
        TranslateMessage(&nMsg); // 翻译消息
        DispatchMessage(&nMsg) ; // 派发消息,交给窗口处理函数来处理
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    DispatchMessage函数

    DispatchMessage(&nMsg){
        nMsg.hwnd --> 保存窗口数据的内存 --> WndProc
        WndProc(...){
            // 处理消息
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam);
    /**
    * hWnd
    * msgID
    * wParam
    * lParam
    */
    消息的最后两个参数 消息的时间,消息时的鼠标位置没有传递
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    窗口处理函数
    • 每个窗口都必须有窗口处理函数

      LRESULT CALLBACK WindowProc(
      	HWND hwnd,// 窗口句柄
          UINT uMsg, // 消息ID
          WPARAM wParam, // 消息函数
          LPARAM lParam  // 消息参数
      );
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • 当系统通知窗口时,会调用窗口处理函数,同时将消息ID和消息参数传递给窗口处理函数。 在窗口处理函数中,不处理的消息,使用缺省窗口处理函数,DefWindowProc

    • DefWindowProc 给各种消息做默认处理

    消息相关函数
    • GetMessage - 获取本进程的消息

      BOOL GetMessage(
      	LPMSG lpMsg,// 存放获取到的消息BUFF
          HWND hWnd,  // 窗口句柄  --> 抓指明的句柄的消息  NULL--> 抓所有的
          // 消息的范围 (0,0)本进程的消息都抓
          UINT wMsgFilterMin, // 获取消息的最小ID
          UINT wMsgFilterMax  // 获取消息的最大ID
      );
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

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

      hWnd 获取到hWnd所指定窗口的消息

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

      返回值:

      • 如果message 为 WM_QUIT返回0, 其余消息返回非0
    • TranslateMessage 翻译消息

      将按键消息,翻译成字符消息 --> 可见字符按键

      BOOL TranslateMessage(
      	CONST MSG*lpMsg // 要翻译的消息地址
      );
      
      • 1
      • 2
      • 3

      检查消息是否是按键的消息,如果不是按键消息,不做任何处理,继续执行

    • DispatchMessage 派发消息

      LRESULT DispatchMessage(
      	CONST MSG * lpmsg // 要派发的消息
      );
      
      • 1
      • 2
      • 3

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

    常见消息

    产生时间,附带的两个参数,一般用来做什么

    • WM_DESTROY

      • 产生时间 窗口被销毁时

      • 附带消息:

        wParam: 0

        lParam: 0

      • 常用于在窗口被销毁之前,做相应的善后处理,例如:资源,内存等

    • WM_SYSCOMMAND

      • 产生时间: 点击窗口的最大化,最小化,关闭等

      • 附带消息

        wParam: 具体点击的位置 例如关闭: SC_CLOSE

        lParam: 鼠标光标的位置 LOWORD(lParam) 水平位置 HIWORD(lParam) 垂直位置

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

    • WM_CREATE

      • 产生时间: 窗口创建成功,但还没显式时,CreateWindow 和 ShowWindow之间

      • 附带信息

        wParam 为0

        lParam 为CREATESTRUCT 类型的指针,通过这个指针可以获取CreateWindowEx中的全部12个参数的信息

      • 一般用于初始化窗口的参数、资源等等,包含创建子窗口

    • WM_SIZE

      • 产生时间 在窗口的大小发生变化后( 第一次显示,从无到有也会触发 )

      • 附带消息

        wParam: 窗口大小变化的原因

        lParam: 窗口变化后的大小

        • LOWORD(lParam) 变化后的宽度

        • HIWORD(lParam) 变化后的高度

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

    • WM_QUIT

      • 产生时间: 用户发送

      • 附带消息

        wParam : PostQuitMessage 函数传递的参数

        lParam : 0

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

      不需要手动处理

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

    • PeekMessage 以查看的方式从系统获取消息,可以不将消息从系统中移除,非阻塞函数。当系统没有消息时,返回FALSE。

      BOOL PeekMessage(
      	LPMSG lpMsg,  // 消息信息
          HWND hWnd,   // 窗口句柄
          UINT wMsgFilterMin, // first message
          UINT wMsgFilterMax, // last message
          UINT wRemoveMsg     // 是否移除标识  PM_REMOVE / PM_NOREMOVE
      );
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    while(1){
    	if(PeekMessage(&nMsg,NULL,0,0,PM_NOREMOVE)){
            // 有消息
            if(GetMessage(&nMsg,NULL,0,0)){
                TranslateMessage(&nMsg);
                DispatchMessage(&nMsg);
            }else{
                break;
            }
        }else{
            // 无消息
            // 空闲处理
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    发送消息
    • SendMessage 发送消息,会等待消息处理的结果 ( 等到消息处理完 , 会阻塞)

    • PostMessage 投递消息,消息发出后立刻返回,不等待消息执行结果

      将消息放到系统消息队列中

    BOOL SendMessage / PostMessage (
    	HWND hWnd , // 消息发送的目的窗口
        UINT Msg, // 消息ID
        WPARAM wParam , // 消息参数
        LPARAM lParam   // 消息参数
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    消息分类
    • 系统消息 ID范围 0-0x03ff (0-1024)

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

    • 用户自定义消息 ID范围 0x0400 - 0x7fff ( 一共31743)

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

      自定义消息宏: WM_USER

    消息队列
    概念
    1. 消息队列用于存放消息的队列
    2. 先进先出
    3. 所有窗口程序都具有消息队列
    4. 程序可以从队列中获取消息
    分类
    • 系统消息队列,由系统维护的消息队列。存放系统产生的消息,例如:鼠标,键盘等
    • 程序消息队列 属于每一个应用程序(线程)的消息队列,由应用程序维护

    image-20221108165656709

    消息和消息队列的关系
    • 消息和消息队列的关系

      1. 产生消息时,会将消息存放到系统消息队列中
      2. 系统根据存放的消息,找到对应程序的消息队列
      3. 将消息投递到程序的消息队列中
    • 根据消息和消息队列之间使用关系,消息分为两类

      • 队列消息

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

      • 非队列消息

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

    • 队列消息

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

      GetMessage 从消息队列获取消息

      PostMessage 将消息投递到消息队列

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

      WM_QUIT 必须进队列,不然GetMessage的循环不会结束

    • 非队列消息

      消息发送时,首先查找消息接收窗口的窗口处理函数,直接调用函数,完成消息

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

      常见消息: WM_CREATE, WM_SIZE等

      WM_CREATE 必须不能进队列 --> 此时窗口还没有显示

    GetMessage详细解释
    • 在程序消息队列中查找信息,如果队列有消息,检查消息是否满足指定条件( HWND, ID范围 ), 不满足条件就不会取出消息,否则取出返回

    • 如果程序队列没有消息,向系统消息队列获取数据本程序的消息。如果系统队列的当前消息属于本程序,系统会将消息转发到程序消息队列中

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

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

    • 如果没有到时的定时器,整理程序的资源,内存等等

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

    • GetMessage 如果获取到是WM_QUIT函数返回FALSE

    WM_PAINT消息
    • 产生时间: 当窗口需要绘制的时候( 并且系统没有其他消息时 )
    • 附带信息: wParam : 0 lParam : 0
    • 专职用法: 用于绘图
    // paint demo
    // 窗口无效区域: 需要重新绘制的区域
    BOOL InvalidateRect(
    	HWND hWnd, // 窗口句柄
        CONST RECT * lpRect, // 区域的矩形坐标, NULL -> 整个窗口
        BOOL bErase // 重绘前是否先擦除 TRUE / FALSE 
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    WM_LBUTTONDOWN 鼠标左键按下消息

    绘图

    步骤

    1. 开始绘图

      HDC BeginPain(
      	HWND hwnd , // 绘图窗口
          LPPAINTSTRUCT lpPaint // 绘图参数的buff
      ); // 返回绘图设备句柄HDC
      
      • 1
      • 2
      • 3
      • 4
    2. 正式绘图

    3. 结束绘图

      BOOL EndPaint(
      	HWND hWnd, // 绘图窗口
          CONST PAINTSTRUCT * lpPaint // 绘图参数的指针
      );
      
      • 1
      • 2
      • 3
      • 4
    PAINTSTRUCT ps = {0};
    HDC hdc = BeginPaint(hWnd,&ps);
    TextOut(hdc,100,100,"Hello,Wkk",strlen(Hello,Wkk));
    EndPaint(hWnd,&ps);
    // 以上绘图的代码,必须放在处理WM_PAINT消息中
    
    • 1
    • 2
    • 3
    • 4
    • 5
    键盘消息
    键盘消息分类
    • WM_KEYDOWN 按键按下产生
    • WM_KEYUP 按键被放开时产生
    // 附带信息
    WPARAM  按键的Virtual Key --> 虚拟键码值不能区分大小写
    LPARAM  按键的参数,例如按下次数 --> 不重要
    
    • 1
    • 2
    • 3
    • WM_SYSKEYDWON 系统键按下时产生 比如: ALT, F10
    • WM_SYSKEYUP 系统键放开时产生
    字符消息( WM_CHAR )
    • TranslateMessage 在转换WM_KEYDOWN消息时,对于可见字符会产生WM_CHAR,不可见字符无此消息。

      TranslateMessage(&nMsg){
          // 只翻译可见字符的消息
          if(nMsg.message != WM_KEYDWON) return ;
          根据nMsg.wParam键码值可以获知哪个按键被按下
          if(不可见字符的按键) return ;
          查看CapsLock是否处于打开状态
          if(打开) PostMessage(nMsg.hwnd,WM_CHAR,大写字符,...);
          else PostMessage(nMsg.hwnd,WM_CHAR,小写字符,...);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • 附带信息

      WPARAM - 输入的字符的ASCII字符编码值
      LPARAM - 按键的相关参数  --> 不重要
      
      • 1
      • 2
    鼠标消息
    鼠标消息分类
    • 基本鼠标消息

      WM_LBUTTONDOWN 鼠标左键按下

      WM_LBUTTONUP 左键抬起

      WM_RBUTTONDWON

      WM_RBUTTONUP

      WM_MOUSEMOVE 鼠标移动消息

      1. 附带信息

        wPARAM: 其他按键的状态,例如: Ctrl / Shift等

        鼠标左键:1, Ctrl: 8 ,Shift: 4

        IPARAM: 鼠标的位置,窗口客户区坐标系

        • LOWORD x坐标
        • HIWORD y坐标
      2. 一般情况鼠标按下/ 抬起是成对出现的。

        在鼠标移动过程中,会根据移动速度产生一系列的WM_MOUSEMOVE消息

        移动慢产生消息多,移动快产生消息少

    • 双击消息

      WM_LBUTTONDBLCLK 鼠标右键双击

      WM_RBUTTONDBLCLK 左键双击

      1. 附带消息

        同基本鼠标消息

      2. 消息产生顺序

        以左键双击为例:

        WM_LBUTTONDOWN

        WM_LBUTTONUP

        WM_LBUTTONDBLCLK

        WM_LBUTTONUP

        使用时需要在注册窗口类的时候添加CS_DBLCLKS 风格, 加上才会出现双击事件

    • 滚轮消息

      WM_MOUSEWHEEL 鼠标滚轮消息

      1. 附带信息

        wPARAM

        • LOWORD 其他按键的状态

        • HIWORD 滚轮的偏移量,通过正负值表示滚动方向

          正:向前滚动, 负:向后滚动

        IPARAM: 鼠标当前的位置,屏幕坐标系

        • LOWORD x坐标
        • HIWORD y坐标
      2. 使用

        通过偏移量,获取滚动的方向和距离

        一般的滚动偏移都是120的倍数

    定时器消息
    定时器消息介绍
    • 产生时间

      在程序中创建定时器,当到达时间间隔时,定时器( 实际是GetMessage )会向程序发送一个WM_TIMER消息

      定时器的精度是毫秒,但是准确度低

      可以设置时间间隔为1000ms, 但是会在非1000毫秒到达消息

    • 附带信息

      wPARAM: 定时器ID

      IPARAM: 定时器处理函数的指针 --> 一般没用

    • 用途

      周期性做,时间要求不严

    创建销毁定时器
    • 创建定时器

      UINT_PTR SetTimeer(
      	HWND hWnd,// 定时器窗口句柄
          UINT_PTR nIDEvent, // 定时器ID
          UINT uElapse, // 时间间隔   ms为单位
          TIMERPROC lpTimerFunc // 定时器处理函数指针(一般不使用,为NULL)  
      ); // 创建成功,返回非0
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • 关闭定时器

      BOOL KillTimer(
      	HWND hWnd, // 定时器窗口句柄
      	UINT_PTR uIDEvent  // 定时器ID
      );
      
      • 1
      • 2
      • 3
      • 4
    菜单资源
    菜单分类
    • 窗口的顶层菜单

    • 弹出式菜单

      例如:鼠标右键菜单

    • 系统菜单

      image-20221105230101714

      HMENU 类型表示菜单
      ID 表示菜单项  --> 每一个菜单项都有一个ID
      
      • 1
      • 2
    资源相关
    • 资源脚本文件: *.rc文件

      描绘图片、菜单资源

    • 编译器: RC.EXE

      image-20221105230332488

    菜单资源的使用
    1. 添加菜单资源

      • 增加一个.rc文件 --> 会自动增加一个resource.h文件
      • 选中rc文件 --> Add Resource
      • 添加菜单资源
      • 编辑菜单,下拉项 右键可以查看&修改属性
      • 分割线 Separator -> true
      • 菜单ID
    2. 加载菜单资源

      • 注册窗口类时设置菜单

        wc.lpszMenuName = (LPCTSTR)IDR_MENU1;  // 强转一下路径
        
        • 1
      • 创建窗口时传递参数

        倒数第三个参数 传递菜单句柄 ( 不是上面的菜单ID )

        HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,LoadMenu(hIns,(LPCTSTR)IDR_MENU1),hIns,NULL)
        
        • 1

        加载菜单资源:

        HMENU LoadMenu (
        	HINSTANCE hInstance, // handle to module  --> 找本进程的内存
            LPCTSTR lpMenuName   // menu name or resource identifier
        );
        
        • 1
        • 2
        • 3
        • 4
      • 在主窗口WM_CREATE消息中利用SetMenu函数设置菜单

        HMENU hMenu = LoadMenu(hIns,(LPCTSTR)IDR_MENU1));
        SetMenu(hWnd,hMenu);
        
        • 1
        • 2
    命令消息处理

    单击菜单项发出WM_COMMAND消息

    • 附带信息

      wPARAM : HIWORD 对于菜单为0 
               LOWORD 菜单项的ID
      lPARAM : 对于菜单为0
      
      • 1
      • 2
      • 3
    图标资源

    以.ico结尾的图片

    • 添加资源

      注意图标的大小,一个图标文件中,可以有多个不同大小的图标

    • 加载

      HICON LoadIcon(
      	HINSTANCE hInstance, // 当前程序实例句柄
          LPCTSTR lpIconName  // 图标资源id
      );// 成功返回HICON句柄
      
      • 1
      • 2
      • 3
      • 4
    • 设置

      注册窗口类

      wc.hIcon = LoadIcon(hIns,(LPCTSTR)IDI_ICON1);
      
      • 1
    光标资源
    • 添加光标的资源

      关标的大小默认是32x32像素,每个光标有HotSpot( 热点 ), 是当前鼠标的热点

    • 加载资源

      HCURSOR LoadCursor(
      	HINSTANCE hInstance, // 程序实例
          LPCTSTR lpCursorName // 资源ID
      ); // hInstance 可以为NULL, 获取系统默认的Cursor
      
      • 1
      • 2
      • 3
      • 4
    • 设置资源

      • 在注册窗口时,设置光标

        wc.hCursor = LoadCursor(hIns,(char*)IDC_CURSOR1);
        
        • 1
      • 使用SetCursor设置光标

        HCURSOR SetCursor(
        	HCURSOR hCursor // 光标
        ); // 返回原来的光标
        
        • 1
        • 2
        • 3

        必须放在消息的处理中设置

        WM_SETCURSOR 消息参数
            wPARAM 当前使用的光标句柄
            IPARAM - LOWORD 当前区域的代码 在哪个区域活动  HICLIENT  / HTCAPTION  客户区/标题区
                   - HIWORD 当前鼠标消息ID,有没有点右键左键等等
        
        • 1
        • 2
        • 3
        • 4
        DefWindowPro() 默认处理,会将光标重新改成注册窗口类的关标
        所以SetCursor后要直接返回,不能经过DefWindowPro函数    
        
        • 1
        • 2
    字符串资源
    • 添加字符串资源

      添加字符串表,在表中添加字符串

    • 字符串资源的使用

      int LoadString(
      	HINSTANCE hInstance, //程序实例
          UINT uID, // 字符串ID
          LPTSTR lpBuffer, // 存放字符串的BUFF
          int nBufferMax // 字符串BUFF长度
      ); // 成功返回字符串长度,失败0
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    快捷键资源
    • 添加 资源添加快捷键表,增加命令ID对应的快捷键 Accelerator

      让快捷键的ID 和 命令的ID一样 就是绑定

    • 使用

      // 加载加速键表 --> 表
      HACCEL LoadAccelerators(
      	HINSTANce hInstance, // 程序实例句柄
          LPCTSTR lpTableName   // 快捷键表名
      ); // 返回快捷键表句柄
      // 翻译快捷键
      int TranslateAccelerator(
      	HWND hWnd, // 处理消息的窗口句柄
          HACCEL hAccTable, // 快捷键表句柄
          LPMSG lpMsg// 消息
      ); // 如果快捷键,返回非零
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      TranslateAccelerator(hWnd,hAccel,&nMsg){
          if(nMsg.message != WM_KEYDOWN ) return 0; // 没有按键按下,一定不是快捷键
          根据nMsg.wParam(键码值),获知哪些按键按下
          到快捷键表中去匹配查找
          if(找不到) return 0; 
          else{
              SendMessage(hWnd,WM_COMMAND,(HI)1(LO)快捷键对应的ID,...);
              return 1;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      // 调用
      while( GetMessage(&nMsg,NULL,0,0)){
      	if( !TranslateAccelerator(hWnd,hAccel,&nMsg)){
      		TranslateMessage(&nMsg);
      		DispatchMessage(&nMsg);
      	}
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    • 在WM_COMMAND中相应消息,消息参数

      wPARAM : HWORD 为1表示加速键,为0表示菜单

      ​ LOWORD 为命令ID

      lParam: 为0

    绘图编程
    绘图基础
    • 绘图设备 DC (Device Context), 绘图上下文/绘图描述表

    • HDC-DC 句柄,表示绘图设备

    • GDI - Windows graphics device interface ( Win32 提供的绘图API )

    • 颜色 RGB

      32位: 8,8,8,8 --> 透明度

    • 颜色的使用

      COLORREF–> DWORD

      COLORREF nColor = 0;
      
      • 1
    • 赋值使用RGB宏

      nColor = RGB(0,0,255);
      
      • 1
    • 获取RGB值

      GetRValue / GetGValue / GetBValue
      BYTE nRed = GetRValue( nColor );
      
      • 1
      • 2
    HDC hdc = BeginPaint(hWnd,...);  //--> 抓绘图设备
    TextOut(hdc,x,y,"hello",...);
    ...
    EndPaint(hwnd,...);
    
    • 1
    • 2
    • 3
    • 4
    基本图形绘制
    • SetPixel 设置指定点的颜色

      COLORREF SetPixel(
      	HDC hdc, // DC句柄
          int X,
          int Y,
          COLORREF crColor 
      );// 返回原来的颜色
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      PAINTSTRUCT ps = {0};
      HDC hdc = BeginPaint(hWnd,&ps);
      SetPixel(hdc,100,100,RGB(255,0,0));
      EndPaint(hWnd,&ps);
      
      • 1
      • 2
      • 3
      • 4
    • 线得使用(直线,弧线)

      MoveToEx – 指明窗口当前点

      LineTo 从窗口当前点到指定点绘制一条直线

      当前点: 上一次绘制时的最后一点,初始为(0,0)点

      MoveToEx(hdc,100,100,NULL); // 最后一个参数返回上一次的当前点
      LineTo(hdc,300,300);
      
      • 1
      • 2
    • 封闭图形:能够用画刷填充的图形

      Rectangle / Ellipse

      Rectangle(hdc,x,y,x+w,y+h);
      
      • 1
      Ellipse(hdc,100,100,x+w,y+h); // 外接矩形
      
      • 1
    GDI绘图对象
    画笔
    • 画笔的作用

      线的颜色,线型(虚线画笔、点线画笔…),线粗

      HPEN : 画笔句柄

    • 画笔的使用

      1. 创建画笔

        HPEN CreatePen(
        	int fnPenStyle, // 画笔的样式
            int nWidth, // 画笔的粗细
            COLORREF crColor // 画笔的颜色
        ); // 创建成功返回句柄
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • PS_SOILD - 实心线,可以支持多个像素宽,其他线型只能是一个像素宽

        • PS_DASH - 虚线画笔

      2. 将画笔应用到DC中

        	HGDIOBJ SelectObject(
        		HDC hdc, // 绘图设备句柄
        	    HGDIOBJ hgdiobj  // GDI绘图对象句柄
        	); // 返回原来的GDI绘图对象句柄   --> 注意保存原来DC当中画笔
        
        • 1
        • 2
        • 3
        • 4
      3. 绘图

      4. 取出DC中的画笔,将原来的画笔,使用SelectObject函数,放入到设备DC中

      5. 释放画笔

        BOOL DeleteObject(
        	HGDIOBJ hObject   // GUI绘图对象句柄
        );
        
        • 1
        • 2
        • 3

        只能删除不被DC使用的画笔,所以在释放前,必须将画笔从DC中取出

    画刷

    封闭图形的填充的颜色、图案

    HBRUSH 画刷句柄

    1. 创建画刷

      HBRUSH hBursh = CreateSolidBrush(RGB(0,255,0));
      //CreateSolidBrush 创建实心画刷
      HBRUSH hBurshhat = CreateHatchBrush(HS_CROSS,RGB(0,255,0)); // HS_CROSS 经纬线
      //CreateHatchBrush 创建纹理画刷
      
      • 1
      • 2
      • 3
      • 4
    2. 将画刷应用到DC中

      HBRUSH oldBrush = SelectObject(hdc,hBursh); // 原本的刷子是白色的
      
      • 1
    3. 绘图

    4. 将画刷从DC中取出

      SelectObject(hdc,oldBrush);
      
      • 1
    5. 删除画刷

      DeleteObject(hPen);
      
      • 1

    用一条条的单独直线围起来的图形,不是封闭图形,不能填充

    系统GDI对象

    使用GetStockObject 函数获取系统维护的画刷,画笔等

    如果不使用画刷填充,需要使用NULL_BRUSH 参数,获取透明画刷。

    GetStockObject 返回的画刷不需要DeleteObject

    // 透明画刷
    HGDIOBJ hBursh = GetStockObject(NULL_BRUSH);
    // 应用刷子
    // 恢复刷子
    
    • 1
    • 2
    • 3
    • 4
    位图
    位图绘制
    • 位图相关

      光栅图形: 记录图像中每一点的颜色等信息

      矢量图形: 记录图像算法,绘图指令等等

      HBITMAP 位图句柄

    • 位图的使用

      1. 在资源中添加位图资源 --> 资源ID

      2. 从资源中加载位图LoadBitmap

        HBITMAP hBmp = LoadBitmap(g_hInstance,(char*)IDB_BITMAP1);
        
        • 1
      3. 创建一个与当前DC相匹配的DC(内存DC) --> 在内存中绘图

        HDC CreateCompatibleDC(
          HDC hdc  // 当前DC句柄,可以为NULL, 使用屏幕DC
        ); // 返回创建好的DC句柄
        
        • 1
        • 2
        • 3
      4. 将位图放入匹配的内存DC中 --> 在虚拟区域中绘制图形

        SelectObject
        
        • 1
      5. 成像 (1:1)

        BOOL BitBlt(
          HDC hdcDest; // 目的DC
          int nXDest, 
          int nYDest,
          int nWidth,
          int nHeight,
          HDC hdcSrc, // 源DC --> 内存DC
          int nXSrc,  // 源左上X
          int nYSrc,  // 源左上Y
          DWORD dwRop  // 成像方法,SRCCOPY
        );
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11

        缩放成像

        BOOL StretchBlt(
        	HDC hdcDest,
            int nXOriginDest,
            int nYOriginDest,
            int nWidthDest,
            int nHeightDest,
            HDC hdcSrc,
            int nXOriginSrc,
            int nYOriginSrc,
            int nWidthSrc,   // 源DC宽
            int nHeightSrc,  // 源DC高
            DWORD dwRop
        );
        // 目的区域比原始图像小  --> 缩小
        // 目的区域比原始图像大  --> 放大
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
      6. 取出位图

        SelectObject   从内存DC取数据
        
        • 1
      7. 释放位图

        DeleteObject(hBmp);
        
        • 1
      8. 释放匹配的内存DC

        DeleteDC(hMemdc);
        
        • 1
    文本绘制
    • 文字绘制

      TextOut -> 将文字绘制在指定坐标位置,只能单行绘制

      TextOut(hdc,x,y,strdata,nCount);
      
      • 1
      RECT rc;
      rc.left = 100;  // (left,top) (right,bottom)
      rc.top= 150;
      rc.right = 200;
      rc.bottom= 200;
      
      int DrawText(
      	HDC hDC,    // DC句柄
          LPCTSTR lpString,  // 字符串
          int nCount,        // 字符数量
          LPRECT lpRect,     // 绘制文字的矩形框
          UINT uFormat       // 绘制的方式  // DT_LEFT|DT_TOP  水平靠左|垂直靠上  DT_WORDBREAK 多行绘制
      );                                  // DT_NOCLIP 打破矩形区域的限制,不裁剪
      // DT_CENTER DT_VCENTER --> DT_VCENTER、DT_BOTTOM只适用于单行DT_SINGLELINE,和DT_WORDBREAK冲突
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    • 文本颜色和背景

      文字颜色: SetTextColor(hdc,RGB(…));

      文字背景色: SetBkColor(hdc,RGB(…)); --> 仅适用于不透明模式

      文字背景模式: SetBkMode(QPAQUE / TRANSPARENT );

      • QPAQUE 不透明,默认
      • TRANSPARENT 透明
    • 字体

      Windows常用的字体为 TrueType格式的字体文件 --> 保存点阵字型

      字体名 - 标识字体类型 --> 第一行有字体名称

      HFONT - 字体句柄

      1. 创建字体

        HFONT CreateFont(
        	int nHeight, // 字体高度
            int nWidth, // 字体宽度     一般给一个高度,宽度为0,系统字节匹配一个合适的宽度
            int nEscapement, // 字符串倾斜角度(以0.1度为单位)  --> 和斜体不同 
            int nOrientation, // 字符旋转角度   二维看不出效果
            int fnWeight, // 字体的粗细 ,不是以像素为单位  ,  900是细体
            DWORD fdwItalic, // 斜体
            DWORD fdwUnderline, // 下划线
            DWORD fdwStrikeOut, // 删除线    1/0
            DWORD fdwCharSet, // 字符集 --> GB2312_CHARSET  涵盖基本所有的汉字
            DWORD fdwOutputPrecision, // 输出精度  , 废弃
            DWORD fdwClipPrecision, // 剪切精度  , 废弃
            DWORD fdwQuality,  // 输出质量  , 废弃
            DWORD fdwPitchAndFamily,// 匹配字体  , 废弃
            LPCTSRT lpszFace  // 字体名称  ---> 字体文件内容第一行
        );
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
      2. 应用字体到DC

        HGDIOBJ oldfont = SelectObject(hdc,hFont);

      3. 绘制文字

        DrawText / TextOut

      4. 取出字体

        SelectObject(hdc,oldfont);

      5. 删除字体

        DeleteObject(hFont);

    对话框
    • 普通窗口: 自定义函数调用缺省函数

    • 对话框窗口: 缺省函数调用自定义函数

      缺省函数(...){
          ...
          自定义函数(...)
      }
      
      • 1
      • 2
      • 3
      • 4
    对话框原理
    • 对话框分类

      模态对话框:当对话框显式时,会禁止其他窗口和用户的交互操作

      非模态对话框:在对话框显示后,其他窗口仍然可以和用户交互操作

    • 对话框基本使用

      1. 对话框窗口处理函数
      2. 注册窗口类( 不使用 ), 操作系统帮忙注册, 名字–>Dialog
      3. 创建对话框
      4. 对话框的关闭
    • 对话框窗口处理函数 --> 并非真正的对话框窗口处理函数

      INT_PTR CALLBACK DialogProc(
      	HWND hwndDlg , // 窗口句柄
          UINT uMsg, // 消息ID
          WPARAM wParam , // 消息参数
          LPARAM lParam // 消息参数
      );
      // 返回TRUE   缺省处理函数不需要处理
      // 返回FALSE  交给缺省处理函数处理
      // 一般不需要调用缺省对话框窗口处理函数
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    模态对话框
    • 创建对话框

      INT DialogBox(
      	HINSTANCE hInstance, // 应用程序实例句柄
          LPCTSTR lpTemplate, // 对话框资源ID
          HWND hWndParent, // 对话框父窗口
          DLGPROC lpDialogFunc  // 自定义函数
      );
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      DialogBox事一个阻塞函数,只有当对话框关闭后,才会返回,继续执行后续代码

      返回值通过EndDialog设置 --> 通过EndDialog结束对话框

      点击关闭按钮消息: WS_SYSCOMMAND

    • 对话框处理函数

      INT_PTR CALLBACK DlgProc(HWND hwndlg,UINT msgID,WPARAM wParam,LPARAM lParam){
          switch(msgID){
              case  WM_SYSCOMMAND:
                  if(wParam == SC_CLOSE){ // 点击关闭按钮
      				EndDialog(hwndlg,100);
                  }
                  break;
          }
          return FALSE; // 将消息交给真正的对话框处理函数的后续处理部分处理
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    • 对话框的关闭

      BOOL EndDialog(
      	HWND hDlg, // 关闭的对话框窗口句柄
          INT_PTR nResult // 关闭的返回值
      );// 消除模式对话框 & 解除模式对话框的阻塞状态
      
      • 1
      • 2
      • 3
      • 4

      关闭模式对话框,只能使用EndDialog,不能使用DestroyWindow(销毁窗口)等函数。

      DestroyWindow 可以销毁对话框,但是不能消除阻塞

      nResult 是DialogBox函数退出时的返回值

    • 对话框的消息

      WM_INITDIALOG 对话框创建之后显示之前,通知对话框窗口处理函数,可以完成自己的初始化相关的操作。

      其他消息和窗口的消息一致

    非模态对话框
    • 创建对话框

      HWND CreateDialog(
      	HINSTANCE hInstance , // 应用程序实例句柄
          LPCTSTR lpTemplate, // 模板资源ID
          HWND hWndParent, // 父窗口
          DLGPROC lpDialogFunc // 自定义函数
      );
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      非阻塞函数,创建成功后返回窗口句柄**,需要使用ShowWindow函数显示对话框**

    • 对话框的关闭

      关闭时使用DestroyWindow销毁窗口,不能使用EndDialog关闭对话框

    其他
    增加DOS窗口

    用于调试

    HANDLE g_hOutput = 0 ;// 接收标准输出句柄
    WinMain(...){
        AllocConsole(); //增加入口函数
        g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
        // FreeConsole(); // 释放控制台
        
        WriteConsole(g_hOutput,text,strlen(text),NULL,NULL);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    参考
    1. https://www.bilibili.com/video/BV1Qb4y1o7u9
  • 相关阅读:
    C++项目——云备份-⑦-服务端业务处理模块设计与实现
    fastTEXT论文解读并附实例代码
    【附源码】计算机毕业设计SSM实验室资产管理系统
    在线教育行业介绍
    Swin_Unet & Trans_UNet & Unet & Deeplabv3网络推理时间对比
    NMap 使用技巧总结(一)
    Python程序设计教案
    C#(三十)之C#comboBox ListView treeView
    vscode_c++_slambook 编译配置
    存档-链表设计JS/TS版
  • 原文地址:https://blog.csdn.net/first_bug/article/details/127839468