Win32应用程序可以分成两大类:控制台程序和Windows窗口界面程序。其中控制台程序的入口是main(或_tmain),而窗口界面程序的入口函数是WinMain(或_tWinMain)函数。在使用VS2010创建工程时我们也会看到Win32功能的两种类型:Win32 Console application和Win32 Project,后者就是我们将要讨论的窗口界面程序。
Windows为用户提供了一系列的接口用于开发Windows界面程序,即使我们常见MFC也是对这些接口的包装,虽然这些接口用起来很麻烦,但是我们还是有必要了解它们,这样才能让我们真正对界面开发有一个深入的认识。
首先,我们要明白什么是消息以及消息队列。消息就是Windows系统检测到用户的一些操作(比如鼠标、键盘)并将这些操作的信息进行包装成一个结构体对象传递给应用程序。而消息队列就是Windows保存消息的一个容器,应用程序需要创建消息循环来完成监测该队列并获取处理Windows所捕获到的消息。消息循环即是我们后面会提到的GetMessage/DispatchMessage等函数。
我们用VS2010创建一个默认的窗口工程,我们会发现直接调用这些底层接口而实现的一个窗口。为了创建并显示一个窗口,我们需要以下几步:
1. 定义窗口类WndClass(或WndClassEx),其中包括设置窗口的过程回调函数,鼠标、背景、图标、菜单以及该窗口的类型名称(类似车的品牌)。
2. 注册该窗口类型RegisterClass,就像是向windows注册该类型的使用权。
3. 创建自己的窗口window:CreateWindow,该函数返回所创建窗口的句柄;
4. 最后就是现实我们的窗口了ShowWindow。
5. 为了使窗口能够捕获并处理用户的点击等操作,还需要在我们的主线程里创建消息循环并完成窗口过程函数对消息的处理。
下面就用代码来说明每个步骤的内容:
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WWINDOW));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = 0;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
RegisterClassEx(&wcex);
HWND hWnd;
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//Main结束。
//窗口过程函数:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
//… …
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
看到代码我们发现,窗口的消息循环就是一个无限循环一直在捕获消息并分发给窗口,另外消息体MSG(详见MSDN)包含了对应的窗口句柄HWND,消息类型(UINT类型,比如WM_COMMAND、WM_DESTROY、WM_KEY等),鼠标位置Point以及消息其他附属内容WPARAM和LPARAM(这两个参数也是我们最常见到的函数参数)。