LibUIDK界面库原创文章
窗口的创建
一个简单的win32程序如下(假设工程名为“HelloWin32”,下面的代码是使用vc6.0创建一个名为HelloWin32的“Win32 Application”,并且选择“A typical "Hello World" application”后创建的代码精简后得到):
// HelloWin32.cpp : Defines the entry point for the application. //
#include "stdafx.h" #include "resource.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // Register class. static TCHAR szClassName[] = _T("HelloWin32"); WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_HELLOWIN32); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = (LPCSTR)IDC_HELLOWIN32; wcex.lpszClassName = szClassName; wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL); RegisterClassEx(&wcex); // Perform application initialization: HWND hWnd = CreateWindow(szClassName, _T("The hello win32 app"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (hWnd == NULL) { return 0; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); HACCEL hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_HELLOWIN32); // Main message loop: MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; }
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; static TCHAR szHello[] = _T("Hello win32"); switch (message) { case WM_CREATE: break;
case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // TODO: Add any drawing code here... RECT rt; GetClientRect(hWnd, &rt); DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER); EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
我们单步调试上面的代码,在WndProc的WM_PAINT消息中加个断点,在执行UpdateWindow(hWnd)这行代码时,我们就会发现会进入到WM_PAINT中,但这时,还没有进入下面的消息循环。我们知道,消息循环是从消息队列中取消息,既然没有执行消息循环前就可以处理WM_PAINT消息,说明这个WM_PAINT消息是直接发往窗口、而没有进入消息队列的。这句话的意思是:消息循环会调用WndProc,但并不是只有消息循环可以调用WndPorc,windows本身也可以调用。我们可以想像一下CreateWindow的伪代码:
HWND CreateWindow(...) { HWND hWnd = NULL; // 分配窗口内存,生成窗口 hWnd = ...;
CREATESTRUCT cs; // 初始化cs. WndProc(hWnd, WM_CREATE, 0, LPARAM(&cs)); // CreateWindow内部不经消息循环,直接调用WndProc
// 执行其它操作
return hWnd; }
DispatchMessage的伪代码可能如下: LRESULT DispatchMessage(const MSG *lpmsg) { // 执行其它操作
WndProc(lpmsg->hwnd, lpmsg->message, lpmsg->wParam, lpmsg->lParam);
// 执行其它操作
return 0; }
其实,早在CreateWindow时,Windows就调用WndProc并传递了WM_CREATE消息。WM_CREATE是程序收到的第一个消息。类似的,ShowWindow可能会触发WM_SIZE和WM_SHOWWINDOW消息。
被投递到消息队列中的消息叫“队列消息”,不在队列中的为“非队列消息”。一个消息是队列消息还是非队列消息并不是绝对的,比如WM_PAINT可以是队列消息(窗口显示之后调用InvalidateRect,就会往消息队列中增加一条WM_PAINT消息),也可以是非队列消息(上面UpdateWindow发出的那个),对于程序员,大部分时间,不用关心某个消息是队列消息,还是非队列消息,你只需知道它们都最终是交给WndProc窗口过程、以同步方式 处理的就可以了。
在CreateWindow时,指定窗口初始位置和尺寸时,CW_USEDEFAULT参数表示使用默认值,默认值意味着:Windows会将连续新建的窗口的左上角位置沿水平方向和垂直方向分别作步长为1的偏移。