第4章,[标签 Win32] :TextOut 测试案例2

专栏导航

上一篇:第4章,[标签 Win32] :在 TextOut 案例1 中使用 wsprintf

回到目录

下一篇:无

本节前言

对于本节所讲解的知识,有可能,你会需要时不时地参考本专栏的其它文章。真的遇到了需要参考之前的文章的知识点,请你自行查阅。

我呢,也会提到一部分的参考课节。但是呢,你不应该依赖于我的主动提及。最好呢,你自己能够多去了解和查看本专栏目录。

想要学习本节的知识,你需要首先已掌握 wsprintf 函数的使用方法。参考链接如下。

参考课节:Windows 编程基础:wsprintf 函数

最好呢,你也学过了本专栏前几节的知识。这些个推荐先修课节如下。

参考课节:第4章,[标签 Win32] :文本的格式化

参考课节:第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 函数的使用方法,我们以前有讲过。忘了的话,请参阅下述链接。

参考课节:Windows 编程基础: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

回到目录

下一篇:无

相关推荐
i建模1 小时前
Omarchy挂载windows磁盘
linux·运维·windows
tianzhiyi1989sq2 小时前
C++工具库之PugiXML使用指南
java·数据库·c++
tankeven2 小时前
HJ98 喜欢切数组的红
c++·算法
adore.9682 小时前
2.22 oj基础92 93 94+U12
数据结构·c++·算法
消失的旧时光-19432 小时前
C++ 多线程与并发系统取向(四)—— std::condition_variable:线程协作与生产者消费者模型(类比 Java wait/notify)
开发语言·c++
精彩极了吧2 小时前
C++基础知识-(②)面向对象(上)
c++·类和对象·封装·this指针·类的默认成员函数·赋值运算符重载
三水彡彡彡彡2 小时前
深入理解指针:常量、函数与数组
c++·学习
你好!蒋韦杰-(烟雨平生)2 小时前
Opengl模拟水面
c++·游戏·3d
Rhystt2 小时前
代码随想录第二十六天|669. 修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树
数据结构·c++·算法·leetcode