windows/win32编程
Last Update:
本文参考:https://www.bilibili.com/video/BV1pB4y187VB/?spm_id_from=333.337.search-card.all.click
(43条消息) 【Win32】初识Win32编程_半生瓜のblog的博客-CSDN博客
Windows编程
应用程序分类
控制台程序Console
DOS程序,本身没有窗口,通过Windows DOS窗口执行。(DOS是操作系统预留的)
窗口程序
拥有自己的窗口,可以与用户交互。
库程序
存放代码、数据的程序、执行文件可以从中取出代码执行和获取数据
静态库程序:扩展名LIB,在编译链接程序时,将代码放入到执行文件中。
动态库程序:扩展名DLL,在执行文件时从中获取代码 。
静态库中的代码是直接嵌入到你的项目中,而动态库中的内容是通过地址来找到。
静态库程序无法执行,也就是说它最终生成的文件无法进入内存。
动态库程序有入口函数,可以执行。但是它不能独立运行。谁调动态库里面的东西,它就依附于谁。
应用程序对比
入口函数
- 控制台程序-main
- 窗口程序-WinMain
- 动态库程序-DllMain
- 静态库程序-无入口函数
文件存在方式
- 控制台程序、窗口程序-EXE文件
- 动态库程序-DLL文件
- 静态库程序-LIB文件
1 |
|
暂时可以将句柄理解成,句柄是用来找到内存的东西,但绝对不是指针。
1 |
|
窗口创建过程
- 定义WinMain函数
- 定义窗口的处理函数(自定义,消息处理)
- 注册窗口类(向操作系统中写入一些数据)
- 创建窗口(内存中创建窗口)
- 显示窗口(绘制窗口的图像)
- 消息循环(获取/翻译/派发消息)
- 消息处理
第一个windows窗口
1 |
|
窗口有无与进程退不退没有关系。
注册窗口类
窗口类的概念
窗口类是包含了窗口的各种参数信息的数据结构。
每个窗口都具有窗口类,基于窗口类创建窗口。
每个窗口类都具有一个名称,使用前必须注册到系统。
在操作系统内核里存着就叫窗口类,在程序里存着就叫窗口类。
窗口类的分类:
系统窗口类
系统已经定义好的窗口类,所有应用程序都可以直接使用。
不需要注册,直接使用窗口类即可。系统已经注册好了。
例如:按钮-BUTTON,编辑框-EDIT
应用程序全局窗口类
由用户自己定义,当前应用程序所有模块都可以使用。
应用程序局部窗口类
由用户自己定义,当前应用程序中本模块可以直接使用。
全局及局部窗口类 :
注册窗口类的函数
1 |
|
style窗口类风格
应用程序全局窗口类的注册,需要在窗口类的风格中添加CS_GLOBALCLASS。
应用程序局部类窗口类注册,无需添加如上风格。
不建议使用全局窗口类——因为局部窗口类能完成全局窗口类的功能,并且全局窗口类可能会产生冗余。
1 |
|
窗口创建
窗口创建
CreateWindow / CreateWindowEx
CreateWindow内部是如何实现的
系统(CreateWindows函数内部)根据传入的窗口类名称,在应用程序局部窗口类中查找,如果找到执行2 ,没找到执行3。
比较局部窗口与创建窗口时传入的HINSTANCE变量。如果有发现相等。创建和注册类在同一模块,创建窗口返回。如果不相等,继续执行3。
在应用程序全局窗口类,如果找到,执行4, 没找到执行5。
使用找到的窗口类信息,创建窗口返回。
在系统窗口类中查找,如果找到创建窗口返回,否则创建窗口失败。
1 |
|
子窗口创建过程
创建时要设置父窗口句柄
创建风格要增加WS_CHILD | WS_VISBLE
(根据注册的窗口类,来创建多个窗口。)
1 |
|
消息基础
消息的概念和作用
消息组成(windows平台下)
- 窗口句柄
- 消息ID
- 消息的两个参数(两个附带信息)
- 消息产生的时间
- 消息产生时的鼠标位置
消息的作用
当系统通知窗口工作时,就采用消息的方式(DispatchMessage)派发给(调用)窗口的窗口处理函数(将MSG的前四个信息传递给消息处理函数)。
每一个窗口都有窗口处理函数
MSG结构体接收消息
结构体定义如下:
1 |
|
DispatchMessage如何找到窗口处理函数
1 |
|
窗口处理函数
每个窗口都必需有窗口处理函数,只要基于窗口类创建窗口,就肯定要有个窗口处理函数。
窗口处理依照如下结构定义:
1 |
|
当系统通知窗口时,(DispatchMessage)会调用窗口处理函数,同时将消息ID和消息参数传递给窗口处理函数。
在窗口处理函数中,不处理的消息,使用缺省窗口处理函数。
例如:DefWindowProc(可以给各种消息做默认处理)。
消息循环中的相关函数(浅谈)
GetMessage-到系统的某个地方抓本进程的消息
函数原型如下:
1 |
|
其中后三个参数可以限制抓取消息的范围,如果设置为NULL,0,0那其实就是没有进行限制,只要是本进程的消息我都把它抓过来。
GetMessage的返回值
消息WM_QUIT会使GetMessage返回0,从而中终止消息接收。
PostQuitMessage(0);会在进程中扔出WM_QUIT这个消息,get后从而使得消息循环终止。
TranslateMessage-翻译消息——它可不是什么消息都翻译。
将按键(可见字符按键,a~z)消息翻译成字符消息。
所以进入到它的内部, 它所做的第一件事就是检查这个消息是否合法,是否是它要翻译的消息类型。
如果不是按键类型消息,不做任何处理,继续执行。
函数原型如下:
1 |
|
DispatchMessage-派发消息(调用对应窗口的消息处理函数)
函数原型如下
1 |
|
常见消息
WM_DESTORY
产生时间:窗口被销毁时产生
附带信息:wParam:为0,lParam:为0
一般用法:常用于在窗口被销毁前,做相应的善后处理,例如资源、内存等(该回收回收,该释放释放。)。
WM_SYSCOMMAND
产生时间:当点击窗口最大化,最小化,关闭等。
附带信息:
wParam:具体点击的位置,例如关闭SC_CLOSE等,
lParam:鼠标光标的位置(这个不重要,我们只需要知道点没点就行,具体在哪个位置其实无所谓(具体情况具体使用)),LOWORD(lParam);水平位置,HIWORD(lParam);垂直位置。(高两字节传纵坐标,低两字节传横坐标。)
一般用法:常用在窗口关闭时,提示用户处理。
WM_CREATE
产生时间:在窗口创建成功但还没显示时。
附带信息:
wParam:为0
lParam:为CREATESTRUCT类型的指针(强转成这个类型再用),通过这个指针可以获取CreatWindowEx中全部12个参数的信息。
一般用法:常用于初始化窗口函数、资源等等,包括创建子窗口等。
WM_SIZE
产生时间:在窗口的大小发生变化后。
附带信息:
wParam:窗口大小变化的原因。
lParam:窗口变化后的大小
LOWORD(lParam)变化后的宽度
HIWORD(lParam)变化后的高度
一般用法:常用于窗口大小发生变化后,调整窗口内各个部分的布局。
WM_QUIT
产生时间:程序员发送。
附带信息:
wPram:PostQuitmessage函数传递的参数。
lParam:0。
一般用法:用于结束消息循环,当GetMessage收到这个消息后,会返回FALSE,结束while处理,退出消息循环。
这个消息不用我们去处理,进不去我们定义的窗口处理函数,GetMessage()返回了0,无法进入循环获取消息。
消息循环的原理
消息循环的阻塞
GetMessage-从系统获取消息,将消息从系统中移除,阻塞函数。当系统无消息时,会等候下一条消息。
对人来说消息是一直存在的,但是对于CPU来说(速度接近光速),消息不是经常有的,所以会经常发生阻塞。这样程序的效率就不高,从而引出下面这个函数。
PeekMessage-以查看的方式从系统中获取消息,可以不将消息从系统出移除,非阻塞函数。当系统无消息时,返回FALSE,继续执行后续代码。
函数原型如下:
(前四个参数同GetMessage)
最后一个参数是,是否赋予它抓取消息的能力,一般是不给它的,也就是填写
1 |
|
也就是说,更好的流程是,先派PeekMessage去侦查是否有消息,有就告诉GetMessage让它来处 理。没有就不要派Get去了,因为它会一直在那里等着消息的出现。
例如:
1 |
|
发送消息
Windows平台上的消息,都是它们两个造出来的。
SendMessage-发送消息,会等候消息处理的结果。
PostMessage-投递消息,消息发出后立刻返回,不等候消息执行结果。
函数原型如下:
1 |
|
这四个参数就是一个消息的前四个参数,剩下的两个参数函数内部以某种手段自加来获取。
消息分类
系统消息-ID范围0~0x03FF
由系统定义好的消息,可以在程序中直接使用。
程序员只负责一头,要么发送不用处理,要么处理不用发送。
用户自定义的消息-ID范围0x0400(WM_USER) - 0x7FFF(31743)
由用户自己定义,满足用户自己的需求。由用户自己发出消息,并响应处理。
由程序员,自己定制,自己发送,自己处理。
自定义消息宏:WM_USER(叫什么都行)
例如:
定义消息名称
1 |
|
发送,在哪发都可以,附加消息,你自己的,附加什么都行。
PostMessage/SendMessage(hWnd, WM_MYMESSAGE, 1, 2);
消息队列
消息队列的概念
- 消息队列是用于存放消息的队列。
- 消息在队列中先进先出。
- 所有窗口都具有消息队列。
- 程序(GetMessage())可以从队列中获取消息。
静态库
Windows上静态库和Linux上的静态库在原理上没有任何区别,都是封装一堆东西等着别人去掉。
静态库的特点
运行不存在。
没有如何,不能执行,生成的文件无法形成静态影像,无法进内存。
静态库源码被链接到调用程序中。
目标程序的归档。
C语言静态库
C静态库的创建
创建一个静态库程序。
添加库程序,源文件使用C文件。
C静态库的使用
库路径设置:可以使用#pragma关键字设置
#pragma comment(lib,“…/lib/clib.lib”)
C++静态库
C++静态库的创建
创建一个静态库项目
添加库程序,源文件使用CPP文件。
C++静态库的使用
库路径设置:可以使用pragma关键字设置
#pragma comment(lib,“…/xx/xxx.lib”)
示例:
1 |
|