如今,编写Windows应用程序是再常见不过的事情了,但在Windows最初刚刚问世时并不是这样。当时,与MS-DOS应用程序一起成长起来的程序员认为Windows方法真的不可思议,好像旧有方法被彻底推翻。尽管精确的MS-DOS应用程序可以控制所有对象,在需要时可以调用操作系统功能,但Windows采用的是一种疯狂的途径!这种操作系统控制应用程序,并在想要驱动程序执行某些操作(例如,刷新UI或者执行菜单命令)时调用相应的应用程序。
这要求开发人员拥有较高的技能。使用C编程语言(曾经风靡一时的语言)在MS-DOS中编写最简单的“Hello, world”应用程序不费吹灰之力,程序代码如下所示:
#include <stdio.h>
main()
{
printf("Hello, world");
}
然而,为了取得相同的结果,Windows需要完成更多工作。它要求围绕单个printf函数编写“基架”代码,可以不太直观的方式调用它,如程序清单1-1所示。
程序清单1-1:Windows 3.1中的“Hello, world”程序(节选)
#include <windows.h>
/* 导出要被Windows调用的入口点 */
long FAR PASCAL _export WndProc(HWND, UINT, UINT, LONG)
/* 应用程序的入口点 */
int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdParam, int nCmdShow)
{
static char szApplication[] = "HelloW";
HWND hwnd;
MSG msg;
WNDCLASS wndClass;
/* 创建窗口类 */
if (!hPrevInstance)
{
wndClass.Style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
/* 为简便起见省略了部分代码行 */
wndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = szApplication;
RegisterClass(&wndClass);
}
/* 为该类创建窗口实例 */
hwnd = CreateWindow(szApplication,
"My Hello World Program",
WS_OVERLAPPEDWINDOW,
/* 为简便起见省略了部分参数 */
hInstance,
NULL);
/* 开始显示窗口 */
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
/* 管理消息循环 */
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg)
}
}
/* 处理消息 */
long FAR PASCAL _export WndProc(HWND hwnd, UINT message,
UINT wParam, LONG lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
DrawText(hdc, "Hello, world", -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hwnd, &pd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
该程序包含很多行代码,因为它通过仅提供低级操作系统函数的API编写。尽管上面的源代码很长,但是它并未揭示Windows的重要内部细节。所有细节仍然在Windows 8之中,当然,以一种改进的形式。
最开始,程序通过设置wndClass结构的字段并使用RegisterClass方法来创建窗口类。窗口类是一个概念,用于标识处理发送到窗口的消息的过程(称为窗口过程)。
程序使用注册窗口类创建窗口(通过CreateWindow方法),然后使用ShowWindow方法显示该窗口。UpdateWindow方法向窗口发送一条消息,以便重新绘制其UI。
该应用程序的灵魂在于消息循环,如下所示:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg)
}
该循环包含来自某个队列的消息,将按键转化为等效的消息(例如,就好像使用了鼠标),然后将它们分派到相应的窗口过程。
如果说消息循环是灵魂,窗口过程就是核心。在程序清单1-1中,消息循环调用WndProc。其message参数包含消息的代码(要处理的事件),然后通过一条switch语句将处理各条消息的代码段封装起来。
WM_PAINT消息通知窗口应该重新绘制自己。通过BeginPaint方法,程序获取设备上下文资源,以用于在窗口的客户端区域进行绘制。然后,使用该设备上下文在窗口中央编写“Hello, World”消息。ReleasePaint方法用于释放该设备上下文,因为它恰巧是系统中一种非常有限的资源。
可以想象,在当时,Windows开发是一件多么耗时而又让人感到痛苦的事情,因为程序员必须通过Windows API使用低级操作系统结构。