第一个Windows
窗口程序
创建一个窗口程序的空项目,或者创建一个控制台空项目,调整下面的选项:
Windows
是一个消息(事件)驱动的操作系统:用户操作、系统变化、其他程序的通知都属于事件,抽象成一个个消息,程序接收到消息后,就要处理消息,从而程序具有一定的功能
消息是发送给窗口的,但处理消息的位置是消息的回调函数,根据发送者是否等待消息处理完毕可分为队列消息和非队列消息
消息本身不区分是否是队列的,是否是队列消息由函数决定
队列消息 PostMessage
:每个窗口程序都有一个消息队列,系统首先得到用户的操作,系统会区分这个操作在哪一个程序,然后给那个程序的窗口发消息,但发送时是发到消息队列中,因为操作系统不会等待消息被处理完毕,这种机制叫异步,系统只是发送通知,何时处理是不管的
非队列消息 SendMessage
:发送消息不走消息队列也是可以的,相当于直接调用目标窗口的回调函数,函数结束后,发送者才能继续执行代码,这种机制叫同步,就要等待
队列消息还是非队列消息是由发送者决定的,与消息类型无关
接收到消息后要做什么写的是逻辑代码。
获取窗口句柄:
1 | FindWindow(窗口类名, 窗口名);//得到窗口句柄 |
代码:
1 |
|
系统需要知道这个类是哪个程序注册的,窗口是哪个程序创建的,数值是主模块的加载地址
句柄从概念上来说是某个对象的标识,系统可以根据标识识别出它是哪个对象,类似于 C++
对象的 this
指针,API
相当于操作系统对外的接口。句柄从数值上来说,HINSTANCE
是模块的加载基址,窗口句柄就不是什么加载基址,进程、线程句柄是一个数组的序号
Windows
下没有比较好用的输出数字的函数,通常需要自己转换,所有的字符串相关的 API
函数,都分成两个版本:A
版和 W
版
1 |
|
一般我们都使用 T
版函数
1 |
|
ascii
和 unicoed
间的转换
1 | // 宽字符转换为多字符(Unicode --> ASCII) |
Windows
下输出调试信息
1 | bool _trace(const TCHAR* format, ...) {//变参函数 |
我们也可自己封装带格式控制符的 MessageBox
1 | bool _MyMessageBox(const TCHAR* format, ...) {//变参函数 |
关于 Windows
程序的错误码
1 | int WINAPI _tWinMain( |
在调试时,快速监视到错误码的方法:
制作一个能够解析错误码的工具,能够提供出错的具体位置
1 |
|
第一个窗口程序:
- 设计一个窗口类
- 注册窗口:
RegisterClass
- 创建窗口:
CreateWindow
函数,得到一个窗口句柄 - 更新显示窗口:
ShowWindow
UpdateWindow
- 编写一个消息循环
- 编写一个消息处理函数(窗口回调函数)
这段代码是固定形式,是微软规定好的流程
只有窗口才能接受消息,窗口的消息需要通过 GetMessage
从消息队列中获取到
大部分事情都是操作系统帮你完成的,比如说往消息队列添加消息,从消息队列中得到消息,删除已得到的消息
回调函数不是程序员调用的函数,程序员提供一个函数给操作系统,操作系统在合适的时机去调用所提供的函数,因为只有操作系统才知道什么时候得到了消息,只有程序员才知道程序怎么处理
创建窗口先要注册窗口类:需要先有一个模板,在创建窗口时根据模板去创建,模板是窗口共有的一些特性,放在窗口类里
更新显示窗口:刚创建的窗口是隐藏起来的,需要用 ShowWindow
显示出来,UpdateWindow
能让窗口产生一次自绘
GetMessage
的作用就是从消息队列中获取消息
DispatchMessage
的作用就是调用窗口相对应的回调函数
msg
结构体里的数值不应该去修改它,因为是系统提供的,处理消息的操作应该在回调函数里
GetMessage
函数里4个参数的作用:
1 | BOOL WINAPI GetMessageW( |
TranslateMessage
会将 WM_KEYDOWN
翻译为 WM_CHAR
消息发送一遍,按下按键会自动产生 WM_KEYDOWN
,但并非所有的按键都是可见字符,如回车、F1~Fn
等。 WM_CHAR
会告诉你哪些可见字符被按下,通常来说处理文本时使用 WM_CHAR
,所有按键都处理时,使用 WM_KEYDOWN
1 |
|
常见的消息处理:
1 | //WM_CLOSE 窗口关闭的消息 |
1 | LRESULT CALLBACK WndProc( |
关于PostMessage,SendMessage,自定义消息
1 |
|
1 |
|