控制台程序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 // 窗口的显示方式 最大化显示,最小化显示,原样显示
);
句柄可以找到进程对应的内存 --> 句柄是表的索引
int MessageBox(
HWND hWnd, // 父窗口句柄
LPCTSTR lpText, // 显示在消息框中的文字
LPCTSTR lpCaption, // 显示在标题栏中的文字
UINT uType // 消息框中的按钮,图标显示类型
); // 返回点击的按钮ID
Hxxx --> 大概率是句柄
后缀: .rc
100 ICON small.ico
100 数字标识 ICON 图标资源 small.ico 文件名称
编译后称为 .res文件
.obj & .res 统称为目标文件
#include
int WinMain(HINSTANCE hIns,HINSTANCE hPreIns,LPSTR lpCmdLine, int nCmdShow){
MessageBox(NULL,"hello world","Information",MB_YESNO);
return 0;
}
#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;
}
ASC
7位代表一个字符
ASCII
8位一个字符
DBCS
double byte
单双字节混合编码
UNICODE
wchar_t 每个字符占2个字节
char每个字符占1个字节
wchar_t 实际上是 unsigned short 类型,定义时,需要在前面增加"L",通知编译器按照双字节编译字符串,采用UNICODE编译
需要使用支持wchar_t函数操作宽字节字符串
wchar_t * pwszText = L"Hello wchar";
wprintf(L"%s\n",pwszText);
demo
#include
#include
int main(){
wchar_t * pszText = L"Hello wchar";
int len = wcslen(pszText); // 返回字符个数
wprintf(L"%d,%s",len,pszText);
return 0;
}
TCHAR 数据类型
定义在WINNT.h中:
#ifdef UNICODE
typedef wchar_t TCHAR
#define _TEXT(quote) L##quote
#else
typedef char TCHAR
#define _TEXT(quote) quote
#endif
## 是拼接的作用
TCHAR * pszText = _TEXT("Hello,wkk");
定义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
项目属性设置为UNICODE 字符集
系统会自动增加UNICODE宏的定义
如果是TCHAR * 类型增加了UNICODE宏,字符串字面量前面要加L
系统调用函数的参数类型
LPSTR === char *
LPCSTR === const char *
LPWSTR === wchar_t *
LPCWSTR === const wchar_t *
LPTSTR === TCHAR *
LPCTSTR === const TCHAR*
概念
分类
系统窗口类
系统已经定义好的窗口类,所有应用程序都可以直接使用
应用程序全局窗口类
由用户自己定义,当前应用程序所有模块都可以使用
应用程序局部窗口类
由用户自己定义,当前应用程序中本模块可以使用
不需要用户注册,直接使用即可,系统已经注册好了
#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;
}
ATOM RegisterClass(
CONST WNDCLASS * lpWndClass // 窗口类的数据
);
// 注册成功,返回一个数字标识 非0
// 失败,返回0
// 注册窗口类的结构体
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;
style 窗口类风格
应用程序全局窗口类的注册,需要在窗口类的风格中增加CS_GLOBALCLASS.
WNDCLASS wce = {0};
wce.style = ... | CS_GLOBALCLASS;
应用程序局部窗口类,在注册窗口类时,不添加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
); // 创建成功返回窗口句柄
窗口基本风格
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 垂直滚动条
CreateWindowEx 内部实现
匹配查找窗口类
if(找到窗口类){
申请一大块内存,将窗口的数据信息存入这块内存
return 内存的句柄
}else{
return NULL;
}
#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;
}
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
);
ShowWindow(hWnd,SW_SHOW);
// 根据窗口句柄,找到内存,绘制窗口
UpdateWindow(hWnd);
消息组成(windows下)
typedef struct tagMSG{
HWND hwnd;
UINT message;
WPARAM wparam;
LPARAM lParam;
DWORD time;
POINT pt;
}MSG;
消息的作用
当系统通知窗口工作时,采用消息的方法派发给窗口的处理函数
每个窗口都有窗口处理函数
MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0)){
TranslateMessage(&nMsg); // 翻译消息
DispatchMessage(&nMsg) ; // 派发消息,交给窗口处理函数来处理
}
DispatchMessage函数
DispatchMessage(&nMsg){
nMsg.hwnd --> 保存窗口数据的内存 --> WndProc
WndProc(...){
// 处理消息
}
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam);
/**
* hWnd
* msgID
* wParam
* lParam
*/
消息的最后两个参数 消息的时间,消息时的鼠标位置没有传递
每个窗口都必须有窗口处理函数
LRESULT CALLBACK WindowProc(
HWND hwnd,// 窗口句柄
UINT uMsg, // 消息ID
WPARAM wParam, // 消息函数
LPARAM lParam // 消息参数
);
当系统通知窗口时,会调用窗口处理函数,同时将消息ID和消息参数传递给窗口处理函数。 在窗口处理函数中,不处理的消息,使用缺省窗口处理函数,DefWindowProc
DefWindowProc 给各种消息做默认处理
GetMessage - 获取本进程的消息
BOOL GetMessage(
LPMSG lpMsg,// 存放获取到的消息BUFF
HWND hWnd, // 窗口句柄 --> 抓指明的句柄的消息 NULL--> 抓所有的
// 消息的范围 (0,0)本进程的消息都抓
UINT wMsgFilterMin, // 获取消息的最小ID
UINT wMsgFilterMax // 获取消息的最大ID
);
lpMsg 当获取到消息后,将消息的参数存放到MSG结构中
hWnd 获取到hWnd所指定窗口的消息
wMsgFilterMin 和 wMsgFilterMax 只能获取到由它们指定的消息范围内的消息,如果都为0,表示没有范围
返回值:
TranslateMessage 翻译消息
将按键消息,翻译成字符消息 --> 可见字符按键
BOOL TranslateMessage(
CONST MSG*lpMsg // 要翻译的消息地址
);
检查消息是否是按键的消息,如果不是按键消息,不做任何处理,继续执行
DispatchMessage 派发消息
LRESULT DispatchMessage(
CONST MSG * lpmsg // 要派发的消息
);
将消息派发到该消息所属窗口的窗口处理函数上
产生时间,附带的两个参数,一般用来做什么
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
);
while(1){
if(PeekMessage(&nMsg,NULL,0,0,PM_NOREMOVE)){
// 有消息
if(GetMessage(&nMsg,NULL,0,0)){
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);
}else{
break;
}
}else{
// 无消息
// 空闲处理
}
}
SendMessage 发送消息,会等待消息处理的结果 ( 等到消息处理完 , 会阻塞)
PostMessage 投递消息,消息发出后立刻返回,不等待消息执行结果
将消息放到系统消息队列中
BOOL SendMessage / PostMessage (
HWND hWnd , // 消息发送的目的窗口
UINT Msg, // 消息ID
WPARAM wParam , // 消息参数
LPARAM lParam // 消息参数
);
系统消息 ID范围 0-0x03ff (0-1024)
系统定义好的消息,可以在程序中直接使用
用户自定义消息 ID范围 0x0400 - 0x7fff ( 一共31743)
用户自定义,满足用户自己的需求,由用户自己发出消息,并相应处理
自定义消息宏: WM_USER
消息和消息队列的关系
根据消息和消息队列之间使用关系,消息分为两类
队列消息
消息的发送和获取,都是通过消息队列完成
非队列消息
消息的发送和获取,是直接调用消息的窗口处理完成
队列消息
消息发送后,首先放入队列,然后通过消息循环,从队列中获取
GetMessage 从消息队列获取消息
PostMessage 将消息投递到消息队列
常见队列消息: WM_PAINT, 键盘,鼠标,定时器
WM_QUIT 必须进队列,不然GetMessage的循环不会结束
非队列消息
消息发送时,首先查找消息接收窗口的窗口处理函数,直接调用函数,完成消息
SendMessage 直接将消息发送给窗口的处理函数,并等候处理结果
常见消息: WM_CREATE, WM_SIZE等
WM_CREATE 必须不能进队列 --> 此时窗口还没有显示
在程序消息队列中查找信息,如果队列有消息,检查消息是否满足指定条件( HWND, ID范围 ), 不满足条件就不会取出消息,否则取出返回
如果程序队列没有消息,向系统消息队列获取数据本程序的消息。如果系统队列的当前消息属于本程序,系统会将消息转发到程序消息队列中
如果系统消息队列也没有消息,检查当前进程的所有窗口的需要重新绘制的区域,如果发现有需要绘制的区域,产生WM_PAINt 消息,取得消息返回处理
如果没有重新绘制区域,检查定时器如果有到的定时器,产生WM_TIMER返回处理执行
如果没有到时的定时器,整理程序的资源,内存等等
GetMessage 会继续等候下一条消息。PeekMessage 返回 FALSE, 交出程序的控制权
GetMessage 如果获取到是WM_QUIT函数返回FALSE
// paint demo
// 窗口无效区域: 需要重新绘制的区域
BOOL InvalidateRect(
HWND hWnd, // 窗口句柄
CONST RECT * lpRect, // 区域的矩形坐标, NULL -> 整个窗口
BOOL bErase // 重绘前是否先擦除 TRUE / FALSE
);
WM_LBUTTONDOWN 鼠标左键按下消息
步骤
开始绘图
HDC BeginPain(
HWND hwnd , // 绘图窗口
LPPAINTSTRUCT lpPaint // 绘图参数的buff
); // 返回绘图设备句柄HDC
正式绘图
结束绘图
BOOL EndPaint(
HWND hWnd, // 绘图窗口
CONST PAINTSTRUCT * lpPaint // 绘图参数的指针
);
PAINTSTRUCT ps = {0};
HDC hdc = BeginPaint(hWnd,&ps);
TextOut(hdc,100,100,"Hello,Wkk",strlen(Hello,Wkk));
EndPaint(hWnd,&ps);
// 以上绘图的代码,必须放在处理WM_PAINT消息中
// 附带信息
WPARAM 按键的Virtual Key --> 虚拟键码值不能区分大小写
LPARAM 按键的参数,例如按下次数 --> 不重要
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,小写字符,...);
}
附带信息
WPARAM - 输入的字符的ASCII字符编码值
LPARAM - 按键的相关参数 --> 不重要
基本鼠标消息
WM_LBUTTONDOWN 鼠标左键按下
WM_LBUTTONUP 左键抬起
WM_RBUTTONDWON
WM_RBUTTONUP
WM_MOUSEMOVE 鼠标移动消息
附带信息
wPARAM: 其他按键的状态,例如: Ctrl / Shift等
鼠标左键:1, Ctrl: 8 ,Shift: 4
IPARAM: 鼠标的位置,窗口客户区坐标系
一般情况鼠标按下/ 抬起是成对出现的。
在鼠标移动过程中,会根据移动速度产生一系列的WM_MOUSEMOVE消息
移动慢产生消息多,移动快产生消息少
双击消息
WM_LBUTTONDBLCLK 鼠标右键双击
WM_RBUTTONDBLCLK 左键双击
附带消息
同基本鼠标消息
消息产生顺序
以左键双击为例:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
WM_LBUTTONUP
使用时需要在注册窗口类的时候添加CS_DBLCLKS 风格, 加上才会出现双击事件
滚轮消息
WM_MOUSEWHEEL 鼠标滚轮消息
附带信息
wPARAM
LOWORD 其他按键的状态
HIWORD 滚轮的偏移量,通过正负值表示滚动方向
正:向前滚动, 负:向后滚动
IPARAM: 鼠标当前的位置,屏幕坐标系
使用
通过偏移量,获取滚动的方向和距离
一般的滚动偏移都是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
关闭定时器
BOOL KillTimer(
HWND hWnd, // 定时器窗口句柄
UINT_PTR uIDEvent // 定时器ID
);
添加菜单资源
加载菜单资源
注册窗口类时设置菜单
wc.lpszMenuName = (LPCTSTR)IDR_MENU1; // 强转一下路径
创建窗口时传递参数
倒数第三个参数 传递菜单句柄 ( 不是上面的菜单ID )
HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,LoadMenu(hIns,(LPCTSTR)IDR_MENU1),hIns,NULL)
加载菜单资源:
HMENU LoadMenu (
HINSTANCE hInstance, // handle to module --> 找本进程的内存
LPCTSTR lpMenuName // menu name or resource identifier
);
在主窗口WM_CREATE消息中利用SetMenu函数设置菜单
HMENU hMenu = LoadMenu(hIns,(LPCTSTR)IDR_MENU1));
SetMenu(hWnd,hMenu);
单击菜单项发出WM_COMMAND消息
附带信息
wPARAM : HIWORD 对于菜单为0
LOWORD 菜单项的ID
lPARAM : 对于菜单为0
以.ico结尾的图片
添加资源
注意图标的大小,一个图标文件中,可以有多个不同大小的图标
加载
HICON LoadIcon(
HINSTANCE hInstance, // 当前程序实例句柄
LPCTSTR lpIconName // 图标资源id
);// 成功返回HICON句柄
设置
注册窗口类
wc.hIcon = LoadIcon(hIns,(LPCTSTR)IDI_ICON1);
添加光标的资源
关标的大小默认是32x32像素,每个光标有HotSpot( 热点 ), 是当前鼠标的热点
加载资源
HCURSOR LoadCursor(
HINSTANCE hInstance, // 程序实例
LPCTSTR lpCursorName // 资源ID
); // hInstance 可以为NULL, 获取系统默认的Cursor
设置资源
在注册窗口时,设置光标
wc.hCursor = LoadCursor(hIns,(char*)IDC_CURSOR1);
使用SetCursor设置光标
HCURSOR SetCursor(
HCURSOR hCursor // 光标
); // 返回原来的光标
必须放在消息的处理中设置
WM_SETCURSOR 消息参数
wPARAM 当前使用的光标句柄
IPARAM - LOWORD 当前区域的代码 在哪个区域活动 HICLIENT / HTCAPTION 客户区/标题区
- HIWORD 当前鼠标消息ID,有没有点右键左键等等
DefWindowPro() 默认处理,会将光标重新改成注册窗口类的关标
所以SetCursor后要直接返回,不能经过DefWindowPro函数
添加字符串资源
添加字符串表,在表中添加字符串
字符串资源的使用
int LoadString(
HINSTANCE hInstance, //程序实例
UINT uID, // 字符串ID
LPTSTR lpBuffer, // 存放字符串的BUFF
int nBufferMax // 字符串BUFF长度
); // 成功返回字符串长度,失败0
添加 资源添加快捷键表,增加命令ID对应的快捷键 Accelerator
让快捷键的ID 和 命令的ID一样 就是绑定
使用
// 加载加速键表 --> 表
HACCEL LoadAccelerators(
HINSTANce hInstance, // 程序实例句柄
LPCTSTR lpTableName // 快捷键表名
); // 返回快捷键表句柄
// 翻译快捷键
int TranslateAccelerator(
HWND hWnd, // 处理消息的窗口句柄
HACCEL hAccTable, // 快捷键表句柄
LPMSG lpMsg// 消息
); // 如果快捷键,返回非零
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;
}
}
// 调用
while( GetMessage(&nMsg,NULL,0,0)){
if( !TranslateAccelerator(hWnd,hAccel,&nMsg)){
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);
}
}
在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;
赋值使用RGB宏
nColor = RGB(0,0,255);
获取RGB值
GetRValue / GetGValue / GetBValue
BYTE nRed = GetRValue( nColor );
HDC hdc = BeginPaint(hWnd,...); //--> 抓绘图设备
TextOut(hdc,x,y,"hello",...);
...
EndPaint(hwnd,...);
SetPixel 设置指定点的颜色
COLORREF SetPixel(
HDC hdc, // DC句柄
int X,
int Y,
COLORREF crColor
);// 返回原来的颜色
PAINTSTRUCT ps = {0};
HDC hdc = BeginPaint(hWnd,&ps);
SetPixel(hdc,100,100,RGB(255,0,0));
EndPaint(hWnd,&ps);
线得使用(直线,弧线)
MoveToEx – 指明窗口当前点
LineTo 从窗口当前点到指定点绘制一条直线
当前点: 上一次绘制时的最后一点,初始为(0,0)点
MoveToEx(hdc,100,100,NULL); // 最后一个参数返回上一次的当前点
LineTo(hdc,300,300);
封闭图形:能够用画刷填充的图形
Rectangle / Ellipse
Rectangle(hdc,x,y,x+w,y+h);
Ellipse(hdc,100,100,x+w,y+h); // 外接矩形
画笔的作用
线的颜色,线型(虚线画笔、点线画笔…),线粗
HPEN : 画笔句柄
画笔的使用
创建画笔
HPEN CreatePen(
int fnPenStyle, // 画笔的样式
int nWidth, // 画笔的粗细
COLORREF crColor // 画笔的颜色
); // 创建成功返回句柄
PS_SOILD - 实心线,可以支持多个像素宽,其他线型只能是一个像素宽
PS_DASH - 虚线画笔
将画笔应用到DC中
HGDIOBJ SelectObject(
HDC hdc, // 绘图设备句柄
HGDIOBJ hgdiobj // GDI绘图对象句柄
); // 返回原来的GDI绘图对象句柄 --> 注意保存原来DC当中画笔
绘图
取出DC中的画笔,将原来的画笔,使用SelectObject函数,放入到设备DC中
释放画笔
BOOL DeleteObject(
HGDIOBJ hObject // GUI绘图对象句柄
);
只能删除不被DC使用的画笔,所以在释放前,必须将画笔从DC中取出
封闭图形的填充的颜色、图案
HBRUSH 画刷句柄
创建画刷
HBRUSH hBursh = CreateSolidBrush(RGB(0,255,0));
//CreateSolidBrush 创建实心画刷
HBRUSH hBurshhat = CreateHatchBrush(HS_CROSS,RGB(0,255,0)); // HS_CROSS 经纬线
//CreateHatchBrush 创建纹理画刷
将画刷应用到DC中
HBRUSH oldBrush = SelectObject(hdc,hBursh); // 原本的刷子是白色的
绘图
将画刷从DC中取出
SelectObject(hdc,oldBrush);
删除画刷
DeleteObject(hPen);
用一条条的单独直线围起来的图形,不是封闭图形,不能填充
使用GetStockObject 函数获取系统维护的画刷,画笔等
如果不使用画刷填充,需要使用NULL_BRUSH 参数,获取透明画刷。
GetStockObject 返回的画刷不需要DeleteObject
// 透明画刷
HGDIOBJ hBursh = GetStockObject(NULL_BRUSH);
// 应用刷子
// 恢复刷子
位图相关
光栅图形: 记录图像中每一点的颜色等信息
矢量图形: 记录图像算法,绘图指令等等
HBITMAP 位图句柄
位图的使用
在资源中添加位图资源 --> 资源ID
从资源中加载位图LoadBitmap
HBITMAP hBmp = LoadBitmap(g_hInstance,(char*)IDB_BITMAP1);
创建一个与当前DC相匹配的DC(内存DC) --> 在内存中绘图
HDC CreateCompatibleDC(
HDC hdc // 当前DC句柄,可以为NULL, 使用屏幕DC
); // 返回创建好的DC句柄
将位图放入匹配的内存DC中 --> 在虚拟区域中绘制图形
SelectObject
成像 (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
);
缩放成像
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
);
// 目的区域比原始图像小 --> 缩小
// 目的区域比原始图像大 --> 放大
取出位图
SelectObject 从内存DC取数据
释放位图
DeleteObject(hBmp);
释放匹配的内存DC
DeleteDC(hMemdc);
文字绘制
TextOut -> 将文字绘制在指定坐标位置,只能单行绘制
TextOut(hdc,x,y,strdata,nCount);
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冲突
文本颜色和背景
文字颜色: SetTextColor(hdc,RGB(…));
文字背景色: SetBkColor(hdc,RGB(…)); --> 仅适用于不透明模式
文字背景模式: SetBkMode(QPAQUE / TRANSPARENT );
字体
Windows常用的字体为 TrueType格式的字体文件 --> 保存点阵字型
字体名 - 标识字体类型 --> 第一行有字体名称
HFONT - 字体句柄
创建字体
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 // 字体名称 ---> 字体文件内容第一行
);
应用字体到DC
HGDIOBJ oldfont = SelectObject(hdc,hFont);
绘制文字
DrawText / TextOut
取出字体
SelectObject(hdc,oldfont);
删除字体
DeleteObject(hFont);
普通窗口: 自定义函数调用缺省函数
对话框窗口: 缺省函数调用自定义函数
缺省函数(...){
...
自定义函数(...)
}
对话框分类
模态对话框:当对话框显式时,会禁止其他窗口和用户的交互操作
非模态对话框:在对话框显示后,其他窗口仍然可以和用户交互操作
对话框基本使用
对话框窗口处理函数 --> 并非真正的对话框窗口处理函数
INT_PTR CALLBACK DialogProc(
HWND hwndDlg , // 窗口句柄
UINT uMsg, // 消息ID
WPARAM wParam , // 消息参数
LPARAM lParam // 消息参数
);
// 返回TRUE 缺省处理函数不需要处理
// 返回FALSE 交给缺省处理函数处理
// 一般不需要调用缺省对话框窗口处理函数
创建对话框
INT DialogBox(
HINSTANCE hInstance, // 应用程序实例句柄
LPCTSTR lpTemplate, // 对话框资源ID
HWND hWndParent, // 对话框父窗口
DLGPROC lpDialogFunc // 自定义函数
);
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; // 将消息交给真正的对话框处理函数的后续处理部分处理
}
对话框的关闭
BOOL EndDialog(
HWND hDlg, // 关闭的对话框窗口句柄
INT_PTR nResult // 关闭的返回值
);// 消除模式对话框 & 解除模式对话框的阻塞状态
关闭模式对话框,只能使用EndDialog,不能使用DestroyWindow(销毁窗口)等函数。
DestroyWindow 可以销毁对话框,但是不能消除阻塞
nResult 是DialogBox函数退出时的返回值
对话框的消息
WM_INITDIALOG 对话框创建之后显示之前,通知对话框窗口处理函数,可以完成自己的初始化相关的操作。
其他消息和窗口的消息一致
创建对话框
HWND CreateDialog(
HINSTANCE hInstance , // 应用程序实例句柄
LPCTSTR lpTemplate, // 模板资源ID
HWND hWndParent, // 父窗口
DLGPROC lpDialogFunc // 自定义函数
);
非阻塞函数,创建成功后返回窗口句柄**,需要使用ShowWindow函数显示对话框**
对话框的关闭
关闭时使用DestroyWindow销毁窗口,不能使用EndDialog关闭对话框
用于调试
HANDLE g_hOutput = 0 ;// 接收标准输出句柄
WinMain(...){
AllocConsole(); //增加入口函数
g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
// FreeConsole(); // 释放控制台
WriteConsole(g_hOutput,text,strlen(text),NULL,NULL);
}