张银峰的编程课堂

完善:用色彩提升信息的辨识度

统一样式的输出不能让我们轻松识别出可用的信息。比如用户会非常关注那些清理失败的文件,但目前在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;
}

glimix.com

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

glimix.com

这个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;
}

至此,我们这个小工具算是打造完成了!