1 基础概念
- 什么是窗口?
答:窗口就是屏幕上的一片区域,接受用户的输入,显示程序的输出。可以包含标题栏、菜单栏、工具栏以及控件等。 - 什么是句柄?
答: 作为一种管理和操作系统资源的机制,提供了对各种对象和资源的访问能力。通过使用句柄,程序可以与特定的资源进行交互和操作。(资源的编号、二级指针) - 窗口类对象是啥?
C++窗口类对象与窗口并不是一回事,它们之间惟一的关系是 C++窗口类对象内部定义了一个窗口句柄变量,保存了与这个 C++窗口类对象相关的那个窗口的句柄。窗口销毁时,与之对应的 C++窗口类对象销毁与否,要看其生命周期是否结束。但 C++窗口类对象销毁时,与之相关的窗口也将销毁 - 常用的结构体以及函数等
//1.用户提供的基于 Windows 的图形应用程序的入口点
int __clrcall WinMain(// __clrcall是一种调用约定,主要涉及函数参数传递方式、函数参数的压栈顺序等
[in] HINSTANCE hInstance,// 应用程序的当前实例的句柄
[in, optional] HINSTANCE hPrevInstance,// 应用程序上一个实例的句柄。 此参数始终为NULL。
[in] LPSTR lpCmdLine,// 应用程序的命令行,不包括程序名称。
[in] int nShowCmd// 控制窗口的显示方式。
);
//2.窗口结构体
typedef struct tagWNDCLASSA {
UINT style;// 类样式
WNDPROC lpfnWndProc;// 指向窗口过程的指针
int cbClsExtra;// 要根据窗口类结构分配的额外字节数。 系统将字节初始化为零
int cbWndExtra;// 在窗口实例之后分配的额外字节数。 系统将字节初始化为零。
HINSTANCE hInstance;// 实例的句柄,该实例包含类的窗口过程。
HICON hIcon;// 类图标的句柄。此成员必须是图标资源的句柄。如果此成员为NULL,则系统会提供默认图标。
HCURSOR hCursor;// 类游标的句柄。 此成员必须是游标资源的句柄。
HBRUSH hbrBackground;// 类背景画笔的句柄。
LPCSTR lpszMenuName;// 类菜单的资源名称,该名称显示在资源文件中。
LPCSTR lpszClassName;// 指向以 null 结尾的字符串的指针或是原子。
} WNDCLASSA, *PWNDCLASSA, *NPWNDCLASSA, *LPWNDCLASSA;
//3.从与应用程序实例关联的可执行文件 (.EXE) 文件中加载指定的游标资源
// 如果函数成功,则返回值是新加载的游标的句柄。如果函数失败,则返回值为 NULL。
HCURSOR LoadCursorA(
// DLL或可执行文件(.exe 模块的句柄)包含要加载的游标的文件。
// 若要加载预定义的系统游标,请将此参数设置为 NULL。
[in, optional] HINSTANCE hInstance,
// 如果 hInstance 为非 NULL, 则 lpCursorName 按名称或序号指定游标资源
//如果 hInstance 为 NULL, 则 lpCursorName 将指定标识符 (从要加载的预定义系统游标的 IDC_前缀) 开始
[in] LPCSTR lpCursorName
);
//4.从与应用程序实例关联的可执行 (.exe) 文件中加载指定的图标资源。
HICON LoadIconA(
// DLL 或可执行文件 (.exe 模块的句柄,) 包含要加载的图标的文件
// 若要加载预定义的系统图标,请将此参数设置为 NULL
[in, optional] HINSTANCE hInstance,
// 如果hInstance为非NULL,则 lpIconName 按名称或序号指定图标资源。
// 如果hInstance为NULL,则 lpIconName 将指定标识符(从要加载的预定义系统图标的IDI_前缀)开始
[in] LPCSTR lpIconName
);
//5.检索其中一支股票笔、画笔、字体或调色板的句柄
HGDIOBJ GetStockObject(
[in] int i
);
//6.注册一个窗口类
ATOM RegisterClassA(
[in] const WNDCLASSA *lpWndClass// 指向 WNDCLASS 结构的指针
);
// 7. 该函数创建一个重叠式窗口、弹出式窗口或子窗口。
// 它指定窗口类,窗口标题,窗口风格,以及窗口的初始位置及大小(可选的)。
// 函数也指该窗口的父窗口或所属窗口(如果存在的话),及窗口的菜单。
HWND WINAPI CreateWindow(
_In_opt_ LPCTSTR lpClassName, // 窗口类名称
_In_opt_ LPCTSTR lpWindowName, // 窗口标题
_In_ DWORD dwStyle, // 窗口风格,或称窗口格式
_In_ int x, // 初始 x 坐标
_In_ int y, // 初始 y 坐标
_In_ int nWidth, // 初始 x 方向尺寸
_In_ int nHeight, // 初始 y 方向尺寸
_In_opt_ HWND hWndParent, // 父窗口句柄
_In_opt_ HMENU hMenu, // 窗口菜单句柄
_In_opt_ HINSTANCE hInstance, // 程序实例句柄
_In_opt_ LPVOID lpParam // 创建参数
);
//8.设置指定窗口的显示状态
BOOL ShowWindow(// 如果窗口以前可见,则返回值为非零值,若隐藏则为0
[in] HWND hWnd,// 窗口的句柄
[in] int nCmdShow// 控制窗口的显示方式
);
// 9。如果窗口的更新区域不为空, UpdateWindow 函数通过向窗口发送 WM_PAINT 消息来更新指定窗口的工作区。 函数绕过应用程序队列,将 WM_PAINT 消息直接发送到指定窗口的窗口过程。 如果更新区域为空,则不发送任何消息。
BOOL UpdateWindow(
[in] HWND hWnd
);
//10.从调用线程的消息队列中检索消息。 函数调度传入的已发送消息,直到已发布的消息可供检索。
BOOL GetMessage(
[out] LPMSG lpMsg,// 指向 MSG 结构的指针,该结构从线程的消息队列接收消息信息。
[in, optional] HWND hWnd,// 要检索其消息的窗口的句柄。 窗口必须属于当前线程
[in] UINT wMsgFilterMin,// 要检索的最低消息值的整数值。
[in] UINT wMsgFilterMax// 要检索的最高消息值的整数值。
);
//11.将虚拟密钥消息转换为字符消息。
BOOL TranslateMessage(
[in] const MSG *lpMsg// 指向 MSG 结构的指针
);
//12.将消息调度到窗口过程
LRESULT DispatchMessage(
[in] const MSG *lpMsg// 指向包含消息的结构的指针
);
//12.包含来自线程的消息队列的消息信息
typedef struct tagMSG {
HWND hwnd;// 其窗口过程接收消息的窗口的句柄
UINT message;// 消息的标识符
WPARAM wParam;// 关于消息的附加信息
LPARAM lParam;// 关于消息的附加信息
DWORD time;// 消息的发布时间
POINT pt;// 发布消息时的光标位置
DWORD lPrivate;
} MSG, *PMSG, *NPMSG, *LPMSG;
- 消息循环
a.消息是由事件产生的。
b.事件:由输入设备触发比如鼠标、键盘等;由窗体控件触发比如button,file菜单;由Windows内部的事件。
c.消息是事件翻译过来的
d.消息队列:系统消息队列以及应用程序消息队列。产生的消息首先由Windows系统捕获,放在系统消息队列里,再拷贝到对应的应用程序消息队列。
e.消息循环:系统为每个应用程序维护一个消息循环,消息循环会不断检索自身的消息队列。来一个消息,就用GetMessage()取出消息。
2 基本流程
- 设计一个窗口类
// 1 定义和配置窗口信息
WNDCLASS wndcls;
wndcls.cbClsExtra = NULL;
wndcls.cbWndExtra = NULL;
wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);
wndcls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndcls.hInstance = hInstance;
// 2 定义交互响应
wndcls.lpfnWndProc = MyWinProc;//回调
// 3 定义窗口代号
wndcls.lpszClassName = (LPCTSTR)"My";
wndcls.lpszMenuName = NULL;
wndcls.style = CS_HREDRAW | CS_VREDRAW;// 每当窗口更改大小时,让应用程序重新绘制工作区的整个内容
- 注册窗口类
// 注册窗口类
RegisterClass(&wndcls);
- 创建窗口
HWND hwnd; // HWND是唯一标识和操作窗口对象
hwnd = CreateWindow(clsName, msgName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
- 显示以及更新窗口
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
- 消息循环
MSG msg;
while (GetMessage(&msg, NULL, NULL, NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
- 回调函数
LRESULT CALLBACK MyWinProc(
HWND hwnd, // 窗口的句柄
UINT uMsg, // 消息的标识符
WPARAM wParam, // first message parameter word
LPARAM lParam // second message parameter long
) {
//uMsg 消息类型
int ret;
HDC hdc;
switch (uMsg) {
case WM_CHAR: char szChar[20];
sprintf_s(szChar, "您刚才按下了: %c", wParam);
MessageBox(hwnd, szChar, "char", NULL);
break;
case WM_LBUTTONDOWN:
MessageBox(hwnd, "检测鼠标左键按下", "msg", NULL);
break;
case WM_PAINT: PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 0, 0, "www.baidu.com", strlen("www.baidu.com"));
EndPaint(hwnd, &ps); MessageBox(hwnd, "重绘", "msg", NULL);
break;
case WM_CLOSE: ret = MessageBox(hwnd, "是否真的结束?", "msg", MB_YESNO);
if (ret == IDYES) { DestroyWindow(hwnd); }
break;
case WM_DESTROY: PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
3 整体代码以及效果
// 创建第一个win32窗口程序
#include<Windows.h>
#include<stdio.h>
LPCTSTR clsName = (LPCTSTR)"My";
LPCTSTR msgName = (LPCTSTR)"欢迎学习";
// 声明回调函数
LRESULT CALLBACK MyWinProc(
HWND hwnd, // 窗口的句柄
UINT uMsg, // 消息的标识符
WPARAM wParam, // first message parameter word
LPARAM lParam // second message parameter long
);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
// a 设计一个窗口类
// 1 定义和配置窗口信息
WNDCLASS wndcls;
wndcls.cbClsExtra = NULL;
wndcls.cbWndExtra = NULL;
wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);
wndcls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndcls.hInstance = hInstance;
// 2 定义交互响应
wndcls.lpfnWndProc = MyWinProc;//回调
// 3 定义窗口代号
wndcls.lpszClassName = clsName;
wndcls.lpszMenuName = NULL;
wndcls.style = CS_HREDRAW | CS_VREDRAW;
// b 注册窗口类
RegisterClass(&wndcls);
// c 创建窗口
HWND hwnd;
hwnd = CreateWindow(clsName, msgName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
// d 显示和刷新窗口
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
//e 消息循环 GetMessage 只有在接收到 WM_QUIT 才会返回 0
//TranslateMessage 翻译消息 WM_KEYDOWN 和 WM_KEYUP 合并为 WM_CAHR
MSG msg;
while (GetMessage(&msg, NULL, NULL, NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK MyWinProc(
HWND hwnd, // 窗口的句柄
UINT uMsg, // 消息的标识符
WPARAM wParam, // first message parameter word
LPARAM lParam // second message parameter long
) {
//uMsg 消息类型
int ret;
HDC hdc;
switch (uMsg) {
case WM_CHAR: char szChar[20];
sprintf_s(szChar, "您刚才按下了: %c", wParam);
MessageBox(hwnd, szChar, "char", NULL);
break;
case WM_LBUTTONDOWN:
MessageBox(hwnd, "检测鼠标左键按下", "msg", NULL);
break;
case WM_PAINT: PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 0, 0, "www.baidu.com", strlen("www.baidu.com"));
EndPaint(hwnd, &ps); MessageBox(hwnd, "重绘", "msg", NULL);
break;
case WM_CLOSE: ret = MessageBox(hwnd, "是否真的结束?", "msg", MB_YESNO);
if (ret == IDYES) { DestroyWindow(hwnd); }
break;
case WM_DESTROY: PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
效果如下:
4 Windows数据类型
WORD: 16位无符号整形数据
DWORD: 32位无符号整型数据(DWORD32)
DWORD64: 64位无符号整型数据
INT: 32位有符号整型数据类型
INT_PTR: 指向INT数据类型的指针类型
INT32: 32位符号整型
INT64: 64位符号整型
UINT: 无符号INT
LONG: 32位符号整型(LONG32)
ULONG: 无符号LONG
LONGLONG: 64位符号整型(LONG64)
SHORT: 无符号短整型(16位)
LPARAM: 消息的L参数
WPARAM: 消息的W参数
HANDLE: 对象的句柄,最基本的句柄类型
HICON: 图标的句柄
HINSTANCE: 程序实例的句柄
HKEY: 注册表键的句柄
HMODULE: 模块的句柄
HWND: 窗口的句柄
LPSTR: 字符指针,也就是字符串变量
LPCSTR: 字符串常量
LPCTSTR: 根据环境配置,如果定义了UNICODE宏,则是LPCWSTR类型,否则则为LPCSTR类型
LPCWSTR: UNICODE字符串常量
LPDWORD: 指向DWORD类型数据的指针
CHAR: 8比特字节
TCHAR: 如果定义了UNICODE,则为WCHAR,否则为CHAR
UCHAR: 无符号CHAR
WCHAR: 16位Unicode字符
BOOL: 布尔型变量
BYTE: 字节类型(8位)
CONST: 常量
FLOAT: 浮点数据类型
SIZE_T: 表示内存大小,以字节为单位,其最大值是CPU最大寻址范围
VOID: 无类型,相当于标准C语言中的void