第2章,[标签 Win32] :格式化的消息框

发布于:2025-07-05 ⋅ 阅读:(16) ⋅ 点赞:(0)

专栏导航

上一篇:第2章 :在 Windows 中使用 vsprintf 函数族

回到目录

下一篇:无

本节前言

本节的学习内容,需要以上一节的内容为先修课节。而上一节的内容,又需要许多的先修课节。因此,在这里,我把这些个,你需要首先学好的先修课节给列出来。有可能呢,列举得不全面。如果列举得不全,还请大家去目录里面,寻找相应的课节。

或者,你也可以与我联系。

先修课节如下。

参考课节:编程技能:格式化打印01,vsprintf 函数族简介

参考课节:编程技能:格式化打印02,vsprintf

参考课节:编程技能:格式化打印03,printf

参考课节:编程技能:格式化打印04,sprintf

参考课节:编程技能:格式化打印05,格式控制符

参考课节:第2章 :在 Windows 中使用 vsprintf 函数族

在上一节的里面,我谈到了,在 Windows 里面,不能够使用 printf 和 scanf 。可是,可能还是有些人,喜欢 C 语言里面的 printf 函数。在本节呢,我们尝试着,实现一个类似的东西。

在 printf 里面,它是把格式化之后的字符串,显示在控制台窗口里面。在本节呢,我们的程序是,将格式化之后的字符串,显示在消息对话框里面。

所以,本节的标题,才叫做格式化的消息框。

一.    准备函数

本节呢,我们需要来学习一个准备性质的函数。这个函数的名字,叫做 GetSystemMetrics 。我们来看一下它的函数声明。

int GetSystemMetrics(int nIndex );

这个函数的返回值,是一个 int 类型的整数。参数只有一个,也是 int 类型,是一个索引值。

在这里呢,这个索引值,具体地,可以是哪些值,我们暂时不去细讲。因为,在以后,我们自然会更为详细地去介绍它,包括较为全面地介绍它的各个索引值。

然而呢,本节,我们只需要了解其中的两个索引值即可。

这两个索引值,分别是 SM_CXSCREEN 和 SM_CYSCREEN 。

当传入的索引值为 SM_CXSCREEN 时,GetSystemMetrics 函数返回的值,为视频显示器的像素宽度。世间的显示器有很多,规格有很多,那么,这个函数显示的是哪一款显示器的规格呢?运行这个函数的计算机所配备的,是什么显示器,那么,返回值就是哪个显示器的像素宽度。

所以呢,有可能,我这里运行的结果是 1024,而你哪里,可能会是 1280,1440,或者 1920,等等。

我们再来看另一个索引值。

当传入的索引值为 SM_CYSCREEN 时,GetSystemMetrics 函数返回的值,为当前计算机所配备的视频显示器的像素高度。运行此函数时,计算机配备的是什么显示器,返回值就是哪个显示器的像素高度。

这样一来呢,准备函数,我们就算是讲完了。

我们接着往下讲。

二.    参考函数,printf

本节,我们想要实现的是一个格式化的消息框。

直接让我们自己写的话,我觉得,可能会有点费劲儿。

所以呢,我首先列出来一个参考代码。然后呢,大家根据参考代码,来写正式的格式化的消息框程序。

我们来看一看,本节前言中,讲解 printf 的那一节,所给出的,printf 的实现代码。

static char printbuf[1024];
extern int printf(const char *fmt, ...)
{
	va_list args;
	int i;
 
	va_start(args, fmt);
	i = vsprintf(printbuf, fmt, args);
	va_end(args);
	write(1, printbuf, i);
	
	return i;
}

这就是 printf 的实现代码。这是我从 Linux 0.12 内核里面,将其摘录出来,并作了一点改动,而形成的代码。

接下来呢,我们就尝试着,去修改它,来形成格式化的消息框代码。

三.    格式化的消息框

#include <Windows.h>
#include <tchar.h>
#include <stdio.h>

static TCHAR MessageBoxBuf[1024];
int MessageBoxPrintf(LPCTSTR szCaption,
	LPCTSTR szFormat, ...)
{
	va_list args;
	int res;
	
	va_start(args, szFormat);
	wvsprintf(MessageBoxBuf, szFormat, args);
	va_end(args);
	
	res = MessageBox(NULL, MessageBoxBuf, szCaption, 0);

	return res;
}

int WINAPI WinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance, PSTR szCmdLine,
	int iCmdShow)
{
	int cxScreen, cyScreen;

	cxScreen = GetSystemMetrics(SM_CXSCREEN);
	cyScreen = GetSystemMetrics(SM_CYSCREEN);

	MessageBoxPrintf(_T("屏幕尺寸"),
		_T("屏幕宽度为 %d 像素,屏幕高度为 %d 像素"),
		cxScreen, cyScreen);

	return 0;
}

上面的程序,我们首先来看 WinMain 函数。它的意思是,通过分别传递 SM_CXSCREEN 和 SM_CYSCREEN 参数来 GetSystemMetriucs 函数,求得运行此程序的计算机所配备的视频显示器的像素宽度和像素高度。

在求得了像素宽度和像素高度以后,通过调用我们自定义的 MessageBoxPrintf 函数,来将屏幕尺寸给打印出来。

接下来呢,我们来看 MessageBoxPrintf 函数。我们首先来看一看它的函数声明。

int MessageBoxPrintf(LPCTSTR szCaption, LPCTSTR szFormat, ...);

返回值为 int 类型。

参数列表里面,前两个参数,类型为 LPCTSTR,其实就是【const TCHAR *】类型。第三个参数,为可变参数列表。

关于 LPCTSTR,我们在之前有去讲解过。请大家参考如下信息,来了解 Windows 字符类型。

参考课节:第2章 :Windows 字符数据类型

然后呢,在形参变量方面,前两个参数,都是以 sz 作为前缀。

我们之前,有去讲解过匈牙利标记法。在这里,我再次简单地来谈一谈匈牙利标记法。

匈牙利标记法,它是变量命名规范的一种。

它的基本内容是,用一个或者多个小写字母作为前缀,来表明变量的数据类型,随后是英文单词的组合,每一个英文单词的首字母为大写。英文单词组合,表达的是变量的含义。

比如说,有一个变量,为 int 类型,含义是高度。int,可以用前缀 i 或者 n 来表示。而高度,英文单词为 Height,组合起来,这个变量名,就是 nHeight 或者 iHeight 。

而与本节有关的前缀,是 sz 。通常,用前缀 sz 表示【以零结尾的字符串】这一数据类型。字符串的话,可以是 char *,可以是 WCHAR *,可以是 const char *,等等。只要是以零结尾的字符串,都可以用前缀 sz 来标识。

接下来,我们来看各个参数。

第一个参数,szCaption,它其实是待显示的消息框的标题。Caption 是【标题】的意思。谁调用了这个函数,谁就来设置这个消息框标题。先回头看一看 WinMain 函数,在 WinMain 函数里面,我们看到,调用 MessageBoxPrintf 函数时,传递的第一个参数,为【_T("屏幕尺寸")】,所以呢,我们是统一地,给这个待显示的消息框,设置了名为【屏幕尺寸】的标题。

第二个参数,szFormat,它是格式字符串。第三个参数,为可变参数列表。MessageBoxPrintf 函数若是不看第一个参数的话,则它的第二个参数和第三个参数的组合,与 printf 函数的第一个参数和第二个参数的组合是很像的,都是说,先是格式字符串指针,然后是可变参数列表。

在 printf 函数的外面,我们给它设置了一个字符缓冲区,代码如下。

static char printbuf[1024];

在 MessageBoxPrintf 函数的外面,我们也给它设置了一个字符缓冲区,代码如下。

static TCHAR MessageBoxBuf[1024];

我们的 printf 函数有一个可变参数列表型变量,代码如下。

va_list args;

我们的 MessageBoxPrintf 函数也有一行与之相同的可变参数列表型变量,代码都是相同的。

printf 函数申请了一个 int 型变量,代码如下。

int i;

我们的 MessageBoxPrintf 函数也有一行,用来申请 int 型变量,代码如下。

int res;

printf 函数里面,有着三行代码,分别调用了 va_start 宏函数,vsprintf 函数,还有 va_end 宏函数。代码如下。

va_start(args, fmt);
i = vsprintf(printbuf, fmt, args);
va_end(args);

我们的 MessageBoxPrintf 函数也有三行代码,分别用来调用 va_start 宏函数,wvsprintf 函数,还有 va_end 宏函数。代码如下。

va_start(args, szFormat);
wvsprintf(MessageBoxBuf, szFormat, args);
va_end(args);

在这里,printf 函数和 MessageBoxPrintf 函数都调用了 vs_start 宏函数和 va_end 宏函数。所不同的是,在格式化操作里面,printf 函数调用的是 vsprintf 函数,而 MessageBoxPrintf 函数调用的是 wvsprintf 函数。然而,vsprintf 函数和 wvsprintf 函数,函数名很像,参数个数也相同。

并且呢,我们在上一节,也讲解过 vsprintf 函数的各个版本。我们再一次将那个表格贴在下面。

ASCII 版 Unicode 版 兼容版
sprintf 类
标准版 sprintf swprintf _stprintf
最大长度版 _snprintf _snwprintf _sntprintf
Windows 版 wsprintfA wsprintfW wsprintf
vsprintf 类
标准版 vsprintf vswprintf _vstprintf
最大长度版 _vsnprintf _vsnwprintf _vsntprintf
Windows 版 wvsprintfA wvsprintfW wvsprintf

请问,vsprintf 函数与 wvsprintf 函数有何区别?一个是 C 函数中的函数,专门用来处理 ASCII 字符串的格式化。另一个是 Windows API 中的函数,并且它是一个兼容版的函数,既可以用来处理 ASCII 字符串的格式化,也可以处理 Unicode 字符串的格式化。两个函数的参数个数一样,参数类型,则是差不多吧。

想要理解 wvsprintf,你需要先理解 vsprintf 的逻辑。然后呢,将 vsprintf 中的ASCII 字符与字符串,都给 TCHAR 化,然后你就理解了 wvsprintf 了。

不过,如何学好 vsprintf,这个则是需要你下一番功夫了。在本节前言中,我给出了 vsprintf 的课节,请你务必学好它。

这样一来,va_start 宏函数,格式化转换函数 vsprintf 或 wvsprintf,va_end 宏函数,这三行调用,我们就差不多说完了。

接下来呢,在 printf 函数里面,是将经过 vsprintf 格式化转换之后,并且存放在字符缓冲区 printbuf 中的字符串,将其作为参数传递给 write 函数,把结果给打印出来,代码如下。

write(1, printbuf, i);

通过这个 write 函数,printf 函数将字符缓冲区 printbuf 中的格式化转换结果,给输出显示在控制台窗口里面。

与此相对地,在 MessageBoxPrintf 函数里面,将形参 szCaption 所接收的消息框标题,与经过 wvsprintf 格式化转换之后,并且存放在字符缓冲区 MessageBoxBuf 中的字符串,将其作为参数传递给 MessageBox 函数,将结果以消息框的形式输出显示,代码如下。

res = MessageBox(NULL, MessageBoxBuf, szCaption, 0);

printf 函数里面的 int 型变量 i,接收的是 vsprintf 的返回值,而 MessageBoxPrintf 函数里面的 int 型变量 res,接收的是 MessageBox 函数的返回值,并且都将各自申请的 int 型变量给返回了。

四.    编译运行

接下来,请你新建一个 Windows SDK 程序解决方案,在里面添加一个名为 WinMain.cpp 的源文件,并且将第三分节开头的代码块中的代码,复制到 WinMain.cpp 里面,然后呢,编译运行它。

建立 Winodws SDK 解决方案,以及添加源文件,编译运行的方法,你如果不会,可以参考下述文章链接。 

参考课节:使用 VS2010 编写 C语言程序

参考课节:用 VS2019 编写C语言程序

推荐你学习 VS2019 的操作方法。

因为,现在是 2025 年了,能用 VS2019 来解决的,尽量用 VS2019 吧。或者,你如果安装了 VS2022,用它也行。能用新的,就尽量用新的。

在我这里,运行结果如下。

图1

我这里,大概是因为,笔记本电脑的屏幕小了一点,所以呢,屏幕的宽度为 1536 像素,高度为 864 像素。在你那里,有可能,屏幕的宽度高度都比我的大,也有可能比我的小。

结束语

这一节,希望你能够学好。如果未能够学好,务必要及时复习与熟悉相关课节,将本节的知识给搞清楚。

看过佩措尔德先生的教材的同学,可能会发现,我这里的代码,与佩措尔德先生有所不同。佩措尔德先生在格式化操作上,用的是 _vsntprintf 函数,而我这里用的是 wvsprintf 函数。

其实呢,用哪个都可以。最好呢,你两个都会用。

本节没有使用 _vsntprintf 函数。你可以自行修改本节代码,将 wvsprintf 替换为 _vsntprintf 函数,并且修改相关的调用参数。

如果,本节你能够学会的话,那么,这说明,格式化打印类的函数,你就掌握得差不多了。

希望你能够学好本节。加油。

专栏导航

上一篇:第2章 :在 Windows 中使用 vsprintf 函数族

回到目录

下一篇:无