完善:用色彩提升信息的辨识度
统一样式的输出不能让我们轻松识别出可用的信息。比如用户会非常关注那些清理失败的文件,但目前在ccleaner中并不能做到一眼定位。系统提供了一些控制台相关的API,可以让我们对其进行个性化定制,比如设置窗口的大小,标题、文本属性等。利用它们就可以制作出一个设置文本色的函数,这里我们只需要将其当作黑盒使用。
/**
* 设置文本色
* color: 文本色索引
*/
const int gray = 7;
const int red = 4;
static void set_textcolor(int color)
{
static HANDLE handle = NULL;
static int curclr = 0;
if (handle == NULL)
{
handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(handle, gray);
}
if (color != curclr)
{
curclr = color;
SetConsoleTextAttribute(handle, curclr);
}
}
函数GetStdHandle获得标准输出句柄,这个操作只需要进行一次,因此我们使用一个static变量。当函数第一次执行了,程序获得此句柄,然后调用函数SetConsoleTextAttribute设置文本为灰白色(gray),此后你可以传递一个0-255间的数值,改变当前输出色,直到再次更改。现在只需根据删除文件的状态值,选择一个输出色,这微小的改动让我们的程序增色不少。
static bool delete_file(const char *path, bool isdir)
{
bool state;
if (!isdir)
state = remove(path) == 0 ? true : false;
else
state = _rmdir(path) == 0 ? true : false;
set_textcolor(state ? gray : red);
printf(state ? "[OK] " : "[ER] ");
printf("%s\n", path);
return state;
}

你可能没有留意上面所说的,“改变当前输出色,直到再次更改”。这意味着,如果恰好最后一个文件删除失败了,那会造成我们的报告清单也会以红色输出。

这个BUG看似很好解决:只需要在输出报告前重置颜色即可。但是set_textcolor并不是接口函数,我们无法在main.c中访问它!这有何难?只需要将它声明为接口就行了。但是,从模块的角度讲,这是ccleaner模块应该提供的功能吗?既然美化致使ccleaner内聚了一个外观相关的功能,那为什么不将它以另一个模块的方式提供呢?如此一来,平台相关的细节被封装了起来,独立的模块,也使得复用更加简单,这就是我们提供console模块的动机。
/**
* console.h
* 控制台功能封装
*/
// 预定义文本色彩方案
#define RED 4
#define GRAY 7
/**
* 控制台初始化
*/
int console_init();
/**
* 设置文本色彩方案
* 此设置是全局性的,后继所有输出将使用当前色彩方案。
* color: 新的文本色彩方案,取值在[0-255]之间。
*/
void set_text_color(int color);
/**
* 重置文本色彩方案为预设值
* 此设置是全局性的,后继所有输出将使用当前色彩方案。
*/
void reset_text_color();
/**
* 控制台退出
*/
void console_exit();
最终结构上,由主函数驱动两个模块,ccleaner引用console模块,辅助完成内部实现。主函数内部在输出报告之前,使用reset_text_color恢复清单的文本色。
int main(int argc, char *argv[])
{
//...
console_init();
//...
ccleaner_config(suffix, rmdir);
ccleaner_run(cpath);
ccleaner_result(NULL, &mcnt, &dcnt, &ncnt);
reset_text_color();
//...
printf("C.C.L.E.A.N.E.R\n");
//...
console_exit();
return 0;
}
至此,我们这个小工具算是打造完成了!