65.9K
CodeProject 正在变化。 阅读更多。
Home

用于处理注册表的模板类集合

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (30投票s)

2002年7月4日

3分钟阅读

viewsIcon

172276

downloadIcon

1084

使用模板和C++标准库编写泛型代码。

又一个注册表类!?但为什么?

我最近在 CSDN 上读了一篇文章,其中描述了一组用于操作注册表的类。必然会出现对模板版本的类的请求。该文章的作者有几个不编写模板版本的原因。经过一番思考,我得出的结论是确实可以创建一个模板。所以,它就在这里。

用法

这个类实际上是三个类。但是,我并没有为每种要存储的类型创建一个类,而是为注册表支持的每种数据类型创建了一个模板类。嗯,不完全是。提供了用于字符串、dword 和二进制存储的类。我认为这三者涵盖了大多数你想存储在注册表中的内容。

所以,假设我们想存储一个字符串

typedef registry_string<std::string> regstring;

regstring str("Software\\RegValueTest\\blah", HKEY_CURRENT_USER);

str = "Hello world!";
std::string stored = str;
std::cout << stored << std::endl;
首先是一个方便的类型别名。我们的模板类型是 std::string,这是我们希望加载或存储的类型。因为我们使用了 registry_string 模板,所以字符串将以 REG_SZ 类型存储在注册表中。存储就像赋值给 registring 对象一样简单。从注册表中读取也是这样执行的,只需创建一个正确类型的对象并从中赋值 regstring。这一切都归功于 operator= 和 operator T(转换运算符)。如果有人不喜欢使用转换运算符(它们并不是世界上最好的东西),那么只需更改函数名称就足够了。

存储一个 POINT

typedef registry_binary<POINT> regpoint;

regpoint pnt("Software\\RegValueTest\\blah", HKEY_CURRENT_USER);

POINT p = {99,88};
pnt = p;
POINT stored = p;
std::cout << stored.x << "," << stored.y << std::endl;
最后一个类,registry_int,与另外两个类的工作方式相同,所以应该不需要示例。这三个类可以用来存储大量的类。一些例子
typedef registry_string<std::string>    regstring;
typedef registry_int<bool>              regbool;
typedef registry_binary<RECT>           regRECT;
typedef registry_string<double>         regdouble;

在 registry_string 中存储一个 double?

registry_string 模板类非常方便。为了能够将任意类型存储在字符串中,使用了 std::stringstream。这使得该模板类可以与任何可以将其自身序列化到流的类型一起使用。这包括内置类型; booldouble char 等。 registry_string<double> 将用户希望存储的 double 转换为该值的文本表示。这可能比存储数字的二进制版本更好(但如果你想以二进制形式存储它,那么制作一个 registry_binary<double> 就很简单了)。

这些类还能够做一些其他有用的事情

registry_value val("Software\\RegValueTest\\blah", HKEY_CURRENT_USER);

if(val.exists())
{
    val.remove_value();
    val.remove_key();
}
这将检查该值是否存在,如果存在,则删除该值和键。registry_value 类是其他三个类的基类。更多内容将在下一节中介绍。

实现

我将所有三个类都使用的函数放在一个公共基类 registry_value 中。这是该类的一个简化概述
class registry_value
{
    public:
        registry_value(const std::string & name, HKEY base_);

        bool exists();
        void remove_value(void);
        void remove_key(void);

    protected:

        bool open(bool write_access);
        void close(void);
        bool query_value(DWORD * type, DWORD * size, void * buffer);
        bool set_value(DWORD type, DWORD size, const void * buffer);

    private:

        void make_path(const std::string & name);

        HKEY key;
        HKEY base;

        std::string valuename;
        std::string keyname;
};

这里没有什么特别的。公共接口由上面描述的三个函数组成。受保护的接口包含用于访问键以及读取/写入的函数。私有部分包括一个实用函数和其他函数使用的数据。如上所述,该类本身也可以用于检查值是否存在等。

显而易见, registry_value 不是一个模板类。让我们仔细看看 registry_binary

template<class T>
class registry_binary : public registry_value
{
    public:
        registry_binary(const std::string & name, HKEY base) : 
            registry_value(name, base)
        {
        }
        
构造函数只是将参数转发给基类。
operator T()
{
    T returnval = T();

    if(open(false) == true)
    {
        DWORD type, size = sizeof(T);
        if(query_value(&type, &size, &returnval) == true)
        {
            assert(type == REG_BINARY);
        }

        close();
    }

    return returnval;
}
转换运算符处理从注册表中读取。这里没有什么特别值得注意的,除了初始化我们存储数据的那个值。之所以使用 '= T();',是因为它确保 returnval 不包含随机垃圾数据,因为如果查询出现问题,该值将未更改地从函数返回。
const registry_binary & operator=(const T & value)
{
    if(open(true) == true)
    {
        set_value(REG_BINARY, sizeof(T), &value);
        close();
    }

    return *this;
}
operator= 处理写入注册表。 registry_value 中定义的 set_value 函数需要一个类型、一个大小和一个指向数据的指针。这就是 registry_binary 的全部内容。

结束语

这篇文章更多的是展示模板如何用于以更少的代码获得更多功能,而不是关于注册表访问本身。

顺便说一句:还有一个 registry_string<std::string> 的特化版本,它省略了 registry_string 通常使用的 stringstream 代码。

© . All rights reserved.