01 注册窗口类
1.窗口类的概念
- 窗口类包含了窗口的各种参数信息的数据结构。
- 每个窗口都具有窗口类,基于窗口类创建窗口。
- 每个窗口类都具有一个名称,使用前必须注册到系统。
2.窗口类的分类
- 系统窗口类
系统已经定义好的窗口类,所有应用程序都可以直接使用。 - 应用程序全局窗口类
由用户自己定义,当前应用程序所有模块都可以使用。 - 应用程序局部窗口类
由用户自己定义,当前应用程序中本模块可以使用。
3.系统窗口类
- 不需要注册,直接使用窗口类即可。系统已经注册好了。
例如:
按钮 -BUTTON
编辑框 -EDIT
#include<windows.h>
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
//在内存创建窗口
HWND hWnd = CreateWindow("Button", "window", 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;
}
整个大框一个按钮:
4.全局及局部窗口类
-
注册窗口类的函数
ATOM RegisterClass(
CONST WNDCLASS *lpWndClass //窗口类的数据
);注册成功后,返回一个数字标识 -
注册窗口类的结构体
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 lpseClassName;//窗口类的名称
}WNDCLASS,*PWNDCLASS; -
style窗口类风格
应用程序全局窗口类的注册,需要在窗口类的风格中的增加CS_GLOBALCLASS.
例如:
WNDCLASS wce = {0};
wce.style = …|CS_GLOBALCLASS;
应用程序局部窗口类:在注册窗口类时,不添加CS_GLOBALCLASS风格CS_HREDRAW -当窗口水平变化时,窗口重新绘制
CS_VREDRAW -当窗口垂直变化时,窗口重新绘制
CS_DBLCLKS -允许窗口接收鼠标双击
CS_NOCLOSE -窗口没有关闭按钮
02 窗口创建
1.窗口创建
-
CreateWindow/CreateWindowEx
HWND CreateWindowEx(
DWORD dwExStyle, //窗口的扩展风格 (CreateWindow没有)
LPCTSTR lpClassName, //已经注册的窗口类名称
LPCTSTR lpWindowName, //窗口标题栏的名字
DWORD dwStyle, //窗口的基本风格
int x, //窗口左上角水平坐标位置
int y, //窗口左上角垂直坐标位置
int nWidth, //窗口的宽度
int nHeight, //窗口的高度
HWND hWndParent, //窗口的父窗口句柄
HMENU hMenu, //窗口菜单句柄
HINSTANCE hInstance, //应用程序实例句柄
LPVOID lpParam //窗口创建时附加参数
);//创建成功返回窗口句柄
2.窗口创建执行过程
- 系统根据传入的窗口类名称,在应用程序局部窗口类中查找,如果找到执行2,如果未找到执行3。
- 比较局部窗口类与创建窗口时传入的HINSTANCE变量。如果发现相等,创建和注册的窗口类在同一模块,创建窗口返回。如果不相等,继续执行3.
- 在应用程序全局窗口类,如果找到,执行4,如果未找到执行5。
- 使用找到的窗口类的信息,创建窗口返回。
- 在系统窗口类中查找,如果找到创建窗口返回,否则创建窗口失败。
3.子窗口创建
- 创建时要设置父窗口句柄
- 创建风格要增加WS_CHILD|WS_VISIBLE
#include<Windows.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
//注册窗口类
WNDCLASS wc = { 0 };
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统。
//在内存创建窗口
HWND hWnd = CreateWindowEx(0,"Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
//注册窗口类
wc = { 0 };
wc.cbClsExtra = 0;
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 hWnd1 = CreateWindowEx(0,"Child", "c1", WS_CHILD | WS_VISIBLE|WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, hWnd, NULL, hIns, NULL);
HWND hWnd2 = CreateWindowEx(0,"Child", "c2", WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW, 200, 0, 200, 200, hWnd, NULL, hIns, NULL);
//显示窗口
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = { 0 };
while (GetMessage(&nMsg, NULL, 0, 0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
03 消息基础
1.消息的概念和作用
- 消息组成(windows平台下)
窗口句柄
消息ID
消息的两个参数(两个附带消息)
消息产生的时间
消息产生时的鼠标位置 - 消息的作用
当系统通过窗口工作时,就采用消息的方式派发给窗口(窗口处理函数)。
2.窗口处理函数
- 每个窗口都必须具有窗口处理函数
LRESULT CALLBACK WindowProc(
HWND hwnd, //窗口句柄
UINT uMsg, //消息ID
WPARAM wParam, //消息参数
LPARAM lParam //消息参数
); - 当系统通知窗口时,会调用窗口处理函数,同时将消息ID和消息参数传递给窗口处理函数。在窗口处理函数中,不处理的消息,使用缺省窗口处理函数。例如:DefWindowProc。
3.浅谈消息相关函数
-
GetMessage -获取消息
BOOL GetMessage(
LPMSG lpMsg, //存放获取到的消息BUFF
HWND hWnd, //窗口句柄
UINT wMsgFilterMin, //获取消息的最小ID
UINT wMsgFilterMax //获取消息的最大ID
);
lpMsg -当获取到消息后,将消息的参数存放到MSG结构中。
hWnd -获取到hWnd所指定窗口的消息。NULL:获取所有
wMsgFilterMin和wMsgFilterMax -只能获取到由它们指定的消息范围内的消息,如果都为0,表示没有范围。 -
TranslateMessage -翻译消息。将按键消息,翻译成字符消息
BOOL TranslateMessage(
CONST MSG * lpMsg //要翻译的消息地址
); -
检查消息是否是按键的消息,如果不是按键消息,不做任何处理,继续执行。
04 常见消息
1.WM_DESTROY
- 产出时间:窗口被销毁时的消息。
- 附带消息:wParam:为0。
lParam:为0。 - 一般用法:常用于在窗口被销毁之前,做相应的善后处理,例如资源,内存等。
2.WM_SYSCOMMAND
- 产生时间:当点击窗口的最大化,最小化,关闭等。
- 附带消息:wParam:具体点击的位置,例如关闭SC_CLOSE等。
lParam:鼠标光标的位置。
LOWORD(lParam);//水平位置(4字节低2位)
HIWORD(lParam);//垂直位置(4字节高2位) - 一般用法:常用在窗口关闭时,提升用户处理。
3.WM_CREATE
- 产出时间:在窗口创建成功但还未显示时。
- 附带消息:wParam:为0。
lParam:为CREATESTRUCT类型的指针。
通过这个指针可以获取CreateWindowEx中的全部12个参数的信息。 - 一般用法:常用于初始化窗口的参数,资源等等,包括创建子窗口等。
4.WM_SIZE
- 产生时间:在窗口的大小发生变化后。
- 附带信息:wParam:窗口大小变化的原因。
lParam:窗口变化后的大小。
LOWORD(lParam) //变化后的宽度
HIWORD(lParam)//变化后的高度 - 一般用法:常用于窗口大小变化后,调整窗口内各个部分的布局。
5.WM_QUIT
- 产生时间:程序员发送。
- 附带消息:wParam:PostQuitMessage函数传递的参数
lParam:0 - 一般用法:用于结束消息循环,当GetMessage接受到这个消息后,会返回FALSE,结束while处理,退出消息循环。
#include<Windows.h>
#include<stdio.h>
HANDLE g_hOutput = 0;//结束标准输出句柄
void OnCreate(HWND hWnd, LPARAM lParam)
{
CREATESTRUCT* pcs = (CREATESTRUCT*)lParam;
char* pszText = (char*)pcs->lpCreateParams;
MessageBox(NULL, pszText, "Info", MB_OK);
CreateWindowEx(0, "EDIT", "hello", WS_CHILD|WS_VISIBLE|WS_BORDER, 0, 0, 200, 200, hWnd, NULL, 0, NULL);
}
void OnSize(HWND hWnd, LPARAM lParam)
{
short nWidth = LOWORD(lParam);
short nHight = LOWORD(lParam);
char szText[256] = { 0 };
sprintf(szText, "WM_SIZE:宽:%d,高:%d\n", nWidth, nHight);
WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
switch (msgID)
{
case WM_CREATE:
OnCreate(hWnd, lParam);
break;
case WM_SIZE:
OnSize(hWnd, lParam);
break;
case WM_DESTROY:
PostQuitMessage(0);//可以使用GetMessage函数返回0
break;
case WM_SYSCOMMAND:
if (wParam == SC_CLOSE)
{
int nRet = MessageBox(hWnd, "是否退出", "Infor", MB_YESNO);
if (nRet == IDYES)
{
//什么都不写
}
else
{
return 0;
}
}
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
AllocConsole();//程序中增加DOS窗口
g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//标准输出句柄
//注册窗口类
WNDCLASS wc = { 0 };
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统。
//在内存创建窗口
char* pszText = "hello data";
HWND hWnd = CreateWindowEx(0,"Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, pszText);
//显示窗口
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = { 0 };
while (GetMessage(&nMsg, NULL, 0, 0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
05 消息循环的原理
1.消息循环的阻塞
- GetMessage -从系统获取消息,将消息从系统中移除,阻塞消息。当系统无消息时,会等待下一个消息。
- PeekMessage -以查看的方式从系统获取消息,可以不将消息从系统移除,非阻塞函数。当系统无消息时,返回FALSE,继续执行后续代码。
BOOL PeekMessage(
LPMSG lpMsg ,//message information
HWND hWnd, //handle to window
UINT wMsgFilterMin, //first message
UINT wMsgFilterMax, //last message
UINT wRemoveMsg //移除标记 PM_REMOVE/PM_NOREMOVE
);
2.发送消息
-
SendMessage -发送消息,会等候消息处理的结果。
-
PostMessage -投递消息,消息发出后立刻返回,不等候消息执行结果。
BOOL SendMessage/PostMessage(
HWND hWnd, //消息发送的目的窗口
UINT Msg, //消息ID
WPARAM wParam, //消息参数
LPARAM lParam //消息参数
);
3.消息分类
- 系统消息 -ID范围 0-0x03FF
由系统定义好的消息,可以在程序中直接使用。 - 用户自定义消息 -ID范围 0x0400 -0x7FFF(31743)
由用户自己定义,满足用户自己的需求。由用户自己发出消息,并响应处理
自定义消息宏:WM_USER
#include<Windows.h>
#include<stdio.h>
HANDLE g_hOutput = 0;//结束标准输出句柄
#define WM_MYMESSAGE WM_USER+1001
void OnCreate(HWND hWnd, LPARAM lParam)
{
CREATESTRUCT* pcs = (CREATESTRUCT*)lParam;
char* pszText = (char*)pcs->lpCreateParams;
MessageBox(NULL, pszText, "Info", MB_OK);
PostMessage(hWnd, WM_MYMESSAGE, 1, 2);
CreateWindowEx(0, "EDIT", "hello", WS_CHILD|WS_VISIBLE|WS_BORDER, 0, 0, 200, 200, hWnd, NULL, 0, NULL);
}
void OnSize(HWND hWnd, LPARAM lParam)
{
short nWidth = LOWORD(lParam);
short nHight = LOWORD(lParam);
char szText[256] = { 0 };
sprintf(szText, "WM_SIZE:宽:%d,高:%d\n", nWidth, nHight);
WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
}
void OnMyMessage(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
char szText[256] = { 0 };
sprintf(szText, "自定义消息被处理:wParam:%d,lParam:%d\n", wParam, lParam);
MessageBox(hWnd, szText, "Info", MB_OK);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
switch (msgID)
{
case WM_MYMESSAGE:
OnMyMessage(hWnd, wParam, lParam);
break;
case WM_CREATE:
OnCreate(hWnd, lParam);
break;
case WM_SIZE:
OnSize(hWnd, lParam);
break;
case WM_DESTROY:
//PostQuitMessage(0);//可以使用GetMessage函数返回0
//SendMessage(hWnd, WM_QUIT, 0, 0);
PostMessage(hWnd, WM_QUIT, 0, 0);
break;
case WM_SYSCOMMAND:
if (wParam == SC_CLOSE)
{
int nRet = MessageBox(hWnd, "是否退出", "Infor", MB_YESNO);
if (nRet == IDYES)
{
//什么都不写
}
else
{
return 0;
}
}
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
AllocConsole();//程序中增加DOS窗口
g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//标准输出句柄
//注册窗口类
WNDCLASS wc = { 0 };
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统。
//在内存创建窗口
char* pszText = "hello data";
HWND hWnd = CreateWindowEx(0,"Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, pszText);
//显示窗口
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = { 0 };
/*while (GetMessage(&nMsg, NULL, 0, 0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}*/
while (1)
{
if (PeekMessage(&nMsg, NULL, 0, 0, PM_NOREMOVE))
{
//有消息
if (GetMessage(&nMsg, NULL, 0, 0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);
}
else {
return 0;
}
}
else {
//空闲处理
WriteConsole(g_hOutput, "OnIdle", strlen("OnIdle"), NULL, NULL);
}
}
return 0;
}