概念:
windows中程序的窗口和系统调用的实现模式都是通过一种消息机制来完成的。
窗口调用系统接口,系统发送消息给窗口,窗口轮询消息并处理。
SendMessage阻塞的,需要等待消息处理函数结束才返回。
PostMessage非阻塞的,只是将消息放入消息队列即返回。
Windows窗口程序的实现:
1. 注册窗口类
2. 创建及显示窗口
3. 创建消息循环
4. 消息处理函数
1. 注册窗口类
作用:设置窗口的属性及特征
实现:调用RegisterClass(&wndClass),其中wndClass为结构体对象,其中的参数设置了窗口的属性等。
typedef struct tagWNDCLASSW { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCWSTR lpszMenuName; LPCWSTR lpszClassName; } WNDCLASSW, *PWNDCLASSW, NEAR *NPWNDCLASSW, FAR *LPWNDCLASSW;
其中:
style:指定窗口的样式
CS_HREDRAW
CS_VREDRAW
CS_NOCLOSE
CS_DBLCLKS
lpfnWndProc:窗口过程函数
将函数名赋值给该变量,函数声明为
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
hbrBackground: 当窗口重绘时,重绘的默认背景颜色
2. 创建及显示窗口
以VS2017默认的Windows 桌面应用程序为例
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; // 将实例句柄存储在全局变量中 HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
UpdateWindow会发送一个WM_PAINT的消息来刷新窗口。
3. 创建消息循环
还是以windows桌面应用程序为例子
// 主消息循环: while (GetMessage(&msg, nullptr, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
解释:
GetMessage的原型,获取队列中的消息
BOOL GetMessage(LPMSG lpMsg,//消息结构体 HWND hWnd,//接收指定窗口的消息 UINT wMsgFilterMin,//获取的消息最小值 UINT wMsgFilterMax);//获取的消息最大值
TranslateMessage():用来将虚拟按键转换成字符,并放到消息队列中,下次GetMessage会返回。
TranslateMessage并不会修改消息,只是会新增一个消息。
DispatchMessage():用来将消息传递给系统,系统会调用第一步中注册的消息处理回调函数
4. 消息处理函数
即第一步中传递的回掉函数,一般在函数内部用switch处理
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COMMAND: { int wmId = LOWORD(wParam); // 分析菜单选择: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); // TODO: 在此处添加使用 hdc 的任何绘图代码... EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
WM_CHAR:当用户按下键盘上的一个字符键,这个分支会被调用
WM_PAINT:当窗口的一部分或全部变为无效时就会调用这个分支,具体有以下几种情况触发:
窗口刚创建时
调用UpdateWindow时
窗口大小变化时(前提需要注册窗口时设置了CS_HREDRAW和CS_VREDRAW标志)
窗口被遮盖再显示时
注意,只有在WM_PAINT分支内部才可以使用BeginPaint(对应EndPaint),在外部只能通过GetDC函数来获取DC(对应ReleaseDC)。