windows消息
消息机制
主要的三个动态链接库 内核(KERNEL32.DLL)用户(User32.DLL) 窗口管理(GDI32.DLL)。windows程序运行时通过一个“动态链接”进程与windows接口,每个EXE包含它所需要的各个动态链接库以及库中函数的reference引用地址。当exe被装入内存后,程序中函数调用都被解析成DLL函数入口的指针,同时这些调用函数也被装入内存。
链接生成exe时会链接编程环境提供的特殊“导入库”,其包含所有windows函数调用要碰到的动态链接库的名字及引用信息。链接程序利用这些信息构建exe文件中的表格,当装入程序时windows通过这些表格解析windows函数调用。
#include<windwos.h>
int
WINAPI // 即 __stdcall
WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PSTR szCmdLine, // 命令行参数
int iCmdShow) { // 程序的最初显示
return 0;
}
windows.h 囊括了其他几个重要头文件
WINDEF.H 基本数据类型定义
WINNT.H 支持 Unicode 的类型定义,包含了CTYPE.H
typedef char CHAR;
typedef wchar_t WCHAR;
字符串指针类型
typedef CHAR *PCHAR, *LPCH, *PCH;
typedef CONST CHAR *LPCCH, *PCCH;
宽字符串指针类型
typedef WCHAR *PWCHAR, *LPWCH, *PWCH;
typedef CONST WCHAR *LPCWCH, *PCWCH;
WINBASE.H 内核函数
WINUSER.H 用户界面函数
WINGDI.H 图形设备接口函数
Unicode
在win中使用 wchar_t
表示宽字符,在 WCHAR.H 中 typedef unsigned short wchar_t;
定义一个单个宽字符的变量
wchar_t c = 'A'; // 变量 c 为 0x0041,Intel使用小端存储,故在内存中为 0x41 0x00
wchar_t *p = L"Hello!"; // L紧接着字符串表示这是一个宽字符串,末端有一个两字节的'\0'表示结束
sizeof(p); // 14
p[1]; // 'e' 0x0065
字符串 "hello!" 在内存中
0x0048 0x0065 0x006c 0x006c 0x006f 0x0021
48 00 65 00 6c 00 6c 00 6f 00 21 00 在内存中的真正存储
使用strlen时会将第二个字节当作字符结束符号,所以对于宽字符应该使用特殊函数,定义在STRING.H和WCHAR.H下的 wcslen
size_t __cdecl strlen(const char*);
size_t __cdecl wcslen(const wchar_t*);
wcslen(p); // 6 返回字符个数
当在Unicode和非Unicode环境下时将不同的函数、类型定义为通用函数、类型
// _UNICODE
#define _tcslen wcslen
typedef wchar_t TCHAR;
#define __T(x) L##x // 相当模糊的语法使字母L与宏参数拼接在一起
// not _UNICODE
#define _tcslen strlen
typedef char TCHAR;
#define __T(x) x
// 更通用的宏
#define _T(x) __T(x)
#define _TEXT(x) __T(x)
_TEXT("Hello!") // 如果定义 _UNICODE 则解释为宽字符,否则解释为普通字符
TCHAR
以 w 开头表示宽字符,T 开头表示通用字符
#ifdef UNICODE
typedef WCHAR TCHAR, *PTCHAR;
typedef LPWSTR LPTCH, PTCH, PTSTR, LPTSTR;
typedef LPCWSTR LPCTSTR;
#else
typedef char TCHAR, *PTCHAR;
typedef LPSTR LPTCH, PTCH, PTSTR, LPTSTR;
typedef LPCSTR LPCTSTR;
#endif
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif
// 更通用的c字符串函数
lstrlen, lstrcpy, lstrcpyn, lstrcat, lstrcmp, lstrcmpi
win32中字符串输入输出函数都需要使用格式兼容版本,sprintf -> vsprintf 等。
窗口与消息-窗口的创建
#include <Windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PSTR szCmdLine,
int iCmdShow)
{
static TCHAR szAppName[] = TEXT("HelloWin");
WNDCLASS wndclass;
HWND hwnd;
MSG msg;
// 创建窗口类
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//GetStockObject 获取图形对象
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
// 注册窗口类
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("regest windwos class failed!"), szAppName, MB_ICONERROR);
return 0;
}
// 创建窗口
hwnd = CreateWindow(szAppName, // 窗口类名称
TEXT("The Hello Program"), // 标题
WS_OVERLAPPEDWINDOW, // 风格,窗口格式。此处使用的是常见的通过位运算符合的类型
CW_USEDEFAULT, // 初始x坐标,相对屏幕左上角
CW_USEDEFAULT, // 初始y坐标,使用的是默认值
CW_USEDEFAULT, // 初始x方向尺寸,窗口初始宽度
CW_USEDEFAULT, // 初始y方向尺寸
NULL, // 父窗口句柄,子窗口总是在父窗口前
NULL, // 窗口菜单句柄
hInstance, // 程序实例句柄
NULL); // 创建参数
// 显示窗口,第二个参数决定窗口在屏幕中的初始显示形式
// 正常 SW_SHOWNORMAL
// 最小化 SW_SHOWMAXIMIZED
// 最大化 SW_SHOWMINNOACTIVE
ShowWindow(hwnd, iCmdShow);
// 指示窗口对自身进行重绘,向窗口过程发送一条 WM_PAINT 消息
UpdateWindow(hwnd);
// win为每个程序维护了一个消息队列,输入事件发生后win会将这些事件转化为“消息”
// 从消息队列中检索,当message字段不为 WM_QUIT 时返回非0值,否则返回0
while (GetMessage(&msg,
NULL,
0,
0))
{
// 翻译一些键盘消息
TranslateMessage(&msg);
// 将消息发送给窗口过程
DispatchMessage(&msg);
}
return msg.wParam;//参数通常为 0
}
/************************* 窗口过程 *************************/
// win prock :处理消息的函数。这4个参数与Message结构体的前4个参数一一对应
LRESULT CALLBACK WndProc(HWND hwnd, // 接受消息的窗口的句柄
UINT message, // 标识消息的数字,WINUSER.H中定义各种以WM开头的标识符
WPARAM wParam,
LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
// 调用 CreateWindow 函数创建窗口时生成此消息
case WM_CREATE:
MessageBox(hwnd, TEXT("创建成功"), TEXT("title"), MB_DEFBUTTON1);
return 0;
/*
当窗口的客户区的部分或全部“无效”且需要“更新”时,应用得到此通知,意味着窗口需要重绘。
何时客户区无效?
首次创建时整个客户区都是无效的,因为此时应用尚未在该窗口上绘制任何东西。第一条WM_PAINT 消息通常在UpdateWindow时出现。
调整窗口大小时客户区也会变得无效,此后窗口过程接收到一条 WM_PAINT 消息。
最小化最大化、拖动窗口发生遮盖时都会引起无效。
*/
case WM_PAINT://DefWindowProc 中的默认处理就是简单调用 BeginPaint EndPaint
// 表明窗口绘画开始,第二个参数是指向PAINTSTRUCT结构的指针
// 返回一个设备环境句柄,指物理输出设备机器驱动。使用它只能在客户区内绘制
hdc = BeginPaint(hwnd, &ps);
// 获取窗口客户区的尺寸,设置rect结构体中的 left top right bottom,left top总为0,right bottom为像素个数
GetClientRect(hwnd, &rect);
// 显示一个文本字符串
DrawText(hdc,
TEXT("Hello, win32!"),
-1, // 表示文本字符串以0结尾
&rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);//规定格式
// 结束窗口绘画,释放 hdc
EndPaint(hwnd, &ps);
return 0;
// 窗口大小发生变化,低位字宽度、高位字高度
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
case WM_DESTROY:
// 将“退出”消息 WM_QUIT 插入消息队列
PostQuitMessage(0);
return 0;
default:
break;
}
// 执行默认消息处理,不进行处理的消息都必须传给此函数。必须要有,否则结束程序等功能无法进行
return DefWindowProc(hwnd, message, wParam, lParam);
}
大写标识符前缀
前缀 | 常量 |
---|---|
CS | 类窗口风格 |
CW | 创建窗口选项 |
DT | 文本绘制选项 |
IDI | 图标的ID号 |
IDC | 光标的ID号 |
MB | 消息框选项 |
SND | 声音选项 |
WM | 窗口消息 |
WS | 窗口风格 |
结构 | 含义 |
---|---|
MSG | 消息结构 |
WNDCLASS | 窗口类结构 |
PAINTSTRUCT | 绘制结构 |
RECT | 矩形结构 |
句柄
标识符 | 含义 |
---|---|
HINSTANCE | 实例句柄——程序本身 |
HWND | 窗口句柄 |
HDC | 设备句柄本身 |
匈牙利命名法
前缀:
前缀 | 数据类型 |
---|---|
c | char / WCHAR / TCHAR |
by | BYTE |
cb | 字节数 |
n | short |
i | int |
x,y | int x坐标 y坐标 |
cx, cy | int x或y的长度,c表示count |
B f | BOOL / flag |
w | WORD (usigned int) |
l | LONG |
dw | DWORD |
fn | 函数 |
s | 字符串 |
sz | 以零结束的字符串 |
h | 句柄 |
hbr | 画刷的句柄 handle to a brush |
p lp | 指针 |
常见结构体
窗口类
typedef struct tagWNDCLASSW {
UINT style; // 可选 CS_VREDRAW | CS_DBLCLKS | CS_NOCLOSE
WNDPROC lpfnWndProc; // 窗口函数
// 用于在类结构和窗口结构中预留一些额外的空间
int cbClsExtra;
int cbWndExtra;
// 应用程序的实例句柄
HINSTANCE hInstance;
HICON hIcon;//配合 LoadIcon(程序实例句柄, 图标标识);
HCURSOR hCursor;
HBRUSH hbrBackground;//填充背景色
LPCWSTR lpszMenuName;//菜单栏
LPCWSTR lpszClassName; // 窗口类名称
} WNDCLASSW, *PWNDCLASSW, NEAR *NPWNDCLASSW, FAR *LPWNDCLASSW;
typedef WNDCLASSW WNDCLASS;
消息类
typedef struct tagMSG {
HWND hwnd; // 消息指向的窗口
UINT message; // 消息标识符
WPARAM wParam; // 消息参数,取决于具体消息
LPARAM lParam; // 另一个消息参数,取决于具体消息
DWORD time; // 进入消息队列的事件
POINT pt; // 消息进入消息队列时鼠标指针的位置
#ifdef _MAC
DWORD lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
typedef struct tagPOINT
{
LONG x;
LONG y;
} POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT;