第4章,[标签 Win32] :文本的格式化,等待完善

专栏导航

上一篇:第4章,[标签 Win32] :文本尺寸的度量

回到目录

下一篇:无

本节前言

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

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

本节内容,来自对佩措尔德教材内容的大段抄录。

我们开始。

一. 本节正文

Windows 运行时,系统字体不会变化。因此,应用程序只需要调用一次 GetTextMetrics 函数。最好的时机,是在窗口过程处理 WM_CREATE 消息时。 WM_CREATE 消息是窗口过程收到的第一条消息。当应用程序在 WinMain 中调用 CreateWindow 时,Windows 将发送一条 WM_CREATE 消息给窗口过程。

假设你的 Windows 程序需要在客户区内显示几行文本,你需要获取字符的宽度和高度。在窗口过程中,可以定义两个变量来保存字符的平均宽度和总高度。

static int cxChar, cyChar;

变量名的前缀 c 代表 "count",也就是 "个数" ,在这里代表像素数。和 x 与 y 结合,分别表示宽度和高度。它们被定义为静态变量,因为它们必须在 Windows 处理其它消息(比如 WM_PRINT)时还有效。它们也可以定义为全局变量,也就是定义在所有函数之外。

忘了 static 语法的,我们可以来略微复习一下。请看下面的函数。

复制代码
func_01(int * py)
{
    int x = 3;
    x++;
    *py = 3 * x + 1;
}

这段代码,其实还是不难的。假定,我们在主函数中调用了 func_01 函数三次,则两次调用的结果是一样的,每一次调用结束以后,*py 的值都是 13 。

然后呢,我们将上面的代码修改一下,修改为下面的样子。

复制代码
func_01(int * py)
{
    static int x = 3;
    x++;
    *py = 3 * x + 1;
}

修改之后的代码,相比之前的版本,我们只是将 x 的声明加上了 static 关键字。可是,加上了关键字之后,执行机制就会有所不同了。

加上了 static 之后,变量 x 成了一个局部静态变量。它在程序运行的整个周期里面都有效,但只能在 func_01 内部引用它。

我们依然假定,我们在主函数中调用 func_01 函数三次。

第一次调用之后,*py 的值为 13 。第二次调用之后,*py 的值为 16 。第三次调用之后,*py 的值为 19 。

这里,我简单地领大家复习了一下,在函数体中,用 static 关键字来静态声明一个局部变量的用法。我相信,你能够理解我的意思,尽管我的概念表述不精确。

我们接着讲。

以下的 WM_CREATE 代码显示了怎样获取系统字体的宽度和高度。

复制代码
case WM_CREATE:
    hdc = GetDC (hwnd);

    GetTextMetrics (hdc, &tm);
    cxChar = tm.tmAveCharWidth;
    cyChar = tm.tmHeight + tm.tmExternalLeading;

    ReleaseDC (hwnd, hdc);
    return 0;

注意,这里在 cyChar 中包括了 tmExternalLeading 。尽管在我前面用到的系统字体中,这一项通常是 0,但考虑到它可能不是 0,所以还是要包括它,因为这会使行与行之间更加清晰易读。在窗口中,每一行文本显示在上一行文本下方 cyChar 个像素处。

通常需要显示的是格式化的数字和字符串。如第 2 章所述,不能用传统的工具(例如 print 函数),但是可以使用 Windows 版的字符处理函数 ------ wsprintf 。这个函数与 printf 函数功能类似,区别在于格式化之后的字符保存在一个字符串里。这个字符串可以通过 TextOut 函数输出到窗口中。方便的是,wsprintf 函数的返回值是字符串中包含的字符的个数。

注意哦,是字符个数,而不是字符串所包含的字节数。而且,这一个数,并不包含字符串结尾的 NULL 字符。

可以将 wsprintf 的返回值传递给 TextOut 函数,作为 iLength 参数。下面的代码是典型的 wsprintf 和 TextOut 的组合。

复制代码
int iLength;
TCHAR szBuffer[40];
[其他程序行]
iLength = wsprintf (szBuffer, TEXT("the sum of %d and %d is %d"),
                iA, iB, iA + iB);
TextOut (hdc, x, y, szBuffer, iLength);

对于上述代码,wsprintf 的返回值会由 iLength 来接收,而 iLength 又会作为 TextOut 的参数,因此,我们可以省略 iLength 变量的定义,而将 TextOut 函数写作下面的模样。

复制代码
iLength = ;
TextOut (hdc, x, y, szBuffer, 
        wsprintf (szBuffer, TEXT("the sum of %d and %d is %d"),
                iA, iB, iA + iB));

这么写,看上去并不直观,而功能是一样的。

我个人更喜欢分着写的方式,因为这样子写,逻辑上更为清楚。合起来写的方式,虽说代码简略了一点,但是,读起来也会更为费力一些。

我个人,还是更喜欢让代码容易阅读一些。不过,你还是需要能够理解这样的代码。

在调用一个函数的时候,首先呢,会将此被调函数的各个实参压入栈中,参数入栈的顺序,是从右到左。也就是,在参数入栈之时,会先将右侧的参数压入栈中,然后再将左边的参数压入栈中。全部参数入栈以后,才是调用此被掉函数,进入被调函数的函数体之中。

对于上面的代码块的代码,本来应该是从右到左,依次将参数给压入栈中的。然而,当想要将最右边的参数压入栈中的时候,却发现,最右边的参数部分,不是一个已确定的数值,而是一个函数调用,此函数调用尚未有返回值。因此,计算机将先去执行 wsprintf 函数,从 wsprintf 函数返回之后,返回值就有了。有了返回值以后,计算机会将 wsprintf 的返回值压入栈中,然后依次将 szBuffer,y,x 和 hdc 压入栈中,最后才是去执行 TextOut 函数。

我这样子的表述,不知道,你能否理解。多看几遍吧。此处,务必理解。

这算是一个小的攻坚战,硬骨头,务必将其啃下来。

在我的专栏里面,如果呢,我所采用的代码,是佩措尔德的代码,那么,佩措尔德使用的合写的、逻辑复杂一些的代码,那么,我也就这么来写,不会对这种合写的风格作出修改。

但是呢,如果是说,我自己编写某一个程序的话,那么,我尽量采用分着写,容易理解的风格,来书写代码。

你呢,其实最好还是说,你能够把这种复杂的、合写的、不太好理解的代码风格,给啃下来。但是呢,当前阶段,我还是希望,尽量地,分解难度,希望尽量地,能够让你学得会,看得懂。所以呢,我自己写的代码,我会尽量地采用容易理解的、分着写的写法。

如果你只能看懂分着写的、容易理解的代码,而看不懂合写的、不好理解的代码,那么,你还是需要多锻炼。编程学习的路上,难免会有人喜欢用复杂风格的代码。自己的能力上来了,难的,我们可以应付,简单的,也能够应付,那不是很好吗?

总 结

本节的主体内容,是对佩措尔德教材的大段抄录。我自己呢,还是在写教程的时候,加上了我自己的一点讲解,以便让你好理解一些。

以后,有机会,我应该还是会尽量地去修改内容,以便,我以我的讲法,分解难度,让学员更容易学。

专栏导航

上一篇:第4章,[标签 Win32] :文本尺寸的度量

回到目录

下一篇:无

相关推荐
似霰1 小时前
Android 平台智能指针使用与分析
android·c++
软件资深者2 小时前
系统引导修复(免费)系统不能启动,一键修复系统引导
windows·电脑·系统安全·windows11
阿猿收手吧!2 小时前
【C++】实现自旋锁:三种高效实现与实战指南
服务器·网络·c++
代码游侠2 小时前
C语言核心概念复习(三)
开发语言·数据结构·c++·笔记·学习·算法
love530love2 小时前
Windows 11 配置 CUDA 版 llama.cpp 并实现系统全局调用(GGUF 模型本地快速聊天)
人工智能·windows·大模型·llama·llama.cpp·gguf·cuda 加速
明洞日记2 小时前
【软考每日一练030】软件维护:逆向工程与再工程的区别与联系
c++·软件工程·软考·逆向工程
郝学胜-神的一滴2 小时前
深入Linux网络编程:accept函数——连接请求的“摆渡人”
linux·服务器·开发语言·网络·c++·程序人生
茉莉玫瑰花茶2 小时前
C++ 17 详细特性解析(3)
开发语言·c++
Herbert_hwt3 小时前
数据结构与算法绪论:为何学、学什么、如何避坑
c语言·数据结构·算法