专栏导航
上一篇:第4章,[标签 Win32] :在 TextOut 案例1 中使用 wsprintf
下一篇:无
本节前言
对于本节所讲解的知识,有可能,你会需要时不时地参考本专栏的其它文章。真的遇到了需要参考之前的文章的知识点,请你自行查阅。
我呢,也会提到一部分的参考课节。但是呢,你不应该依赖于我的主动提及。最好呢,你自己能够多去了解和查看本专栏目录。
想要学习本节的知识,你需要首先已掌握 wsprintf 函数的使用方法。参考链接如下。
最好呢,你也学过了本专栏前几节的知识。这些个推荐先修课节如下。
参考课节:第4章,[标签 Win32] :TextOut 测试案例1,李白诗一首
参考课节:第4章,[标签 Win32] :在 TextOut 案例1 中使用 wsprintf
我们开始本节的学习任务。
一. GetSystemMetrics 函数
本节,我们在代码中,会用到 GetSystemMetrics 函数。因此,在这里,我们来讲一讲这个函数。
我们来看一看这个函数的描述。
int GetSystemMetrics(int nIndex);
此函数的返回值类型和参数类型都是 int 型。参数值为索引值,以 SM_ 为前缀。
此函数的作用,是获得索引值 nIndex 所对应的某一系统属性的信息。获得的返回值数值,都以像素为单位。
索引值 nIndex,可以采用许多的值。本节,我们需要用到的,是两个索引值,它们是 SM_CXSCREEN 和 SM_CYSCREEN 。
当索引值为 SM_CXSCREEN 时,此函数返回的,是屏幕的像素宽度。
当索引值为 SM_CYSCREEN 时,此函数返回的,是屏幕的像素高度。
大家用不同的电脑来运行本节程序时,你的结果,很可能会是不同的。
在下一分节,我给出本节程序。
二. 本节程序代码
/*----------------------------------------------------
SYSMETS1.C -- System Metrics Display Program
(c) Charles Petzold, 1998
水饺编程改编版,2026
----------------------------------------------------*/
#define WINVER 0x0500
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("SysMets_me01");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("窗口类注册失败!"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, TEXT("Get System Metrics 预备版1号"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
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 cxChar, cxCaps, cyChar;
static int cxSysScreenWidth, cySysScreenHeight;
HDC hdc;
int nLength;
PAINTSTRUCT ps;
TCHAR szBuffer[30];
TEXTMETRIC tm;
switch (message)
{
case WM_CREATE:
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
cxChar = tm.tmAveCharWidth;
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
cyChar = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC(hwnd, hdc);
cxSysScreenWidth = GetSystemMetrics(SM_CXSCREEN);
cySysScreenHeight = GetSystemMetrics(SM_CYSCREEN);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
nLength = wsprintf(szBuffer, TEXT("屏幕像素宽度:%d"), cxSysScreenWidth);
TextOut(hdc, 10 * cxCaps, cyChar * 6, szBuffer, nLength);
nLength = wsprintf(szBuffer, TEXT("屏幕像素高度:%d"), cySysScreenHeight);
TextOut(hdc, 10 * cxCaps, cyChar * 10, szBuffer, nLength);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
我给本节程序代码的解决方案名称与项目名称,都命名为 SysMets_me01 。而将 C++ 源文件,命名为 WinMain.cpp 。
请大家自行建立解决方案,添加源文件和编译程序。
运行结果如下。
图1
由图1 可知,我所用的电脑显示器,它的像素宽度为 1536,像素高度为 864 。
三. 代码讲解
(一)WM_CREATE 消息
在 WM_CREATE 消息的处理代码中,我们调用 GetTextMetrics 函数,获取了当前设备环境中的字体的信息。
代码【cyChar = tm.tmHeight + tm.tmExternalLeading;】的作用,是获取字体的高度。
我们来看下面的截图中,画红色框线部分的代码。
图2
行号 71 行,以平均字符宽度 tmAveCharWidth 来设置 cxVhar 变量。
代码 72 行,设置大写字符的平均宽度,将此平均宽度值保存在变量 cxCaps 中。
TEXTMETRIC 结构的成员变量 tmPitchAndFamily 的位 0 的值,决定着所使用的字体为等宽字体还是变宽字体。若为 0,则是等宽字体。若为 1,则是变宽字体。
对于等宽字体,大写字符的平均宽度与字符的平均宽度是相等的。也就是,对于等宽字体,我们将 cxCaps 赋值为 cxChar 就可以了。
对于变宽字体,大写字符的平均宽度为字符平均宽度的 1.5 倍。也就是,对于变宽字体,我们将 cxCaps 设置为 cxChar 的 1.5 倍。
1.5,这是一个小数,在程序中,需要用浮点型数来表示。而在 Windows 程序中,我们更多地,是使用整数。为了将 cxCaps 设置为 cxChar 的 1.5 倍,我们可以这样子写代码。
cxCaps = cxChar * 3 / 2;
这样一来,图2 中的 71 行到 73 行的代码,我们就都讲完了。
进行这样的设置,是因为,我们在后面,会调用 TextOut 函数,需要用字符的宽度高度信息,来设置书写位置。
图2 中,71 行到 73 行,这几行代码,你可以将其看作是一种固定搭配。在很多的程序中,都会用到这样的几行代码。
我们来总的看一看 WM_CREATE 消息的处理代码。
图3
在调用 GetTextMetrics 函数之前,我们调用了 GetDC 函数并保存了返回值 hdc 。调用 GetDC 是因为,我们需要在 GetTextMetrics 函数中使用设备环境句柄 hdc 。而在设置完了字符宽度与高度变量 cxChar,cxCaps 和 cyChar 之后,我们就调用了 ReleaseDC 函数。
在这里,调用 GetDC 以获取设备环境句柄的步骤,我认为大家应该都能够理解。重点的地方,其实是在 ReleaseDC 函数的调用时机上。
在 WM_CREATE 消息中,我们获取设备环境句柄,就是为了调用 GetTextMetrics 函数,将设备环境中的字体信息拷贝到 TEXTMETRIC 结构体变量 tm 中。因此,在调用完 GetTextMetrics 函数之后,其实我们是可以立即调用ReleaseDC 函数,以释放设备环境具备的。不过呢,像图3 那样子,在设置完了字符宽度与高度变量 cxChar,cxCaps 和 cyChar 之后,再去调用了 ReleaseDC 函数以释放设备环境句柄,也是可以的。早几行晚几行,都是可以的。
重点在于,在调用了 GetDC 函数以后,你在使用完了设备环境句柄以后,要及时地调用 ReleaseDC 函数来释放设备环境句柄。
Windows 程序设计的一个有趣的地方,就在于,我们在用完了某些个资源以后,要及时地释放掉它们,不可以一直占用着资源不松手。
释放了设备环境句柄以后,接下来,在 77 和 78 行,我们调用 GetSystemMetrics 函数两次,分别以 SM_CXSCREEN 和 SM_CYSCREEN 为索引值,来分别获取屏幕像素宽度和屏幕像素高度,并将结果保存在变量 cxSysScreenWidth 和 cySysScreenHeight 中。
请注意,在窗口过程函数的变量申请区域中,我们将变量 cxSysScreenWidth 和 cySysScreenHeight 设置为了 static 类型。这是因为,我们需要在 WM_CREATE 中为这俩变量赋值,而在 WM_PRINT 消息中使用 WM_CREATE 消息中所设置的值。如果不声明为 static 类型,则 WM_CREATE 消息中设置好的值,在 WM_PRINT 消息中就不可见了。
到了这里,WM_CREATE 消息的处理代码,我们就算是讲完了。
(二)WM_PRINT 消息
我们来看一看 WM_PRINT 消息的处理代码。
图4
wsprintf 函数的使用方法,我们以前有讲过。忘了的话,请参阅下述链接。
代码 85 行,我们调用 wsprintf 函数,来对格式字符串 TEXT("屏幕像素宽度:%d") 进行格式化替换,并将转换结果存放在缓冲区 szBuffer 之中。szBuffer 缓冲区,我们有在窗口过程函数的开头的变量声明区中声明,如下所示。
图5
wsprintf 函数的返回值,是转换后的字符串的有效字符个数,不包含结尾的 NULL 字符。我们将这个字符数,保存在了变量 nLength 中。
然后呢,我们在 86 行,调用了 TextOut 函数。绘制文字时,起始的 x 和 y 坐标分别为 10 * cxCaps 和 cyChar * 6,cxCaps 和 cyChar 我们均已在 WM_CREATE 中设置过。待绘制字符的缓冲区为 szBuffer,绘制字符数为 85 行保存的字符数 nLength 。在 86 行中,szBuffer 中已经保存了 85 行的格式换转换结果。
86 行执行完的时候,程序的客户区中也就绘制好了 szBuffer 缓冲区中的字符了。
87 行和 88 行的逻辑与 85、86 行差不多,只不过是将绘制文字的起始 y 坐标给修改了一下,变为了 cyChar * 10 。
到了这里,WM_PRINT 消息的处理,我们就讲完了。
其余的消息,我们之前的课节中讲过,在此不再赘述。忘了的,请参阅之前的相关课节。
总 结
本节内容不难,看一看,应该是容易看懂的。
专栏导航
上一篇:第4章,[标签 Win32] :在 TextOut 案例1 中使用 wsprintf
下一篇:无