下面的程序使用所介绍的这些知识在用户区中显示多行文本。
// 2-5.c
#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int PASCAL WinMain(
HINSTANCE hInstance, // 应用程序的实例句柄
HINSTANCE hPrevInstance, // 该应用程序前一个实例的句柄
LPSTR lpszCmdLine, // 命令行参数串
int nCmdShow ) // 程序在初始化时如何显示窗口
{
char szAppName[] = "DispText";
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
if (!hPrevInstance) {
// 该实例是程序的第一个实例,注册窗口类
wndclass.style = CS_VREDRAW | CS_HREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
// 如果注册失败
return FALSE;
}
// 对每个实例,创建一个窗口对象
hwnd = CreateWindow(
szAppName,
"Display Text",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL );
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while( GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int xChar, yChar;
char szBuffer[256];
int line;
HDC hDC;
PAINTSTRUCT ps;
TEXTMETRIC tm;
FILE *fp;
switch(message)
{
case WM_CREATE:
hDC = GetDC(hwnd);
GetTextMetric(hDC, &tm);
xChar = tm.tmAveCharWidth;
yChar = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC(hwnd, hDC);
return 0L;
case WM_PAINT:
hDC = BeginPaint(hwnd, &ps);
line = 0;
if((fp = fopen("disptext.cpp", "r")) != NULL)
{
while(!feof(fp)) {
int i = 0;
char ch;
while((ch = fgetc(fp)) != '\n' && ch != EOF)
szBuffer[i++] = (char)ch;
TextOut(hDC, xChar, line*yChar, szBuffer, i);
line++;
}
fclose(fp);
}
EndPaint(hwnd, &ps);
return 0L;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
该程序运行后,在用户区中将显示本程序的部分内容。
当窗口对象被第一次创建时(调用函数CreateWindow()),Windows向窗口对象发送消息WM_CREAT,这是窗口对象接收的第一个(对应用程序有意义的)消息,我们一般使用这条消息进行与本窗口对象有关的初始化工作。例如,在本程序示例中,我们用于计算字符的宽度和高度(包括了空隙量)。由于使用函数GetTextMetric()需要一个设备对象句柄,我们使用函数GetDC()从Windows那里借用一个公用显示对象。
Windows只有有限的五个公用的显示设备对象供正在运行的所有的应用程序使用,这些显示设备对象是Windows的一个宝贵的资源,当一个窗口对象不再使用通过函数GetDC()借用的显示设备对象时,应使用函数ReleaseDC()将它归还给Windows,否则,其它窗口对象将由于不能借用到显示设备对象而不能绘制它的用户区。因此,函数GetDC()和ReleaseDC()要配合使用,当窗口对象使用GetDC借用了一个显示设备对象之后,在从窗口函数返回之前,应使用ReleaseDC函数归还所借用的显示设备对象。
函数GetDC/ReleaseDC用于在处理非WM_PAINT消息其间需要显示设备对象进行绘制时。在处理WM_PAINT消息其间应使用函数BeginPaint/EndPaint,因为使用GetDC/ReleaseDC不能清除应用程序的消息队列中的WM_PAINT消息。如果WM_PAINT消息不能被清除,则窗口对象会不断地接收到WM_PAINT消息。
设备对象总定义有一个裁剪区,使用函数GetDC()得到的设备对象将整个用户区作为裁剪区,因此,使用该设备对象所做的绘图不会出现在用户区之外。
2.6、小结
本章介绍了在用户区中显示信息的有关问题,同时介绍了消息WM_CREATE、WM_SIZE和WM_PAINT的意义和用途。WM_CREATE是在建立窗口对象时Windows发送给窗口对象的第一条对应用程序有意义的消息,我们可以利用这条消息进行程序的初始化工作。窗口对象接收到最后一条对应用程序有意义的消息是WM_DESTROY消息,我们可以使用这条消息完成一些程序终止之前的清除工作,例如释放内存对象等。根据应用程序的使用要求,利用其它消息完成一些别的处理工作,例如响应用户的输入操作和命令等。
WM_PAINT消息是一条非常重要的消息,它的作用是通知窗口对象用户区显示的内容已被破坏,需要重绘。虽然在处理任何消息时都可以绘制用户区,但将用户区的绘制工作集中于处理WM_PAINT消息的程序段中是合理的,这可以保证用户区中总显示出合适的内容。学习Windows程序设计,重要的内容之一是掌握Windows函数的功用和使用方法,但更重要的是掌握一条消息是怎么样产生的,窗口对象利用这条消息能做什么工作。
本章节附带的例程(4KB)