Win32前端

2022/4/4

应用程序分类

  • 控制台应用程序Console

    DOS程序,本身没有窗口,通过一个Windows DOS窗口执行

  • 窗口程序

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

  • 库程序

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

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

静态库无法运行,没有入口函数

动态库不能独立运行,有入口函数,必须依附于其他可执行程序运行

应用程序对比

  • 入口函数

    控制台程序 - main

    窗口程序 - Winmain

    动态库程序 - Dllmain

    静态库程序 - 无入口函数

  • 文件存在方式

    控制台程序、窗口程序 - EXE文件

    动态库程序 - DLL文件

    静态库程序 - LIB文件

开发工具和库

01 win下的编译工具

  • 编译器CL.EXE 将源代码编译成目标代码.obj
  • 链接器LINK.EXE 将目标代码、库链接,生成最终文件
  • 资源编译器RC.EXE (.rc)将资源编译,最终通过链接器存入最终文件 //自动生成的脚本语言

02 库和头文件

  • Windows库

    kernel32.dll - 提供了核心的API,例如进程、线程、内存管理等

    user32.dll - 提供了窗口、消息等API

    gdi32.dll - 提供了绘图相关的API

    C:\Windows\System32

  • 头文件

    windows.h - 所有windows头文件的集合

    windef.h - windows数据类型

    winbase.h - kerbel32的API

    wingdi.h - gdi32的API

    winuser.h - user32的API

    winnt.h - UNICODE字符集支持

03 相关函数

  • WinMain

    int WINAPI WinMain(
    HINSTANCE hInstance,//当前程序的实例句柄,寻址当前进程的内存
    HINSTANCE hPrevInstance,//当前程序前一个实例句柄
    LPSTR lpCmdLine,//命令行参数字符串
    int nCmdShow//窗口的显示方式(最大化,最小化,原型)
    )
    
  • MessageBox

    int MessageBox(
    HWND hWnd//父窗口句柄
    LPCSTR lpText//显示在消息框中的文字
    LPCSTR lpCaption//显示在标题栏中的文字
    UINT uType //消息框中的按钮、图标显示类型
        );//返回点击的按钮ID
    
  • 用DOS编译

    • 运行开发环境中的vcvars32.bat
    • CL.EXE .c/cpp文件 -c(只编译不链接)
    • LINK.EXE .obj文件,生成.EXE文件

04 程序编译过程

  • 编译环境准备

    VCVARS32.BAT

  • 编译程序 -CL

    CL.EXE -c xxx.c

  • 链接程序 -LINK

    LINK.EXE xxx.obj xxx.lib

  • 执行

2022/4/5

  • .rc文件的几点解释

    • .rc文件为资源脚本文件,编写资源的文件
    • 编译rc文件 - RC.EXE 生成.res文件(脚本文件生成的机器码)
    • 将资源链接到程序中 - LINK.EXE

尝试手写.rc文件

100 ICON small.ico   //标号   文件类型     文件名

心得:最终链接的过程,链接器将.obj文件和.res文件(统称为中间文件)以及使用的库链接到一起,生成最终的.exe文件,即

         CL.EXE
 .c/.cpp--------->.obj-   LINK.EXE
 		 RC.EXE         ---------->.exe
 .rc------------->.res-

第一个windows窗口

01 窗口创建过程

  • 定义WinMain函数
  • 定义窗口处理函数(自定义,处理消息)
  • 注册窗口类(向操作系统写入一些数据)
  • 创建窗口(内存中创建窗口)
  • 显示窗口(绘制窗口的图像)
  • 消息循环(获取/翻译/派发消息,派发至自己编写的窗口处理函数)
  • 消息处理

具体实现:

#include<windows.h>

//窗口处理函数(自定义,用来处理消息)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{   
    switch(msgID){
        case WM_DESTROY:
            PostQuitMessage(0);
            break;//结束进程
    }
	return DefWindowProc(hWnd, msgID, wParam, lParam);//DefwindowProc给各种消息作默认处理
}



//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{   //注册窗口类
	WNDCLASS wc = { 0 };						//初始化一个WNDCLASS类
	wc.cbClsExtra = 0;							//申请0个字节的缓冲区
	wc.cbWndExtra = 0;							//同上但是有所不同
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);//设置背景色
	wc.hCursor = NULL;							//默认光标
	wc.hIcon = NULL;							//初始化为默认图标(标题栏左上角)
	wc.hInstance = hIns;						//将当前程序实例句柄赋给窗口类
	wc.lpfnWndProc = WndProc;					//将窗口处理函数的名字(函数地址)赋给窗口类
	wc.lpszClassName = "CNMD";					//给窗口类一个名字
	wc.lpszMenuName = NULL;						//不使用菜单功能
	wc.style = CS_HREDRAW | CS_VREDRAW;			//窗口在水平方向或垂直方向大小有变化时,重新绘制窗口图像
	RegisterClass(&wc);							//将以上所有赋值全部写入操作系统
	//在内存中创建窗口,返回一个实例句柄
	HWND hWnd =
		CreateWindow("CNMD", "WCNMD", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, 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 2022/4/9 :注册窗口类时将一部分窗口类的信息写入操作系统内核,在内存中开辟空间时又写入了窗口的一些信息,可以类比为类的定义与类的实例化,注册时写入的窗口类可以作为一个模板,基于这个模板可以create无数个共同特征的窗口,而这些窗口又可以根据create时的参数不同而加以区分。

心得2 2022/4/9:窗口是进程的一部分,是操作系统为用户提供的一个与进程交互的图形界面,这就解释了为什么有两个句柄,hIns和hWnd

2022/4/6

字符编码

01 编码历史背景

  • ASC
  • ASCII
  • DBCS
  • UNICODE

02 DBCS和UNICODE码

  • DBCS字符编码

    A 我 是 程 序 员

    01 0203 0405 0607 0809 0A0B

    采用单双字节混合编码,在解析时,可能为:

    0102 0304 0506 0708 090A 0B

    出错(大多数情况)

  • UNICODE编码: (linux采用UTF-8编码,windows采用UTF-16编码)

    A 我 是 程 序 员

    0001 0203 0405 0607 0809 0A0B

03 宽字节字符

  • wchar_t每个字符占2个字节

  • char每个字符1个字节

  • wchar_t实际是unsigned short类型,定义时,需要增加“L”,通知编译器按照双字节编译字符串,采用unicode编码

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

    ​ wchar_t* pwszText = L"Hello wchar";

    ​ wprintf(L"%s\n",pwszText);

04 TCHAR类型

头文件中的宏定义

#ifdef UNICODE
typedef wchar_t TCHAR
    #define_TEXT(quote)L##quote
   #else
    typedef char TCHAR;
#define_TEXT(quote)quote
#endif
//如果存在Unicode宏定义,则采用宽字符编码,如果没有定义,则采用正常char编码,因此当分不清用char还是wchar_t编码时,直接用TCHAR类型

05 打印UNICODE 字符

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

在windows下使用WriteConsoleAPI打印Unicode字符GetStdHandle

要想正常输出UNICODE字符编码,需要此API

#define UNICODE//等价于在属性使用UNICODE字符编码
#include<windows.h>
#include<stdio.h>

	void printUnicode()
	{
		wchar_t* pszTEXT= (wchar_t*)L"暗杀就开始大家看到";
		HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//获得标准输出句柄
		WriteConsole(hOut, pszTEXT, wcslen(pszTEXT), NULL, NULL);
}

int main()
{
	printUnicode();
	return 0;
}

总结:如果定义了UNICODE宏,则Win下的TCHAR类型等价于wchar_t类型,如果没有定义UNICODE宏,则等价于char类型,但是wprintf对于汉字的输出具有局限性,因此用WriteConsole输出,更改项目属性为使用Unicode字符集会自动添加Unicode宏

注册窗口类

01 窗口类的概念

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

02 窗口类的分类

  • 系统窗口类

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

  • 应用程序全局窗口类

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

  • 应用程序局部窗口类

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

03 系统窗口类

  • 不需要注册,直接使用窗口类即可,系统已经注册好了

    ​ 按钮 -BUTTON

    ​ 编辑框 -EDIT

    #include<windows.h>
    //入口函数
    int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
    {   
    	//在内存中创建窗口,返回一个实例句柄
    	HWND hWnd =
    		CreateWindow("button", "WCNMD", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, 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;
    }
     
    
    
    

04 全局及局部窗口类

  • 注册窗口类的函数

    ATOM RegisterClass(CONST WNDCLASS *plWndClass);// 注册成功后,返回一个数字标识
    

2022/4/7

  • 注册窗口类的结构体

    typedef struct _WNDCLASS{
        UINT style;//窗口类的风格
        WNDPROC lpfnWndProc;//窗口处理函数
        int cbClsExtra;//窗口类附加数据缓冲区大小
        int cbWndExtra;//窗口附加数据缓冲区大小
        HINSTANCE hInstance;//当前模块的实例句柄
        HICON hIcon;//窗口图标句柄
        HCURSOR hCursor;//鼠标的句柄
        HBRUSH hbrBackground;//绘制窗口背景的画刷句柄
        LPCTSTR lpszMenuName;//窗口菜单的资源ID字符串
        LPCTSTR lpszClassName;//窗口类的名称
    }WNDCLASS,*PWNDCLASS;
    
    • style窗口类风格

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

      例如:

      ​ WNDCLASS wce = {0};

      ​ wce.style = …|CS_GLOBALCLASS

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

      • CS_HREDRAW - 当窗口水平变化时,窗口重新绘制
      • CS_VREDRAW - 当窗口垂直变化时,窗口重新绘制
      • CS_DBLCLKS - 允许窗口接收鼠标双击
      • CS_NOCLOSE - 窗口没有关闭按钮

窗口创建

01 CreateWindow/CreateWindowEx的定义

来看CreateWindow的声明

HWND CreateWindowEx(
DWORD dwExStyle,//窗口的扩展风格
LPCTSTR lpClassName,//已经注册的窗口类名称
LPCTSTR lpWindowName,//窗口标题栏的名字
DWORD dwStyle,//窗口的基本风格
int x, //窗口左上角水平坐标位置
int y,//窗口左上角垂直坐标位置
int nWidth,//窗口的宽度
int nHeight,//窗口的高度
HWND hWndParent,//窗口的父窗口句柄
HMENU hMenu,//窗口菜单句柄
HINSTANCE hInstance,//应用程序实例句柄
LPVOID lpParam//窗口创建时附加参数
)

补充:LPCTSTR理解

LPCTSTR类型:

L表示long指针 这是为了兼容Windows 3.1等16位操作系统遗留下来的,在win32中以及其他的32位操作系统中, long指针和near指针**及far修饰符都是为了兼容的作用。没有实际意义。

P表示这是一个指针

C表示是一个常量

T表示在Win32环境中, 有一个_T宏

STR表示这个变量是一个字符串

instance:实例

2022/4/8

03 窗口创建的过程

  • 此时,RegisterClass函数已经将窗口信息写入操作系统内核,creat函数需要寻找该信息,在内存中开辟区域

  • 消息的概念与作用

    • 系统根据传入的窗口类名称,在应用程序局部窗口类中查找,找到执行2,未找到执行3
    • 比较局部窗口类与创建窗口时传入的HINSTANCE变量,如果发现相等,创建和注册的窗口类在同一模块,创建窗口返回,如果不相等,继续执行3
    • 在应用程序全局窗口类,如果找到,执行4,未找到执行5
    • 使用找到的窗口类的信息,创建窗口返回
    • 在系统窗口类中查找,如果找到创建窗口返回,否则创建窗口失败

“main":creatwindow寻找窗口的名称依据

HINSTANCE 变量:比对创建窗口的进程(在局部窗口类有用,因为局部窗口类创建的窗口只能被创建窗口的进程所使用,而全局窗口和系统窗口可以被所有进程所使用)

  • 当查找过程完成后,要执行创建过程

    • CreateWindowEX(.."Main"........."hIns")
      {
          step1.匹配查找窗口类;
          if(找到窗口类)
          {
              申请一大块内存,将窗口的数据信息存入这块内存//数据信息包括注册窗口类时填写的信息和传入Createwindow函数的参数信息
              return 这块内存的句柄;
          }else{
              return NULL;
          }
      }
      

04 子窗口的创建过程

  • 创建时要设置父窗口句柄
  • 创建风格要增加WS_CHILD|WS_VISIBLE

实例心得:子窗口在设置好父窗口与实例句柄时,基于父窗口的创建而创建

2022/4/9

  • 显示窗口ShowWIndow(hWnd,SW_SHOW);
  • 刷新窗口UpdateWindow(hWnd); 都是基于当前窗口的句柄

消息基础

观察消息循环代码:

MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0))
{
    TranslateMessage(&nMsg);//trans
    DisPatchMessage(&nMsg);//Pass
    
}
return 0;

01 消息的概念及作用

  • 消息(类)组成(Windows平台下)
    • 窗口句柄 (谁发的?)
    • 消息ID
    • 消息的两个参数
    • 消息产生的时间
    • 消息产生时的鼠标位置
  • 消息的作用
    • 当通知窗口工作时,就采用消息的方式派发给窗口

我们转到消息类的定义:

typedef struct tagMSG {
    HWND        hwnd;
    UINT        message;
    WPARAM      wParam;
    LPARAM      lParam;
    DWORD       time;
    POINT       pt;
}
  • 消息循环是如何实现的?

    我们知道,GetMessage函数在捕捉到消息时,传递给TranslateMessage函数翻译信息,接着传给DispatchMessage函数传递消息,传递给我们窗口(类)的消息处理WndProc函数,然而DispatchMessage函数只有一个实例化消息类的地址参数,它是如何精确找到窗口消息处理函数的呢?

    • Dispatch(&nMsg)
      {
         nMsg.hwnd----->寻址到保存窗口类所有数据的内存------>找到WndProc函数(的地址)----->WndProc(nMsg.hWnd,nMsg.message,nMsg.wParam,nMsg.lParam)---->回到自己的代码
      }
      

02 窗口处理函数

  • 每个窗口都必须具有窗口处理函数

    LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
    
  • 当系统通知窗口时,会调用窗口处理函数,同时将消息ID和消息参数传递给窗口处理函数。在窗口处理函数中,不处理的消息,使用缺省窗口处理函数,例如:DefWindowProc

2022/4/14

03 其他消息函数

GetMessage - 获取消息

BooL GetMessageA(
    LPMSG lpMsg,
	HWND hWnd,//窗口句柄,限制抓取消息的范围
    UINT wMsgFilterMin,
    UINT wMsgFilterMax);//两个参数从ID的大小范围来限制抓取消息范围

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

hWnd - 获取到hWnd所指定窗口的信息

wMsgFilterMin/Max:限定抓取信息的范围0,表示没有范围

  • 想要让进程结束,就要return 0,就要结束while循环,就要让GetMessage返回0,当抓取的消息为WM_QUIT时,GetMessage才会返回0

    • 这样定义消息处理函数
    LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
    {
    	switch (msgID) {
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		break;//结束进程
    	}
    	return DefWindowProc(hWnd, msgID, wParam, lParam);
    }
    
    

    PostQuitMessage把WM_QUIT消息扔到GetMessage抓取消息的位置,令GetMessage返回0

TranslateMessage - 翻译消息

  • 具体为将按键消息翻译成字符消息,因此参数为被翻译消息的地址
  • 检查消息是否为按键的消息,如果不是,不做任何处理,继续执行

DispatchMessage - 派发消息

  • 将消息派发到该消息所属窗口的窗口处理函数上(通过消息地址内的窗口句柄找到窗口处理函数名并调用)

创建消息

不同消息的wParam和lParam不同,换句话说,wParam和lParam是基于消息产生的,不仅仅和hWnd有关,因此与hWnd并列

AllocConsole()//增加DOS窗口

01 WM_DESTROY

  • 产生时间:窗口被销毁时的信息

  • 附带信息:wParam:为0

    ​ lParam:为0

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

02 WM_SYSCOMMAND

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

  • 附带信息:wParam:鼠标点击的具体位置,例如关闭SC_CLOSE等

    ​ lParam:鼠标光标的位置

    ​ LOWORD(lParam);//水平位置

    ​ HIWORD(lParam);//垂直位置

  • 一般用法:常用在窗口关闭时,提示用户处理

  • msgbox执行,令用户点击,返回一个消息ID

  • Getmessage抓取的消息WM_格式

  • 想要维持窗口,就让GetMessage不返回0,那么winProc可以返回任何数。(结束程序的关键是WinProc中的PostQuitMessage(0)),窗口一销毁,WM_DESTROY一定会产生,但是进程结束与否还是取决于GetMessage的返回值

03 WM_CREATE

  • 产生时间:在窗口创建成功但未显示时(CreateWindow后ShowWnd前)

  • 附带信息:wParam:为0

    ​ lParam:为CREATESTRUCT类型(将CreateWindowEx的参数封装为结构体)的指针,通过这个指针可以获取 CreatewindowEx中所有参数的信息

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

04 WM_SIZE消息

  • 产生时间:在窗口的大小发生变化后

  • 附带信息:wParam:窗口大小变化的原因

    ​ lParam:窗口变化后的大小---->LOWORD(lParam):变化后的宽度 HIWORD(Param):变化后的高度

  • 一般用法:常用于窗口大小发生变化后,调整窗口内各个部分的布局

  • writeconsole函数:

    DefWindowProc函数调用缺省的窗口过程来为应用程序没有处理的任何窗口消息提供缺省的处理。该函数确保每一个消息得到处理。

    WriteConsoleA(
        _In_ HANDLE hConsoleOutput,
        _In_reads_(nNumberOfCharsToWrite) CONST VOID* lpBuffer,
        _In_ DWORD nNumberOfCharsToWrite,
        _Out_opt_ LPDWORD lpNumberOfCharsWritten,
        _Reserved_ LPVOID lpReserved
        );
    

    05 WM_QUIT消息

  • 产生时间:程序员发送(PostQuitMessage(0))

  • 附带信息: wParam:PostQuitMessage 函数传递的参数

    ​ lParam: 0

  • 一般用法:用于结束消息循环,当GetMessage接受到这个消息时,会返回False,结束While循环,退出消息循环,根据其特性推出这个消息只能进GetMessage函数,不会进窗口处理函数

2022/4/16

消息循环的原理

01 消息循环的阻塞

  • GetMessage - 从系统获取消息,将消息从系统中移除,阻塞函数。当系统无消息时,会等候下一条消息(打死也不返回)

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

    PeekMessageA(
        _Out_ LPMSG lpMsg,         //message information
        _In_opt_ HWND hWnd,             //handle to window
        _In_ UINT wMsgFilterMin,         //first message
        _In_ UINT wMsgFilterMax,        //last message
        _In_ UINT wRemoveMsg);     //移除标识 PM_REMOVE  PM_NOREMOVE
    
    
  • 一个经常阻塞的消息循环

    while(GetMessage(&nMsg,NULL,0,0))
    {
        TranslateMessage(&nMsg);
        DispatchMessage(&nMsg);
    }
    

    当GetMessage阻塞了消息循环中,整个进程进入休眠状态,但是GetMessage占用着大量内存,效率极低,而且一旦遇到WM_QUIT直接结束程序

  • 改良后的消息循环

    while(1)
    {
        if(PeekMessage(&nMsg,NULL,0,0,PM_NOREMOVE))//有消息true
        {
            if(GetMessage(&nMsg,NULL,0,0)){
                TranslateMessage(&nMsg);
                DispatchMessage(&nMsg);
            }else{
                return 0;// or break;
            }
        }
        else
        {
            //空闲处理
            WriteConsole(g_hOutput,"我很闲",strlen("我很闲"),NULL,NULL);
        }
    }
    

WriteConsole(标准输出句柄,输出对象,对象长度,NULL,NULL)

2022/4/17

01键盘消息

  • 键盘消息分类
    • WM_KEYDOWN 按键被按下时产生
    • WM_KEYUP 按键松开时产生
    • WM_SYSKEYDOWN 系统键按下时产生
    • WM_SYSKEYUP 系统键松开时产生
    • 附带信息
      • wParam 按键的Virtual Key 哪个按键?
      • lParam 按键的参数,例如按下次数

02字符消息

  • TranslateMessage函数在翻译WM_KEYDOWN消息时,对于可见字符可以产生WM_CHAR,不可见字符无此消息
  • 对于WM_CHAR,附带信息如下
    • lParam:按键的相关参数
    • wParam:输入的字符相关ASCII码值(说明键盘消息被翻译时确定到底是哪个键)

03鼠标消息

  • 鼠标消息分类

    • 基本鼠标消息
      • WM_LBUTTONDOWN 左键按下
      • WM_LBUTTONUP 左键抬起
      • WM_RBUTTONDOWN 右键按下
      • WM_RBUTTONUP 右键抬起
      • WM_MOUSEMOVE 鼠标移动消息
    • 双击消息
      • WM_LBUTTONDBLCLK 鼠标左键双击
      • WM_RBUTTONDBLCLK 鼠标右键双击
    • 滚轮消息
      • WM_MOUSEWHEEL 鼠标滚轮滚动
  • 鼠标基本消息通性

    • 附带信息

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

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

      ​ LOWORD(wParam) x

      ​ HIWORD(lParam) y

    • 一般情况下鼠标消息成对出现,在鼠标移动过程中,会根据移动速度产生WM_MOUSEMOVE消息

  • 鼠标双击消息

    • 附带信息

      wParam:同其他消息

      lParam:同其他消息

    • 消息产生顺序,以左键双击为例

      WM_LBUTTONDOWN

      WM_LBUTTONUP

      WM_LBUTTONDBLCLK

      WM_LBUTTONUP

      使用时需要在注册窗口类的时候添加CS_DBLCLKS风格

  • 鼠标滚轮消息

    • 附带信息

      wParam:同其他消息,但是

      • LOWORD:其他按键的状态
      • HIWORD:滚轮的偏移量,通过正负值表示滚动方向 正:向前滚动 负:向后滚动 一般为±120

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

      • LOWORD:X坐标
      • HIWORD:Y坐标
    • 使用:

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

04定时器消息

为了满足周期性任务实现

  • 产生时间:

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

  • 附带信息:

    wParam:定时器ID

    lParam:定时器处理函数的指针

  • 创建定时器

    UINT SetTimer (
    HWND hWnd,//传给哪一个窗口的处理函数?
    UINT_PTR nIDEvent,//定时器ID
    UINT UElapse,//时间间隔
    TIMERPROC lpTimerFunc//定时器处理函数指针
    );//创建函数,返回非0
        
        
    
  • 关闭定时器

    BOOL KillTimer(
    HWND hWnd,
    UINT_PTR nIDEvent);
    

05 菜单消息

  • 菜单分类

    • 窗口的顶层菜单
    • 弹出式菜单(右键、下拉)
    • 系统菜单
    • HMENU类型表示菜单,ID表示菜单项
  • 资源相关

    • 详见资源文件.rc编译过程
  • 菜单资源的使用

    • 添加菜单资源: 在资源文件中添加Menu项

    • 加载菜单资源

      1、注册窗口类时设置菜单 将MENU的ID强转为字符串类型,包含头文件resourse.h

      2、创建窗口传参设置菜单

      RegisterClass(&WndClass);
      HMENU hMenu = LoadMenu(hIns,(char*)IDR_MENU1);
      HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,hMenu,hIns,NULL);
      

      3、在主窗口WM_CREATE消息中利用SetMenu函数设置菜单

      加载菜单资源

      HMENU LoadMenu(
      HINSTANCE hInstance,//进程句柄
      LPCTSTR lpMenuName //菜单名称或者资源ID
      );
      

    06 命令消息的处理

    • 附带信息
      • wParam
        • HIWORD 对于菜单为0
        • LOWORD 菜单项的ID
      • lParam 对于菜单为0

2022/4/18

06 图标资源,光标资源

图标资源

  • 添加资源

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

  • 加载

    HICON LoadIcon(
    HINSTANCE hInstance,//应用程序实例句柄
    LPCTSTR lpIconName//字符串或资源的命名ID
    );//成功返回HICON句柄
    
  • 设置

    注册窗口类

光标资源

  • 添加光标资源

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

  • 加载资源

    HCURSOR LoadCursor(
    HINSTANCE hInstance
    LPCTSTR lpCursorName
    );
    
  • 设置资源

    在注册窗口时,设置光标

    使用SetCursor设置光标

  • 移动光标时WM_SETCURSOR消息参数

    wParam-当前使用的光标句柄

    lParam

    LOWORD 当前区域的代码 HTCLIENT/HTCAPTION

    HIWORD 当前鼠标消息ID

    这个消息配合SetCursor函数可以随时改变光标

    HCURSOR SetCursor(
    HCURSOR hCursor
    );
    

字符串资源

  • 添加字符串资源

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

  • 字符串资源的使用

    int LoadString(
    HINSTANCE hInstance,//进程句柄
    UINT uID,//字符串ID
    LPTSTR lpBuffer,//存放字符串的缓冲区
    int nBufferMax)//字符串缓冲区长度
    

加速键资源(有空再学)

07 绘图编程

所有的绘图操作,都是基于绘图消息WM_PAINT下处理

首先初始化DC:

PAINTSTRUCT ps = {0};
HDC hdc = BeginPaint(hWnd,&ps);
for(int i = 0;i<256;i++){
    for(int j = 0;j<256;j++)
    {
        SetPixel(hdc,i,j,RGB(i,j,0));//渐变之色
    }
}

  • 绘图基础

    • 绘图设备DC,

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

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

    • 颜色 RGB

      R- 0~255

      G- 0~255

      B- 0~255

      每一个点颜色是3个字节24位保存 0-1^24-1

      16位:5,5,6

      32位:8,8,8,8绘图或透明度

    • 颜色的使用

      COLORREF - 实际DWORD

      例如:COLORREF nColor = 0;

    • 赋值使用RGB宏

      例如:nColor = RGB(0,0,255);

    • 获取RGB值

      GetRValue/GetGValue/GetBValue

      例如:BYTE nRed = GetRValue(nColor)

  • 基本图形绘制

    • SetPixel 设置指定点的颜色

      COLORREF SetPixel(
      HDC hdc,//DC句柄
      int X,//X坐标
      int Y,//Y坐标
      COLORREF crColor//设置的颜色
      );//返回原来的颜色
      
    • 线的使用(直线,弧线)

      MoveToEx - 指明窗口当前点

      MoveToEx(hdc,100,100,NULL);//指明窗口当前点为(100,100),且不需要函数返回前一个点
      LineTo(hdc,300,300);//从当前点(100,100)到(300,300)的一条线,自动更新窗口当前点
      

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

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

    • 封闭图形:能够用画刷填充的图形

      Rectang / Ellipse

  • GDI绘图对象————

    画笔

    • 画笔的作用

    线的颜色,线型,线粗

    HPEN - 画笔句柄

    • 画笔的使用

    ​ 1 创建画笔

    HPEN CreatePen(
    int fnPenStyle,//画笔的样式
    int nWidth,//画笔的粗细
    COLORREF crColor//画笔的颜色
    );//创建成功返回句柄
    

    PS_SOILD - 实心线,可以支持多个像素宽其他线性只能是一个像素宽

    2 将画笔应用到DC中

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

    3 绘图

    4 取出DC中的画笔,将原来的画笔,使用SelectObject函数,放入到设备DC中,就会将我们创建的画笔取出

    5 释放画笔

    BOOL DeleObject(
    HGDIOBJ hObject  //GDI绘图对象句柄,画笔句柄
    );
    

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

    • GDI绘图对象 - 画刷 - 在封闭图形绘制的时候生效 注意:封闭的不一定是封闭图形

      • 画刷相关

        画刷 - 封闭图形的填充的颜色、图案

        HBRUSH - 画刷句柄

      • 画刷的使用

        1-创建画刷

        ​ CreateSolidBrush - 创建实心画刷

        ​ CreateHatchBrush - 创建纹理画刷

        2-将画刷应用到DC中

        ​ SelectObject

        3-绘图

        4-将画刷从DC中取出

        ​ SelectObject

        5-删除画刷

      • 其他

        ​ 可以使用GetStockObject函数获取系统维护的画刷、画笔等。

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

        ​ GetStockObject返回的画刷不需要DeleteObject

      • GDI绘图对象——

        位图

        • 位图相关

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

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

          HBITMAP - 位图句柄

        • 位图的使用

          1- 在资源中添加位图资源(无需代码)

          2- 从资源中加载位图LoadingBitmap

          3- 创建一个与当前DC相匹配的DC(内存DC)

          HDC CreateCompatibleDC(

          HDC hdc //当前DC句柄,可以为NULL(使用屏幕DC)

          );//返回创建好的DC句柄

          void OnPaintBmp(HWND hWnd)
          {
          	PAINTSTRUCT ps = { 0 };
          	HDC hdc = BeginPaint(hWnd, &ps);
          	//添加位图资源,已添加
          	//从进程中加载位图
          	HBITMAP hBitMap = LoadBitmap(TmpIns, (CHAR*)IDB_BITMAP1);
          	//创建一个内存DC,并构建一个虚拟区域,内存DC在虚拟区域绘图
          	HDC hMemDC = CreateCompatibleDC(hdc);
          	//将位图数据送给内存DC,内存DC在虚拟区域绘制bmp
          	HGDIOBJ hPreBitMap = SelectObject(hMemDC, hBitMap);
          	//将在虚拟区域绘制好的图像在内存中成像
          	BitBlt(hdc, 100, 100, 48, 48,  hMemDC,        0,0,          SRCCOPY);
          	//   绘图者|  位置  |开辟区域|原绘图者|在虚拟区域的何处拷贝|拷贝(原样成像)
          	SelectObject(hMemDC, hPreBitMap);
          	//归还原有Bmp
          	DeleteObject(hBitMap);
          	DeleteDC(hMemDC);
          }
          
        • 位图绘制

          ​ 缩放成像

          BOOL StretchBlt(
          HDC hdcDest,//绘图者
          int nXOriginDest,//位置
          int nYOriginDest,//位置
          int nWidthDest,//宽
          int nHeightDest,//高
          HDC hdcSrc,//原绘图者
          int nXOriginSrc,//拷贝区域位置
          int nYOriginSrc,//拷贝区域位置
          int nWidthSrc,//拷贝宽度(相比BitBlt多出的两个参数之一)
          int nHeightSrc,//拷贝高度(相比BitBlt多出的两个参数之一)
          DWORD dwRop//拷贝模式
          )

      2022/4/20

      文本绘制

      • 文字的绘制

        TextOut - 将文字绘制在指定坐标位置

        int DrawText(
        HDC hDC,   //DC句柄
        LPCTSTR lpString, //字符串
        int nCount, //字符数量
        LPRECT lpRect //绘制文字的矩形框
        UINT uFormat   //绘制的方式DT_...
        );
        
      • 文字的颜色和背景

        文字颜色:SetTextColor

        文字背景色:SetBkColor

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

        ps:在绘制文本之前先行通知DC

      字体宏

      • 字体相关

        Windows常用的字体为TrueType格式的字体文件

        ​ 字体名 - 标识字体类型

        ​ HFONT - 字体句柄

      • 字体的使用

        1 创建字体

        HFONT CreateFont(
        int nHeight,//字体高度
        int nWidth,//字体宽度
        int nEscapement,//字符串倾斜角度
        int nOrientation,//字符旋转角度
        int fnWeight,//字体的粗细
        DWORD fdwItalic,//斜体 1 0
        DWORD fdwUnderline,//字符下划线 1 0
        DWORD fdwStrikeOut,//删除线 1 0
        DWORD fdwCharSet,//字符集 
        DWORD fdwOutputPrecision,//输出精度 0
        DWORD fdwClipPrecision,//剪切精度 0
        DWORD fdwQuality,//输出质量 0
        DWORD fdwPitchAndFamily,//匹配字体 ...
        LPCTSTR lpszFace//字体名称 ..
            
        );
        

        2 应用字体到DC

        SelectObject

        3 绘制文字

        DrawText/TextOut

        4 取出字体

        SelectObject

        5 删除字体

        DeleteObject

2022/4/21

win内核:对话框

  • 引入

    之前的窗口程序,使用我们自己编写的窗口处理函数处理特定(我们想要处理)的消息,返回调用默认消息处理函数DefWinProc,即 自定义函数调用缺省函数

    而对话框消息处理的过程恰好相反,即 缺省函数调用自定义函数

    原因:

    ​ 注册窗口类时,需要基于当前进程的实例句柄来创建,普通窗口是由用户创建,即基于用户的进程,而对话框类是由操作系统创建,即基于操作系统的进程。对于窗口处理函数,基于哪个进程就调用哪个创建的,因此它先调用操作系统内核里的缺省处理函数

01 对话框原理

  • 对话框分类

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

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

  • 对话框基本使用

    1 对话框窗口处理函数

    2 注册窗口类(不使用)

    3 创建对话框

    4 对话框的关闭

  • 对话框原理

    • 对话框窗口处理函数(非真正的对话框窗口处理函数)

      INT CALLBACK DialogProc(
      	HWND hwndDlg,//窗口句柄
      	UINT uMsg,//消息ID
      	WPARAM wParam,//消息参数
      	LPARAM lParam,//消息参数
      );
      if(true)
          return 0;
      if(false)
          继续执行
       返回TRUE - 缺省处理函数不需要处理
       返回FALSE - 交给缺省处理函数处理
       不需要单独调用缺省对话框窗口处理函数
      
  • 模式对话框

    • 创建对话框

      INT DialogBox(
      HINSTANCE hInstance,//应用程序实例句柄
      LPCTSTR lpTemplate,//对话框资源ID
      HWND hWndParent,//对话框父窗口
      DLGPROC lpDialogFunc//自定义函数
      );//DialpgBox是一个阻塞函数,只有当对话框关闭后,才会返回,继续执行后续代码
      //返回值是通过E'n'd'Dialog
      
    • 对话框的关闭

      BOOL EndDialog(
      HWND hDlg,//关闭的对话框窗口句柄,销毁对话框
      INT_PTR nResult//关闭的返回值,解除DialogBox的阻塞
      );//关闭模式对话框,只能使用EndDialog,不能使用DestroyWindow等函数。
      //nResult是DialogBox函数退出时的返回值
      
    • 对话框的消息

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

注意消息发送到哪里,是对话框还是主窗口?

  • 无模式对话框

    • 创建对话框

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

      唯一区别:非阻塞函数,创建成功返回窗口句柄,需要使用ShowWindow函数显示对话框

    • 对话框的关闭

      关闭时使用DestroyWindow销毁窗口,不能使用EndDialog关闭对话框(它没有阻塞程序)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值