仍在使用 VC6 的同学注意了!

前言

VC6,全称 Visual C++ 6.0,诞生于 1998 年,恐怕是目前唯一一款寿命超过 20 岁还仍在大量使用的软件。
因为 VC6 真的太好用了!

但 20 多年造成的科技差距是你必须要知道的。如果你还在用 VC6,请务必读一下本文。

VC6 的问题

1. 对新系统支持差,对调试支持不好

新买的电脑都是 Windows 10 操作系统,VC6 在该系统下,勉强可以通过管理员权限进行调试。但是在调试的时候,如果中断调试,将会造成锁定 exe 无法再次编译的问题,必须重启 VC6 才可以解决。

学习编程时,调试是非常重要的步骤,通常调试的时间会超过代码编写时间的两倍以上,可见调试有多么重要。而“中断调试、再次编译”是非常常见的操作步骤,如果对这个步骤的支持有问题,那会直接影响编程的学习。“不会调试,等于不会编程”,这句话一点也不夸张。

如果用 VC6 是因为电脑配置比较低,那么推荐改用 VC2010 Ex

...

使用程序代码输出常见的字体图标(标志图片)

前言

程序中经常会使用一些常规的标志图片,例如,音量开关、各种方向箭头、扑克牌花色、邮件、电话、磁盘、笑脸/哭脸、五星等等。通常做法是,制作好图片,然后在特定的位置将图片显示出来。本文讲述如何通过输出文字的形式将这些常规的标志图片进行输出。

这种以文字方式表示的表示图片,一般称作:字体图标。

使用方法

简单来说:Windows 内置了许多字体图标的字库,只需要设置为相应字体,然后再输出相应字符即可。不过多讲解,看代码吧,以下代码会画出一个电话机:

#include <graphics.h>
#include <conio.h>

int main()
{
	initgraph(640, 480);

	settextstyle(60, 0, L"Wingdings");
	wchar_t c = 0x28;		// 0x28 是电话机在 Wingdings 字体中的对应编码
	outtextxy...

打开帮助文件 EasyX_Help.chm 后,内容为空白的解决办法

有些同学遇到这样的问题,打开 EasyX 的帮助文档 EasyX_Help.chm 之后,发现所有的内容点击后都是空白的,就像下面这样:

这是由于 Windows 的安全机制造成的,当你从网上直接下载一个 chm 文件,或者用 Windows 资源管理器从压缩包里面解压出来 chm 文件,都会被 Windows 认为目标文件不够安全,然后默认被锁定。

所以,只需要简单解锁就可以了。

这样做:右击 EasyX_Help.chm 文件,在属性页中,会看到底部的“安全”一项,“解除锁定”没有勾选,如下图:

只需要简单的勾选“解除锁定”,然后点击确定,就可以正常浏览 chm 文件了。

为什么屏幕分辨率是 640x480?

常见的屏幕分辨率很奇怪,为什么总用一些不零不整的数字?比如以前最常见的分辨率是 640x480,当初为什么这么设计?

因为这个分辨率运算速度比较快,人也相对比较好记。

大家知道,屏幕上的每一个点,都会对应内存中的一个位置。比如 640x480@256色,表示每个点占用一个字节,整个屏幕有 640x480=300k 个点,占用 300KB 内存。屏幕的每一个像素的保存,在内存中是线性存储结构。在屏幕上的坐标 (x, y) 画一个点,就相当于在对应显存偏移 y * 640 + x 的位置赋值。

PS:在这篇文章中有一段代码,可以实践一下通过内存赋值来直接画点:http://www.easyx.cn/skills/View.aspx?id=55

每绘制一个点,都需要执行一次乘法和一次加法运算。所有的绘图操作都需要调用画点函数,因此提高画点的效率,将会提升所有绘图操作的速度。

现在我们将计算内存偏移量的算法修改一下:

d = y * 6...

在 VC 下清空键盘缓冲区的方法

(以下代码在 VC 下通过)

控制台窗口是有输入缓冲区的,当你按键后程序没有来得及处理,系统会将按键缓存,等到程序获取按键的时候,系统会把缓冲区里面之前的按键返回。

例如这个程序:

#include <graphics.h>
#include <stdio.h>
#include <conio.h>

int main()
{
	printf("延时 5 秒钟。请在 5 秒钟内随便按键。\n");
	Sleep(5000);

	printf("延时结束。刚才您按下的按键是:\n");

	while (_kbhit())
	{
		char c = _getch();
		printf("%c", c);
	};

	return 0;
}

请自己执行一下前面的程序,在 5 秒延时内随便按键,等到延时结束,可以看到程序可以读出刚才的按键。

但是有些时候,

...

学习保存应用程序的配置信息

应用程序启动后,用户会有各种操作。当用户下一次启动应用程序时,就会希望保持上一次的操作状态。或者游戏结束后,需要将最高分记录等等。因此,我们需要在程序关闭的时候,将一些必要的变量保存下来,并且在下一次程序启动的时候,读取这些变量。

通常,可以把这些变量保存在“配置文件”中。配置文件一般以 .ini 为扩展名。例如在 \windows 文件夹里面可以找到很多 .ini 文件。.ini 文件的内容是纯文本的,可以直接双击打开编辑。举一个实际例子,下面是一个标准的 .ini 配置文件的内容:

; EasyX 范例
[Ball]
x=10
y=37
r=20
[Wall]
width=70
height=60

可以看到,配置文件的格式很简单,包括三种情况:

  1. 注释:写在半角分号后面;
  2. 节:用方括号括起来;
  3. 变量:用“键=值”这种形式记录,整形和字符串不做区分。
...

用 Visual C++ 编写可以在 Borland C++ 3.1 下编译执行的程序

由于某些学校的硬性规定,课设必须用 Borland C++ 3.1(以下简称 BC) 一类 DOS 下的编程工具完成。

但是,BC 用起来很费劲,毕竟是 dos 时代的工具,无论编辑还是调试,都很吃力。尤其是图形程序,由于涉及到硬件及系统的问题,导致程序不能在 win7 及以上版本的 Windows 中运行,必须借助 dosbox 一类工具或者虚拟机才可以,麻烦多多。

本文通过一些常用的技巧,讲解怎样用 Visual C++(以下简称 VC) 编写可以在 BC 下直接编译运行的程序。

这样做的好处就是,可以节约大量的编码和调试时间。众所周知,当程序规模比较大的时候,调试时间会超过编码时间。而 VC 的强大调试功能,远不是 BC 能比的。同时,windows 下便捷的编辑环境,也比 BC 能节约很多时间。再比如有些人的显示器可以旋转,将 24 寸的显示器旋转 90 度竖着用,一屏可以阅读上百行代码,这种效率,绝对不是 BC 那一屏 20 多行代码可以比的。即便 BC 启用 43 行模式,效果仍然很差。

所以,下面的这个

...

初学者必读:讲解 VC 下如何正确的创建及管理项目

本文讲解 Visual C++ 的项目文件组成,以及如何正确的创建及管理项目。

本文所设计的内容是初学者必须要掌握的。不能正确的管理项目,就不能进一步写有规模的程序。

一、项目下各种常见文件类型的功能

1. 代码文件

扩展名为 .cpp、.c、.h 等。

通常情况下,项目会由多个代码文件和头文件组成,每个文件实现不同的功能。

2. 资源文件

扩展名为 .rc 。

资源文件中定义了项目中所需的各种资源,例如图片、图标、对话框、声音等。项目中的资源引用情况均会记录在其中。有些资源会完整的记录在 .rc 里面,有些资源仅仅在 .rc 里面记录一个链接。

3. 项目管理文件

扩展名在不同版本的 VC 下有区别:VC6 为 .dsp,VC2008 / VC2010 为 .vcproj,VC2012 为 .vcxproj 。

项目管理文件管理所有代码、资源文件以及项目设置。

4. 工作区管理文件

...

正确处理鼠标消息(解决鼠标操作不灵敏的问题)

本文讲解怎样处理鼠标消息。编程思路有很多种,本文只是提供一种思路,并非一定要照这么做。

通常写小游戏时,很多人会有一个主循环,类似这样:

while(true)
{
	获取用户控制();

	进行游戏运算();

	绘制游戏内容();

	Sleep(xx);
}

当添加鼠标操作时,会这样写(错误代码):

// 定义变量,保存鼠标消息
MOUSEMSG msg;

// 游戏的主循环
while(true)
{
	if (MouseHit())				// 当有鼠标消息的时候执行
	{
		msg = GetMouseMsg();	// 获取鼠标消息

		switch(msg.uMsg)		// 根据不同的鼠标消息,执行不同的代码
		{
			case xxxx: 进行游戏运算(); break;
			case xxxx: 进行游戏运算...

实现桌面截图

本文讲解怎样截取整个桌面的图像。

对于控制台程序,执行后首先会创建一个黑色的控制台窗口,然后才会启动你的程序。因此这时候抓到的图,会包括一个黑色的控制台窗口,我想,这是大多数人不希望看到的。因此,实现抓图,要建立的项目需要是 Win32 Application,而不能是平时用的 Win32 Console Application。怎样创建一个 Win32 Application,请参考文章《在没有控制台的 Win32 Application 中使用 EasyX 绘图》。

[名词介绍]

DC:英文单词 Device Contexts(设备上下文) 的缩写。DC 中包含有用于该设备的一组绘图对象以及这些对象的属性(例如画笔、画刷等)。

[原理简介]

要获取桌面内容,先要获取桌面的 DC,然后再通过图像拷贝语句将桌面图像的内容拷贝到 IMAGE 的 DC 中。

[实现步骤]

因为实现起来简单,步骤已经以注释的形式写到了下面的示例代码中:

...

一步步拓展程序,实现文字在背景上的移动

本文适用读者:已经学完 C 语言基本知识,并对 EasyX 略有了解。

本文范例实现的功能:文字在背景上移动,并且不破坏背景。

本文范例编译平台:VC6 / VC2010 + EasyX 20140321(beta)。

本文并不直接写出最终代码,而是从最开始绘制背景,一步步拓展到我们需要的功能。希望这个过程能对初学者有所启发。

一、绘制背景

为了实现文字移动而不破坏背景的效果,我们先画个背景。做法很简单,就是画一连串的竖线,并且每次变换颜色,这样就能弄一个渐变色的背景,代码如下:

#include <graphics.h>
#include <conio.h>

// 主函数
void main()
{
	// 创建绘图窗口
	initgraph(640, 480);
	
	// 绘制背景
	for(int i = 0; i < 640; i++)
	{
		set...

怎样编写格式优美的代码

0. 序

很多初学者会被人一眼就看出来是初学者,为什么?原因很多。其中,代码格式是很重要的一项。不注重代码格式,不仅别人看起来不舒服,还会影响自己对代码的阅读。虽然错误的格式不会产生编译错误,但却会让你更容易犯错,并且更难排查错误。所以,学会编写格式优美的代码,是初学者必须要学的一课。

优秀的代码格式并不是唯一的,有很多种风格。本文仅讲解常见的 C/C++ 语言编码规则,而其它规则相信大家在学习工作中会慢慢接触到。

再有就是,很多网站为了便于阅读,都是比较注意代码格式的。例如,如果打算投稿到 CodeBus,看看本文也很有必要。

以下讲解编写代码时在格式上一些常见的注意事项。

1. 正确缩进

所谓缩进,是指在某一行代码的左端留出一部分空白。采用缩进的目的为了清楚的定义一个块的开始和结束,这样会使程序更加清晰易读。虽然目前主流的开发平台都具备智能缩进的功能,但是很多时候修改代码还是需要自己注意缩进问题。

下面的范例代码是求解 100 以内的质数,该范例中有多种缩进情况的例子:&

...

怎样产生随机方向(上下左右)?

比如坦克游戏,敌人坦克移动的四个方向通常不会用 1~4 这样四个数值表示,而是用 dx、dy 这样的 x、y 轴上的增量表示。对应关系如下:

dx =  0;  dy = -1; 表示向上
dx =  0;  dy =  1; 表示向下
dx = -1;  dy =  0; 表示向左
dx =  1;  dy =  0; 表示向右

如果用 1~4 表示 4 个方向,敌人坦克前进一步的代码要这样写:

...

同时执行两个或更多功能的方法

我留意到群里和贴吧有不少人都在问怎样同时执行两个过程,比如:

  1. 贪吃蛇游戏,怎样实现双人对抗?就是控制两条蛇移动。
  2. 俄罗斯方块游戏,要加一个时钟,怎样使时钟和游戏同时进行?
  3. 打砖块游戏,怎样实现一边让小球自由移动、一边实现玩家控制木板?

有些人会想到用多线程来解决。我这里首先要说:这个问题不要用多线程,多线程要解决是另一种问题,这里不再详述,本文只讲一下怎样实现前面说的问题。(当然不用多线程解决)

先说一个简单的程序要求:有一个变量 a,初值为 0,每秒钟增加 2,按任意键停止;还有一个变量 b,初值为 0,每秒钟增加 3。现在要求将这两个功能同时执行,该怎么做?

我相信很多人都能写出来这个简单的程序:

#include <stdio.h>
#include <conio.h>
#include <graphics.h>

void ...

禁用 EasyX 绘图窗口关闭按钮的方法(控制程序流程)

目前,点 EasyX 绘图窗口的“关闭”,会直接退出整个程序。有时候,程序需要执行完整的流程,因此,禁止关闭按钮就是一个很需要的方法。

可以通过 initgraph 函数的第三个参数禁用关闭按钮,确保程序按实际流程完整执行。

以下代码禁用关闭按钮(VC6 / VC2010 编译通过):

#include <graphics.h>
#include <conio.h>

void main()
{
	initgraph(640, 480, NOCLOSE);

	circle(320, 240, 100);
	
	_getch();
	closegraph();
}